diff options
Diffstat (limited to 'phpBB/assets/javascript')
-rw-r--r-- | phpBB/assets/javascript/core.js | 371 | ||||
-rw-r--r-- | phpBB/assets/javascript/editor.js | 69 | ||||
-rw-r--r-- | phpBB/assets/javascript/installer.js | 515 | ||||
-rw-r--r-- | phpBB/assets/javascript/plupload.js | 314 |
4 files changed, 925 insertions, 344 deletions
diff --git a/phpBB/assets/javascript/core.js b/phpBB/assets/javascript/core.js index 806db7d35f..91f5521c7a 100644 --- a/phpBB/assets/javascript/core.js +++ b/phpBB/assets/javascript/core.js @@ -1,3 +1,5 @@ +/* global bbfontstyle */ + var phpbb = {}; phpbb.alertTime = 100; @@ -13,7 +15,7 @@ var keymap = { }; var $dark = $('#darkenwrapper'); -var $loadingIndicator = $('#loading_indicator'); +var $loadingIndicator; var phpbbAlertTimer = null; phpbb.isTouch = (window && typeof window.ontouchstart !== 'undefined'); @@ -21,9 +23,17 @@ phpbb.isTouch = (window && typeof window.ontouchstart !== 'undefined'); /** * Display a loading screen * - * @returns object Returns loadingIndicator. + * @returns {object} Returns loadingIndicator. */ phpbb.loadingIndicator = function() { + if (!$loadingIndicator) { + $loadingIndicator = $('<div />', { + id: 'loading_indicator', + class: 'loading_indicator', + }); + $loadingIndicator.appendTo('#page-footer'); + } + if (!$loadingIndicator.is(':visible')) { $loadingIndicator.fadeIn(phpbb.alertTime); // Wait fifteen seconds and display an error if nothing has been returned by then. @@ -54,7 +64,7 @@ phpbb.clearLoadingTimeout = function() { /** * Close popup alert after a specified delay * -* @param int Delay in ms until darkenwrapper's click event is triggered +* @param {int} delay Delay in ms until darkenwrapper's click event is triggered */ phpbb.closeDarkenWrapper = function(delay) { phpbbAlertTimer = setTimeout(function() { @@ -67,14 +77,12 @@ phpbb.closeDarkenWrapper = function(delay) { * * You can only call one alert or confirm box at any one time. * - * @param string title Title of the message, eg "Information" (HTML). - * @param string msg Message to display (HTML). - * @param bool fadedark Remove the dark background when done? Defaults - * to yes. + * @param {string} title Title of the message, eg "Information" (HTML). + * @param {string} msg Message to display (HTML). * - * @returns object Returns the div created. + * @returns {object} Returns the div created. */ -phpbb.alert = function(title, msg, fadedark) { +phpbb.alert = function(title, msg) { var $alert = $('#phpbb_alert'); $alert.find('.alert_title').html(title); $alert.find('.alert_text').html(msg); @@ -94,14 +102,14 @@ phpbb.alert = function(title, msg, fadedark) { /** * Handler for opening an alert box. * -* @param jQuery $alert jQuery object. +* @param {jQuery} $alert jQuery object. */ phpbb.alert.open = function($alert) { if (!$dark.is(':visible')) { $dark.fadeIn(phpbb.alertTime); } - if ($loadingIndicator.is(':visible')) { + if ($loadingIndicator && $loadingIndicator.is(':visible')) { $loadingIndicator.fadeOut(phpbb.alertTime, function() { $dark.append($alert); $alert.fadeIn(phpbb.alertTime); @@ -134,8 +142,8 @@ phpbb.alert.open = function($alert) { /** * Handler for closing an alert box. * -* @param jQuery $alert jQuery object. -* @param bool fadedark Whether to remove dark background. +* @param {jQuery} $alert jQuery object. +* @param {bool} fadedark Whether to remove dark background. */ phpbb.alert.close = function($alert, fadedark) { var $fade = (fadedark) ? $dark : $alert; @@ -153,13 +161,13 @@ phpbb.alert.close = function($alert, fadedark) { * * You can only call one alert or confirm box at any one time. * - * @param string msg Message to display (HTML). - * @param function callback Callback. Bool param, whether the user pressed + * @param {string} msg Message to display (HTML). + * @param {function} callback Callback. Bool param, whether the user pressed * yes or no (or whatever their language is). - * @param bool fadedark Remove the dark background when done? Defaults + * @param {bool} fadedark Remove the dark background when done? Defaults * to yes. * - * @returns object Returns the div created. + * @returns {object} Returns the div created. */ phpbb.confirm = function(msg, callback, fadedark) { var $confirmDiv = $('#phpbb_confirm'); @@ -197,8 +205,8 @@ phpbb.confirm = function(msg, callback, fadedark) { /** * Turn a querystring into an array. * - * @argument string string The querystring to parse. - * @returns object The object created. + * @argument {string} string The querystring to parse. + * @returns {object} The object created. */ phpbb.parseQuerystring = function(string) { var params = {}, i, split; @@ -223,12 +231,7 @@ phpbb.parseQuerystring = function(string) { * For more info, view the following page on the phpBB wiki: * http://wiki.phpbb.com/JavaScript_Function.phpbb.ajaxify * - * @param object options Options. - * @param bool/function refresh If we are sent back a refresh, should it be - * acted upon? This can either be true / false / a function. - * @param function callback Callback to call on completion of event. Has - * three parameters: the element that the event was evoked from, the JSON - * that was returned and (if it is a form) the form action. + * @param {object} options Options. */ phpbb.ajaxify = function(options) { var $elements = $(options.selector), @@ -262,11 +265,16 @@ phpbb.ajaxify = function(options) { console.log('AJAX error. status: ' + textStatus + ', message: ' + errorThrown); } phpbb.clearLoadingTimeout(); - var errorText = false; - if (typeof errorThrown === 'string' && errorThrown.length > 0) { + var responseText, errorText = false; + try { + responseText = JSON.parse(jqXHR.responseText); + responseText = responseText.message; + } catch (e) {} + if (typeof responseText === 'string' && responseText.length > 0) { + errorText = responseText; + } else if (typeof errorThrown === 'string' && errorThrown.length > 0) { errorText = errorThrown; - } - else { + } else { errorText = $dark.attr('data-ajax-error-text-' + textStatus); if (typeof errorText !== 'string' || !errorText.length) { errorText = $dark.attr('data-ajax-error-text'); @@ -283,7 +291,7 @@ phpbb.ajaxify = function(options) { * It cannot be called from outside this function, and is purely here to * avoid repetition of code. * - * @param object res The object sent back by the server. + * @param {object} res The object sent back by the server. */ function returnHandler(res) { var alert; @@ -298,6 +306,10 @@ phpbb.ajaxify = function(options) { alert = phpbb.alert(res.MESSAGE_TITLE, res.MESSAGE_TEXT); } else { $dark.fadeOut(phpbb.alertTime); + + if ($loadingIndicator) { + $loadingIndicator.fadeOut(phpbb.alertTime); + } } if (typeof phpbb.ajaxCallbacks[callback] === 'function') { @@ -389,8 +401,11 @@ phpbb.ajaxify = function(options) { error: errorHandler, cache: false }); + request.always(function() { - $loadingIndicator.fadeOut(phpbb.alertTime); + if ($loadingIndicator && $loadingIndicator.is(':visible')) { + $loadingIndicator.fadeOut(phpbb.alertTime); + } }); }; @@ -427,10 +442,10 @@ phpbb.search = { }; /** - * Get cached search data. + * Get cached search data. * - * @param string id Search ID. - * @return bool|object. Cached data object. Returns false if no data exists. + * @param {string} id Search ID. + * @returns {bool|object} Cached data object. Returns false if no data exists. */ phpbb.search.cache.get = function(id) { if (this.data[id]) { @@ -440,39 +455,35 @@ phpbb.search.cache.get = function(id) { }; /** - * Set search cache data value. - * - * @param string id Search ID. - * @param string key Data key. - * @param string value Data value. + * Set search cache data value. * - * @return undefined + * @param {string} id Search ID. + * @param {string} key Data key. + * @param {string} value Data value. */ phpbb.search.cache.set = function(id, key, value) { if (!this.data[id]) { - this.data[id] = {results: []}; + this.data[id] = { results: [] }; } this.data[id][key] = value; }; /** - * Cache search result. - * - * @param string id Search ID. - * @param string keyword Keyword. - * @param array results Search results. + * Cache search result. * - * @return undefined + * @param {string} id Search ID. + * @param {string} keyword Keyword. + * @param {Array} results Search results. */ -phpbb.search.cache.setResults = function(id, keyword, value) { - this.data[id].results[keyword] = value; +phpbb.search.cache.setResults = function(id, keyword, results) { + this.data[id].results[keyword] = results; }; /** * Trim spaces from keyword and lower its case. * - * @param string keyword Search keyword to clean. - * @return string Cleaned string. + * @param {string} keyword Search keyword to clean. + * @returns {string} Cleaned string. */ phpbb.search.cleanKeyword = function(keyword) { return $.trim(keyword).toLowerCase(); @@ -482,11 +493,11 @@ phpbb.search.cleanKeyword = function(keyword) { * Get clean version of search keyword. If textarea supports several keywords * (one per line), it fetches the current keyword based on the caret position. * - * @param jQuery $input Search input|textarea. - * @param string keyword Input|textarea value. - * @param bool multiline Whether textarea supports multiple search keywords. + * @param {jQuery} $input Search input|textarea. + * @param {string} keyword Input|textarea value. + * @param {bool} multiline Whether textarea supports multiple search keywords. * - * @return string Clean string. + * @returns string Clean string. */ phpbb.search.getKeyword = function($input, keyword, multiline) { if (multiline) { @@ -498,10 +509,10 @@ phpbb.search.getKeyword = function($input, keyword, multiline) { /** * Get the textarea line number on which the keyword resides - for textareas - * that support multiple keywords (one per line). + * that support multiple keywords (one per line). * - * @param jQuery $textarea Search textarea. - * @return int + * @param {jQuery} $textarea Search textarea. + * @returns {int} The line number. */ phpbb.search.getKeywordLine = function ($textarea) { var selectionStart = $textarea.get(0).selectionStart; @@ -512,11 +523,9 @@ phpbb.search.getKeywordLine = function ($textarea) { * Set the value on the input|textarea. If textarea supports multiple * keywords, only the active keyword is replaced. * - * @param jQuery $input Search input|textarea. - * @param string value Value to set. - * @param bool multiline Whether textarea supports multiple search keywords. - * - * @return undefined + * @param {jQuery} $input Search input|textarea. + * @param {string} value Value to set. + * @param {bool} multiline Whether textarea supports multiple search keywords. */ phpbb.search.setValue = function($input, value, multiline) { if (multiline) { @@ -529,14 +538,13 @@ phpbb.search.setValue = function($input, value, multiline) { }; /** - * Sets the onclick event to set the value on the input|textarea to the selected search result. - * - * @param jQuery $input Search input|textarea. - * @param object value Result object. - * @param jQuery $row Result element. - * @param jQuery $container jQuery object for the search container. + * Sets the onclick event to set the value on the input|textarea to the + * selected search result. * - * @return undefined + * @param {jQuery} $input Search input|textarea. + * @param {object} value Result object. + * @param {jQuery} $row Result element. + * @param {jQuery} $container jQuery object for the search container. */ phpbb.search.setValueOnClick = function($input, value, $row, $container) { $row.click(function() { @@ -551,16 +559,16 @@ phpbb.search.setValueOnClick = function($input, value, $row, $container) { * already, those are displayed instead. Executes the AJAX request function * itself due to the need to use a timeout to limit the number of requests. * - * @param array data Data to be sent to the server. - * @param object event Onkeyup event object. - * @param function sendRequest Function to execute AJAX request. + * @param {Array} data Data to be sent to the server. + * @param {object} event Onkeyup event object. + * @param {function} sendRequest Function to execute AJAX request. * - * @return bool Returns false. + * @returns {bool} Returns false. */ phpbb.search.filter = function(data, event, sendRequest) { var $this = $(this), dataName = ($this.attr('data-name') !== undefined) ? $this.attr('data-name') : $this.attr('name'), - minLength = parseInt($this.attr('data-min-length')), + minLength = parseInt($this.attr('data-min-length'), 10), searchID = $this.attr('data-results'), keyword = phpbb.search.getKeyword($this, data[dataName], $this.attr('data-multiline')), cache = phpbb.search.cache.get(searchID), @@ -582,7 +590,10 @@ phpbb.search.filter = function(data, event, sendRequest) { } else { // Do we already have results for this? if (cache.results[keyword]) { - var response = {keyword: keyword, results: cache.results[keyword]}; + var response = { + keyword: keyword, + results: cache.results[keyword] + }; phpbb.search.handleResponse(response, $this, true); proceed = false; } @@ -593,8 +604,8 @@ phpbb.search.filter = function(data, event, sendRequest) { phpbb.search.cache.set(searchID, 'lastSearch', keyword); phpbb.search.cache.setResults(searchID, keyword, []); proceed = false; - } - } + } + } } if (proceed) { @@ -607,14 +618,12 @@ phpbb.search.filter = function(data, event, sendRequest) { }; /** - * Handle search result response. + * Handle search result response. * - * @param object res Data received from server. - * @param jQuery $input Search input|textarea. - * @param bool fromCache Whether the results are from the cache. - * @param function callback Optional callback to run when assigning each search result. - * - * @return undefined + * @param {object} res Data received from server. + * @param {jQuery} $input Search input|textarea. + * @param {bool} fromCache Whether the results are from the cache. + * @param {function} callback Optional callback to run when assigning each search result. */ phpbb.search.handleResponse = function(res, $input, fromCache, callback) { if (typeof res !== 'object') { @@ -641,12 +650,10 @@ phpbb.search.handleResponse = function(res, $input, fromCache, callback) { /** * Show search results. * - * @param array results Search results. - * @param jQuery $input Search input|textarea. - * @param jQuery $container Search results container element. - * @param function callback Optional callback to run when assigning each search result. - * - * @return undefined + * @param {Array} results Search results. + * @param {jQuery} $input Search input|textarea. + * @param {jQuery} $container Search results container element. + * @param {function} callback Optional callback to run when assigning each search result. */ phpbb.search.showResults = function(results, $input, $container, callback) { var $resultContainer = $('.search-results', $container); @@ -683,8 +690,7 @@ phpbb.search.showResults = function(results, $input, $container, callback) { /** * Clear search results. * - * @param jQuery $container Search results container. - * @return undefined + * @param {jQuery} $container Search results container. */ phpbb.search.clearResults = function($container) { $container.children(':not(.search-result-tpl)').remove(); @@ -703,8 +709,8 @@ phpbb.history = {}; /** * Check whether a method in the native history object is supported. * -* @param string fn Method name. -* @return bool Returns true if the method is supported. +* @param {string} fn Method name. +* @returns {bool} Returns true if the method is supported. */ phpbb.history.isSupported = function(fn) { return !(typeof history === 'undefined' || typeof history[fn] === 'undefined'); @@ -714,12 +720,10 @@ phpbb.history.isSupported = function(fn) { * Wrapper for the pushState and replaceState methods of the * native history object. * -* @param string mode Mode. Either push or replace. -* @param string url New URL. -* @param string title Optional page title. -* @patam object obj Optional state object. -* -* @return undefined +* @param {string} mode Mode. Either push or replace. +* @param {string} url New URL. +* @param {string} [title] Optional page title. +* @param {object} [obj] Optional state object. */ phpbb.history.alterUrl = function(mode, url, title, obj) { var fn = mode + 'State'; @@ -740,11 +744,9 @@ phpbb.history.alterUrl = function(mode, url, title, obj) { /** * Wrapper for the native history.replaceState method. * -* @param string url New URL. -* @param string title Optional page title. -* @patam object obj Optional state object. -* -* @return undefined +* @param {string} url New URL. +* @param {string} [title] Optional page title. +* @param {object} [obj] Optional state object. */ phpbb.history.replaceUrl = function(url, title, obj) { phpbb.history.alterUrl('replace', url, title, obj); @@ -753,11 +755,9 @@ phpbb.history.replaceUrl = function(url, title, obj) { /** * Wrapper for the native history.pushState method. * -* @param string url New URL. -* @param string title Optional page title. -* @patam object obj Optional state object. -* -* @return undefined +* @param {string} url New URL. +* @param {string} [title] Optional page title. +* @param {object} [obj] Optional state object. */ phpbb.history.pushUrl = function(url, title, obj) { phpbb.history.alterUrl('push', url, title, obj); @@ -766,7 +766,8 @@ phpbb.history.pushUrl = function(url, title, obj) { /** * Hide the optgroups that are not the selected timezone * -* @param bool keepSelection Shall we keep the value selected, or shall the user be forced to repick one. +* @param {bool} keepSelection Shall we keep the value selected, or shall the +* user be forced to repick one. */ phpbb.timezoneSwitchDate = function(keepSelection) { var $timezoneCopy = $('#timezone_copy'); @@ -789,7 +790,7 @@ phpbb.timezoneSwitchDate = function(keepSelection) { } if ($tzDate.val() !== '') { - $timezone.children('optgroup').remove(':not([data-tz-value="' + $('#tz_date').val() + '"])'); + $timezone.children('optgroup').remove(':not([data-tz-value="' + $tzDate.val() + '"])'); } if ($tzDate.val() === $tzSelectDateSuggest.attr('data-suggested-tz')) { @@ -797,7 +798,7 @@ phpbb.timezoneSwitchDate = function(keepSelection) { } else { $tzSelectDateSuggest.css('display', 'inline'); } - + var $tzOptions = $timezone.children('optgroup[data-tz-value="' + $tzDate.val() + '"]').children('option'); if ($tzOptions.length === 1) { @@ -824,7 +825,7 @@ phpbb.timezoneEnableDateSelection = function() { /** * Preselect a date/time or suggest one, if it is not picked. * -* @param bool forceSelector Shall we select the suggestion? +* @param {bool} forceSelector Shall we select the suggestion? */ phpbb.timezonePreselectSelect = function(forceSelector) { @@ -893,8 +894,8 @@ phpbb.ajaxCallbacks = {}; * * See the phpbb.ajaxify comments for information on stuff like parameters. * - * @param string id The name of the callback. - * @param function callback The callback to be called. + * @param {string} id The name of the callback. + * @param {function} callback The callback to be called. */ phpbb.addAjaxCallback = function(id, callback) { if (typeof callback === 'function') { @@ -960,12 +961,6 @@ phpbb.addAjaxCallback('toggle_link', function() { $anchor.each(function() { var $this = $(this); - // Toggle link text - toggleText = $this.attr('data-toggle-text'); - $this.attr('data-toggle-text', $this.text()); - $this.attr('title', $.trim(toggleText)); - $this.text(toggleText); - // Toggle link url toggleUrl = $this.attr('data-toggle-url'); $this.attr('data-toggle-url', $this.attr('href')); @@ -973,8 +968,14 @@ phpbb.addAjaxCallback('toggle_link', function() { // Toggle class of link parent toggleClass = $this.attr('data-toggle-class'); - $this.attr('data-toggle-class', $this.parent().attr('class')); - $this.parent().attr('class', toggleClass); + $this.attr('data-toggle-class', $this.children().attr('class')); + $this.children('.icon').attr('class', toggleClass); + + // Toggle link text + toggleText = $this.attr('data-toggle-text'); + $this.attr('data-toggle-text', $this.children('span').text()); + $this.attr('title', $.trim(toggleText)); + $this.children('span').text(toggleText); }); }); @@ -985,7 +986,7 @@ phpbb.addAjaxCallback('toggle_link', function() { * types text. * * @param {jQuery} $items jQuery object(s) to resize -* @param {object} options Optional parameter that adjusts default +* @param {object} [options] Optional parameter that adjusts default * configuration. See configuration variable * * Optional parameters: @@ -1022,15 +1023,19 @@ phpbb.resizeTextArea = function($items, options) { function resetAutoResize(item) { var $item = $(item); if ($item.hasClass('auto-resized')) { - $(item).css({height: '', resize: ''}).removeClass('auto-resized'); + $(item) + .css({ height: '', resize: '' }) + .removeClass('auto-resized'); configuration.resetCallback.call(item, $item); } } function autoResize(item) { function setHeight(height) { - height += parseInt($item.css('height')) - $item.height(); - $item.css({height: height + 'px', resize: 'none'}).addClass('auto-resized'); + height += parseInt($item.css('height'), 10) - $item.height(); + $item + .css({ height: height + 'px', resize: 'none' }) + .addClass('auto-resized'); configuration.resizeCallback.call(item, $item); } @@ -1046,7 +1051,7 @@ phpbb.resizeTextArea = function($items, options) { configuration.maxHeight ), $item = $(item), - height = parseInt($item.height()), + height = parseInt($item.height(), 10), scrollHeight = (item.scrollHeight) ? item.scrollHeight : 0; if (height < 0) { @@ -1055,8 +1060,7 @@ phpbb.resizeTextArea = function($items, options) { if (height > maxHeight) { setHeight(maxHeight); - } - else if (scrollHeight > (height + 5)) { + } else if (scrollHeight > (height + 5)) { setHeight(Math.min(maxHeight, scrollHeight)); } } @@ -1085,7 +1089,7 @@ phpbb.resizeTextArea = function($items, options) { * @param {Array} endTags List of end tags to look for * For example, Array('[/code]') * -* @return {boolean} True if cursor is in bbcode tag +* @returns {boolean} True if cursor is in bbcode tag */ phpbb.inBBCodeTag = function(textarea, startTags, endTags) { var start = textarea.selectionStart, @@ -1157,7 +1161,7 @@ phpbb.applyCodeEditor = function(textarea) { * @param {boolean} stripCodeStart If true, only part of line * after [code] tag will be returned. * - * @return {string} Line of text + * @returns {string} Line of text */ function getLastLine(stripCodeStart) { var start = textarea.selectionStart, @@ -1173,7 +1177,7 @@ phpbb.applyCodeEditor = function(textarea) { var tagLength = startTags[i].length; value = value.substring(index + tagLength); - if (startTags[i].lastIndexOf(startTagsEnd) != tagLength) { + if (startTags[i].lastIndexOf(startTagsEnd) !== tagLength) { index = value.indexOf(startTagsEnd); if (index >= 0) { @@ -1190,7 +1194,7 @@ phpbb.applyCodeEditor = function(textarea) { /** * Append text at cursor position * - * @param {string} Text Text to append + * @param {string} text Text to append */ function appendText(text) { var start = textarea.selectionStart, @@ -1233,6 +1237,31 @@ phpbb.applyCodeEditor = function(textarea) { }; /** + * Show drag and drop animation when textarea is present + * + * This function will enable the drag and drop animation for a specified + * textarea. + * + * @param {HTMLElement} textarea Textarea DOM object to apply editor to + */ +phpbb.showDragNDrop = function(textarea) { + if (!textarea) { + return; + } + + $('body').on('dragenter dragover', function () { + $(textarea).addClass('drag-n-drop'); + }).on('dragleave dragout dragend drop', function() { + $(textarea).removeClass('drag-n-drop'); + }); + $(textarea).on('dragenter dragover', function () { + $(textarea).addClass('drag-n-drop-highlight'); + }).on('dragleave dragout dragend drop', function() { + $(textarea).removeClass('drag-n-drop-highlight'); + }); +}; + +/** * List of classes that toggle dropdown menu, * list of classes that contain visible dropdown menu * @@ -1324,12 +1353,15 @@ phpbb.toggleDropdown = function() { fullFreeSpace = freeSpace + parent.outerWidth(); options.dropdown.find('.dropdown-contents').each(function() { - contentWidth = parseInt($(this).outerWidth()); - $(this).css({marginLeft: 0, left: 0}); + contentWidth = parseInt($(this).outerWidth(), 10); + $(this).css({ marginLeft: 0, left: 0 }); }); var maxOffset = Math.min(contentWidth, fullFreeSpace) + 'px'; - options.dropdown.css({'width': maxOffset, 'margin-left': '-' + maxOffset}); + options.dropdown.css({ + width: maxOffset, + marginLeft: -maxOffset + }); } } else { options.dropdown.css('margin-right', '-' + (windowWidth + freeSpace) + 'px'); @@ -1391,14 +1423,12 @@ phpbb.registerDropdown = function(toggle, dropdown, options) { /** * Get the HTML for a color palette table. * -* @param string dir Palette direction - either v or h -* @param int width Palette cell width. -* @param int height Palette cell height. +* @param {string} dir Palette direction - either v or h +* @param {int} width Palette cell width. +* @param {int} height Palette cell height. */ phpbb.colorPalette = function(dir, width, height) { - var r = 0, - g = 0, - b = 0, + var r, g, b, numberList = new Array(6), color = '', html = ''; @@ -1409,32 +1439,33 @@ phpbb.colorPalette = function(dir, width, height) { numberList[3] = 'BF'; numberList[4] = 'FF'; - var tableClass = (dir == 'h') ? 'horizontal-palette' : 'vertical-palette'; + var tableClass = (dir === 'h') ? 'horizontal-palette' : 'vertical-palette'; html += '<table class="not-responsive colour-palette ' + tableClass + '" style="width: auto;">'; for (r = 0; r < 5; r++) { - if (dir == 'h') { + if (dir === 'h') { html += '<tr>'; } for (g = 0; g < 5; g++) { - if (dir == 'v') { + if (dir === 'v') { html += '<tr>'; } for (b = 0; b < 5; b++) { - color = String(numberList[r]) + String(numberList[g]) + String(numberList[b]); - html += '<td style="background-color: #' + color + '; width: ' + width + 'px; height: ' + height + 'px;">'; - html += '<a href="#" data-color="' + color + '" style="display: block; width: ' + width + 'px; height: ' + height + 'px; " alt="#' + color + '" title="#' + color + '"></a>'; + color = '' + numberList[r] + numberList[g] + numberList[b]; + html += '<td style="background-color: #' + color + '; width: ' + width + 'px; height: ' + + height + 'px;"><a href="#" data-color="' + color + '" style="display: block; width: ' + + width + 'px; height: ' + height + 'px; " alt="#' + color + '" title="#' + color + '"></a>'; html += '</td>'; } - if (dir == 'v') { + if (dir === 'v') { html += '</tr>'; } } - if (dir == 'h') { + if (dir === 'h') { html += '</tr>'; } } @@ -1445,7 +1476,7 @@ phpbb.colorPalette = function(dir, width, height) { /** * Register a color palette. * -* @param object el jQuery object for the palette container. +* @param {jQuery} el jQuery object for the palette container. */ phpbb.registerPalette = function(el) { var orientation = el.attr('data-orientation'), @@ -1474,15 +1505,15 @@ phpbb.registerPalette = function(el) { } e.preventDefault(); }); -} +}; /** * Set display of page element * -* @param string id The ID of the element to change -* @param int action Set to 0 if element display should be toggled, -1 for +* @param {string} id The ID of the element to change +* @param {int} action Set to 0 if element display should be toggled, -1 for * hiding the element, and 1 for showing it. -* @param string type Display type that should be used, e.g. inline, block or +* @param {string} type Display type that should be used, e.g. inline, block or * other CSS "display" types */ phpbb.toggleDisplay = function(id, action, type) { @@ -1503,8 +1534,7 @@ phpbb.toggleDisplay = function(id, action, type) { * Toggle additional settings based on the selected * option of select element. * -* @param jQuery el jQuery select element object. -* @return undefined +* @param {jQuery} el jQuery select element object. */ phpbb.toggleSelectSettings = function(el) { el.children().each(function() { @@ -1518,11 +1548,11 @@ phpbb.toggleSelectSettings = function(el) { * Get function from name. * Based on http://stackoverflow.com/a/359910 * -* @param string functionName Function to get. -* @return function +* @param {string} functionName Function to get. +* @returns function */ phpbb.getFunctionByName = function (functionName) { - var namespaces = functionName.split('.'), + var namespaces = functionName.split('.'), func = namespaces.pop(), context = window; @@ -1588,6 +1618,21 @@ phpbb.registerPageDropdowns = function() { }; /** + * Handle avatars to be lazy loaded. + */ +phpbb.lazyLoadAvatars = function loadAvatars() { + $('.avatar[data-src]').each(function () { + var $avatar = $(this); + + $avatar + .attr('src', $avatar.data('src')) + .removeAttr('data-src'); + }); +}; + +$(window).load(phpbb.lazyLoadAvatars); + +/** * Apply code editor to all textarea elements with data-bbcode attribute */ $(function() { diff --git a/phpBB/assets/javascript/editor.js b/phpBB/assets/javascript/editor.js index 5fd4f7eae3..df353bc29d 100644 --- a/phpBB/assets/javascript/editor.js +++ b/phpBB/assets/javascript/editor.js @@ -111,7 +111,6 @@ function bbfontstyle(bbopen, bbclose) { } textarea.focus(); - return; } /** @@ -159,7 +158,7 @@ function insert_text(text, spaces, popup) { /** * Add inline attachment at position */ -function attach_inline(index, filename) { +function attachInline(index, filename) { insert_text('[attachment=' + index + ']' + filename + '[/attachment]'); document.forms[form_name].elements[text_name].focus(); } @@ -167,7 +166,7 @@ function attach_inline(index, filename) { /** * Add quote text to message */ -function addquote(post_id, username, l_wrote) { +function addquote(post_id, username, l_wrote, attributes) { var message_name = 'message_' + post_id; var theSelection = ''; var divarea = false; @@ -177,6 +176,9 @@ function addquote(post_id, username, l_wrote) { // Backwards compatibility l_wrote = 'wrote'; } + if (typeof attributes !== 'object') { + attributes = {}; + } if (document.all) { divarea = document.all[message_name]; @@ -213,7 +215,8 @@ function addquote(post_id, username, l_wrote) { if (theSelection) { if (bbcodeEnabled) { - insert_text('[quote="' + username + '"]' + theSelection + '[/quote]'); + attributes.author = username; + insert_text(generateQuote(theSelection, attributes)); } else { insert_text(username + ' ' + l_wrote + ':' + '\n'); var lines = split_lines(theSelection); @@ -222,8 +225,61 @@ function addquote(post_id, username, l_wrote) { } } } +} - return; +/** +* Create a quote block for given text +* +* Possible attributes: +* - author: author's name (usually a username) +* - post_id: post_id of the post being quoted +* - user_id: user_id of the user being quoted +* - time: timestamp of the original message +* +* @param {!string} text Quote's text +* @param {!Object} attributes Quote's attributes +* @return {!string} Quote block to be used in a new post/text +*/ +function generateQuote(text, attributes) { + text = text.replace(/^\s+/, '').replace(/\s+$/, ''); + var quote = '[quote'; + if (attributes.author) { + // Add the author as the BBCode's default attribute + quote += '=' + formatAttributeValue(attributes.author); + delete attributes.author; + } + for (var name in attributes) { + if (attributes.hasOwnProperty(name)) { + var value = attributes[name]; + quote += ' ' + name + '=' + formatAttributeValue(value.toString()); + } + } + quote += ']'; + var newline = ((quote + text + '[/quote]').length > 80 || text.indexOf('\n') > -1) ? '\n' : ''; + quote += newline + text + newline + '[/quote]'; + + return quote; +} + +/** +* Format given string to be used as an attribute value +* +* Will return the string as-is if it can be used in a BBCode without quotes. Otherwise, +* it will use either single- or double- quotes depending on whichever requires less escaping. +* Quotes and backslashes are escaped with backslashes where necessary +* +* @param {!string} str Original string +* @return {!string} Same string if possible, escaped string within quotes otherwise +*/ +function formatAttributeValue(str) { + if (!/[ "'\\\]]/.test(str)) { + // Return as-is if it contains none of: space, ' " \ or ] + return str; + } + var singleQuoted = "'" + str.replace(/[\\']/g, '\\$&') + "'", + doubleQuoted = '"' + str.replace(/[\\"]/g, '\\$&') + '"'; + + return (singleQuoted.length < doubleQuoted.length) ? singleQuoted : doubleQuoted; } function split_lines(text) { @@ -355,6 +411,9 @@ function getCaretPosition(txtarea) { textarea = doc.forms[form_name].elements[text_name]; phpbb.applyCodeEditor(textarea); + if ($('#attach-panel').length) { + phpbb.showDragNDrop(textarea); + } }); })(jQuery); diff --git a/phpBB/assets/javascript/installer.js b/phpBB/assets/javascript/installer.js new file mode 100644 index 0000000000..c5909556c6 --- /dev/null +++ b/phpBB/assets/javascript/installer.js @@ -0,0 +1,515 @@ +/** + * Installer's AJAX frontend handler + */ + +(function($) { // Avoid conflicts with other libraries + 'use strict'; + + // Installer variables + var pollTimer = null; + var nextReadPosition = 0; + var progressBarTriggered = false; + var progressTimer = null; + var currentProgress = 0; + var refreshRequested = false; + + // Template related variables + var $contentWrapper = $('.install-body').find('.main'); + + // Intercept form submits + interceptFormSubmit($('#install_install')); + + /** + * Creates an XHR object + * + * jQuery cannot be used as the response is streamed, and + * as of now, jQuery does not provide access to the response until + * the connection is not closed. + * + * @return XMLHttpRequest + */ + function createXhrObject() { + return new XMLHttpRequest(); + } + + /** + * Displays error, warning and log messages + * + * @param type + * @param messages + */ + function addMessage(type, messages) { + // Get message containers + var $errorContainer = $('#error-container'); + var $warningContainer = $('#warning-container'); + var $logContainer = $('#log-container'); + + var $title, $description, $msgElement, arraySize = messages.length; + for (var i = 0; i < arraySize; i++) { + $msgElement = $('<div />'); + $title = $('<strong />'); + $title.text(messages[i].title); + $msgElement.append($title); + + if (messages[i].hasOwnProperty('description')) { + $description = $('<p />'); + $description.html(messages[i].description); + $msgElement.append($description); + } + + switch (type) { + case 'error': + $msgElement.addClass('errorbox'); + $errorContainer.append($msgElement); + break; + case 'warning': + $msgElement.addClass('warningbox'); + $warningContainer.append($msgElement); + break; + case 'log': + $msgElement.addClass('log'); + $logContainer.prepend($msgElement); + $logContainer.addClass('show_log_container'); + break; + case 'success': + $msgElement.addClass('successbox'); + $errorContainer.prepend($msgElement); + break; + } + } + } + + /** + * Render a download box + */ + function addDownloadBox(downloadArray) + { + var $downloadContainer = $('#download-wrapper'); + var $downloadBox, $title, $content, $link; + + for (var i = 0; i < downloadArray.length; i++) { + $downloadBox = $('<div />'); + $downloadBox.addClass('download-box'); + + $title = $('<strong />'); + $title.text(downloadArray[i].title); + $downloadBox.append($title); + + if (downloadArray[i].hasOwnProperty('msg')) { + $content = $('<p />'); + $content.text(downloadArray[i].msg); + $downloadBox.append($content); + } + + $link = $('<a />'); + $link.addClass('button1'); + $link.attr('href', downloadArray[i].href); + $link.text(downloadArray[i].download); + $downloadBox.append($link); + + $downloadContainer.append($downloadBox); + } + } + + /** + * Render update files' status + */ + function addUpdateFileStatus(fileStatus) + { + var $statusContainer = $('#file-status-wrapper'); + $statusContainer.html(fileStatus); + } + + /** + * Displays a form from the response + * + * @param formHtml + */ + function addForm(formHtml) { + var $formContainer = $('#form-wrapper'); + $formContainer.html(formHtml); + var $form = $('#install_install'); + interceptFormSubmit($form); + } + + /** + * Handles navigation status updates + * + * @param navObj + */ + function updateNavbarStatus(navObj) { + var navID, $stage, $stageListItem, $active; + $active = $('#activemenu'); + + if (navObj.hasOwnProperty('finished')) { + // This should be an Array + var navItems = navObj.finished; + + for (var i = 0; i < navItems.length; i++) { + navID = 'installer-stage-' + navItems[i]; + $stage = $('#' + navID); + $stageListItem = $stage.parent(); + + if ($active.length && $active.is($stageListItem)) { + $active.removeAttr('id'); + } + + $stage.addClass('completed'); + } + } + + if (navObj.hasOwnProperty('active')) { + navID = 'installer-stage-' + navObj.active; + $stage = $('#' + navID); + $stageListItem = $stage.parent(); + + if ($active.length && !$active.is($stageListItem)) { + $active.removeAttr('id'); + } + + $stageListItem.attr('id', 'activemenu'); + } + } + + /** + * Renders progress bar + * + * @param progressObject + */ + function setProgress(progressObject) { + var $statusText, $progressBar, $progressText, $progressFiller; + + if (progressObject.task_name.length) { + if (!progressBarTriggered) { + // Create progress bar + var $progressBarWrapper = $('#progress-bar-container'); + + // Create progress bar elements + $progressBar = $('<div />'); + $progressBar.attr('id', 'progress-bar'); + $progressText = $('<p />'); + $progressText.attr('id', 'progress-bar-text'); + $progressFiller = $('<span />'); + $progressFiller.attr('id', 'progress-bar-filler'); + $progressFiller.html($progressText); + + $statusText = $('<p />'); + $statusText.attr('id', 'progress-status-text'); + + $progressBar.append($progressFiller); + + $progressBarWrapper.append($statusText); + $progressBarWrapper.append($progressBar); + + progressBarTriggered = true; + } else if (progressObject.hasOwnProperty('restart')) { + clearInterval(progressTimer); + + $progressFiller = $('#progress-bar-filler'); + $progressText = $('#progress-bar-text'); + $statusText = $('#progress-status-text'); + + $progressText.text('0%'); + $progressFiller.css('width', '0%'); + + currentProgress = 0; + } else { + $statusText = $('#progress-status-text'); + } + + // Update progress bar + $statusText.text(progressObject.task_name + '…'); + incrementProgressBar(Math.round(progressObject.task_num / progressObject.task_count * 100)); + } + } + + // Set cookies + function setCookies(cookies) { + var cookie; + + for (var i = 0; i < cookies.length; i++) { + // Set cookie name and value + cookie = encodeURIComponent(cookies[i].name) + '=' + encodeURIComponent(cookies[i].value); + // Set path + cookie += '; path=/'; + document.cookie = cookie; + } + } + + /** + * Parse messages from the response object + * + * @param messageJSON + */ + function parseMessage(messageJSON) { + $('#loading_indicator').css('display', 'none'); + var responseObject; + + try { + responseObject = JSON.parse(messageJSON); + } catch (err) { + if (window.console) { + console.log('Failed to parse JSON object\n\nMessage: ' + err.message + '\n\nServer Response: ' + messageJSON); + } else { + alert('Failed to parse JSON object\n\nMessage: ' + err.message + '\n\nServer Response: ' + messageJSON); + } + + resetPolling(); + return; + } + + // Parse object + if (responseObject.hasOwnProperty('errors')) { + addMessage('error', responseObject.errors); + } + + if (responseObject.hasOwnProperty('warnings')) { + addMessage('warning', responseObject.warnings); + } + + if (responseObject.hasOwnProperty('logs')) { + addMessage('log', responseObject.logs); + } + + if (responseObject.hasOwnProperty('success')) { + addMessage('success', responseObject.success); + } + + if (responseObject.hasOwnProperty('form')) { + addForm(responseObject.form); + } + + if (responseObject.hasOwnProperty('progress')) { + setProgress(responseObject.progress); + } + + if (responseObject.hasOwnProperty('download')) { + addDownloadBox(responseObject.download); + } + + if (responseObject.hasOwnProperty('file_status')) { + addUpdateFileStatus(responseObject.file_status); + } + + if (responseObject.hasOwnProperty('nav')) { + updateNavbarStatus(responseObject.nav); + } + + if (responseObject.hasOwnProperty('cookies')) { + setCookies(responseObject.cookies); + } + + if (responseObject.hasOwnProperty('refresh')) { + refreshRequested = true; + } + } + + /** + * Process updates in streamed response + * + * @param xhReq XHR object + */ + function pollContent(xhReq) { + var messages = xhReq.responseText; + var msgSeparator = '}\n\n'; + var unprocessed, messageEndIndex, endOfMessageIndex, message; + + do { + unprocessed = messages.substring(nextReadPosition); + messageEndIndex = unprocessed.indexOf(msgSeparator); + + if (messageEndIndex !== -1) { + endOfMessageIndex = messageEndIndex + msgSeparator.length; + message = unprocessed.substring(0, endOfMessageIndex); + parseMessage($.trim(message)); + nextReadPosition += endOfMessageIndex; + } + } while (messageEndIndex !== -1); + + if (xhReq.readyState === 4) { + $('#loading_indicator').css('display', 'none'); + resetPolling(); + + if (refreshRequested) { + refreshRequested = false; + doRefresh(); + } + } + } + + /** + * Animates the progress bar + * + * @param $progressText + * @param $progressFiller + * @param progressLimit + */ + function incrementFiller($progressText, $progressFiller, progressLimit) { + if (currentProgress >= progressLimit || currentProgress >= 100) { + clearInterval(progressTimer); + return; + } + + currentProgress++; + $progressText.text(currentProgress + '%'); + $progressFiller.css('width', currentProgress + '%'); + } + + /** + * Wrapper function for progress bar rendering and animating + * + * @param progressLimit + */ + function incrementProgressBar(progressLimit) { + var $progressFiller = $('#progress-bar-filler'); + var $progressText = $('#progress-bar-text'); + var progressStart = $progressFiller.width() / $progressFiller.offsetParent().width() * 100; + currentProgress = Math.floor(progressStart); + + clearInterval(progressTimer); + progressTimer = setInterval(function() { + incrementFiller($progressText, $progressFiller, progressLimit); + }, 10); + } + + /** + * Resets the polling timer + */ + function resetPolling() { + clearInterval(pollTimer); + nextReadPosition = 0; + } + + /** + * Sets up timer for processing the streamed HTTP response + * + * @param xhReq + */ + function startPolling(xhReq) { + resetPolling(); + pollTimer = setInterval(function () { + pollContent(xhReq); + }, 250); + } + + /** + * Refresh page + */ + function doRefresh() { + resetPolling(); + + var xhReq = createXhrObject(); + xhReq.open('GET', $(location).attr('pathname'), true); + xhReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhReq.send(); + + startPolling(xhReq); + } + + /** + * Renders the AJAX UI layout + */ + function setupAjaxLayout() { + progressBarTriggered = false; + + // Clear content + $contentWrapper.html(''); + + var $header = $('<div />'); + $header.attr('id', 'header-container'); + $contentWrapper.append($header); + + var $description = $('<div />'); + $description.attr('id', 'description-container'); + $contentWrapper.append($description); + + var $errorContainer = $('<div />'); + $errorContainer.attr('id', 'error-container'); + $contentWrapper.append($errorContainer); + + var $warningContainer = $('<div />'); + $warningContainer.attr('id', 'warning-container'); + $contentWrapper.append($warningContainer); + + var $progressContainer = $('<div />'); + $progressContainer.attr('id', 'progress-bar-container'); + $contentWrapper.append($progressContainer); + + var $logContainer = $('<div />'); + $logContainer.attr('id', 'log-container'); + $contentWrapper.append($logContainer); + + var $installerContentWrapper = $('<div />'); + $installerContentWrapper.attr('id', 'content-container'); + $contentWrapper.append($installerContentWrapper); + + var $installerDownloadWrapper = $('<div />'); + $installerDownloadWrapper.attr('id', 'download-wrapper'); + $installerContentWrapper.append($installerDownloadWrapper); + + var $updaterFileStatusWrapper = $('<div />'); + $updaterFileStatusWrapper.attr('id', 'file-status-wrapper'); + $installerContentWrapper.append($updaterFileStatusWrapper); + + var $formWrapper = $('<div />'); + $formWrapper.attr('id', 'form-wrapper'); + $installerContentWrapper.append($formWrapper); + + var $spinner = $('<div />'); + $spinner.attr('id', 'loading_indicator'); + $spinner.html(' '); + $contentWrapper.append($spinner); + } + + // Submits a form + function submitForm($form, $submitBtn) { + $form.css('display', 'none'); + + var xhReq = createXhrObject(); + xhReq.open('POST', $form.attr('action'), true); + xhReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhReq.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); + xhReq.send(getFormFields($form, $submitBtn)); + + // Disable language selector + $('#language_selector :input, label').css('display', 'none'); + + // Clear content + setupAjaxLayout(); + $('#loading_indicator').css('display', 'block'); + + startPolling(xhReq); + } + + /** + * Add submit button to the POST information + * + * @param $form + * @param $submitBtn + * + * @returns {*} + */ + function getFormFields($form, $submitBtn) { + var formData = $form.serialize(); + formData += ((formData.length) ? '&' : '') + encodeURIComponent($submitBtn.attr('name')) + '='; + formData += encodeURIComponent($submitBtn.attr('value')); + + return formData; + } + + /** + * Intercept form submit events and determine the submit button used + * + * @param $form + */ + function interceptFormSubmit($form) { + if (!$form.length) { + return; + } + + $form.find(':submit').bind('click', function (event) { + event.preventDefault(); + submitForm($form, $(this)); + }); + } +})(jQuery); // Avoid conflicts with other libraries diff --git a/phpBB/assets/javascript/plupload.js b/phpBB/assets/javascript/plupload.js index a58c71e64d..8b3543880f 100644 --- a/phpBB/assets/javascript/plupload.js +++ b/phpBB/assets/javascript/plupload.js @@ -1,26 +1,26 @@ +/* global phpbb, plupload, attachInline */ + plupload.addI18n(phpbb.plupload.i18n); phpbb.plupload.ids = []; (function($) { // Avoid conflicts with other libraries -"use strict"; +'use strict'; /** * Set up the uploader. - * - * @return undefined */ phpbb.plupload.initialize = function() { // Initialize the Plupload uploader. - uploader.init(); + phpbb.plupload.uploader.init(); // Set attachment data. phpbb.plupload.setData(phpbb.plupload.data); phpbb.plupload.updateMultipartParams(phpbb.plupload.getSerializedData()); // Only execute if Plupload initialized successfully. - uploader.bind('Init', function() { - phpbb.plupload.form = $(phpbb.plupload.config.form_hook)[0], + phpbb.plupload.uploader.bind('Init', function() { + phpbb.plupload.form = $(phpbb.plupload.config.form_hook)[0]; phpbb.plupload.rowTpl = $('#attach-row-tpl')[0].outerHTML; // Hide the basic upload panel and remove the attach row template. @@ -29,18 +29,18 @@ phpbb.plupload.initialize = function() { $('#attach-panel-multi').show(); }); - uploader.bind('PostInit', function() { + phpbb.plupload.uploader.bind('PostInit', function() { // Point out the drag-and-drop zone if it's supported. - if (uploader.features.dragdrop) { + if (phpbb.plupload.uploader.features.dragdrop) { $('#drag-n-drop-message').show(); } // Ensure "Add files" button position is correctly calculated. if ($('#attach-panel-multi').is(':visible')) { - uploader.refresh(); + phpbb.plupload.uploader.refresh(); } $('[data-subpanel="attach-panel"]').one('click', function() { - uploader.refresh(); + phpbb.plupload.uploader.refresh(); }); }); }; @@ -48,37 +48,32 @@ phpbb.plupload.initialize = function() { /** * Unsets all elements in the object uploader.settings.multipart_params whose keys * begin with 'attachment_data[' - * - * @return undefined */ phpbb.plupload.clearParams = function() { - var obj = uploader.settings.multipart_params; + var obj = phpbb.plupload.uploader.settings.multipart_params; for (var key in obj) { if (!obj.hasOwnProperty(key) || key.indexOf('attachment_data[') !== 0) { continue; } - delete uploader.settings.multipart_params[key]; + delete phpbb.plupload.uploader.settings.multipart_params[key]; } }; /** * Update uploader.settings.multipart_params object with new data. * - * @param object obj - * @return undefined + * @param {object} obj */ phpbb.plupload.updateMultipartParams = function(obj) { - uploader.settings.multipart_params = $.extend( - uploader.settings.multipart_params, - obj - ); + var settings = phpbb.plupload.uploader.settings; + settings.multipart_params = $.extend(settings.multipart_params, obj); }; /** * Convert the array of attachment objects into an object that PHP would expect as POST data. * - * @return object An object in the form 'attachment_data[i][key]': value as + * @returns {object} An object in the form 'attachment_data[i][key]': value as * expected by the server */ phpbb.plupload.getSerializedData = function() { @@ -100,22 +95,19 @@ phpbb.plupload.getSerializedData = function() { * Get the index from the phpbb.plupload.data array where the given * attachment id appears. * - * @param int attach_id The attachment id of the file. - * @return bool Returns false if the id cannot be found. - * @return int Returns the index of the file if it exists. + * @param {int} attachId The attachment id of the file. + * @returns {bool|int} Index of the file if exists, otherwise false. */ -phpbb.plupload.getIndex = function(attach_id) { - var index = $.inArray(Number(attach_id), phpbb.plupload.ids); +phpbb.plupload.getIndex = function(attachId) { + var index = $.inArray(Number(attachId), phpbb.plupload.ids); return (index !== -1) ? index : false; }; /** * Set the data in phpbb.plupload.data and phpbb.plupload.ids arrays. - * - * @param array data Array containing the new data to use. In the form of - * array(index => object(property: value). Requires attach_id to be one of the object properties. * - * @return undefined + * @param {Array} data Array containing the new data to use. In the form of + * array(index => object(property: value). Requires attach_id to be one of the object properties. */ phpbb.plupload.setData = function(data) { // Make sure that the array keys are reset. @@ -129,12 +121,11 @@ phpbb.plupload.setData = function(data) { /** * Update the attachment data in the HTML and the phpbb & phpbb.plupload objects. - * - * @param array data Array containing the new data to use. - * @param string action The action that required the update. Used to update the inline attachment bbcodes. - * @param int index The index from phpbb.plupload_ids that was affected by the action. - * @param array downloadUrl Optional array of download urls to update. - * @return undefined + * + * @param {Array} data Array containing the new data to use. + * @param {string} action The action that required the update. Used to update the inline attachment bbcodes. + * @param {int} index The index from phpbb.plupload_ids that was affected by the action. + * @param {Array} downloadUrl Optional array of download urls to update. */ phpbb.plupload.update = function(data, action, index, downloadUrl) { @@ -147,9 +138,8 @@ phpbb.plupload.update = function(data, action, index, downloadUrl) { /** * Update the relevant elements and hidden data for all attachments. - * - * @param array downloadUrl Optional array of download urls to update. - * @return undefined + * + * @param {Array} downloadUrl Optional array of download urls to update. */ phpbb.plupload.updateRows = function(downloadUrl) { for (var i = 0; i < phpbb.plupload.ids.length; i++) { @@ -162,9 +152,8 @@ phpbb.plupload.updateRows = function(downloadUrl) { * using the id "attach-row-tpl" to be present. This snippet is cloned and the * data for the file inserted into it. The row is then appended or prepended to * #file-list based on the attach_order setting. - * - * @param object file Plupload file object for the new attachment. - * @return undefined + * + * @param {object} file Plupload file object for the new attachment. */ phpbb.plupload.insertRow = function(file) { var row = $(phpbb.plupload.rowTpl); @@ -173,7 +162,7 @@ phpbb.plupload.insertRow = function(file) { row.find('.file-name').html(plupload.xmlEncode(file.name)); row.find('.file-size').html(plupload.formatSize(file.size)); - if (phpbb.plupload.order == 'desc') { + if (phpbb.plupload.order === 'desc') { $('#file-list').prepend(row); } else { $('#file-list').append(row); @@ -182,10 +171,9 @@ phpbb.plupload.insertRow = function(file) { /** * Update the relevant elements and hidden data for an attachment. - * - * @param int index The index from phpbb.plupload.ids of the attachment to edit. - * @param array downloadUrl Optional array of download urls to update. - * @return undefined + * + * @param {int} index The index from phpbb.plupload.ids of the attachment to edit. + * @param {Array} downloadUrl Optional array of download urls to update. */ phpbb.plupload.updateRow = function(index, downloadUrl) { var attach = phpbb.plupload.data[index], @@ -197,7 +185,7 @@ phpbb.plupload.updateRow = function(index, downloadUrl) { link = $('<a></a>'); link.attr('href', url).html(attach.real_filename); - row.find('.file-name').html(link) + row.find('.file-name').html(link); } row.find('textarea').attr('name', 'comment_list[' + index + ']'); @@ -207,18 +195,21 @@ phpbb.plupload.updateRow = function(index, downloadUrl) { /** * Update hidden input data for an attachment. * - * @param object row jQuery object for the attachment row. - * @param object attach Attachment data object from phpbb.plupload.data - * @param int index Attachment index from phpbb.plupload.ids - * @return undefined + * @param {object} row jQuery object for the attachment row. + * @param {object} attach Attachment data object from phpbb.plupload.data + * @param {int} index Attachment index from phpbb.plupload.ids */ phpbb.plupload.updateHiddenData = function(row, attach, index) { row.find('input[type="hidden"]').remove(); for (var key in attach) { + if (!attach.hasOwnProperty(key)) { + return; + } + var input = $('<input />') .attr('type', 'hidden') - .attr('name', 'attachment_data[' + index + '][' + key +']') + .attr('name', 'attachment_data[' + index + '][' + key + ']') .attr('value', attach[key]); $('textarea', row).after(input); } @@ -230,16 +221,14 @@ phpbb.plupload.updateHiddenData = function(row, attach, index) { * responds with the updated attachment data list so that any future * uploads can maintain state with the server * - * @param object row jQuery object for the attachment row. - * @param int attachId Attachment id of the file to be removed. - * - * @return undefined + * @param {object} row jQuery object for the attachment row. + * @param {int} attachId Attachment id of the file to be removed. */ phpbb.plupload.deleteFile = function(row, attachId) { // If there's no attach id, then the file hasn't been uploaded. Simply delete the row. if (typeof attachId === 'undefined') { - var file = uploader.getFile(row.attr('id')); - uploader.removeFile(file); + var file = phpbb.plupload.uploader.getFile(row.attr('id')); + phpbb.plupload.uploader.removeFile(file); row.slideUp(100, function() { row.remove(); @@ -267,7 +256,7 @@ phpbb.plupload.deleteFile = function(row, attachId) { // trigger_error() was called which likely means a permission error was encountered. if (typeof response.title !== 'undefined') { - uploader.trigger('Error', {message: response.message}); + phpbb.plupload.uploader.trigger('Error', { message: response.message }); // We will have to assume that the deletion failed. So leave the file status as uploaded. row.find('.file-status').toggleClass('file-uploaded'); @@ -278,21 +267,21 @@ phpbb.plupload.deleteFile = function(row, attachId) { phpbb.plupload.handleMaxFilesReached(); if (row.attr('id')) { - var file = uploader.getFile(row.attr('id')); - uploader.removeFile(file); + var file = phpbb.plupload.uploader.getFile(row.attr('id')); + phpbb.plupload.uploader.removeFile(file); } row.slideUp(100, function() { row.remove(); // Hide the file list if it's empty now. phpbb.plupload.hideEmptyList(); }); - uploader.trigger('FilesRemoved'); + phpbb.plupload.uploader.trigger('FilesRemoved'); }; $.ajax(phpbb.plupload.config.url, { type: 'POST', data: $.extend(fields, phpbb.plupload.getSerializedData()), - headers: {'X-PHPBB-USING-PLUPLOAD': '1', 'X-Requested-With': 'XMLHttpRequest'} + headers: phpbb.plupload.config.headers }) .always(always) .done(done); @@ -300,25 +289,21 @@ phpbb.plupload.deleteFile = function(row, attachId) { /** * Check the attachment list and hide its container if it's empty. - * - * @return undefined */ phpbb.plupload.hideEmptyList = function() { if (!$('#file-list').children().length) { $('#file-list-container').slideUp(100); } -} +}; /** - * Update the indices used in inline attachment bbcodes. This ensures that the bbcodes - * correspond to the correct file after a file is added or removed. This should be called - * before the phpbb.plupload,data and phpbb.plupload.ids arrays are updated, otherwise it will - * not work correctly. + * Update the indices used in inline attachment bbcodes. This ensures that the + * bbcodes correspond to the correct file after a file is added or removed. + * This should be called before the phpbb.plupload,data and phpbb.plupload.ids + * arrays are updated, otherwise it will not work correctly. * - * @param string action The action that occurred -- either "addition" or "removal" - * @param int index The index of the attachment from phpbb.plupload.ids that was affected. - * - * @return undefined + * @param {string} action The action that occurred -- either "addition" or "removal" + * @param {int} index The index of the attachment from phpbb.plupload.ids that was affected. */ phpbb.plupload.updateBbcode = function(action, index) { var textarea = $('#message', phpbb.plupload.form), @@ -330,64 +315,59 @@ phpbb.plupload.updateBbcode = function(action, index) { return; } - // Private function used to replace the bbcode. - var updateBbcode = function(match, fileName) { - // Remove the bbcode if the file was removed. - if (removal && index === i) { - return ''; - } - var newIndex = i + ((removal) ? -1 : 1); - return '[attachment=' + newIndex +']' + fileName + '[/attachment]'; - }; - - // Private function used to generate search regexp - var searchRegexp = function(index) { - return new RegExp('\\[attachment=' + index + '\\](.*?)\\[\\/attachment\\]', 'g'); + function runUpdate(i) { + var regex = new RegExp('\\[attachment=' + i + '\\](.*?)\\[\\/attachment\\]', 'g'); + text = text.replace(regex, function updateBbcode(_, fileName) { + // Remove the bbcode if the file was removed. + if (removal && index === i) { + return ''; + } + var newIndex = i + ((removal) ? -1 : 1); + return '[attachment=' + newIndex + ']' + fileName + '[/attachment]'; + }); } - // The update order of the indices is based on the action taken to ensure that we don't corrupt - // the bbcode index by updating it several times as we move through the loop. - // Removal loop starts at the removed index and moves to the end of the array. - // Addition loop starts at the end of the array and moves to the added index at 0. - var searchLoop = function() { - if (typeof i === 'undefined') { - i = (removal) ? index : phpbb.plupload.ids.length - 1; + + // Loop forwards when removing and backwards when adding ensures we don't + // corrupt the bbcode index. + var i; + if (removal) { + for (i = index; i < phpbb.plupload.ids.length; i++) { + runUpdate(i); + } + } else { + for (i = phpbb.plupload.ids.length - 1; i >= index; i--) { + runUpdate(i); } - return (removal) ? (i < phpbb.plupload.ids.length): (i >= index); } - var i; - while (searchLoop()) { - text = text.replace(searchRegexp(i), updateBbcode); - (removal) ? i++ : i--; - } textarea.val(text); }; /** * Get Plupload file objects based on their upload status. * - * @param int status Plupload status - plupload.DONE, plupload.FAILED, plupload.QUEUED, - * plupload.STARTED, plupload.STOPPED + * @param {int} status Plupload status - plupload.DONE, plupload.FAILED, + * plupload.QUEUED, plupload.STARTED, plupload.STOPPED * - * @return Returns an array of the Plupload file objects matching the status. + * @returns {Array} The Plupload file objects matching the status. */ phpbb.plupload.getFilesByStatus = function(status) { var files = []; - $.each(uploader.files, function(i, file) { + $.each(phpbb.plupload.uploader.files, function(i, file) { if (file.status === status) { files.push(file); } }); return files; -} +}; /** * Check whether the user has reached the maximun number of files that he's allowed * to upload. If so, disables the uploader and marks the queued files as failed. Otherwise * makes sure that the uploader is enabled. * - * @return bool Returns true if the limit has been reached. False if otherwise. + * @returns {bool} True if the limit has been reached. False if otherwise. */ phpbb.plupload.handleMaxFilesReached = function() { // If there is no limit, the user is an admin or moderator. @@ -400,41 +380,36 @@ phpbb.plupload.handleMaxFilesReached = function() { phpbb.plupload.markQueuedFailed(phpbb.plupload.lang.TOO_MANY_ATTACHMENTS); // Disable the uploader. phpbb.plupload.disableUploader(); - uploader.trigger('Error', {message: phpbb.plupload.lang.TOO_MANY_ATTACHMENTS}); + phpbb.plupload.uploader.trigger('Error', { message: phpbb.plupload.lang.TOO_MANY_ATTACHMENTS }); return true; - } else if(phpbb.plupload.maxFiles > phpbb.plupload.ids.length) { + } else if (phpbb.plupload.maxFiles > phpbb.plupload.ids.length) { // Enable the uploader if the user is under the limit phpbb.plupload.enableUploader(); } return false; -} +}; /** * Disable the uploader - * - * @return undefined */ phpbb.plupload.disableUploader = function() { $('#add_files').addClass('disabled'); - uploader.disableBrowse(); -} + phpbb.plupload.uploader.disableBrowse(); +}; /** * Enable the uploader - * - * @return undefined */ phpbb.plupload.enableUploader = function() { $('#add_files').removeClass('disabled'); - uploader.disableBrowse(false); -} + phpbb.plupload.uploader.disableBrowse(false); +}; /** * Mark all queued files as failed. * - * @param string error Error message to present to the user. - * @return undefined + * @param {string} error Error message to present to the user. */ phpbb.plupload.markQueuedFailed = function(error) { var files = phpbb.plupload.getFilesByStatus(plupload.QUEUED); @@ -443,48 +418,49 @@ phpbb.plupload.markQueuedFailed = function(error) { $('#' + file.id).find('.file-progress').hide(); phpbb.plupload.fileError(file, error); }); -} +}; /** * Marks a file as failed and sets the error message for it. * - * @param object file Plupload file object that failed. - * @param string error Error message to present to the user. - * @return undefined + * @param {object} file Plupload file object that failed. + * @param {string} error Error message to present to the user. */ phpbb.plupload.fileError = function(file, error) { file.status = plupload.FAILED; file.error = error; - $('#' + file.id).find('.file-status').addClass('file-error').attr({'data-error-title': phpbb.plupload.lang.ERROR, 'data-error-message': error}); -} - - + $('#' + file.id).find('.file-status') + .addClass('file-error') + .attr({ + 'data-error-title': phpbb.plupload.lang.ERROR, + 'data-error-message': error + }); +}; /** * Set up the Plupload object and get some basic data. */ -var uploader = new plupload.Uploader(phpbb.plupload.config); +phpbb.plupload.uploader = new plupload.Uploader(phpbb.plupload.config); phpbb.plupload.initialize(); - - +var $fileList = $('#file-list'); /** * Insert inline attachment bbcode. */ - $('#file-list').on('click', '.file-inline-bbcode', function(e) { +$fileList.on('click', '.file-inline-bbcode', function(e) { var attachId = $(this).parents('.attach-row').attr('data-attach-id'), index = phpbb.plupload.getIndex(attachId); - attach_inline(index, phpbb.plupload.data[index].real_filename); + attachInline(index, phpbb.plupload.data[index].real_filename); e.preventDefault(); }); /** * Delete a file. */ -$('#file-list').on('click', '.file-delete', function(e) { +$fileList.on('click', '.file-delete', function(e) { var row = $(this).parents('.attach-row'), attachId = row.attr('data-attach-id'); @@ -495,7 +471,7 @@ $('#file-list').on('click', '.file-delete', function(e) { /** * Display the error message for a particular file when the error icon is clicked. */ -$('#file-list').on('click', '.file-error', function(e) { +$fileList.on('click', '.file-error', function(e) { phpbb.alert($(this).attr('data-error-title'), $(this).attr('data-error-message')); e.preventDefault(); }); @@ -503,7 +479,7 @@ $('#file-list').on('click', '.file-error', function(e) { /** * Fires when an error occurs. */ -uploader.bind('Error', function(up, error) { +phpbb.plupload.uploader.bind('Error', function(up, error) { error.file.name = plupload.xmlEncode(error.file.name); // The error message that Plupload provides for these is vague, so we'll be more specific. @@ -520,18 +496,15 @@ uploader.bind('Error', function(up, error) { * send the real filename along with the chunk. This is necessary because * for some reason the filename is set to 'blob' whenever a file is chunked * - * @param object up The plupload.Uploader object - * @param object file The plupload.File object that is about to be - * uploaded - * - * @return undefined + * @param {object} up The plupload.Uploader object + * @param {object} file The plupload.File object that is about to be uploaded */ -uploader.bind('BeforeUpload', function(up, file) { +phpbb.plupload.uploader.bind('BeforeUpload', function(up, file) { if (phpbb.plupload.handleMaxFilesReached()) { return; } - phpbb.plupload.updateMultipartParams({'real_filename': file.name}); + phpbb.plupload.updateMultipartParams({ real_filename: file.name }); }); /** @@ -539,14 +512,12 @@ uploader.bind('BeforeUpload', function(up, file) { * response from the server and checks for an error. If an error occurs it * is reported to the user and the upload of this particular file is halted * - * @param object up The plupload.Uploader object - * @param object file The plupload.File object whose chunk has just + * @param {object} up The plupload.Uploader object + * @param {object} file The plupload.File object whose chunk has just * been uploaded - * @param object response The response object from the server - * - * @return undefined + * @param {object} response The response object from the server */ -uploader.bind('ChunkUploaded', function(up, file, response) { +phpbb.plupload.uploader.bind('ChunkUploaded', function(up, file, response) { if (response.chunk >= response.chunks - 1) { return; } @@ -567,7 +538,7 @@ uploader.bind('ChunkUploaded', function(up, file, response) { // If trigger_error() was called, then a permission error likely occurred. if (typeof json.title !== 'undefined') { - json.error = {message: json.message}; + json.error = { message: json.message }; } if (json.error) { @@ -584,10 +555,8 @@ uploader.bind('ChunkUploaded', function(up, file, response) { /** * Fires when files are added to the queue. - * - * @return undefined */ -uploader.bind('FilesAdded', function(up, files) { +phpbb.plupload.uploader.bind('FilesAdded', function(up, files) { // Prevent unnecessary requests to the server if the user already uploaded // the maximum number of files allowed. if (phpbb.plupload.handleMaxFilesReached()) { @@ -595,13 +564,14 @@ uploader.bind('FilesAdded', function(up, files) { } // Switch the active tab if the style supports it - if (typeof activateSubPanel == 'function') { - activateSubPanel('attach-panel'); + if (typeof activateSubPanel === 'function') { + activateSubPanel('attach-panel'); // jshint ignore: line } // Show the file list if there aren't any files currently. - if (!$('#file-list-container').is(':visible')) { - $('#file-list-container').show(100); + var $fileListContainer = $('#file-list-container'); + if (!$fileListContainer.is(':visible')) { + $fileListContainer.show(100); } $.each(files, function(i, file) { @@ -609,7 +579,7 @@ uploader.bind('FilesAdded', function(up, files) { }); up.bind('UploadProgress', function(up, file) { - $('#' + file.id + " .file-progress-bar").css('width', file.percent + '%'); + $('.file-progress-bar', '#' + file.id).css('width', file.percent + '%'); $('#file-total-progress-bar').css('width', up.total.percent + '%'); }); @@ -627,14 +597,12 @@ uploader.bind('FilesAdded', function(up, files) { * appends it to the next file upload so that the server can maintain state * with regards to the attachments in a given post * - * @param object up The plupload.Uploader object - * @param object file The plupload.File object that has just been + * @param {object} up The plupload.Uploader object + * @param {object} file The plupload.File object that has just been * uploaded - * @param string response The response string from the server - * - * @return undefined + * @param {string} response The response string from the server */ -uploader.bind('FileUploaded', function(up, file, response) { +phpbb.plupload.uploader.bind('FileUploaded', function(up, file, response) { var json = {}, row = $('#' + file.id), error; @@ -643,7 +611,7 @@ uploader.bind('FileUploaded', function(up, file, response) { row.find('.file-progress').hide(); try { - json = $.parseJSON(response.response); + json = JSON.parse(response.response); } catch (e) { error = 'Error parsing server response.'; } @@ -651,7 +619,7 @@ uploader.bind('FileUploaded', function(up, file, response) { // If trigger_error() was called, then a permission error likely occurred. if (typeof json.title !== 'undefined') { error = json.message; - up.trigger('Error', {message: error}); + up.trigger('Error', { message: error }); // The rest of the queue will fail. phpbb.plupload.markQueuedFailed(error); @@ -662,25 +630,19 @@ uploader.bind('FileUploaded', function(up, file, response) { if (typeof error !== 'undefined') { phpbb.plupload.fileError(file, error); } else if (file.status === plupload.DONE) { - file.attachment_data = json['data'][0]; + file.attachment_data = json.data[0]; row.attr('data-attach-id', file.attachment_data.attach_id); row.find('.file-inline-bbcode').show(); row.find('.file-status').addClass('file-uploaded'); - phpbb.plupload.update(json['data'], 'addition', 0, [json['download_url']]); + phpbb.plupload.update(json.data, 'addition', 0, [json.download_url]); } }); /** - * Fires when the entire queue of files have been uploaded. - * - * @param object up The plupload.Uploader object - * @param array files An array of plupload.File objects that have just - * been uploaded as part of a queue - * - * @return undefined + * Fires when the entire queue of files have been uploaded. */ -uploader.bind('UploadComplete', function(up, files) { +phpbb.plupload.uploader.bind('UploadComplete', function() { // Hide the progress bar setTimeout(function() { $('#file-total-progress-bar').fadeOut(500, function() { |