1
0
mirror of https://github.com/phpbb/phpbb.git synced 2025-01-18 14:48:28 +01:00
2016-02-29 16:40:09 +01:00

616 lines
15 KiB
JavaScript

/**
* 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