Verzeichnisstruktur phpBB-3.1.0
- Veröffentlicht
- 27.10.2014
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
core.js
0001 var phpbb = {};
0002 phpbb.alertTime = 100;
0003
0004 (function($) { // Avoid conflicts with other libraries
0005
0006 'use strict';
0007
0008 // define a couple constants for keydown functions.
0009 var keymap = {
0010 TAB: 9,
0011 ENTER: 13,
0012 ESC: 27
0013 };
0014
0015 var $dark = $('#darkenwrapper');
0016 var $loadingIndicator = $('#loading_indicator');
0017 var phpbbAlertTimer = null;
0018
0019 phpbb.isTouch = (window && typeof window.ontouchstart !== 'undefined');
0020
0021 /**
0022 * Display a loading screen
0023 *
0024 * @returns object Returns loadingIndicator.
0025 */
0026 phpbb.loadingIndicator = function() {
0027 if (!$loadingIndicator.is(':visible')) {
0028 $loadingIndicator.fadeIn(phpbb.alertTime);
0029 // Wait fifteen seconds and display an error if nothing has been returned by then.
0030 phpbb.clearLoadingTimeout();
0031 phpbbAlertTimer = setTimeout(function() {
0032 var $alert = $('#phpbb_alert');
0033
0034 if ($loadingIndicator.is(':visible')) {
0035 phpbb.alert($alert.attr('data-l-err'), $alert.attr('data-l-timeout-processing-req'));
0036 }
0037 }, 15000);
0038 }
0039
0040 return $loadingIndicator;
0041 };
0042
0043 /**
0044 * Clear loading alert timeout
0045 */
0046 phpbb.clearLoadingTimeout = function() {
0047 if (phpbbAlertTimer !== null) {
0048 clearTimeout(phpbbAlertTimer);
0049 phpbbAlertTimer = null;
0050 }
0051 };
0052
0053
0054 /**
0055 * Close popup alert after a specified delay
0056 *
0057 * @param int Delay in ms until darkenwrapper's click event is triggered
0058 */
0059 phpbb.closeDarkenWrapper = function(delay) {
0060 phpbbAlertTimer = setTimeout(function() {
0061 $('#darkenwrapper').trigger('click');
0062 }, delay);
0063 };
0064
0065 /**
0066 * Display a simple alert similar to JSs native alert().
0067 *
0068 * You can only call one alert or confirm box at any one time.
0069 *
0070 * @param string title Title of the message, eg "Information" (HTML).
0071 * @param string msg Message to display (HTML).
0072 * @param bool fadedark Remove the dark background when done? Defaults
0073 * to yes.
0074 *
0075 * @returns object Returns the div created.
0076 */
0077 phpbb.alert = function(title, msg, fadedark) {
0078 var $alert = $('#phpbb_alert');
0079 $alert.find('.alert_title').html(title);
0080 $alert.find('.alert_text').html(msg);
0081
0082 $(document).on('keydown.phpbb.alert', function(e) {
0083 if (e.keyCode === keymap.ENTER || e.keyCode === keymap.ESC) {
0084 phpbb.alert.close($alert, true);
0085 e.preventDefault();
0086 e.stopPropagation();
0087 }
0088 });
0089 phpbb.alert.open($alert);
0090
0091 return $alert;
0092 };
0093
0094 /**
0095 * Handler for opening an alert box.
0096 *
0097 * @param jQuery $alert jQuery object.
0098 */
0099 phpbb.alert.open = function($alert) {
0100 if (!$dark.is(':visible')) {
0101 $dark.fadeIn(phpbb.alertTime);
0102 }
0103
0104 if ($loadingIndicator.is(':visible')) {
0105 $loadingIndicator.fadeOut(phpbb.alertTime, function() {
0106 $dark.append($alert);
0107 $alert.fadeIn(phpbb.alertTime);
0108 });
0109 } else if ($dark.is(':visible')) {
0110 $dark.append($alert);
0111 $alert.fadeIn(phpbb.alertTime);
0112 } else {
0113 $dark.append($alert);
0114 $alert.show();
0115 $dark.fadeIn(phpbb.alertTime);
0116 }
0117
0118 $alert.on('click', function(e) {
0119 e.stopPropagation();
0120 });
0121
0122 $dark.one('click', function(e) {
0123 phpbb.alert.close($alert, true);
0124 e.preventDefault();
0125 e.stopPropagation();
0126 });
0127
0128 $alert.find('.alert_close').one('click', function(e) {
0129 phpbb.alert.close($alert, true);
0130 e.preventDefault();
0131 });
0132 };
0133
0134 /**
0135 * Handler for closing an alert box.
0136 *
0137 * @param jQuery $alert jQuery object.
0138 * @param bool fadedark Whether to remove dark background.
0139 */
0140 phpbb.alert.close = function($alert, fadedark) {
0141 var $fade = (fadedark) ? $dark : $alert;
0142
0143 $fade.fadeOut(phpbb.alertTime, function() {
0144 $alert.hide();
0145 });
0146
0147 $alert.find('.alert_close').off('click');
0148 $(document).off('keydown.phpbb.alert');
0149 };
0150
0151 /**
0152 * Display a simple yes / no box to the user.
0153 *
0154 * You can only call one alert or confirm box at any one time.
0155 *
0156 * @param string msg Message to display (HTML).
0157 * @param function callback Callback. Bool param, whether the user pressed
0158 * yes or no (or whatever their language is).
0159 * @param bool fadedark Remove the dark background when done? Defaults
0160 * to yes.
0161 *
0162 * @returns object Returns the div created.
0163 */
0164 phpbb.confirm = function(msg, callback, fadedark) {
0165 var $confirmDiv = $('#phpbb_confirm');
0166 $confirmDiv.find('.alert_text').html(msg);
0167 fadedark = fadedark || true;
0168
0169 $(document).on('keydown.phpbb.alert', function(e) {
0170 if (e.keyCode === keymap.ENTER || e.keyCode === keymap.ESC) {
0171 var name = (e.keyCode === keymap.ENTER) ? 'confirm' : 'cancel';
0172
0173 $('input[name="' + name + '"]').trigger('click');
0174 e.preventDefault();
0175 e.stopPropagation();
0176 }
0177 });
0178
0179 $confirmDiv.find('input[type="button"]').one('click.phpbb.confirmbox', function(e) {
0180 var confirmed = this.name === 'confirm';
0181
0182 if (confirmed) {
0183 callback(true);
0184 }
0185 $confirmDiv.find('input[type="button"]').off('click.phpbb.confirmbox');
0186 phpbb.alert.close($confirmDiv, fadedark || !confirmed);
0187
0188 e.preventDefault();
0189 e.stopPropagation();
0190 });
0191
0192 phpbb.alert.open($confirmDiv);
0193
0194 return $confirmDiv;
0195 };
0196
0197 /**
0198 * Turn a querystring into an array.
0199 *
0200 * @argument string string The querystring to parse.
0201 * @returns object The object created.
0202 */
0203 phpbb.parseQuerystring = function(string) {
0204 var params = {}, i, split;
0205
0206 string = string.split('&');
0207 for (i = 0; i < string.length; i++) {
0208 split = string[i].split('=');
0209 params[split[0]] = decodeURIComponent(split[1]);
0210 }
0211 return params;
0212 };
0213
0214
0215 /**
0216 * Makes a link use AJAX instead of loading an entire page.
0217 *
0218 * This function will work for links (both standard links and links which
0219 * invoke confirm_box) and forms. It will be called automatically for links
0220 * and forms with the data-ajax attribute set, and will call the necessary
0221 * callback.
0222 *
0223 * For more info, view the following page on the phpBB wiki:
0224 * http://wiki.phpbb.com/JavaScript_Function.phpbb.ajaxify
0225 *
0226 * @param object options Options.
0227 * @param bool/function refresh If we are sent back a refresh, should it be
0228 * acted upon? This can either be true / false / a function.
0229 * @param function callback Callback to call on completion of event. Has
0230 * three parameters: the element that the event was evoked from, the JSON
0231 * that was returned and (if it is a form) the form action.
0232 */
0233 phpbb.ajaxify = function(options) {
0234 var $elements = $(options.selector),
0235 refresh = options.refresh,
0236 callback = options.callback,
0237 overlay = (typeof options.overlay !== 'undefined') ? options.overlay : true,
0238 isForm = $elements.is('form'),
0239 isText = $elements.is('input[type="text"], textarea'),
0240 eventName;
0241
0242 if (isForm) {
0243 eventName = 'submit';
0244 } else if (isText) {
0245 eventName = 'keyup';
0246 } else {
0247 eventName = 'click';
0248 }
0249
0250 $elements.on(eventName, function(event) {
0251 var action, method, data, submit, that = this, $this = $(this);
0252
0253 if ($this.find('input[type="submit"][data-clicked]').attr('data-ajax') === 'false') {
0254 return;
0255 }
0256
0257 /**
0258 * Handler for AJAX errors
0259 */
0260 function errorHandler(jqXHR, textStatus, errorThrown) {
0261 if (typeof console !== 'undefined' && console.log) {
0262 console.log('AJAX error. status: ' + textStatus + ', message: ' + errorThrown);
0263 }
0264 phpbb.clearLoadingTimeout();
0265 var errorText = false;
0266 if (typeof errorThrown === 'string' && errorThrown.length > 0) {
0267 errorText = errorThrown;
0268 }
0269 else {
0270 errorText = $dark.attr('data-ajax-error-text-' + textStatus);
0271 if (typeof errorText !== 'string' || !errorText.length) {
0272 errorText = $dark.attr('data-ajax-error-text');
0273 }
0274 }
0275 phpbb.alert($dark.attr('data-ajax-error-title'), errorText);
0276 }
0277
0278 /**
0279 * This is a private function used to handle the callbacks, refreshes
0280 * and alert. It calls the callback, refreshes the page if necessary, and
0281 * displays an alert to the user and removes it after an amount of time.
0282 *
0283 * It cannot be called from outside this function, and is purely here to
0284 * avoid repetition of code.
0285 *
0286 * @param object res The object sent back by the server.
0287 */
0288 function returnHandler(res) {
0289 var alert;
0290
0291 phpbb.clearLoadingTimeout();
0292
0293 // Is a confirmation required?
0294 if (typeof res.S_CONFIRM_ACTION === 'undefined') {
0295 // If a confirmation is not required, display an alert and call the
0296 // callbacks.
0297 if (typeof res.MESSAGE_TITLE !== 'undefined') {
0298 alert = phpbb.alert(res.MESSAGE_TITLE, res.MESSAGE_TEXT);
0299 } else {
0300 $dark.fadeOut(phpbb.alertTime);
0301 }
0302
0303 if (typeof phpbb.ajaxCallbacks[callback] === 'function') {
0304 phpbb.ajaxCallbacks[callback].call(that, res);
0305 }
0306
0307 // If the server says to refresh the page, check whether the page should
0308 // be refreshed and refresh page after specified time if required.
0309 if (res.REFRESH_DATA) {
0310 if (typeof refresh === 'function') {
0311 refresh = refresh(res.REFRESH_DATA.url);
0312 } else if (typeof refresh !== 'boolean') {
0313 refresh = false;
0314 }
0315
0316 phpbbAlertTimer = setTimeout(function() {
0317 if (refresh) {
0318 window.location = res.REFRESH_DATA.url;
0319 }
0320
0321 // Hide the alert even if we refresh the page, in case the user
0322 // presses the back button.
0323 $dark.fadeOut(phpbb.alertTime, function() {
0324 if (typeof alert !== 'undefined') {
0325 alert.hide();
0326 }
0327 });
0328 }, res.REFRESH_DATA.time * 1000); // Server specifies time in seconds
0329 }
0330 } else {
0331 // If confirmation is required, display a dialog to the user.
0332 phpbb.confirm(res.MESSAGE_BODY, function(del) {
0333 if (!del) {
0334 return;
0335 }
0336
0337 phpbb.loadingIndicator();
0338 data = $('<form>' + res.S_HIDDEN_FIELDS + '</form>').serialize();
0339 $.ajax({
0340 url: res.S_CONFIRM_ACTION,
0341 type: 'POST',
0342 data: data + '&confirm=' + res.YES_VALUE + '&' + $('form', '#phpbb_confirm').serialize(),
0343 success: returnHandler,
0344 error: errorHandler
0345 });
0346 }, false);
0347 }
0348 }
0349
0350 // If the element is a form, POST must be used and some extra data must
0351 // be taken from the form.
0352 var runFilter = (typeof options.filter === 'function');
0353 data = {};
0354
0355 if (isForm) {
0356 action = $this.attr('action').replace('&', '&');
0357 data = $this.serializeArray();
0358 method = $this.attr('method') || 'GET';
0359
0360 if ($this.find('input[type="submit"][data-clicked]')) {
0361 submit = $this.find('input[type="submit"][data-clicked]');
0362 data.push({
0363 name: submit.attr('name'),
0364 value: submit.val()
0365 });
0366 }
0367 } else if (isText) {
0368 var name = $this.attr('data-name') || this.name;
0369 action = $this.attr('data-url').replace('&', '&');
0370 data[name] = this.value;
0371 method = 'POST';
0372 } else {
0373 action = this.href;
0374 data = null;
0375 method = 'GET';
0376 }
0377
0378 var sendRequest = function() {
0379 var dataOverlay = $this.attr('data-overlay');
0380 if (overlay && (typeof dataOverlay === 'undefined' || dataOverlay === 'true')) {
0381 phpbb.loadingIndicator();
0382 }
0383
0384 var request = $.ajax({
0385 url: action,
0386 type: method,
0387 data: data,
0388 success: returnHandler,
0389 error: errorHandler
0390 });
0391 request.always(function() {
0392 $loadingIndicator.fadeOut(phpbb.alertTime);
0393 });
0394 };
0395
0396 // If filter function returns false, cancel the AJAX functionality,
0397 // and return true (meaning that the HTTP request will be sent normally).
0398 if (runFilter && !options.filter.call(this, data, event, sendRequest)) {
0399 return;
0400 }
0401
0402 sendRequest();
0403 event.preventDefault();
0404 });
0405
0406 if (isForm) {
0407 $elements.find('input:submit').click(function () {
0408 var $this = $(this);
0409
0410 $this.siblings('[data-clicked]').removeAttr('data-clicked');
0411 $this.attr('data-clicked', 'true');
0412 });
0413 }
0414
0415 return this;
0416 };
0417
0418 phpbb.search = {
0419 cache: {
0420 data: []
0421 },
0422 tpl: [],
0423 container: []
0424 };
0425
0426 /**
0427 * Get cached search data.
0428 *
0429 * @param string id Search ID.
0430 * @return bool|object. Cached data object. Returns false if no data exists.
0431 */
0432 phpbb.search.cache.get = function(id) {
0433 if (this.data[id]) {
0434 return this.data[id];
0435 }
0436 return false;
0437 };
0438
0439 /**
0440 * Set search cache data value.
0441 *
0442 * @param string id Search ID.
0443 * @param string key Data key.
0444 * @param string value Data value.
0445 *
0446 * @return undefined
0447 */
0448 phpbb.search.cache.set = function(id, key, value) {
0449 if (!this.data[id]) {
0450 this.data[id] = {results: []};
0451 }
0452 this.data[id][key] = value;
0453 };
0454
0455 /**
0456 * Cache search result.
0457 *
0458 * @param string id Search ID.
0459 * @param string keyword Keyword.
0460 * @param array results Search results.
0461 *
0462 * @return undefined
0463 */
0464 phpbb.search.cache.setResults = function(id, keyword, value) {
0465 this.data[id].results[keyword] = value;
0466 };
0467
0468 /**
0469 * Trim spaces from keyword and lower its case.
0470 *
0471 * @param string keyword Search keyword to clean.
0472 * @return string Cleaned string.
0473 */
0474 phpbb.search.cleanKeyword = function(keyword) {
0475 return $.trim(keyword).toLowerCase();
0476 };
0477
0478 /**
0479 * Get clean version of search keyword. If textarea supports several keywords
0480 * (one per line), it fetches the current keyword based on the caret position.
0481 *
0482 * @param jQuery $input Search input|textarea.
0483 * @param string keyword Input|textarea value.
0484 * @param bool multiline Whether textarea supports multiple search keywords.
0485 *
0486 * @return string Clean string.
0487 */
0488 phpbb.search.getKeyword = function($input, keyword, multiline) {
0489 if (multiline) {
0490 var line = phpbb.search.getKeywordLine($input);
0491 keyword = keyword.split('\n').splice(line, 1);
0492 }
0493 return phpbb.search.cleanKeyword(keyword);
0494 };
0495
0496 /**
0497 * Get the textarea line number on which the keyword resides - for textareas
0498 * that support multiple keywords (one per line).
0499 *
0500 * @param jQuery $textarea Search textarea.
0501 * @return int
0502 */
0503 phpbb.search.getKeywordLine = function ($textarea) {
0504 var selectionStart = $textarea.get(0).selectionStart;
0505 return $textarea.val().substr(0, selectionStart).split('\n').length - 1;
0506 };
0507
0508 /**
0509 * Set the value on the input|textarea. If textarea supports multiple
0510 * keywords, only the active keyword is replaced.
0511 *
0512 * @param jQuery $input Search input|textarea.
0513 * @param string value Value to set.
0514 * @param bool multiline Whether textarea supports multiple search keywords.
0515 *
0516 * @return undefined
0517 */
0518 phpbb.search.setValue = function($input, value, multiline) {
0519 if (multiline) {
0520 var line = phpbb.search.getKeywordLine($input),
0521 lines = $input.val().split('\n');
0522 lines[line] = value;
0523 value = lines.join('\n');
0524 }
0525 $input.val(value);
0526 };
0527
0528 /**
0529 * Sets the onclick event to set the value on the input|textarea to the selected search result.
0530 *
0531 * @param jQuery $input Search input|textarea.
0532 * @param object value Result object.
0533 * @param jQuery $row Result element.
0534 * @param jQuery $container jQuery object for the search container.
0535 *
0536 * @return undefined
0537 */
0538 phpbb.search.setValueOnClick = function($input, value, $row, $container) {
0539 $row.click(function() {
0540 phpbb.search.setValue($input, value.result, $input.attr('data-multiline'));
0541 $container.hide();
0542 });
0543 };
0544
0545 /**
0546 * Runs before the AJAX search request is sent and determines whether
0547 * there is a need to contact the server. If there are cached results
0548 * already, those are displayed instead. Executes the AJAX request function
0549 * itself due to the need to use a timeout to limit the number of requests.
0550 *
0551 * @param array data Data to be sent to the server.
0552 * @param object event Onkeyup event object.
0553 * @param function sendRequest Function to execute AJAX request.
0554 *
0555 * @return bool Returns false.
0556 */
0557 phpbb.search.filter = function(data, event, sendRequest) {
0558 var $this = $(this),
0559 dataName = ($this.attr('data-name') !== undefined) ? $this.attr('data-name') : $this.attr('name'),
0560 minLength = parseInt($this.attr('data-min-length')),
0561 searchID = $this.attr('data-results'),
0562 keyword = phpbb.search.getKeyword($this, data[dataName], $this.attr('data-multiline')),
0563 cache = phpbb.search.cache.get(searchID),
0564 proceed = true;
0565 data[dataName] = keyword;
0566
0567 if (cache.timeout) {
0568 clearTimeout(cache.timeout);
0569 }
0570
0571 var timeout = setTimeout(function() {
0572 // Check min length and existence of cache.
0573 if (minLength > keyword.length) {
0574 proceed = false;
0575 } else if (cache.lastSearch) {
0576 // Has the keyword actually changed?
0577 if (cache.lastSearch === keyword) {
0578 proceed = false;
0579 } else {
0580 // Do we already have results for this?
0581 if (cache.results[keyword]) {
0582 var response = {keyword: keyword, results: cache.results[keyword]};
0583 phpbb.search.handleResponse(response, $this, true);
0584 proceed = false;
0585 }
0586
0587 // If the previous search didn't yield results and the string only had characters added to it,
0588 // then we won't bother sending a request.
0589 if (keyword.indexOf(cache.lastSearch) === 0 && cache.results[cache.lastSearch].length === 0) {
0590 phpbb.search.cache.set(searchID, 'lastSearch', keyword);
0591 phpbb.search.cache.setResults(searchID, keyword, []);
0592 proceed = false;
0593 }
0594 }
0595 }
0596
0597 if (proceed) {
0598 sendRequest.call(this);
0599 }
0600 }, 350);
0601 phpbb.search.cache.set(searchID, 'timeout', timeout);
0602
0603 return false;
0604 };
0605
0606 /**
0607 * Handle search result response.
0608 *
0609 * @param object res Data received from server.
0610 * @param jQuery $input Search input|textarea.
0611 * @param bool fromCache Whether the results are from the cache.
0612 * @param function callback Optional callback to run when assigning each search result.
0613 *
0614 * @return undefined
0615 */
0616 phpbb.search.handleResponse = function(res, $input, fromCache, callback) {
0617 if (typeof res !== 'object') {
0618 return;
0619 }
0620
0621 var searchID = $input.attr('data-results'),
0622 $container = $(searchID);
0623
0624 if (this.cache.get(searchID).callback) {
0625 callback = this.cache.get(searchID).callback;
0626 } else if (typeof callback === 'function') {
0627 this.cache.set(searchID, 'callback', callback);
0628 }
0629
0630 if (!fromCache) {
0631 this.cache.setResults(searchID, res.keyword, res.results);
0632 }
0633
0634 this.cache.set(searchID, 'lastSearch', res.keyword);
0635 this.showResults(res.results, $input, $container, callback);
0636 };
0637
0638 /**
0639 * Show search results.
0640 *
0641 * @param array results Search results.
0642 * @param jQuery $input Search input|textarea.
0643 * @param jQuery $container Search results container element.
0644 * @param function callback Optional callback to run when assigning each search result.
0645 *
0646 * @return undefined
0647 */
0648 phpbb.search.showResults = function(results, $input, $container, callback) {
0649 var $resultContainer = $('.search-results', $container);
0650 this.clearResults($resultContainer);
0651
0652 if (!results.length) {
0653 $container.hide();
0654 return;
0655 }
0656
0657 var searchID = $container.attr('id'),
0658 tpl,
0659 row;
0660
0661 if (!this.tpl[searchID]) {
0662 tpl = $('.search-result-tpl', $container);
0663 this.tpl[searchID] = tpl.clone().removeClass('search-result-tpl');
0664 tpl.remove();
0665 }
0666 tpl = this.tpl[searchID];
0667
0668 $.each(results, function(i, item) {
0669 row = tpl.clone();
0670 row.find('.search-result').html(item.display);
0671
0672 if (typeof callback === 'function') {
0673 callback.call(this, $input, item, row, $container);
0674 }
0675 row.appendTo($resultContainer).show();
0676 });
0677 $container.show();
0678 };
0679
0680 /**
0681 * Clear search results.
0682 *
0683 * @param jQuery $container Search results container.
0684 * @return undefined
0685 */
0686 phpbb.search.clearResults = function($container) {
0687 $container.children(':not(.search-result-tpl)').remove();
0688 };
0689
0690 $('#phpbb').click(function() {
0691 var $this = $(this);
0692
0693 if (!$this.is('.live-search') && !$this.parents().is('.live-search')) {
0694 $('.live-search').hide();
0695 }
0696 });
0697
0698 phpbb.history = {};
0699
0700 /**
0701 * Check whether a method in the native history object is supported.
0702 *
0703 * @param string fn Method name.
0704 * @return bool Returns true if the method is supported.
0705 */
0706 phpbb.history.isSupported = function(fn) {
0707 return !(typeof history === 'undefined' || typeof history[fn] === 'undefined');
0708 };
0709
0710 /**
0711 * Wrapper for the pushState and replaceState methods of the
0712 * native history object.
0713 *
0714 * @param string mode Mode. Either push or replace.
0715 * @param string url New URL.
0716 * @param string title Optional page title.
0717 * @patam object obj Optional state object.
0718 *
0719 * @return undefined
0720 */
0721 phpbb.history.alterUrl = function(mode, url, title, obj) {
0722 var fn = mode + 'State';
0723
0724 if (!url || !phpbb.history.isSupported(fn)) {
0725 return;
0726 }
0727 if (!title) {
0728 title = document.title;
0729 }
0730 if (!obj) {
0731 obj = null;
0732 }
0733
0734 history[fn](obj, title, url);
0735 };
0736
0737 /**
0738 * Wrapper for the native history.replaceState method.
0739 *
0740 * @param string url New URL.
0741 * @param string title Optional page title.
0742 * @patam object obj Optional state object.
0743 *
0744 * @return undefined
0745 */
0746 phpbb.history.replaceUrl = function(url, title, obj) {
0747 phpbb.history.alterUrl('replace', url, title, obj);
0748 };
0749
0750 /**
0751 * Wrapper for the native history.pushState method.
0752 *
0753 * @param string url New URL.
0754 * @param string title Optional page title.
0755 * @patam object obj Optional state object.
0756 *
0757 * @return undefined
0758 */
0759 phpbb.history.pushUrl = function(url, title, obj) {
0760 phpbb.history.alterUrl('push', url, title, obj);
0761 };
0762
0763 /**
0764 * Hide the optgroups that are not the selected timezone
0765 *
0766 * @param bool keepSelection Shall we keep the value selected, or shall the user be forced to repick one.
0767 */
0768 phpbb.timezoneSwitchDate = function(keepSelection) {
0769 var $timezoneCopy = $('#timezone_copy');
0770 var $timezone = $('#timezone');
0771 var $tzDate = $('#tz_date');
0772 var $tzSelectDateSuggest = $('#tz_select_date_suggest');
0773
0774 if ($timezoneCopy.length === 0) {
0775 // We make a backup of the original dropdown, so we can remove optgroups
0776 // instead of setting display to none, because IE and chrome will not
0777 // hide options inside of optgroups and selects via css
0778 $timezone.clone()
0779 .attr('id', 'timezone_copy')
0780 .css('display', 'none')
0781 .attr('name', 'tz_copy')
0782 .insertAfter('#timezone');
0783 } else {
0784 // Copy the content of our backup, so we can remove all unneeded options
0785 $timezone.html($timezoneCopy.html());
0786 }
0787
0788 if ($tzDate.val() !== '') {
0789 $timezone.children('optgroup').remove(':not([data-tz-value="' + $('#tz_date').val() + '"])');
0790 }
0791
0792 if ($tzDate.val() === $tzSelectDateSuggest.attr('data-suggested-tz')) {
0793 $tzSelectDateSuggest.css('display', 'none');
0794 } else {
0795 $tzSelectDateSuggest.css('display', 'inline');
0796 }
0797
0798 var $tzOptions = $timezone.children('optgroup[data-tz-value="' + $tzDate.val() + '"]').children('option');
0799
0800 if ($tzOptions.length === 1) {
0801 // If there is only one timezone for the selected date, we just select that automatically.
0802 $tzOptions.prop('selected', true);
0803 keepSelection = true;
0804 }
0805
0806 if (typeof keepSelection !== 'undefined' && !keepSelection) {
0807 var $timezoneOptions = $timezone.find('optgroup option');
0808 if ($timezoneOptions.filter(':selected').length <= 0) {
0809 $timezoneOptions.filter(':first').prop('selected', true);
0810 }
0811 }
0812 };
0813
0814 /**
0815 * Display the date/time select
0816 */
0817 phpbb.timezoneEnableDateSelection = function() {
0818 $('#tz_select_date').css('display', 'block');
0819 };
0820
0821 /**
0822 * Preselect a date/time or suggest one, if it is not picked.
0823 *
0824 * @param bool forceSelector Shall we select the suggestion?
0825 */
0826 phpbb.timezonePreselectSelect = function(forceSelector) {
0827
0828 // The offset returned here is in minutes and negated.
0829 var offset = (new Date()).getTimezoneOffset();
0830 var sign = '-';
0831
0832 if (offset < 0) {
0833 sign = '+';
0834 offset = -offset;
0835 }
0836
0837 var minutes = offset % 60;
0838 var hours = (offset - minutes) / 60;
0839
0840 if (hours < 10) {
0841 hours = '0' + hours.toString();
0842 } else {
0843 hours = hours.toString();
0844 }
0845
0846 if (minutes < 10) {
0847 minutes = '0' + minutes.toString();
0848 } else {
0849 minutes = minutes.toString();
0850 }
0851
0852 var prefix = 'UTC' + sign + hours + ':' + minutes;
0853 var prefixLength = prefix.length;
0854 var selectorOptions = $('option', '#tz_date');
0855 var i;
0856
0857 var $tzSelectDateSuggest = $('#tz_select_date_suggest');
0858
0859 for (i = 0; i < selectorOptions.length; ++i) {
0860 var option = selectorOptions[i];
0861
0862 if (option.value.substring(0, prefixLength) === prefix) {
0863 if ($('#tz_date').val() !== option.value && !forceSelector) {
0864 // We do not select the option for the user, but notify him,
0865 // that we would suggest a different setting.
0866 phpbb.timezoneSwitchDate(true);
0867 $tzSelectDateSuggest.css('display', 'inline');
0868 } else {
0869 option.selected = true;
0870 phpbb.timezoneSwitchDate(!forceSelector);
0871 $tzSelectDateSuggest.css('display', 'none');
0872 }
0873
0874 var suggestion = $tzSelectDateSuggest.attr('data-l-suggestion');
0875
0876 $tzSelectDateSuggest.attr('title', suggestion.replace('%s', option.innerHTML));
0877 $tzSelectDateSuggest.attr('value', suggestion.replace('%s', option.innerHTML.substring(0, 9)));
0878 $tzSelectDateSuggest.attr('data-suggested-tz', option.innerHTML);
0879
0880 // Found the suggestion, there cannot be more, so return from here.
0881 return;
0882 }
0883 }
0884 };
0885
0886 phpbb.ajaxCallbacks = {};
0887
0888 /**
0889 * Adds an AJAX callback to be used by phpbb.ajaxify.
0890 *
0891 * See the phpbb.ajaxify comments for information on stuff like parameters.
0892 *
0893 * @param string id The name of the callback.
0894 * @param function callback The callback to be called.
0895 */
0896 phpbb.addAjaxCallback = function(id, callback) {
0897 if (typeof callback === 'function') {
0898 phpbb.ajaxCallbacks[id] = callback;
0899 }
0900 return this;
0901 };
0902
0903 /**
0904 * This callback handles live member searches.
0905 */
0906 phpbb.addAjaxCallback('member_search', function(res) {
0907 phpbb.search.handleResponse(res, $(this), false, phpbb.getFunctionByName('phpbb.search.setValueOnClick'));
0908 });
0909
0910 /**
0911 * This callback alternates text - it replaces the current text with the text in
0912 * the alt-text data attribute, and replaces the text in the attribute with the
0913 * current text so that the process can be repeated.
0914 */
0915 phpbb.addAjaxCallback('alt_text', function() {
0916 var $anchor,
0917 updateAll = $(this).data('update-all'),
0918 altText;
0919
0920 if (updateAll !== undefined && updateAll.length) {
0921 $anchor = $(updateAll);
0922 } else {
0923 $anchor = $(this);
0924 }
0925
0926 $anchor.each(function() {
0927 var $this = $(this);
0928 altText = $this.attr('data-alt-text');
0929 $this.attr('data-alt-text', $this.text());
0930 $this.attr('title', $.trim(altText));
0931 $this.text(altText);
0932 });
0933 });
0934
0935 /**
0936 * This callback is based on the alt_text callback.
0937 *
0938 * It replaces the current text with the text in the alt-text data attribute,
0939 * and replaces the text in the attribute with the current text so that the
0940 * process can be repeated.
0941 * Additionally it replaces the class of the link's parent
0942 * and changes the link itself.
0943 */
0944 phpbb.addAjaxCallback('toggle_link', function() {
0945 var $anchor,
0946 updateAll = $(this).data('update-all') ,
0947 toggleText,
0948 toggleUrl,
0949 toggleClass;
0950
0951 if (updateAll !== undefined && updateAll.length) {
0952 $anchor = $(updateAll);
0953 } else {
0954 $anchor = $(this);
0955 }
0956
0957 $anchor.each(function() {
0958 var $this = $(this);
0959
0960 // Toggle link text
0961 toggleText = $this.attr('data-toggle-text');
0962 $this.attr('data-toggle-text', $this.text());
0963 $this.attr('title', $.trim(toggleText));
0964 $this.text(toggleText);
0965
0966 // Toggle link url
0967 toggleUrl = $this.attr('data-toggle-url');
0968 $this.attr('data-toggle-url', $this.attr('href'));
0969 $this.attr('href', toggleUrl);
0970
0971 // Toggle class of link parent
0972 toggleClass = $this.attr('data-toggle-class');
0973 $this.attr('data-toggle-class', $this.parent().attr('class'));
0974 $this.parent().attr('class', toggleClass);
0975 });
0976 });
0977
0978 /**
0979 * Automatically resize textarea
0980 *
0981 * This function automatically resizes textarea elements when user
0982 * types text.
0983 *
0984 * @param {jQuery} $items jQuery object(s) to resize
0985 * @param {object} options Optional parameter that adjusts default
0986 * configuration. See configuration variable
0987 *
0988 * Optional parameters:
0989 * minWindowHeight {number} Minimum browser window height when textareas are resized. Default = 500
0990 * minHeight {number} Minimum height of textarea. Default = 200
0991 * maxHeight {number} Maximum height of textarea. Default = 500
0992 * heightDiff {number} Minimum difference between window and textarea height. Default = 200
0993 * resizeCallback {function} Function to call after resizing textarea
0994 * resetCallback {function} Function to call when resize has been canceled
0995
0996 * Callback function format: function(item) {}
0997 * this points to DOM object
0998 * item is a jQuery object, same as this
0999 */
1000 phpbb.resizeTextArea = function($items, options) {
1001 // Configuration
1002 var configuration = {
1003 minWindowHeight: 500,
1004 minHeight: 200,
1005 maxHeight: 500,
1006 heightDiff: 200,
1007 resizeCallback: function() {},
1008 resetCallback: function() {}
1009 };
1010
1011 if (phpbb.isTouch) {
1012 return;
1013 }
1014
1015 if (arguments.length > 1) {
1016 configuration = $.extend(configuration, options);
1017 }
1018
1019 function resetAutoResize(item) {
1020 var $item = $(item);
1021 if ($item.hasClass('auto-resized')) {
1022 $(item).css({height: '', resize: ''}).removeClass('auto-resized');
1023 configuration.resetCallback.call(item, $item);
1024 }
1025 }
1026
1027 function autoResize(item) {
1028 function setHeight(height) {
1029 height += parseInt($item.css('height')) - $item.height();
1030 $item.css({height: height + 'px', resize: 'none'}).addClass('auto-resized');
1031 configuration.resizeCallback.call(item, $item);
1032 }
1033
1034 var windowHeight = $(window).height();
1035
1036 if (windowHeight < configuration.minWindowHeight) {
1037 resetAutoResize(item);
1038 return;
1039 }
1040
1041 var maxHeight = Math.min(
1042 Math.max(windowHeight - configuration.heightDiff, configuration.minHeight),
1043 configuration.maxHeight
1044 ),
1045 $item = $(item),
1046 height = parseInt($item.height()),
1047 scrollHeight = (item.scrollHeight) ? item.scrollHeight : 0;
1048
1049 if (height < 0) {
1050 return;
1051 }
1052
1053 if (height > maxHeight) {
1054 setHeight(maxHeight);
1055 }
1056 else if (scrollHeight > (height + 5)) {
1057 setHeight(Math.min(maxHeight, scrollHeight));
1058 }
1059 }
1060
1061 $items.on('focus change keyup', function() {
1062 $(this).each(function() {
1063 autoResize(this);
1064 });
1065 }).change();
1066
1067 $(window).resize(function() {
1068 $items.each(function() {
1069 if ($(this).hasClass('auto-resized')) {
1070 autoResize(this);
1071 }
1072 });
1073 });
1074 };
1075
1076 /**
1077 * Check if cursor in textarea is currently inside a bbcode tag
1078 *
1079 * @param {object} textarea Textarea DOM object
1080 * @param {Array} startTags List of start tags to look for
1081 * For example, Array('[code]', '[code=')
1082 * @param {Array} endTags List of end tags to look for
1083 * For example, Array('[/code]')
1084 *
1085 * @return {boolean} True if cursor is in bbcode tag
1086 */
1087 phpbb.inBBCodeTag = function(textarea, startTags, endTags) {
1088 var start = textarea.selectionStart,
1089 lastEnd = -1,
1090 lastStart = -1,
1091 i, index, value;
1092
1093 if (typeof start !== 'number') {
1094 return false;
1095 }
1096
1097 value = textarea.value.toLowerCase();
1098
1099 for (i = 0; i < startTags.length; i++) {
1100 var tagLength = startTags[i].length;
1101 if (start >= tagLength) {
1102 index = value.lastIndexOf(startTags[i], start - tagLength);
1103 lastStart = Math.max(lastStart, index);
1104 }
1105 }
1106 if (lastStart === -1) {
1107 return false;
1108 }
1109
1110 if (start > 0) {
1111 for (i = 0; i < endTags.length; i++) {
1112 index = value.lastIndexOf(endTags[i], start - 1);
1113 lastEnd = Math.max(lastEnd, index);
1114 }
1115 }
1116
1117 return (lastEnd < lastStart);
1118 };
1119
1120
1121 /**
1122 * Adjust textarea to manage code bbcode
1123 *
1124 * This function allows to use tab characters when typing code
1125 * and keeps indentation of previous line of code when adding new
1126 * line while typing code.
1127 *
1128 * Editor's functionality is changed only when cursor is between
1129 * [code] and [/code] bbcode tags.
1130 *
1131 * @param {object} textarea Textarea DOM object to apply editor to
1132 */
1133 phpbb.applyCodeEditor = function(textarea) {
1134 // list of allowed start and end bbcode code tags, in lower case
1135 var startTags = ['[code]', '[code='],
1136 startTagsEnd = ']',
1137 endTags = ['[/code]'];
1138
1139 if (!textarea || typeof textarea.selectionStart !== 'number') {
1140 return;
1141 }
1142
1143 if ($(textarea).data('code-editor') === true) {
1144 return;
1145 }
1146
1147 function inTag() {
1148 return phpbb.inBBCodeTag(textarea, startTags, endTags);
1149 }
1150
1151 /**
1152 * Get line of text before cursor
1153 *
1154 * @param {boolean} stripCodeStart If true, only part of line
1155 * after [code] tag will be returned.
1156 *
1157 * @return {string} Line of text
1158 */
1159 function getLastLine(stripCodeStart) {
1160 var start = textarea.selectionStart,
1161 value = textarea.value,
1162 index = value.lastIndexOf('\n', start - 1);
1163
1164 value = value.substring(index + 1, start);
1165
1166 if (stripCodeStart) {
1167 for (var i = 0; i < startTags.length; i++) {
1168 index = value.lastIndexOf(startTags[i]);
1169 if (index >= 0) {
1170 var tagLength = startTags[i].length;
1171
1172 value = value.substring(index + tagLength);
1173 if (startTags[i].lastIndexOf(startTagsEnd) != tagLength) {
1174 index = value.indexOf(startTagsEnd);
1175
1176 if (index >= 0) {
1177 value = value.substr(index + 1);
1178 }
1179 }
1180 }
1181 }
1182 }
1183
1184 return value;
1185 }
1186
1187 /**
1188 * Append text at cursor position
1189 *
1190 * @param {string} Text Text to append
1191 */
1192 function appendText(text) {
1193 var start = textarea.selectionStart,
1194 end = textarea.selectionEnd,
1195 value = textarea.value;
1196
1197 textarea.value = value.substr(0, start) + text + value.substr(end);
1198 textarea.selectionStart = textarea.selectionEnd = start + text.length;
1199 }
1200
1201 $(textarea).data('code-editor', true).on('keydown', function(event) {
1202 var key = event.keyCode || event.which;
1203
1204 // intercept tabs
1205 if (key === keymap.TAB &&
1206 !event.ctrlKey &&
1207 !event.shiftKey &&
1208 !event.altKey &&
1209 !event.metaKey) {
1210 if (inTag()) {
1211 appendText('\t');
1212 event.preventDefault();
1213 return;
1214 }
1215 }
1216
1217 // intercept new line characters
1218 if (key === keymap.ENTER) {
1219 if (inTag()) {
1220 var lastLine = getLastLine(true),
1221 code = '' + /^\s*/g.exec(lastLine);
1222
1223 if (code.length > 0) {
1224 appendText('\n' + code);
1225 event.preventDefault();
1226 }
1227 }
1228 }
1229 });
1230 };
1231
1232 /**
1233 * List of classes that toggle dropdown menu,
1234 * list of classes that contain visible dropdown menu
1235 *
1236 * Add your own classes to strings with comma (probably you
1237 * will never need to do that)
1238 */
1239 phpbb.dropdownHandles = '.dropdown-container.dropdown-visible .dropdown-toggle';
1240 phpbb.dropdownVisibleContainers = '.dropdown-container.dropdown-visible';
1241
1242 /**
1243 * Dropdown toggle event handler
1244 * This handler is used by phpBB.registerDropdown() and other functions
1245 */
1246 phpbb.toggleDropdown = function() {
1247 var $this = $(this),
1248 options = $this.data('dropdown-options'),
1249 parent = options.parent,
1250 visible = parent.hasClass('dropdown-visible'),
1251 direction;
1252
1253 if (!visible) {
1254 // Hide other dropdown menus
1255 $(phpbb.dropdownHandles).each(phpbb.toggleDropdown);
1256
1257 // Figure out direction of dropdown
1258 direction = options.direction;
1259 var verticalDirection = options.verticalDirection,
1260 offset = $this.offset();
1261
1262 if (direction === 'auto') {
1263 if (($(window).width() - $this.outerWidth(true)) / 2 > offset.left) {
1264 direction = 'right';
1265 } else {
1266 direction = 'left';
1267 }
1268 }
1269 parent.toggleClass(options.leftClass, direction === 'left')
1270 .toggleClass(options.rightClass, direction === 'right');
1271
1272 if (verticalDirection === 'auto') {
1273 var height = $(window).height(),
1274 top = offset.top - $(window).scrollTop();
1275
1276 verticalDirection = (top < height * 0.7) ? 'down' : 'up';
1277 }
1278 parent.toggleClass(options.upClass, verticalDirection === 'up')
1279 .toggleClass(options.downClass, verticalDirection === 'down');
1280 }
1281
1282 options.dropdown.toggle();
1283 parent.toggleClass(options.visibleClass, !visible)
1284 .toggleClass('dropdown-visible', !visible);
1285
1286 // Check dimensions when showing dropdown
1287 // !visible because variable shows state of dropdown before it was toggled
1288 if (!visible) {
1289 var windowWidth = $(window).width();
1290
1291 options.dropdown.find('.dropdown-contents').each(function() {
1292 var $this = $(this);
1293
1294 $this.css({
1295 marginLeft: 0,
1296 left: 0,
1297 maxWidth: (windowWidth - 4) + 'px'
1298 });
1299
1300 var offset = $this.offset().left,
1301 width = $this.outerWidth(true);
1302
1303 if (offset < 2) {
1304 $this.css('left', (2 - offset) + 'px');
1305 } else if ((offset + width + 2) > windowWidth) {
1306 $this.css('margin-left', (windowWidth - offset - width - 2) + 'px');
1307 }
1308
1309 // Check whether the vertical scrollbar is present.
1310 $this.toggleClass('dropdown-nonscroll', this.scrollHeight === $this.innerHeight());
1311
1312 });
1313 var freeSpace = parent.offset().left - 4;
1314
1315 if (direction === 'left') {
1316 options.dropdown.css('margin-left', '-' + freeSpace + 'px');
1317
1318 // Try to position the notification dropdown correctly in RTL-responsive mode
1319 if (options.dropdown.hasClass('dropdown-extended')) {
1320 var contentWidth,
1321 fullFreeSpace = freeSpace + parent.outerWidth();
1322
1323 options.dropdown.find('.dropdown-contents').each(function() {
1324 contentWidth = parseInt($(this).outerWidth());
1325 $(this).css({marginLeft: 0, left: 0});
1326 });
1327
1328 var maxOffset = Math.min(contentWidth, fullFreeSpace) + 'px';
1329 options.dropdown.css({'width': maxOffset, 'margin-left': '-' + maxOffset});
1330 }
1331 } else {
1332 options.dropdown.css('margin-right', '-' + (windowWidth + freeSpace) + 'px');
1333 }
1334 }
1335
1336 // Prevent event propagation
1337 if (arguments.length > 0) {
1338 try {
1339 var e = arguments[0];
1340 e.preventDefault();
1341 e.stopPropagation();
1342 } catch (error) { }
1343 }
1344 return false;
1345 };
1346
1347 /**
1348 * Toggle dropdown submenu
1349 */
1350 phpbb.toggleSubmenu = function(e) {
1351 $(this).siblings('.dropdown-submenu').toggle();
1352 e.preventDefault();
1353 };
1354
1355 /**
1356 * Register dropdown menu
1357 * Shows/hides dropdown, decides which side to open to
1358 *
1359 * @param {jQuery} toggle Link that toggles dropdown.
1360 * @param {jQuery} dropdown Dropdown menu.
1361 * @param {Object} options List of options. Optional.
1362 */
1363 phpbb.registerDropdown = function(toggle, dropdown, options) {
1364 var ops = {
1365 parent: toggle.parent(), // Parent item to add classes to
1366 direction: 'auto', // Direction of dropdown menu. Possible values: auto, left, right
1367 verticalDirection: 'auto', // Vertical direction. Possible values: auto, up, down
1368 visibleClass: 'visible', // Class to add to parent item when dropdown is visible
1369 leftClass: 'dropdown-left', // Class to add to parent item when dropdown opens to left side
1370 rightClass: 'dropdown-right', // Class to add to parent item when dropdown opens to right side
1371 upClass: 'dropdown-up', // Class to add to parent item when dropdown opens above menu item
1372 downClass: 'dropdown-down' // Class to add to parent item when dropdown opens below menu item
1373 };
1374 if (options) {
1375 ops = $.extend(ops, options);
1376 }
1377 ops.dropdown = dropdown;
1378
1379 ops.parent.addClass('dropdown-container');
1380 toggle.addClass('dropdown-toggle');
1381
1382 toggle.data('dropdown-options', ops);
1383
1384 toggle.click(phpbb.toggleDropdown);
1385 $('.dropdown-toggle-submenu', ops.parent).click(phpbb.toggleSubmenu);
1386 };
1387
1388 /**
1389 * Get the HTML for a color palette table.
1390 *
1391 * @param string dir Palette direction - either v or h
1392 * @param int width Palette cell width.
1393 * @param int height Palette cell height.
1394 */
1395 phpbb.colorPalette = function(dir, width, height) {
1396 var r = 0,
1397 g = 0,
1398 b = 0,
1399 numberList = new Array(6),
1400 color = '',
1401 html = '';
1402
1403 numberList[0] = '00';
1404 numberList[1] = '40';
1405 numberList[2] = '80';
1406 numberList[3] = 'BF';
1407 numberList[4] = 'FF';
1408
1409 var tableClass = (dir == 'h') ? 'horizontal-palette' : 'vertical-palette';
1410 html += '<table class="not-responsive colour-palette ' + tableClass + '" style="width: auto;">';
1411
1412 for (r = 0; r < 5; r++) {
1413 if (dir == 'h') {
1414 html += '<tr>';
1415 }
1416
1417 for (g = 0; g < 5; g++) {
1418 if (dir == 'v') {
1419 html += '<tr>';
1420 }
1421
1422 for (b = 0; b < 5; b++) {
1423 color = String(numberList[r]) + String(numberList[g]) + String(numberList[b]);
1424 html += '<td style="background-color: #' + color + '; width: ' + width + 'px; height: ' + height + 'px;">';
1425 html += '<a href="#" data-color="' + color + '" style="display: block; width: ' + width + 'px; height: ' + height + 'px; " alt="#' + color + '" title="#' + color + '"></a>';
1426 html += '</td>';
1427 }
1428
1429 if (dir == 'v') {
1430 html += '</tr>';
1431 }
1432 }
1433
1434 if (dir == 'h') {
1435 html += '</tr>';
1436 }
1437 }
1438 html += '</table>';
1439 return html;
1440 };
1441
1442 /**
1443 * Register a color palette.
1444 *
1445 * @param object el jQuery object for the palette container.
1446 */
1447 phpbb.registerPalette = function(el) {
1448 var orientation = el.attr('data-orientation'),
1449 height = el.attr('data-height'),
1450 width = el.attr('data-width'),
1451 target = el.attr('data-target'),
1452 bbcode = el.attr('data-bbcode');
1453
1454 // Insert the palette HTML into the container.
1455 el.html(phpbb.colorPalette(orientation, width, height));
1456
1457 // Add toggle control.
1458 $('#color_palette_toggle').click(function(e) {
1459 el.toggle();
1460 e.preventDefault();
1461 });
1462
1463 // Attach event handler when a palette cell is clicked.
1464 $(el).on('click', 'a', function(e) {
1465 var color = $(this).attr('data-color');
1466
1467 if (bbcode) {
1468 bbfontstyle('[color=#' + color + ']', '[/color]');
1469 } else {
1470 $(target).val(color);
1471 }
1472 e.preventDefault();
1473 });
1474 }
1475
1476 /**
1477 * Set display of page element
1478 *
1479 * @param string id The ID of the element to change
1480 * @param int action Set to 0 if element display should be toggled, -1 for
1481 * hiding the element, and 1 for showing it.
1482 * @param string type Display type that should be used, e.g. inline, block or
1483 * other CSS "display" types
1484 */
1485 phpbb.toggleDisplay = function(id, action, type) {
1486 if (!type) {
1487 type = 'block';
1488 }
1489
1490 var $element = $('#' + id);
1491
1492 var display = $element.css('display');
1493 if (!action) {
1494 action = (display === '' || display === type) ? -1 : 1;
1495 }
1496 $element.css('display', ((action === 1) ? type : 'none'));
1497 };
1498
1499 /**
1500 * Toggle additional settings based on the selected
1501 * option of select element.
1502 *
1503 * @param jQuery el jQuery select element object.
1504 * @return undefined
1505 */
1506 phpbb.toggleSelectSettings = function(el) {
1507 el.children().each(function() {
1508 var $this = $(this),
1509 $setting = $($this.data('toggle-setting'));
1510 $setting.toggle($this.is(':selected'));
1511 });
1512 };
1513
1514 /**
1515 * Get function from name.
1516 * Based on http://stackoverflow.com/a/359910
1517 *
1518 * @param string functionName Function to get.
1519 * @return function
1520 */
1521 phpbb.getFunctionByName = function (functionName) {
1522 var namespaces = functionName.split('.'),
1523 func = namespaces.pop(),
1524 context = window;
1525
1526 for (var i = 0; i < namespaces.length; i++) {
1527 context = context[namespaces[i]];
1528 }
1529 return context[func];
1530 };
1531
1532 /**
1533 * Register page dropdowns.
1534 */
1535 phpbb.registerPageDropdowns = function() {
1536 var $body = $('body');
1537
1538 $body.find('.dropdown-container').each(function() {
1539 var $this = $(this),
1540 $trigger = $this.find('.dropdown-trigger:first'),
1541 $contents = $this.find('.dropdown'),
1542 options = {
1543 direction: 'auto',
1544 verticalDirection: 'auto'
1545 },
1546 data;
1547
1548 if (!$trigger.length) {
1549 data = $this.attr('data-dropdown-trigger');
1550 $trigger = data ? $this.children(data) : $this.children('a:first');
1551 }
1552
1553 if (!$contents.length) {
1554 data = $this.attr('data-dropdown-contents');
1555 $contents = data ? $this.children(data) : $this.children('div:first');
1556 }
1557
1558 if (!$trigger.length || !$contents.length) {
1559 return;
1560 }
1561
1562 if ($this.hasClass('dropdown-up')) {
1563 options.verticalDirection = 'up';
1564 }
1565 if ($this.hasClass('dropdown-down')) {
1566 options.verticalDirection = 'down';
1567 }
1568 if ($this.hasClass('dropdown-left')) {
1569 options.direction = 'left';
1570 }
1571 if ($this.hasClass('dropdown-right')) {
1572 options.direction = 'right';
1573 }
1574
1575 phpbb.registerDropdown($trigger, $contents, options);
1576 });
1577
1578 // Hide active dropdowns when click event happens outside
1579 $body.click(function(e) {
1580 var $parents = $(e.target).parents();
1581 if (!$parents.is(phpbb.dropdownVisibleContainers)) {
1582 $(phpbb.dropdownHandles).each(phpbb.toggleDropdown);
1583 }
1584 });
1585 };
1586
1587 /**
1588 * Apply code editor to all textarea elements with data-bbcode attribute
1589 */
1590 $(function() {
1591 $('textarea[data-bbcode]').each(function() {
1592 phpbb.applyCodeEditor(this);
1593 });
1594
1595 phpbb.registerPageDropdowns();
1596
1597 $('#color_palette_placeholder').each(function() {
1598 phpbb.registerPalette($(this));
1599 });
1600
1601 // Update browser history URL to point to specific post in viewtopic.php
1602 // when using view=unread#unread link.
1603 phpbb.history.replaceUrl($('#unread[data-url]').data('url'));
1604
1605 // Hide settings that are not selected via select element.
1606 $('select[data-togglable-settings]').each(function() {
1607 var $this = $(this);
1608
1609 $this.change(function() {
1610 phpbb.toggleSelectSettings($this);
1611 });
1612 phpbb.toggleSelectSettings($this);
1613 });
1614 });
1615
1616 })(jQuery); // Avoid conflicts with other libraries
1617