[ Index ] |
PHP Cross Reference of phpBB-3.1.12-deutsch |
[Summary view] [Print] [Text view]
1 /* global bbfontstyle */ 2 3 var phpbb = {}; 4 phpbb.alertTime = 100; 5 6 (function($) { // Avoid conflicts with other libraries 7 8 'use strict'; 9 10 // define a couple constants for keydown functions. 11 var keymap = { 12 TAB: 9, 13 ENTER: 13, 14 ESC: 27 15 }; 16 17 var $dark = $('#darkenwrapper'); 18 var $loadingIndicator; 19 var phpbbAlertTimer = null; 20 21 phpbb.isTouch = (window && typeof window.ontouchstart !== 'undefined'); 22 23 /** 24 * Display a loading screen 25 * 26 * @returns {object} Returns loadingIndicator. 27 */ 28 phpbb.loadingIndicator = function() { 29 if (!$loadingIndicator) { 30 $loadingIndicator = $('<div />', { id: 'loading_indicator' }); 31 $loadingIndicator.appendTo('#page-footer'); 32 } 33 34 if (!$loadingIndicator.is(':visible')) { 35 $loadingIndicator.fadeIn(phpbb.alertTime); 36 // Wait 60 seconds and display an error if nothing has been returned by then. 37 phpbb.clearLoadingTimeout(); 38 phpbbAlertTimer = setTimeout(function() { 39 phpbb.showTimeoutMessage(); 40 }, 60000); 41 } 42 43 return $loadingIndicator; 44 }; 45 46 /** 47 * Show timeout message 48 */ 49 phpbb.showTimeoutMessage = function () { 50 var $alert = $('#phpbb_alert'); 51 52 if ($loadingIndicator.is(':visible')) { 53 phpbb.alert($alert.attr('data-l-err'), $alert.attr('data-l-timeout-processing-req')); 54 } 55 }; 56 57 /** 58 * Clear loading alert timeout 59 */ 60 phpbb.clearLoadingTimeout = function() { 61 if (phpbbAlertTimer !== null) { 62 clearTimeout(phpbbAlertTimer); 63 phpbbAlertTimer = null; 64 } 65 }; 66 67 68 /** 69 * Close popup alert after a specified delay 70 * 71 * @param {int} delay Delay in ms until darkenwrapper's click event is triggered 72 */ 73 phpbb.closeDarkenWrapper = function(delay) { 74 phpbbAlertTimer = setTimeout(function() { 75 $('#darkenwrapper').trigger('click'); 76 }, delay); 77 }; 78 79 /** 80 * Display a simple alert similar to JSs native alert(). 81 * 82 * You can only call one alert or confirm box at any one time. 83 * 84 * @param {string} title Title of the message, eg "Information" (HTML). 85 * @param {string} msg Message to display (HTML). 86 * 87 * @returns {object} Returns the div created. 88 */ 89 phpbb.alert = function(title, msg) { 90 var $alert = $('#phpbb_alert'); 91 $alert.find('.alert_title').html(title); 92 $alert.find('.alert_text').html(msg); 93 94 $(document).on('keydown.phpbb.alert', function(e) { 95 if (e.keyCode === keymap.ENTER || e.keyCode === keymap.ESC) { 96 phpbb.alert.close($alert, true); 97 e.preventDefault(); 98 e.stopPropagation(); 99 } 100 }); 101 phpbb.alert.open($alert); 102 103 return $alert; 104 }; 105 106 /** 107 * Handler for opening an alert box. 108 * 109 * @param {jQuery} $alert jQuery object. 110 */ 111 phpbb.alert.open = function($alert) { 112 if (!$dark.is(':visible')) { 113 $dark.fadeIn(phpbb.alertTime); 114 } 115 116 if ($loadingIndicator && $loadingIndicator.is(':visible')) { 117 $loadingIndicator.fadeOut(phpbb.alertTime, function() { 118 $dark.append($alert); 119 $alert.fadeIn(phpbb.alertTime); 120 }); 121 } else if ($dark.is(':visible')) { 122 $dark.append($alert); 123 $alert.fadeIn(phpbb.alertTime); 124 } else { 125 $dark.append($alert); 126 $alert.show(); 127 $dark.fadeIn(phpbb.alertTime); 128 } 129 130 $alert.on('click', function(e) { 131 e.stopPropagation(); 132 }); 133 134 $dark.one('click', function(e) { 135 phpbb.alert.close($alert, true); 136 e.preventDefault(); 137 e.stopPropagation(); 138 }); 139 140 $alert.find('.alert_close').one('click', function(e) { 141 phpbb.alert.close($alert, true); 142 e.preventDefault(); 143 }); 144 }; 145 146 /** 147 * Handler for closing an alert box. 148 * 149 * @param {jQuery} $alert jQuery object. 150 * @param {bool} fadedark Whether to remove dark background. 151 */ 152 phpbb.alert.close = function($alert, fadedark) { 153 var $fade = (fadedark) ? $dark : $alert; 154 155 $fade.fadeOut(phpbb.alertTime, function() { 156 $alert.hide(); 157 }); 158 159 $alert.find('.alert_close').off('click'); 160 $(document).off('keydown.phpbb.alert'); 161 }; 162 163 /** 164 * Display a simple yes / no box to the user. 165 * 166 * You can only call one alert or confirm box at any one time. 167 * 168 * @param {string} msg Message to display (HTML). 169 * @param {function} callback Callback. Bool param, whether the user pressed 170 * yes or no (or whatever their language is). 171 * @param {bool} fadedark Remove the dark background when done? Defaults 172 * to yes. 173 * 174 * @returns {object} Returns the div created. 175 */ 176 phpbb.confirm = function(msg, callback, fadedark) { 177 var $confirmDiv = $('#phpbb_confirm'); 178 $confirmDiv.find('.alert_text').html(msg); 179 fadedark = fadedark || true; 180 181 $(document).on('keydown.phpbb.alert', function(e) { 182 if (e.keyCode === keymap.ENTER || e.keyCode === keymap.ESC) { 183 var name = (e.keyCode === keymap.ENTER) ? 'confirm' : 'cancel'; 184 185 $('input[name="' + name + '"]').trigger('click'); 186 e.preventDefault(); 187 e.stopPropagation(); 188 } 189 }); 190 191 $confirmDiv.find('input[type="button"]').one('click.phpbb.confirmbox', function(e) { 192 var confirmed = this.name === 'confirm'; 193 194 if (confirmed) { 195 callback(true); 196 } 197 $confirmDiv.find('input[type="button"]').off('click.phpbb.confirmbox'); 198 phpbb.alert.close($confirmDiv, fadedark || !confirmed); 199 200 e.preventDefault(); 201 e.stopPropagation(); 202 }); 203 204 phpbb.alert.open($confirmDiv); 205 206 return $confirmDiv; 207 }; 208 209 /** 210 * Turn a querystring into an array. 211 * 212 * @argument {string} string The querystring to parse. 213 * @returns {object} The object created. 214 */ 215 phpbb.parseQuerystring = function(string) { 216 var params = {}, i, split; 217 218 string = string.split('&'); 219 for (i = 0; i < string.length; i++) { 220 split = string[i].split('='); 221 params[split[0]] = decodeURIComponent(split[1]); 222 } 223 return params; 224 }; 225 226 227 /** 228 * Makes a link use AJAX instead of loading an entire page. 229 * 230 * This function will work for links (both standard links and links which 231 * invoke confirm_box) and forms. It will be called automatically for links 232 * and forms with the data-ajax attribute set, and will call the necessary 233 * callback. 234 * 235 * For more info, view the following page on the phpBB wiki: 236 * http://wiki.phpbb.com/JavaScript_Function.phpbb.ajaxify 237 * 238 * @param {object} options Options. 239 */ 240 phpbb.ajaxify = function(options) { 241 var $elements = $(options.selector), 242 refresh = options.refresh, 243 callback = options.callback, 244 overlay = (typeof options.overlay !== 'undefined') ? options.overlay : true, 245 isForm = $elements.is('form'), 246 isText = $elements.is('input[type="text"], textarea'), 247 eventName; 248 249 if (isForm) { 250 eventName = 'submit'; 251 } else if (isText) { 252 eventName = 'keyup'; 253 } else { 254 eventName = 'click'; 255 } 256 257 $elements.on(eventName, function(event) { 258 var action, method, data, submit, that = this, $this = $(this); 259 260 if ($this.find('input[type="submit"][data-clicked]').attr('data-ajax') === 'false') { 261 return; 262 } 263 264 /** 265 * Handler for AJAX errors 266 */ 267 function errorHandler(jqXHR, textStatus, errorThrown) { 268 if (typeof console !== 'undefined' && console.log) { 269 console.log('AJAX error. status: ' + textStatus + ', message: ' + errorThrown); 270 } 271 phpbb.clearLoadingTimeout(); 272 var responseText, errorText = false; 273 try { 274 responseText = JSON.parse(jqXHR.responseText); 275 responseText = responseText.message; 276 } catch (e) {} 277 if (typeof responseText === 'string' && responseText.length > 0) { 278 errorText = responseText; 279 } else if (typeof errorThrown === 'string' && errorThrown.length > 0) { 280 errorText = errorThrown; 281 } else { 282 errorText = $dark.attr('data-ajax-error-text-' + textStatus); 283 if (typeof errorText !== 'string' || !errorText.length) { 284 errorText = $dark.attr('data-ajax-error-text'); 285 } 286 } 287 phpbb.alert($dark.attr('data-ajax-error-title'), errorText); 288 } 289 290 /** 291 * This is a private function used to handle the callbacks, refreshes 292 * and alert. It calls the callback, refreshes the page if necessary, and 293 * displays an alert to the user and removes it after an amount of time. 294 * 295 * It cannot be called from outside this function, and is purely here to 296 * avoid repetition of code. 297 * 298 * @param {object} res The object sent back by the server. 299 */ 300 function returnHandler(res) { 301 var alert; 302 303 phpbb.clearLoadingTimeout(); 304 305 // Is a confirmation required? 306 if (typeof res.S_CONFIRM_ACTION === 'undefined') { 307 // If a confirmation is not required, display an alert and call the 308 // callbacks. 309 if (typeof res.MESSAGE_TITLE !== 'undefined') { 310 alert = phpbb.alert(res.MESSAGE_TITLE, res.MESSAGE_TEXT); 311 } else { 312 $dark.fadeOut(phpbb.alertTime); 313 314 if ($loadingIndicator) { 315 $loadingIndicator.fadeOut(phpbb.alertTime); 316 } 317 } 318 319 if (typeof phpbb.ajaxCallbacks[callback] === 'function') { 320 phpbb.ajaxCallbacks[callback].call(that, res); 321 } 322 323 // If the server says to refresh the page, check whether the page should 324 // be refreshed and refresh page after specified time if required. 325 if (res.REFRESH_DATA) { 326 if (typeof refresh === 'function') { 327 refresh = refresh(res.REFRESH_DATA.url); 328 } else if (typeof refresh !== 'boolean') { 329 refresh = false; 330 } 331 332 phpbbAlertTimer = setTimeout(function() { 333 if (refresh) { 334 window.location = res.REFRESH_DATA.url; 335 } 336 337 // Hide the alert even if we refresh the page, in case the user 338 // presses the back button. 339 $dark.fadeOut(phpbb.alertTime, function() { 340 if (typeof alert !== 'undefined') { 341 alert.hide(); 342 } 343 }); 344 }, res.REFRESH_DATA.time * 1000); // Server specifies time in seconds 345 } 346 } else { 347 // If confirmation is required, display a dialog to the user. 348 phpbb.confirm(res.MESSAGE_BODY, function(del) { 349 if (!del) { 350 return; 351 } 352 353 phpbb.loadingIndicator(); 354 data = $('<form>' + res.S_HIDDEN_FIELDS + '</form>').serialize(); 355 $.ajax({ 356 url: res.S_CONFIRM_ACTION, 357 type: 'POST', 358 data: data + '&confirm=' + res.YES_VALUE + '&' + $('form', '#phpbb_confirm').serialize(), 359 success: returnHandler, 360 error: errorHandler 361 }); 362 }, false); 363 } 364 } 365 366 // If the element is a form, POST must be used and some extra data must 367 // be taken from the form. 368 var runFilter = (typeof options.filter === 'function'); 369 data = {}; 370 371 if (isForm) { 372 action = $this.attr('action').replace('&', '&'); 373 data = $this.serializeArray(); 374 method = $this.attr('method') || 'GET'; 375 376 if ($this.find('input[type="submit"][data-clicked]')) { 377 submit = $this.find('input[type="submit"][data-clicked]'); 378 data.push({ 379 name: submit.attr('name'), 380 value: submit.val() 381 }); 382 } 383 } else if (isText) { 384 var name = $this.attr('data-name') || this.name; 385 action = $this.attr('data-url').replace('&', '&'); 386 data[name] = this.value; 387 method = 'POST'; 388 } else { 389 action = this.href; 390 data = null; 391 method = 'GET'; 392 } 393 394 var sendRequest = function() { 395 var dataOverlay = $this.attr('data-overlay'); 396 if (overlay && (typeof dataOverlay === 'undefined' || dataOverlay === 'true')) { 397 phpbb.loadingIndicator(); 398 } 399 400 var request = $.ajax({ 401 url: action, 402 type: method, 403 data: data, 404 success: returnHandler, 405 error: errorHandler, 406 cache: false 407 }); 408 409 request.always(function() { 410 if ($loadingIndicator && $loadingIndicator.is(':visible')) { 411 $loadingIndicator.fadeOut(phpbb.alertTime); 412 } 413 }); 414 }; 415 416 // If filter function returns false, cancel the AJAX functionality, 417 // and return true (meaning that the HTTP request will be sent normally). 418 if (runFilter && !options.filter.call(this, data, event, sendRequest)) { 419 return; 420 } 421 422 sendRequest(); 423 event.preventDefault(); 424 }); 425 426 if (isForm) { 427 $elements.find('input:submit').click(function () { 428 var $this = $(this); 429 430 // Remove data-clicked attribute from any submit button of form 431 $this.parents('form:first').find('input:submit[data-clicked]').removeAttr('data-clicked'); 432 433 $this.attr('data-clicked', 'true'); 434 }); 435 } 436 437 return this; 438 }; 439 440 phpbb.search = { 441 cache: { 442 data: [] 443 }, 444 tpl: [], 445 container: [] 446 }; 447 448 /** 449 * Get cached search data. 450 * 451 * @param {string} id Search ID. 452 * @returns {bool|object} Cached data object. Returns false if no data exists. 453 */ 454 phpbb.search.cache.get = function(id) { 455 if (this.data[id]) { 456 return this.data[id]; 457 } 458 return false; 459 }; 460 461 /** 462 * Set search cache data value. 463 * 464 * @param {string} id Search ID. 465 * @param {string} key Data key. 466 * @param {string} value Data value. 467 */ 468 phpbb.search.cache.set = function(id, key, value) { 469 if (!this.data[id]) { 470 this.data[id] = { results: [] }; 471 } 472 this.data[id][key] = value; 473 }; 474 475 /** 476 * Cache search result. 477 * 478 * @param {string} id Search ID. 479 * @param {string} keyword Keyword. 480 * @param {Array} results Search results. 481 */ 482 phpbb.search.cache.setResults = function(id, keyword, results) { 483 this.data[id].results[keyword] = results; 484 }; 485 486 /** 487 * Trim spaces from keyword and lower its case. 488 * 489 * @param {string} keyword Search keyword to clean. 490 * @returns {string} Cleaned string. 491 */ 492 phpbb.search.cleanKeyword = function(keyword) { 493 return $.trim(keyword).toLowerCase(); 494 }; 495 496 /** 497 * Get clean version of search keyword. If textarea supports several keywords 498 * (one per line), it fetches the current keyword based on the caret position. 499 * 500 * @param {jQuery} $input Search input|textarea. 501 * @param {string} keyword Input|textarea value. 502 * @param {bool} multiline Whether textarea supports multiple search keywords. 503 * 504 * @returns string Clean string. 505 */ 506 phpbb.search.getKeyword = function($input, keyword, multiline) { 507 if (multiline) { 508 var line = phpbb.search.getKeywordLine($input); 509 keyword = keyword.split('\n').splice(line, 1); 510 } 511 return phpbb.search.cleanKeyword(keyword); 512 }; 513 514 /** 515 * Get the textarea line number on which the keyword resides - for textareas 516 * that support multiple keywords (one per line). 517 * 518 * @param {jQuery} $textarea Search textarea. 519 * @returns {int} The line number. 520 */ 521 phpbb.search.getKeywordLine = function ($textarea) { 522 var selectionStart = $textarea.get(0).selectionStart; 523 return $textarea.val().substr(0, selectionStart).split('\n').length - 1; 524 }; 525 526 /** 527 * Set the value on the input|textarea. If textarea supports multiple 528 * keywords, only the active keyword is replaced. 529 * 530 * @param {jQuery} $input Search input|textarea. 531 * @param {string} value Value to set. 532 * @param {bool} multiline Whether textarea supports multiple search keywords. 533 */ 534 phpbb.search.setValue = function($input, value, multiline) { 535 if (multiline) { 536 var line = phpbb.search.getKeywordLine($input), 537 lines = $input.val().split('\n'); 538 lines[line] = value; 539 value = lines.join('\n'); 540 } 541 $input.val(value); 542 }; 543 544 /** 545 * Sets the onclick event to set the value on the input|textarea to the 546 * selected search result. 547 * 548 * @param {jQuery} $input Search input|textarea. 549 * @param {object} value Result object. 550 * @param {jQuery} $row Result element. 551 * @param {jQuery} $container jQuery object for the search container. 552 */ 553 phpbb.search.setValueOnClick = function($input, value, $row, $container) { 554 $row.click(function() { 555 phpbb.search.setValue($input, value.result, $input.attr('data-multiline')); 556 $container.hide(); 557 }); 558 }; 559 560 /** 561 * Runs before the AJAX search request is sent and determines whether 562 * there is a need to contact the server. If there are cached results 563 * already, those are displayed instead. Executes the AJAX request function 564 * itself due to the need to use a timeout to limit the number of requests. 565 * 566 * @param {Array} data Data to be sent to the server. 567 * @param {object} event Onkeyup event object. 568 * @param {function} sendRequest Function to execute AJAX request. 569 * 570 * @returns {bool} Returns false. 571 */ 572 phpbb.search.filter = function(data, event, sendRequest) { 573 var $this = $(this), 574 dataName = ($this.attr('data-name') !== undefined) ? $this.attr('data-name') : $this.attr('name'), 575 minLength = parseInt($this.attr('data-min-length'), 10), 576 searchID = $this.attr('data-results'), 577 keyword = phpbb.search.getKeyword($this, data[dataName], $this.attr('data-multiline')), 578 cache = phpbb.search.cache.get(searchID), 579 proceed = true; 580 data[dataName] = keyword; 581 582 if (cache.timeout) { 583 clearTimeout(cache.timeout); 584 } 585 586 var timeout = setTimeout(function() { 587 // Check min length and existence of cache. 588 if (minLength > keyword.length) { 589 proceed = false; 590 } else if (cache.lastSearch) { 591 // Has the keyword actually changed? 592 if (cache.lastSearch === keyword) { 593 proceed = false; 594 } else { 595 // Do we already have results for this? 596 if (cache.results[keyword]) { 597 var response = { 598 keyword: keyword, 599 results: cache.results[keyword] 600 }; 601 phpbb.search.handleResponse(response, $this, true); 602 proceed = false; 603 } 604 605 // If the previous search didn't yield results and the string only had characters added to it, 606 // then we won't bother sending a request. 607 if (keyword.indexOf(cache.lastSearch) === 0 && cache.results[cache.lastSearch].length === 0) { 608 phpbb.search.cache.set(searchID, 'lastSearch', keyword); 609 phpbb.search.cache.setResults(searchID, keyword, []); 610 proceed = false; 611 } 612 } 613 } 614 615 if (proceed) { 616 sendRequest.call(this); 617 } 618 }, 350); 619 phpbb.search.cache.set(searchID, 'timeout', timeout); 620 621 return false; 622 }; 623 624 /** 625 * Handle search result response. 626 * 627 * @param {object} res Data received from server. 628 * @param {jQuery} $input Search input|textarea. 629 * @param {bool} fromCache Whether the results are from the cache. 630 * @param {function} callback Optional callback to run when assigning each search result. 631 */ 632 phpbb.search.handleResponse = function(res, $input, fromCache, callback) { 633 if (typeof res !== 'object') { 634 return; 635 } 636 637 var searchID = $input.attr('data-results'), 638 $container = $(searchID); 639 640 if (this.cache.get(searchID).callback) { 641 callback = this.cache.get(searchID).callback; 642 } else if (typeof callback === 'function') { 643 this.cache.set(searchID, 'callback', callback); 644 } 645 646 if (!fromCache) { 647 this.cache.setResults(searchID, res.keyword, res.results); 648 } 649 650 this.cache.set(searchID, 'lastSearch', res.keyword); 651 this.showResults(res.results, $input, $container, callback); 652 }; 653 654 /** 655 * Show search results. 656 * 657 * @param {Array} results Search results. 658 * @param {jQuery} $input Search input|textarea. 659 * @param {jQuery} $container Search results container element. 660 * @param {function} callback Optional callback to run when assigning each search result. 661 */ 662 phpbb.search.showResults = function(results, $input, $container, callback) { 663 var $resultContainer = $('.search-results', $container); 664 this.clearResults($resultContainer); 665 666 if (!results.length) { 667 $container.hide(); 668 return; 669 } 670 671 var searchID = $container.attr('id'), 672 tpl, 673 row; 674 675 if (!this.tpl[searchID]) { 676 tpl = $('.search-result-tpl', $container); 677 this.tpl[searchID] = tpl.clone().removeClass('search-result-tpl'); 678 tpl.remove(); 679 } 680 tpl = this.tpl[searchID]; 681 682 $.each(results, function(i, item) { 683 row = tpl.clone(); 684 row.find('.search-result').html(item.display); 685 686 if (typeof callback === 'function') { 687 callback.call(this, $input, item, row, $container); 688 } 689 row.appendTo($resultContainer).show(); 690 }); 691 $container.show(); 692 }; 693 694 /** 695 * Clear search results. 696 * 697 * @param {jQuery} $container Search results container. 698 */ 699 phpbb.search.clearResults = function($container) { 700 $container.children(':not(.search-result-tpl)').remove(); 701 }; 702 703 $('#phpbb').click(function() { 704 var $this = $(this); 705 706 if (!$this.is('.live-search') && !$this.parents().is('.live-search')) { 707 $('.live-search').hide(); 708 } 709 }); 710 711 phpbb.history = {}; 712 713 /** 714 * Check whether a method in the native history object is supported. 715 * 716 * @param {string} fn Method name. 717 * @returns {bool} Returns true if the method is supported. 718 */ 719 phpbb.history.isSupported = function(fn) { 720 return !(typeof history === 'undefined' || typeof history[fn] === 'undefined'); 721 }; 722 723 /** 724 * Wrapper for the pushState and replaceState methods of the 725 * native history object. 726 * 727 * @param {string} mode Mode. Either push or replace. 728 * @param {string} url New URL. 729 * @param {string} [title] Optional page title. 730 * @param {object} [obj] Optional state object. 731 */ 732 phpbb.history.alterUrl = function(mode, url, title, obj) { 733 var fn = mode + 'State'; 734 735 if (!url || !phpbb.history.isSupported(fn)) { 736 return; 737 } 738 if (!title) { 739 title = document.title; 740 } 741 if (!obj) { 742 obj = null; 743 } 744 745 history[fn](obj, title, url); 746 }; 747 748 /** 749 * Wrapper for the native history.replaceState method. 750 * 751 * @param {string} url New URL. 752 * @param {string} [title] Optional page title. 753 * @param {object} [obj] Optional state object. 754 */ 755 phpbb.history.replaceUrl = function(url, title, obj) { 756 phpbb.history.alterUrl('replace', url, title, obj); 757 }; 758 759 /** 760 * Wrapper for the native history.pushState method. 761 * 762 * @param {string} url New URL. 763 * @param {string} [title] Optional page title. 764 * @param {object} [obj] Optional state object. 765 */ 766 phpbb.history.pushUrl = function(url, title, obj) { 767 phpbb.history.alterUrl('push', url, title, obj); 768 }; 769 770 /** 771 * Hide the optgroups that are not the selected timezone 772 * 773 * @param {bool} keepSelection Shall we keep the value selected, or shall the 774 * user be forced to repick one. 775 */ 776 phpbb.timezoneSwitchDate = function(keepSelection) { 777 var $timezoneCopy = $('#timezone_copy'); 778 var $timezone = $('#timezone'); 779 var $tzDate = $('#tz_date'); 780 var $tzSelectDateSuggest = $('#tz_select_date_suggest'); 781 782 if ($timezoneCopy.length === 0) { 783 // We make a backup of the original dropdown, so we can remove optgroups 784 // instead of setting display to none, because IE and chrome will not 785 // hide options inside of optgroups and selects via css 786 $timezone.clone() 787 .attr('id', 'timezone_copy') 788 .css('display', 'none') 789 .attr('name', 'tz_copy') 790 .insertAfter('#timezone'); 791 } else { 792 // Copy the content of our backup, so we can remove all unneeded options 793 $timezone.html($timezoneCopy.html()); 794 } 795 796 if ($tzDate.val() !== '') { 797 $timezone.children('optgroup').remove(':not([data-tz-value="' + $tzDate.val() + '"])'); 798 } 799 800 if ($tzDate.val() === $tzSelectDateSuggest.attr('data-suggested-tz')) { 801 $tzSelectDateSuggest.css('display', 'none'); 802 } else { 803 $tzSelectDateSuggest.css('display', 'inline'); 804 } 805 806 var $tzOptions = $timezone.children('optgroup[data-tz-value="' + $tzDate.val() + '"]').children('option'); 807 808 if ($tzOptions.length === 1) { 809 // If there is only one timezone for the selected date, we just select that automatically. 810 $tzOptions.prop('selected', true); 811 keepSelection = true; 812 } 813 814 if (typeof keepSelection !== 'undefined' && !keepSelection) { 815 var $timezoneOptions = $timezone.find('optgroup option'); 816 if ($timezoneOptions.filter(':selected').length <= 0) { 817 $timezoneOptions.filter(':first').prop('selected', true); 818 } 819 } 820 }; 821 822 /** 823 * Display the date/time select 824 */ 825 phpbb.timezoneEnableDateSelection = function() { 826 $('#tz_select_date').css('display', 'block'); 827 }; 828 829 /** 830 * Preselect a date/time or suggest one, if it is not picked. 831 * 832 * @param {bool} forceSelector Shall we select the suggestion? 833 */ 834 phpbb.timezonePreselectSelect = function(forceSelector) { 835 836 // The offset returned here is in minutes and negated. 837 var offset = (new Date()).getTimezoneOffset(); 838 var sign = '-'; 839 840 if (offset < 0) { 841 sign = '+'; 842 offset = -offset; 843 } 844 845 var minutes = offset % 60; 846 var hours = (offset - minutes) / 60; 847 848 if (hours < 10) { 849 hours = '0' + hours.toString(); 850 } else { 851 hours = hours.toString(); 852 } 853 854 if (minutes < 10) { 855 minutes = '0' + minutes.toString(); 856 } else { 857 minutes = minutes.toString(); 858 } 859 860 var prefix = 'UTC' + sign + hours + ':' + minutes; 861 var prefixLength = prefix.length; 862 var selectorOptions = $('option', '#tz_date'); 863 var i; 864 865 var $tzSelectDateSuggest = $('#tz_select_date_suggest'); 866 867 for (i = 0; i < selectorOptions.length; ++i) { 868 var option = selectorOptions[i]; 869 870 if (option.value.substring(0, prefixLength) === prefix) { 871 if ($('#tz_date').val() !== option.value && !forceSelector) { 872 // We do not select the option for the user, but notify him, 873 // that we would suggest a different setting. 874 phpbb.timezoneSwitchDate(true); 875 $tzSelectDateSuggest.css('display', 'inline'); 876 } else { 877 option.selected = true; 878 phpbb.timezoneSwitchDate(!forceSelector); 879 $tzSelectDateSuggest.css('display', 'none'); 880 } 881 882 var suggestion = $tzSelectDateSuggest.attr('data-l-suggestion'); 883 884 $tzSelectDateSuggest.attr('title', suggestion.replace('%s', option.innerHTML)); 885 $tzSelectDateSuggest.attr('value', suggestion.replace('%s', option.innerHTML.substring(0, 9))); 886 $tzSelectDateSuggest.attr('data-suggested-tz', option.innerHTML); 887 888 // Found the suggestion, there cannot be more, so return from here. 889 return; 890 } 891 } 892 }; 893 894 phpbb.ajaxCallbacks = {}; 895 896 /** 897 * Adds an AJAX callback to be used by phpbb.ajaxify. 898 * 899 * See the phpbb.ajaxify comments for information on stuff like parameters. 900 * 901 * @param {string} id The name of the callback. 902 * @param {function} callback The callback to be called. 903 */ 904 phpbb.addAjaxCallback = function(id, callback) { 905 if (typeof callback === 'function') { 906 phpbb.ajaxCallbacks[id] = callback; 907 } 908 return this; 909 }; 910 911 /** 912 * This callback handles live member searches. 913 */ 914 phpbb.addAjaxCallback('member_search', function(res) { 915 phpbb.search.handleResponse(res, $(this), false, phpbb.getFunctionByName('phpbb.search.setValueOnClick')); 916 }); 917 918 /** 919 * This callback alternates text - it replaces the current text with the text in 920 * the alt-text data attribute, and replaces the text in the attribute with the 921 * current text so that the process can be repeated. 922 */ 923 phpbb.addAjaxCallback('alt_text', function() { 924 var $anchor, 925 updateAll = $(this).data('update-all'), 926 altText; 927 928 if (updateAll !== undefined && updateAll.length) { 929 $anchor = $(updateAll); 930 } else { 931 $anchor = $(this); 932 } 933 934 $anchor.each(function() { 935 var $this = $(this); 936 altText = $this.attr('data-alt-text'); 937 $this.attr('data-alt-text', $this.text()); 938 $this.attr('title', $.trim(altText)); 939 $this.text(altText); 940 }); 941 }); 942 943 /** 944 * This callback is based on the alt_text callback. 945 * 946 * It replaces the current text with the text in the alt-text data attribute, 947 * and replaces the text in the attribute with the current text so that the 948 * process can be repeated. 949 * Additionally it replaces the class of the link's parent 950 * and changes the link itself. 951 */ 952 phpbb.addAjaxCallback('toggle_link', function() { 953 var $anchor, 954 updateAll = $(this).data('update-all') , 955 toggleText, 956 toggleUrl, 957 toggleClass; 958 959 if (updateAll !== undefined && updateAll.length) { 960 $anchor = $(updateAll); 961 } else { 962 $anchor = $(this); 963 } 964 965 $anchor.each(function() { 966 var $this = $(this); 967 968 // Toggle link text 969 toggleText = $this.attr('data-toggle-text'); 970 $this.attr('data-toggle-text', $this.text()); 971 $this.attr('title', $.trim(toggleText)); 972 $this.text(toggleText); 973 974 // Toggle link url 975 toggleUrl = $this.attr('data-toggle-url'); 976 $this.attr('data-toggle-url', $this.attr('href')); 977 $this.attr('href', toggleUrl); 978 979 // Toggle class of link parent 980 toggleClass = $this.attr('data-toggle-class'); 981 $this.attr('data-toggle-class', $this.parent().attr('class')); 982 $this.parent().attr('class', toggleClass); 983 }); 984 }); 985 986 /** 987 * Automatically resize textarea 988 * 989 * This function automatically resizes textarea elements when user 990 * types text. 991 * 992 * @param {jQuery} $items jQuery object(s) to resize 993 * @param {object} [options] Optional parameter that adjusts default 994 * configuration. See configuration variable 995 * 996 * Optional parameters: 997 * minWindowHeight {number} Minimum browser window height when textareas are resized. Default = 500 998 * minHeight {number} Minimum height of textarea. Default = 200 999 * maxHeight {number} Maximum height of textarea. Default = 500 1000 * heightDiff {number} Minimum difference between window and textarea height. Default = 200 1001 * resizeCallback {function} Function to call after resizing textarea 1002 * resetCallback {function} Function to call when resize has been canceled 1003 1004 * Callback function format: function(item) {} 1005 * this points to DOM object 1006 * item is a jQuery object, same as this 1007 */ 1008 phpbb.resizeTextArea = function($items, options) { 1009 // Configuration 1010 var configuration = { 1011 minWindowHeight: 500, 1012 minHeight: 200, 1013 maxHeight: 500, 1014 heightDiff: 200, 1015 resizeCallback: function() {}, 1016 resetCallback: function() {} 1017 }; 1018 1019 if (phpbb.isTouch) { 1020 return; 1021 } 1022 1023 if (arguments.length > 1) { 1024 configuration = $.extend(configuration, options); 1025 } 1026 1027 function resetAutoResize(item) { 1028 var $item = $(item); 1029 if ($item.hasClass('auto-resized')) { 1030 $(item) 1031 .css({ height: '', resize: '' }) 1032 .removeClass('auto-resized'); 1033 configuration.resetCallback.call(item, $item); 1034 } 1035 } 1036 1037 function autoResize(item) { 1038 function setHeight(height) { 1039 height += parseInt($item.css('height'), 10) - $item.innerHeight(); 1040 $item 1041 .css({ height: height + 'px', resize: 'none' }) 1042 .addClass('auto-resized'); 1043 configuration.resizeCallback.call(item, $item); 1044 } 1045 1046 var windowHeight = $(window).height(); 1047 1048 if (windowHeight < configuration.minWindowHeight) { 1049 resetAutoResize(item); 1050 return; 1051 } 1052 1053 var maxHeight = Math.min( 1054 Math.max(windowHeight - configuration.heightDiff, configuration.minHeight), 1055 configuration.maxHeight 1056 ), 1057 $item = $(item), 1058 height = parseInt($item.innerHeight(), 10), 1059 scrollHeight = (item.scrollHeight) ? item.scrollHeight : 0; 1060 1061 if (height < 0) { 1062 return; 1063 } 1064 1065 if (height > maxHeight) { 1066 setHeight(maxHeight); 1067 } else if (scrollHeight > (height + 5)) { 1068 setHeight(Math.min(maxHeight, scrollHeight)); 1069 } 1070 } 1071 1072 $items.on('focus change keyup', function() { 1073 $(this).each(function() { 1074 autoResize(this); 1075 }); 1076 }).change(); 1077 1078 $(window).resize(function() { 1079 $items.each(function() { 1080 if ($(this).hasClass('auto-resized')) { 1081 autoResize(this); 1082 } 1083 }); 1084 }); 1085 }; 1086 1087 /** 1088 * Check if cursor in textarea is currently inside a bbcode tag 1089 * 1090 * @param {object} textarea Textarea DOM object 1091 * @param {Array} startTags List of start tags to look for 1092 * For example, Array('[code]', '[code=') 1093 * @param {Array} endTags List of end tags to look for 1094 * For example, Array('[/code]') 1095 * 1096 * @returns {boolean} True if cursor is in bbcode tag 1097 */ 1098 phpbb.inBBCodeTag = function(textarea, startTags, endTags) { 1099 var start = textarea.selectionStart, 1100 lastEnd = -1, 1101 lastStart = -1, 1102 i, index, value; 1103 1104 if (typeof start !== 'number') { 1105 return false; 1106 } 1107 1108 value = textarea.value.toLowerCase(); 1109 1110 for (i = 0; i < startTags.length; i++) { 1111 var tagLength = startTags[i].length; 1112 if (start >= tagLength) { 1113 index = value.lastIndexOf(startTags[i], start - tagLength); 1114 lastStart = Math.max(lastStart, index); 1115 } 1116 } 1117 if (lastStart === -1) { 1118 return false; 1119 } 1120 1121 if (start > 0) { 1122 for (i = 0; i < endTags.length; i++) { 1123 index = value.lastIndexOf(endTags[i], start - 1); 1124 lastEnd = Math.max(lastEnd, index); 1125 } 1126 } 1127 1128 return (lastEnd < lastStart); 1129 }; 1130 1131 1132 /** 1133 * Adjust textarea to manage code bbcode 1134 * 1135 * This function allows to use tab characters when typing code 1136 * and keeps indentation of previous line of code when adding new 1137 * line while typing code. 1138 * 1139 * Editor's functionality is changed only when cursor is between 1140 * [code] and [/code] bbcode tags. 1141 * 1142 * @param {object} textarea Textarea DOM object to apply editor to 1143 */ 1144 phpbb.applyCodeEditor = function(textarea) { 1145 // list of allowed start and end bbcode code tags, in lower case 1146 var startTags = ['[code]', '[code='], 1147 startTagsEnd = ']', 1148 endTags = ['[/code]']; 1149 1150 if (!textarea || typeof textarea.selectionStart !== 'number') { 1151 return; 1152 } 1153 1154 if ($(textarea).data('code-editor') === true) { 1155 return; 1156 } 1157 1158 function inTag() { 1159 return phpbb.inBBCodeTag(textarea, startTags, endTags); 1160 } 1161 1162 /** 1163 * Get line of text before cursor 1164 * 1165 * @param {boolean} stripCodeStart If true, only part of line 1166 * after [code] tag will be returned. 1167 * 1168 * @returns {string} Line of text 1169 */ 1170 function getLastLine(stripCodeStart) { 1171 var start = textarea.selectionStart, 1172 value = textarea.value, 1173 index = value.lastIndexOf('\n', start - 1); 1174 1175 value = value.substring(index + 1, start); 1176 1177 if (stripCodeStart) { 1178 for (var i = 0; i < startTags.length; i++) { 1179 index = value.lastIndexOf(startTags[i]); 1180 if (index >= 0) { 1181 var tagLength = startTags[i].length; 1182 1183 value = value.substring(index + tagLength); 1184 if (startTags[i].lastIndexOf(startTagsEnd) !== tagLength) { 1185 index = value.indexOf(startTagsEnd); 1186 1187 if (index >= 0) { 1188 value = value.substr(index + 1); 1189 } 1190 } 1191 } 1192 } 1193 } 1194 1195 return value; 1196 } 1197 1198 /** 1199 * Append text at cursor position 1200 * 1201 * @param {string} text Text to append 1202 */ 1203 function appendText(text) { 1204 var start = textarea.selectionStart, 1205 end = textarea.selectionEnd, 1206 value = textarea.value; 1207 1208 textarea.value = value.substr(0, start) + text + value.substr(end); 1209 textarea.selectionStart = textarea.selectionEnd = start + text.length; 1210 } 1211 1212 $(textarea).data('code-editor', true).on('keydown', function(event) { 1213 var key = event.keyCode || event.which; 1214 1215 // intercept tabs 1216 if (key === keymap.TAB && 1217 !event.ctrlKey && 1218 !event.shiftKey && 1219 !event.altKey && 1220 !event.metaKey) { 1221 if (inTag()) { 1222 appendText('\t'); 1223 event.preventDefault(); 1224 return; 1225 } 1226 } 1227 1228 // intercept new line characters 1229 if (key === keymap.ENTER) { 1230 if (inTag()) { 1231 var lastLine = getLastLine(true), 1232 code = '' + /^\s*/g.exec(lastLine); 1233 1234 if (code.length > 0) { 1235 appendText('\n' + code); 1236 event.preventDefault(); 1237 } 1238 } 1239 } 1240 }); 1241 }; 1242 1243 /** 1244 * Show drag and drop animation when textarea is present 1245 * 1246 * This function will enable the drag and drop animation for a specified 1247 * textarea. 1248 * 1249 * @param {HTMLElement} textarea Textarea DOM object to apply editor to 1250 */ 1251 phpbb.showDragNDrop = function(textarea) { 1252 if (!textarea) { 1253 return; 1254 } 1255 1256 $('body').on('dragenter dragover', function () { 1257 $(textarea).addClass('drag-n-drop'); 1258 }).on('dragleave dragout dragend drop', function() { 1259 $(textarea).removeClass('drag-n-drop'); 1260 }); 1261 $(textarea).on('dragenter dragover', function () { 1262 $(textarea).addClass('drag-n-drop-highlight'); 1263 }).on('dragleave dragout dragend drop', function() { 1264 $(textarea).removeClass('drag-n-drop-highlight'); 1265 }); 1266 }; 1267 1268 /** 1269 * List of classes that toggle dropdown menu, 1270 * list of classes that contain visible dropdown menu 1271 * 1272 * Add your own classes to strings with comma (probably you 1273 * will never need to do that) 1274 */ 1275 phpbb.dropdownHandles = '.dropdown-container.dropdown-visible .dropdown-toggle'; 1276 phpbb.dropdownVisibleContainers = '.dropdown-container.dropdown-visible'; 1277 1278 /** 1279 * Dropdown toggle event handler 1280 * This handler is used by phpBB.registerDropdown() and other functions 1281 */ 1282 phpbb.toggleDropdown = function() { 1283 var $this = $(this), 1284 options = $this.data('dropdown-options'), 1285 parent = options.parent, 1286 visible = parent.hasClass('dropdown-visible'), 1287 direction; 1288 1289 if (!visible) { 1290 // Hide other dropdown menus 1291 $(phpbb.dropdownHandles).each(phpbb.toggleDropdown); 1292 1293 // Figure out direction of dropdown 1294 direction = options.direction; 1295 var verticalDirection = options.verticalDirection, 1296 offset = $this.offset(); 1297 1298 if (direction === 'auto') { 1299 if (($(window).width() - $this.outerWidth(true)) / 2 > offset.left) { 1300 direction = 'right'; 1301 } else { 1302 direction = 'left'; 1303 } 1304 } 1305 parent.toggleClass(options.leftClass, direction === 'left') 1306 .toggleClass(options.rightClass, direction === 'right'); 1307 1308 if (verticalDirection === 'auto') { 1309 var height = $(window).height(), 1310 top = offset.top - $(window).scrollTop(); 1311 1312 verticalDirection = (top < height * 0.7) ? 'down' : 'up'; 1313 } 1314 parent.toggleClass(options.upClass, verticalDirection === 'up') 1315 .toggleClass(options.downClass, verticalDirection === 'down'); 1316 } 1317 1318 options.dropdown.toggle(); 1319 parent.toggleClass(options.visibleClass, !visible) 1320 .toggleClass('dropdown-visible', !visible); 1321 1322 // Check dimensions when showing dropdown 1323 // !visible because variable shows state of dropdown before it was toggled 1324 if (!visible) { 1325 var windowWidth = $(window).width(); 1326 1327 options.dropdown.find('.dropdown-contents').each(function() { 1328 var $this = $(this); 1329 1330 $this.css({ 1331 marginLeft: 0, 1332 left: 0, 1333 maxWidth: (windowWidth - 4) + 'px' 1334 }); 1335 1336 var offset = $this.offset().left, 1337 width = $this.outerWidth(true); 1338 1339 if (offset < 2) { 1340 $this.css('left', (2 - offset) + 'px'); 1341 } else if ((offset + width + 2) > windowWidth) { 1342 $this.css('margin-left', (windowWidth - offset - width - 2) + 'px'); 1343 } 1344 1345 // Check whether the vertical scrollbar is present. 1346 $this.toggleClass('dropdown-nonscroll', this.scrollHeight === $this.innerHeight()); 1347 1348 }); 1349 var freeSpace = parent.offset().left - 4; 1350 1351 if (direction === 'left') { 1352 options.dropdown.css('margin-left', '-' + freeSpace + 'px'); 1353 1354 // Try to position the notification dropdown correctly in RTL-responsive mode 1355 if (options.dropdown.hasClass('dropdown-extended')) { 1356 var contentWidth, 1357 fullFreeSpace = freeSpace + parent.outerWidth(); 1358 1359 options.dropdown.find('.dropdown-contents').each(function() { 1360 contentWidth = parseInt($(this).outerWidth(), 10); 1361 $(this).css({ marginLeft: 0, left: 0 }); 1362 }); 1363 1364 var maxOffset = Math.min(contentWidth, fullFreeSpace) + 'px'; 1365 options.dropdown.css({ 1366 width: maxOffset, 1367 marginLeft: -maxOffset 1368 }); 1369 } 1370 } else { 1371 options.dropdown.css('margin-right', '-' + (windowWidth + freeSpace) + 'px'); 1372 } 1373 } 1374 1375 // Prevent event propagation 1376 if (arguments.length > 0) { 1377 try { 1378 var e = arguments[0]; 1379 e.preventDefault(); 1380 e.stopPropagation(); 1381 } catch (error) { } 1382 } 1383 return false; 1384 }; 1385 1386 /** 1387 * Toggle dropdown submenu 1388 */ 1389 phpbb.toggleSubmenu = function(e) { 1390 $(this).siblings('.dropdown-submenu').toggle(); 1391 e.preventDefault(); 1392 }; 1393 1394 /** 1395 * Register dropdown menu 1396 * Shows/hides dropdown, decides which side to open to 1397 * 1398 * @param {jQuery} toggle Link that toggles dropdown. 1399 * @param {jQuery} dropdown Dropdown menu. 1400 * @param {Object} options List of options. Optional. 1401 */ 1402 phpbb.registerDropdown = function(toggle, dropdown, options) { 1403 var ops = { 1404 parent: toggle.parent(), // Parent item to add classes to 1405 direction: 'auto', // Direction of dropdown menu. Possible values: auto, left, right 1406 verticalDirection: 'auto', // Vertical direction. Possible values: auto, up, down 1407 visibleClass: 'visible', // Class to add to parent item when dropdown is visible 1408 leftClass: 'dropdown-left', // Class to add to parent item when dropdown opens to left side 1409 rightClass: 'dropdown-right', // Class to add to parent item when dropdown opens to right side 1410 upClass: 'dropdown-up', // Class to add to parent item when dropdown opens above menu item 1411 downClass: 'dropdown-down' // Class to add to parent item when dropdown opens below menu item 1412 }; 1413 if (options) { 1414 ops = $.extend(ops, options); 1415 } 1416 ops.dropdown = dropdown; 1417 1418 ops.parent.addClass('dropdown-container'); 1419 toggle.addClass('dropdown-toggle'); 1420 1421 toggle.data('dropdown-options', ops); 1422 1423 toggle.click(phpbb.toggleDropdown); 1424 $('.dropdown-toggle-submenu', ops.parent).click(phpbb.toggleSubmenu); 1425 }; 1426 1427 /** 1428 * Get the HTML for a color palette table. 1429 * 1430 * @param {string} dir Palette direction - either v or h 1431 * @param {int} width Palette cell width. 1432 * @param {int} height Palette cell height. 1433 */ 1434 phpbb.colorPalette = function(dir, width, height) { 1435 var r, g, b, 1436 numberList = new Array(6), 1437 color = '', 1438 html = ''; 1439 1440 numberList[0] = '00'; 1441 numberList[1] = '40'; 1442 numberList[2] = '80'; 1443 numberList[3] = 'BF'; 1444 numberList[4] = 'FF'; 1445 1446 var tableClass = (dir === 'h') ? 'horizontal-palette' : 'vertical-palette'; 1447 html += '<table class="not-responsive colour-palette ' + tableClass + '" style="width: auto;">'; 1448 1449 for (r = 0; r < 5; r++) { 1450 if (dir === 'h') { 1451 html += '<tr>'; 1452 } 1453 1454 for (g = 0; g < 5; g++) { 1455 if (dir === 'v') { 1456 html += '<tr>'; 1457 } 1458 1459 for (b = 0; b < 5; b++) { 1460 color = '' + numberList[r] + numberList[g] + numberList[b]; 1461 html += '<td style="background-color: #' + color + '; width: ' + width + 'px; height: ' + 1462 height + 'px;"><a href="#" data-color="' + color + '" style="display: block; width: ' + 1463 width + 'px; height: ' + height + 'px; " alt="#' + color + '" title="#' + color + '"></a>'; 1464 html += '</td>'; 1465 } 1466 1467 if (dir === 'v') { 1468 html += '</tr>'; 1469 } 1470 } 1471 1472 if (dir === 'h') { 1473 html += '</tr>'; 1474 } 1475 } 1476 html += '</table>'; 1477 return html; 1478 }; 1479 1480 /** 1481 * Register a color palette. 1482 * 1483 * @param {jQuery} el jQuery object for the palette container. 1484 */ 1485 phpbb.registerPalette = function(el) { 1486 var orientation = el.attr('data-orientation'), 1487 height = el.attr('data-height'), 1488 width = el.attr('data-width'), 1489 target = el.attr('data-target'), 1490 bbcode = el.attr('data-bbcode'); 1491 1492 // Insert the palette HTML into the container. 1493 el.html(phpbb.colorPalette(orientation, width, height)); 1494 1495 // Add toggle control. 1496 $('#color_palette_toggle').click(function(e) { 1497 el.toggle(); 1498 e.preventDefault(); 1499 }); 1500 1501 // Attach event handler when a palette cell is clicked. 1502 $(el).on('click', 'a', function(e) { 1503 var color = $(this).attr('data-color'); 1504 1505 if (bbcode) { 1506 bbfontstyle('[color=#' + color + ']', '[/color]'); 1507 } else { 1508 $(target).val(color); 1509 } 1510 e.preventDefault(); 1511 }); 1512 }; 1513 1514 /** 1515 * Set display of page element 1516 * 1517 * @param {string} id The ID of the element to change 1518 * @param {int} action Set to 0 if element display should be toggled, -1 for 1519 * hiding the element, and 1 for showing it. 1520 * @param {string} type Display type that should be used, e.g. inline, block or 1521 * other CSS "display" types 1522 */ 1523 phpbb.toggleDisplay = function(id, action, type) { 1524 if (!type) { 1525 type = 'block'; 1526 } 1527 1528 var $element = $('#' + id); 1529 1530 var display = $element.css('display'); 1531 if (!action) { 1532 action = (display === '' || display === type) ? -1 : 1; 1533 } 1534 $element.css('display', ((action === 1) ? type : 'none')); 1535 }; 1536 1537 /** 1538 * Toggle additional settings based on the selected 1539 * option of select element. 1540 * 1541 * @param {jQuery} el jQuery select element object. 1542 */ 1543 phpbb.toggleSelectSettings = function(el) { 1544 el.children().each(function() { 1545 var $this = $(this), 1546 $setting = $($this.data('toggle-setting')); 1547 $setting.toggle($this.is(':selected')); 1548 1549 // Disable any input elements that are not visible right now 1550 if ($this.is(':selected')) { 1551 $($this.data('toggle-setting') + ' input').prop('disabled', false); 1552 } else { 1553 $($this.data('toggle-setting') + ' input').prop('disabled', true); 1554 } 1555 }); 1556 }; 1557 1558 /** 1559 * Get function from name. 1560 * Based on http://stackoverflow.com/a/359910 1561 * 1562 * @param {string} functionName Function to get. 1563 * @returns function 1564 */ 1565 phpbb.getFunctionByName = function (functionName) { 1566 var namespaces = functionName.split('.'), 1567 func = namespaces.pop(), 1568 context = window; 1569 1570 for (var i = 0; i < namespaces.length; i++) { 1571 context = context[namespaces[i]]; 1572 } 1573 return context[func]; 1574 }; 1575 1576 /** 1577 * Register page dropdowns. 1578 */ 1579 phpbb.registerPageDropdowns = function() { 1580 var $body = $('body'); 1581 1582 $body.find('.dropdown-container').each(function() { 1583 var $this = $(this), 1584 $trigger = $this.find('.dropdown-trigger:first'), 1585 $contents = $this.find('.dropdown'), 1586 options = { 1587 direction: 'auto', 1588 verticalDirection: 'auto' 1589 }, 1590 data; 1591 1592 if (!$trigger.length) { 1593 data = $this.attr('data-dropdown-trigger'); 1594 $trigger = data ? $this.children(data) : $this.children('a:first'); 1595 } 1596 1597 if (!$contents.length) { 1598 data = $this.attr('data-dropdown-contents'); 1599 $contents = data ? $this.children(data) : $this.children('div:first'); 1600 } 1601 1602 if (!$trigger.length || !$contents.length) { 1603 return; 1604 } 1605 1606 if ($this.hasClass('dropdown-up')) { 1607 options.verticalDirection = 'up'; 1608 } 1609 if ($this.hasClass('dropdown-down')) { 1610 options.verticalDirection = 'down'; 1611 } 1612 if ($this.hasClass('dropdown-left')) { 1613 options.direction = 'left'; 1614 } 1615 if ($this.hasClass('dropdown-right')) { 1616 options.direction = 'right'; 1617 } 1618 1619 phpbb.registerDropdown($trigger, $contents, options); 1620 }); 1621 1622 // Hide active dropdowns when click event happens outside 1623 $body.click(function(e) { 1624 var $parents = $(e.target).parents(); 1625 if (!$parents.is(phpbb.dropdownVisibleContainers)) { 1626 $(phpbb.dropdownHandles).each(phpbb.toggleDropdown); 1627 } 1628 }); 1629 }; 1630 1631 /** 1632 * Handle avatars to be lazy loaded. 1633 */ 1634 phpbb.lazyLoadAvatars = function loadAvatars() { 1635 $('.avatar[data-src]').each(function () { 1636 var $avatar = $(this); 1637 1638 $avatar 1639 .attr('src', $avatar.data('src')) 1640 .removeAttr('data-src'); 1641 }); 1642 }; 1643 1644 $(window).load(phpbb.lazyLoadAvatars); 1645 1646 /** 1647 * Apply code editor to all textarea elements with data-bbcode attribute 1648 */ 1649 $(function() { 1650 $('textarea[data-bbcode]').each(function() { 1651 phpbb.applyCodeEditor(this); 1652 }); 1653 1654 phpbb.registerPageDropdowns(); 1655 1656 $('#color_palette_placeholder').each(function() { 1657 phpbb.registerPalette($(this)); 1658 }); 1659 1660 // Update browser history URL to point to specific post in viewtopic.php 1661 // when using view=unread#unread link. 1662 phpbb.history.replaceUrl($('#unread[data-url]').data('url')); 1663 1664 // Hide settings that are not selected via select element. 1665 $('select[data-togglable-settings]').each(function() { 1666 var $this = $(this); 1667 1668 $this.change(function() { 1669 phpbb.toggleSelectSettings($this); 1670 }); 1671 phpbb.toggleSelectSettings($this); 1672 }); 1673 }); 1674 1675 })(jQuery); // Avoid conflicts with other libraries
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jan 11 00:25:41 2018 | Cross-referenced by PHPXref 0.7.1 |