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