PDF rausgenommen

This commit is contained in:
aschwarz
2023-01-23 11:03:31 +01:00
parent 82d562a322
commit a6523903eb
28078 changed files with 4247552 additions and 2 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,425 @@
/**
* bbCode control by subBlue design [ www.subBlue.com ]
* Includes unixsafe colour palette selector by SHS`
*/
// Startup variables
var imageTag = false;
var theSelection = false;
var bbcodeEnabled = true;
// Check for Browser & Platform for PC & IE specific bits
// More details from: http://www.mozilla.org/docs/web-developer/sniffer/browser_type.html
var clientPC = navigator.userAgent.toLowerCase(); // Get client info
var clientVer = parseInt(navigator.appVersion, 10); // Get browser version
var is_ie = ((clientPC.indexOf('msie') !== -1) && (clientPC.indexOf('opera') === -1));
var is_win = ((clientPC.indexOf('win') !== -1) || (clientPC.indexOf('16bit') !== -1));
var baseHeight;
/**
* Shows the help messages in the helpline window
*/
function helpline(help) {
document.forms[form_name].helpbox.value = help_line[help];
}
/**
* Fix a bug involving the TextRange object. From
* http://www.frostjedi.com/terra/scripts/demo/caretBug.html
*/
function initInsertions() {
var doc;
if (document.forms[form_name]) {
doc = document;
} else {
doc = opener.document;
}
var textarea = doc.forms[form_name].elements[text_name];
if (is_ie && typeof(baseHeight) !== 'number') {
textarea.focus();
baseHeight = doc.selection.createRange().duplicate().boundingHeight;
if (!document.forms[form_name]) {
document.body.focus();
}
}
}
/**
* bbstyle
*/
function bbstyle(bbnumber) {
if (bbnumber !== -1) {
bbfontstyle(bbtags[bbnumber], bbtags[bbnumber+1]);
} else {
insert_text('[*]');
document.forms[form_name].elements[text_name].focus();
}
}
/**
* Apply bbcodes
*/
function bbfontstyle(bbopen, bbclose) {
theSelection = false;
var textarea = document.forms[form_name].elements[text_name];
textarea.focus();
if ((clientVer >= 4) && is_ie && is_win) {
// Get text selection
theSelection = document.selection.createRange().text;
if (theSelection) {
// Add tags around selection
document.selection.createRange().text = bbopen + theSelection + bbclose;
textarea.focus();
theSelection = '';
return;
}
} else if (textarea.selectionEnd && (textarea.selectionEnd - textarea.selectionStart > 0)) {
mozWrap(textarea, bbopen, bbclose);
textarea.focus();
theSelection = '';
return;
}
//The new position for the cursor after adding the bbcode
var caret_pos = getCaretPosition(textarea).start;
var new_pos = caret_pos + bbopen.length;
// Open tag
insert_text(bbopen + bbclose);
// Center the cursor when we don't have a selection
// Gecko and proper browsers
if (!isNaN(textarea.selectionStart)) {
textarea.selectionStart = new_pos;
textarea.selectionEnd = new_pos;
}
// IE
else if (document.selection) {
var range = textarea.createTextRange();
range.move("character", new_pos);
range.select();
storeCaret(textarea);
}
textarea.focus();
}
/**
* Insert text at position
*/
function insert_text(text, spaces, popup) {
var textarea;
if (!popup) {
textarea = document.forms[form_name].elements[text_name];
} else {
textarea = opener.document.forms[form_name].elements[text_name];
}
if (spaces) {
text = ' ' + text + ' ';
}
// Since IE9, IE also has textarea.selectionStart, but it still needs to be treated the old way.
// Therefore we simply add a !is_ie here until IE fixes the text-selection completely.
if (!isNaN(textarea.selectionStart) && !is_ie) {
var sel_start = textarea.selectionStart;
var sel_end = textarea.selectionEnd;
mozWrap(textarea, text, '');
textarea.selectionStart = sel_start + text.length;
textarea.selectionEnd = sel_end + text.length;
} else if (textarea.createTextRange && textarea.caretPos) {
if (baseHeight !== textarea.caretPos.boundingHeight) {
textarea.focus();
storeCaret(textarea);
}
var caret_pos = textarea.caretPos;
caret_pos.text = caret_pos.text.charAt(caret_pos.text.length - 1) === ' ' ? caret_pos.text + text + ' ' : caret_pos.text + text;
} else {
textarea.value = textarea.value + text;
}
if (!popup) {
textarea.focus();
}
}
/**
* Add inline attachment at position
*/
function attachInline(index, filename) {
insert_text('[attachment=' + index + ']' + filename + '[/attachment]');
document.forms[form_name].elements[text_name].focus();
}
/**
* Add quote text to message
*/
function addquote(post_id, username, l_wrote, attributes) {
var message_name = 'message_' + post_id;
var theSelection = '';
var divarea = false;
var i;
if (l_wrote === undefined) {
// Backwards compatibility
l_wrote = 'wrote';
}
if (typeof attributes !== 'object') {
attributes = {};
}
if (document.all) {
divarea = document.all[message_name];
} else {
divarea = document.getElementById(message_name);
}
// Get text selection - not only the post content :(
// IE9 must use the document.selection method but has the *.getSelection so we just force no IE
if (window.getSelection && !is_ie && !window.opera) {
theSelection = window.getSelection().toString();
} else if (document.getSelection && !is_ie) {
theSelection = document.getSelection();
} else if (document.selection) {
theSelection = document.selection.createRange().text;
}
if (theSelection === '' || typeof theSelection === 'undefined' || theSelection === null) {
if (divarea.innerHTML) {
theSelection = divarea.innerHTML.replace(/<br>/ig, '\n');
theSelection = theSelection.replace(/<br\/>/ig, '\n');
theSelection = theSelection.replace(/&lt\;/ig, '<');
theSelection = theSelection.replace(/&gt\;/ig, '>');
theSelection = theSelection.replace(/&amp\;/ig, '&');
theSelection = theSelection.replace(/&nbsp\;/ig, ' ');
} else if (document.all) {
theSelection = divarea.innerText;
} else if (divarea.textContent) {
theSelection = divarea.textContent;
} else if (divarea.firstChild.nodeValue) {
theSelection = divarea.firstChild.nodeValue;
}
}
if (theSelection) {
if (bbcodeEnabled) {
attributes.author = username;
insert_text(generateQuote(theSelection, attributes));
} else {
insert_text(username + ' ' + l_wrote + ':' + '\n');
var lines = split_lines(theSelection);
for (i = 0; i < lines.length; i++) {
insert_text('> ' + lines[i] + '\n');
}
}
}
}
/**
* 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) {
var lines = text.split('\n');
var splitLines = new Array();
var j = 0;
var i;
for(i = 0; i < lines.length; i++) {
if (lines[i].length <= 80) {
splitLines[j] = lines[i];
j++;
} else {
var line = lines[i];
var splitAt;
do {
splitAt = line.indexOf(' ', 80);
if (splitAt === -1) {
splitLines[j] = line;
j++;
} else {
splitLines[j] = line.substring(0, splitAt);
line = line.substring(splitAt);
j++;
}
}
while(splitAt !== -1);
}
}
return splitLines;
}
/**
* From http://www.massless.org/mozedit/
*/
function mozWrap(txtarea, open, close) {
var selLength = (typeof(txtarea.textLength) === 'undefined') ? txtarea.value.length : txtarea.textLength;
var selStart = txtarea.selectionStart;
var selEnd = txtarea.selectionEnd;
var scrollTop = txtarea.scrollTop;
var s1 = (txtarea.value).substring(0,selStart);
var s2 = (txtarea.value).substring(selStart, selEnd);
var s3 = (txtarea.value).substring(selEnd, selLength);
txtarea.value = s1 + open + s2 + close + s3;
txtarea.selectionStart = selStart + open.length;
txtarea.selectionEnd = selEnd + open.length;
txtarea.focus();
txtarea.scrollTop = scrollTop;
return;
}
/**
* Insert at Caret position. Code from
* http://www.faqts.com/knowledge_base/view.phtml/aid/1052/fid/130
*/
function storeCaret(textEl) {
if (textEl.createTextRange && document.selection) {
textEl.caretPos = document.selection.createRange().duplicate();
}
}
/**
* Caret Position object
*/
function caretPosition() {
var start = null;
var end = null;
}
/**
* Get the caret position in an textarea
*/
function getCaretPosition(txtarea) {
var caretPos = new caretPosition();
// simple Gecko/Opera way
if (txtarea.selectionStart || txtarea.selectionStart === 0) {
caretPos.start = txtarea.selectionStart;
caretPos.end = txtarea.selectionEnd;
}
// dirty and slow IE way
else if (document.selection) {
// get current selection
var range = document.selection.createRange();
// a new selection of the whole textarea
var range_all = document.body.createTextRange();
range_all.moveToElementText(txtarea);
// calculate selection start point by moving beginning of range_all to beginning of range
var sel_start;
for (sel_start = 0; range_all.compareEndPoints('StartToStart', range) < 0; sel_start++) {
range_all.moveStart('character', 1);
}
txtarea.sel_start = sel_start;
// we ignore the end value for IE, this is already dirty enough and we don't need it
caretPos.start = txtarea.sel_start;
caretPos.end = txtarea.sel_start;
}
return caretPos;
}
/**
* Allow to use tab character when typing code
* Keep indentation of last line of code when typing code
*/
(function($) {
$(document).ready(function() {
var doc, textarea;
// find textarea, make sure browser supports necessary functions
if (document.forms[form_name]) {
doc = document;
} else {
doc = opener.document;
}
if (!doc.forms[form_name]) {
return;
}
textarea = doc.forms[form_name].elements[text_name];
phpbb.applyCodeEditor(textarea);
if ($('#attach-panel').length) {
phpbb.showDragNDrop(textarea);
}
$('textarea').on('keydown', function (e) {
if (e.which === 13 && (e.metaKey || e.ctrlKey)) {
$(this).closest('form').find(':submit').click();
}
});
});
})(jQuery);

View File

@ -0,0 +1,615 @@
/**
* 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;
var transmissionOver = false;
var statusCount = 0;
// 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, $progressFillerText;
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 = $('<div />');
$progressFiller.attr('id', 'progress-bar-filler');
$progressFillerText = $('<p />');
$progressFillerText.attr('id', 'progress-bar-filler-text');
$statusText = $('<p />');
$statusText.attr('id', 'progress-status-text');
$progressFiller.append($progressFillerText);
$progressBar.append($progressText);
$progressBar.append($progressFiller);
$progressBarWrapper.append($statusText);
$progressBarWrapper.append($progressBar);
$progressFillerText.css('width', $progressBar.width());
progressBarTriggered = true;
} else if (progressObject.hasOwnProperty('restart')) {
clearInterval(progressTimer);
$progressFiller = $('#progress-bar-filler');
$progressFillerText = $('#progress-bar-filler-text');
$progressText = $('#progress-bar-text');
$statusText = $('#progress-status-text');
$progressText.text('0%');
$progressFillerText.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;
}
}
// Redirects user
function redirect(url, use_ajax) {
if (use_ajax) {
resetPolling();
var xhReq = createXhrObject();
xhReq.open('GET', url, true);
xhReq.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhReq.send();
startPolling(xhReq);
} else {
window.location.href = url;
}
}
/**
* 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;
}
if (responseObject.hasOwnProperty('redirect')) {
redirect(responseObject.redirect.url, responseObject.redirect.use_ajax);
}
if (responseObject.hasOwnProperty('over')) {
if (responseObject.over) {
transmissionOver = true;
}
}
}
/**
* Processes status data
*
* @param status
*/
function processTimeoutResponse(status) {
if (statusCount === 12) { // 1 minute hard cap
status = 'fail';
}
if (status === 'continue') {
refreshRequested = false;
doRefresh();
} else if (status === 'running') {
statusCount++;
$('#loading_indicator').css('display', 'block');
setTimeout(queryInstallerStatus, 5000);
} else {
$('#loading_indicator').css('display', 'none');
addMessage('error',
[{
title: installLang.title,
description: installLang.msg
}]
);
}
}
/**
* Queries the installer's status
*/
function queryInstallerStatus() {
var url = $(location).attr('pathname');
var lookUp = 'install/app.php';
var position = url.indexOf(lookUp);
if (position === -1) {
lookUp = 'install';
position = url.indexOf(lookUp);
if (position === -1) {
return false;
}
}
url = url.substring(0, position) + lookUp + '/installer/status';
$.getJSON(url, function(data) {
processTimeoutResponse(data.status);
});
}
/**
* 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();
var timeoutDetected = !transmissionOver;
if (refreshRequested) {
refreshRequested = false;
doRefresh();
}
if (timeoutDetected) {
statusCount = 0;
queryInstallerStatus();
}
}
}
/**
* Animates the progress bar
*
* @param $progressText
* @param $progressFiller
* @param $progressFillerText
* @param progressLimit
*/
function incrementFiller($progressText, $progressFiller, $progressFillerText, progressLimit) {
if (currentProgress >= progressLimit || currentProgress >= 100) {
clearInterval(progressTimer);
return;
}
var $progressBar = $('#progress-bar');
currentProgress++;
$progressFillerText.css('width', $progressBar.width());
$progressFillerText.text(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 $progressFillerText = $('#progress-bar-filler-text');
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, $progressFillerText, 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();
transmissionOver = false;
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('&nbsp;');
$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

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,657 @@
/* global phpbb, plupload, attachInline */
plupload.addI18n(phpbb.plupload.i18n);
phpbb.plupload.ids = [];
(function($) { // Avoid conflicts with other libraries
'use strict';
/**
* Set up the uploader.
*/
phpbb.plupload.initialize = function() {
// Initialize the Plupload uploader.
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.
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.
$('#attach-row-tpl, #attach-panel-basic').remove();
// Show multi-file upload options.
$('#attach-panel-multi').show();
});
phpbb.plupload.uploader.bind('PostInit', function() {
// Point out the drag-and-drop zone if it's supported.
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')) {
phpbb.plupload.uploader.refresh();
}
$('[data-subpanel="attach-panel"]').one('click', function() {
phpbb.plupload.uploader.refresh();
});
});
};
/**
* Unsets all elements in the object uploader.settings.multipart_params whose keys
* begin with 'attachment_data['
*/
phpbb.plupload.clearParams = function() {
var obj = phpbb.plupload.uploader.settings.multipart_params;
for (var key in obj) {
if (!obj.hasOwnProperty(key) || key.indexOf('attachment_data[') !== 0) {
continue;
}
delete phpbb.plupload.uploader.settings.multipart_params[key];
}
};
/**
* Update uploader.settings.multipart_params object with new data.
*
* @param {object} obj
*/
phpbb.plupload.updateMultipartParams = function(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.
*
* @returns {object} An object in the form 'attachment_data[i][key]': value as
* expected by the server
*/
phpbb.plupload.getSerializedData = function() {
var obj = {};
for (var i = 0; i < phpbb.plupload.data.length; i++) {
var datum = phpbb.plupload.data[i];
for (var key in datum) {
if (!datum.hasOwnProperty(key)) {
continue;
}
obj['attachment_data[' + i + '][' + key + ']'] = datum[key];
}
}
return obj;
};
/**
* Get the index from the phpbb.plupload.data array where the given
* attachment id appears.
*
* @param {int} attachId The attachment id of the file.
* @returns {bool|int} Index of the file if exists, otherwise false.
*/
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.
*/
phpbb.plupload.setData = function(data) {
// Make sure that the array keys are reset.
phpbb.plupload.ids = phpbb.plupload.data = [];
phpbb.plupload.data = data;
for (var i = 0; i < data.length; i++) {
phpbb.plupload.ids.push(Number(data[i].attach_id));
}
};
/**
* 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.
*/
phpbb.plupload.update = function(data, action, index, downloadUrl) {
phpbb.plupload.updateBbcode(action, index);
phpbb.plupload.setData(data);
phpbb.plupload.updateRows(downloadUrl);
phpbb.plupload.clearParams();
phpbb.plupload.updateMultipartParams(phpbb.plupload.getSerializedData());
};
/**
* Update the relevant elements and hidden data for all attachments.
*
* @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++) {
phpbb.plupload.updateRow(i, downloadUrl);
}
};
/**
* Insert a row for a new attachment. This expects an HTML snippet in the HTML
* 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.
*/
phpbb.plupload.insertRow = function(file) {
var row = $(phpbb.plupload.rowTpl);
row.attr('id', file.id);
row.find('.file-name').html(plupload.xmlEncode(file.name));
row.find('.file-size').html(plupload.formatSize(file.size));
if (phpbb.plupload.order === 'desc') {
$('#file-list').prepend(row);
} else {
$('#file-list').append(row);
}
};
/**
* 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.
*/
phpbb.plupload.updateRow = function(index, downloadUrl) {
var attach = phpbb.plupload.data[index],
row = $('[data-attach-id="' + attach.attach_id + '"]');
// Add the link to the file
if (typeof downloadUrl !== 'undefined' && typeof downloadUrl[index] !== 'undefined') {
var url = downloadUrl[index].replace('&amp;', '&'),
link = $('<a></a>');
link.attr('href', url).html(attach.real_filename);
row.find('.file-name').html(link);
}
row.find('textarea').attr('name', 'comment_list[' + index + ']');
phpbb.plupload.updateHiddenData(row, attach, index);
};
/**
* 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
*/
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('value', attach[key]);
$(row).append(input);
}
};
/**
* Deleting a file removes it from the queue and fires an AJAX event to the
* server to tell it to remove the temporary attachment. The server
* 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.
*/
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 = phpbb.plupload.uploader.getFile(row.attr('id'));
phpbb.plupload.uploader.removeFile(file);
row.slideUp(100, function() {
row.remove();
phpbb.plupload.hideEmptyList();
});
}
var index = phpbb.plupload.getIndex(attachId);
row.find('.file-status').toggleClass('file-uploaded file-working');
if (index === false) {
return;
}
var fields = {};
fields['delete_file[' + index + ']'] = 1;
var always = function() {
row.find('.file-status').removeClass('file-working');
};
var done = function(response) {
if (typeof response !== 'object') {
return;
}
// trigger_error() was called which likely means a permission error was encountered.
if (typeof response.title !== 'undefined') {
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');
return;
}
phpbb.plupload.update(response, 'removal', index);
// Check if the user can upload files now if he had reached the max files limit.
phpbb.plupload.handleMaxFilesReached();
if (row.attr('id')) {
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();
});
phpbb.plupload.uploader.trigger('FilesRemoved');
};
$.ajax(phpbb.plupload.config.url, {
type: 'POST',
data: $.extend(fields, phpbb.plupload.getSerializedData()),
headers: phpbb.plupload.config.headers
})
.always(always)
.done(done);
};
/**
* Check the attachment list and hide its container if it's empty.
*/
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.
*
* @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),
text = textarea.val(),
removal = (action === 'removal');
// Return if the bbcode isn't used at all.
if (text.indexOf('[attachment=') === -1) {
return;
}
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]';
});
}
// 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);
}
}
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
*
* @returns {Array} The Plupload file objects matching the status.
*/
phpbb.plupload.getFilesByStatus = function(status) {
var files = [];
$.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.
*
* @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.
if (!phpbb.plupload.maxFiles) {
return false;
}
if (phpbb.plupload.maxFiles <= phpbb.plupload.ids.length) {
// Fail the rest of the queue.
phpbb.plupload.markQueuedFailed(phpbb.plupload.lang.TOO_MANY_ATTACHMENTS);
// Disable the uploader.
phpbb.plupload.disableUploader();
phpbb.plupload.uploader.trigger('Error', { message: phpbb.plupload.lang.TOO_MANY_ATTACHMENTS });
return true;
} 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
*/
phpbb.plupload.disableUploader = function() {
$('#add_files').addClass('disabled');
phpbb.plupload.uploader.disableBrowse();
};
/**
* Enable the uploader
*/
phpbb.plupload.enableUploader = function() {
$('#add_files').removeClass('disabled');
phpbb.plupload.uploader.disableBrowse(false);
};
/**
* Mark all queued files as failed.
*
* @param {string} error Error message to present to the user.
*/
phpbb.plupload.markQueuedFailed = function(error) {
var files = phpbb.plupload.getFilesByStatus(plupload.QUEUED);
$.each(files, function(i, file) {
$('#' + 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.
*/
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
});
};
/**
* Set up the Plupload object and get some basic data.
*/
phpbb.plupload.uploader = new plupload.Uploader(phpbb.plupload.config);
phpbb.plupload.initialize();
var $fileList = $('#file-list');
/**
* Insert inline attachment bbcode.
*/
$fileList.on('click', '.file-inline-bbcode', function(e) {
var attachId = $(this).parents('.attach-row').attr('data-attach-id'),
index = phpbb.plupload.getIndex(attachId);
attachInline(index, phpbb.plupload.data[index].real_filename);
e.preventDefault();
});
/**
* Delete a file.
*/
$fileList.on('click', '.file-delete', function(e) {
var row = $(this).parents('.attach-row'),
attachId = row.attr('data-attach-id');
phpbb.plupload.deleteFile(row, attachId);
e.preventDefault();
});
/**
* Display the error message for a particular file when the error icon is clicked.
*/
$fileList.on('click', '.file-error', function(e) {
phpbb.alert($(this).attr('data-error-title'), $(this).attr('data-error-message'));
e.preventDefault();
});
/**
* Fires when an error occurs.
*/
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.
if (error.code === plupload.FILE_EXTENSION_ERROR) {
error.message = plupload.translate('Invalid file extension:') + ' ' + error.file.name;
} else if (error.code === plupload.FILE_SIZE_ERROR) {
error.message = plupload.translate('File too large:') + ' ' + error.file.name;
}
phpbb.alert(phpbb.plupload.lang.ERROR, error.message);
});
/**
* Fires before a given file is about to be uploaded. This allows us to
* 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
*/
phpbb.plupload.uploader.bind('BeforeUpload', function(up, file) {
if (phpbb.plupload.handleMaxFilesReached()) {
return;
}
phpbb.plupload.updateMultipartParams({ real_filename: file.name });
});
/**
* Fired when a single chunk of any given file is uploaded. This parses the
* 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
* been uploaded
* @param {object} response The response object from the server
*/
phpbb.plupload.uploader.bind('ChunkUploaded', function(up, file, response) {
if (response.chunk >= response.chunks - 1) {
return;
}
var json = {};
try {
json = $.parseJSON(response.response);
} catch (e) {
file.status = plupload.FAILED;
up.trigger('FileUploaded', file, {
response: JSON.stringify({
error: {
message: 'Error parsing server response.'
}
})
});
}
// If trigger_error() was called, then a permission error likely occurred.
if (typeof json.title !== 'undefined') {
json.error = { message: json.message };
}
if (json.error) {
file.status = plupload.FAILED;
up.trigger('FileUploaded', file, {
response: JSON.stringify({
error: {
message: json.error.message
}
})
});
}
});
/**
* Fires when files are added to the queue.
*/
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()) {
return;
}
// Switch the active tab if the style supports it
if (typeof activateSubPanel === 'function') {
activateSubPanel('attach-panel'); // jshint ignore: line
}
// Show the file list if there aren't any files currently.
var $fileListContainer = $('#file-list-container');
if (!$fileListContainer.is(':visible')) {
$fileListContainer.show(100);
}
$.each(files, function(i, file) {
phpbb.plupload.insertRow(file);
});
up.bind('UploadProgress', function(up, file) {
$('.file-progress-bar', '#' + file.id).css('width', file.percent + '%');
$('#file-total-progress-bar').css('width', up.total.percent + '%');
});
// Do not allow more files to be added to the running queue.
phpbb.plupload.disableUploader();
// Start uploading the files once the user has selected them.
up.start();
});
/**
* Fires when an entire file has been uploaded. It checks for errors
* returned by the server otherwise parses the list of attachment data and
* 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
* uploaded
* @param {string} response The response string from the server
*/
phpbb.plupload.uploader.bind('FileUploaded', function(up, file, response) {
var json = {},
row = $('#' + file.id),
error;
// Hide the progress indicator.
row.find('.file-progress').hide();
try {
json = JSON.parse(response.response);
} catch (e) {
error = 'Error parsing server 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 });
// The rest of the queue will fail.
phpbb.plupload.markQueuedFailed(error);
} else if (json.error) {
error = json.error.message;
}
if (typeof error !== 'undefined') {
phpbb.plupload.fileError(file, error);
} else if (file.status === plupload.DONE) {
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]);
}
});
/**
* Fires when the entire queue of files have been uploaded.
*/
phpbb.plupload.uploader.bind('UploadComplete', function() {
// Hide the progress bar
setTimeout(function() {
$('#file-total-progress-bar').fadeOut(500, function() {
$(this).css('width', 0).show();
});
}, 2000);
// Re-enable the uploader
phpbb.plupload.enableUploader();
});
})(jQuery); // Avoid conflicts with other libraries