User:BMacZero/AjaxCfdClose.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
Documentation for this user script can be added at User:BMacZero/AjaxCfdClose. |
- Report page listing warnings and errors.
// Ajax CFD Close adapted by [[User:BMacZero]] from [[MediaWiki:AjaxQuickDelete.js]].
//
// AjaxQuickDelete:
// Original code written by [[User:Ilmari Karonen]]
// Rewritten & extended by [[User:DieBuche]]. Botdetection and encoding fixer by [[User:Lupo]]
// <nowiki>
if (typeof AjaxCfdClose == 'undefined' && mw.config.get('wgNamespaceNumber') >= 0) {
window.AjaxCfdClose = {
/**
** Set up the AjaxCfdClose object and add the toolbox link. Called via $(document).ready() during page loading.
**/
install: function () {
var skin = mw.config.get('skin');
// Abort if the user is using an antique skin & load the legacy version
if ((skin != 'vector' && skin != 'monobook')) {
console.error("AjaxCfdClose - skin compatibility problem?");
}
// Import stylesheet
importStylesheet('MediaWiki:AjaxQuickDelete.css');
//jQuery UI is not loaded on all pages:
if (jQuery.ui == undefined) {
// FIXME: Use mw.loader.using() to depend on jquery.ui something
}
// Set up toolbox link
if (mw.config.get('wgPageName').match(/Commons:Categories_for_discussion\/[0-9][0-9][0-9][0-9]\/[0-9][0-9]\//g)) {
mw.util.addPortletLink('p-tb', 'javascript:AjaxCfdClose.closeCfd();', this.i18n.toolboxLinkCloseCfd, 't-ajaxcfdclose', null);
}
},
closeCfd: function () {
this.tasks = []; // reset task list in case an earlier error left it non-empty
//HACK:?
this.edittoken = mw.user.tokens.values.csrfToken;
this.addTask('formatClosedDiscussion');
//TODO: Remove CFD notice from any linked pages
//TODO: Add appropriate notices to affected talk pages
// finally reload the page to show it in its new closed state
this.addTask('reloadPage');
this.prompt([{
message: '',
prefill: '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true
}], this.i18n.queryCloseRationale);
},
/**
** Edit the current page to add the closing comment.
**/
formatClosedDiscussion: function () {
this.showProgress(this.i18n.closingCategoryDiscussion);
var edit = {
action: 'edit',
summary: "Closing category discussion with [[User:BMacZero/AjaxCfdClose.js|AjaxCfdClose]]",
watchlist: 'watch',
title: mw.config.get('wgPageName'),
token: this.edittoken
};
edit['prependtext'] = "{{cfdh}}\n";
edit['appendtext'] = "\n----\n" + AjaxCfdClose.reason + " ~~" + "~~\n{{cfdf}}";
this.doAPICall(edit, 'nextTask');
},
/**
** Pseudo-Modal JS windows.
**/
prompt: function (questions, title, width) {
var dlgButtons = {};
dlgButtons[this.i18n.submitButtonLabel] = function () {
$.each(questions, function (i, v) {
response = $('#AjaxQuestion' + i).val();
if (v.type == 'checkbox') response = $('#AjaxQuestion' + i).attr('checked');
if (v.cleanup) {
if (v.returnvalue == 'reason') response = AjaxCfdClose.cleanReason(response);
if (v.returnvalue == 'destination') response = AjaxCfdClose.cleanFileName(response);
}
AjaxCfdClose[v.returnvalue] = response;
if (v.returnvalue == 'reason' && AjaxCfdClose.tag) {
AjaxCfdClose.tag = AjaxCfdClose.tag.replace('%PARAMETER%', response);
AjaxCfdClose.img_summary = AjaxCfdClose.img_summary.replace('%PARAMETER%', response);
AjaxCfdClose.img_summary = AjaxCfdClose.img_summary.replace('%PARAMETER-LINKED%', '[[:' + response + ']]');
}
});
$(this).dialog("close");
AjaxCfdClose.nextTask();
};
dlgButtons[this.i18n.cancelButtonLabel] = function () {
$(this).dialog("close");
};
var $dialog = $('<div></div>')
.html('<div id="AjaxDeleteContainer"></div>')
.dialog({
width: (width || 600),
modal: true,
title: title,
draggable: false,
dialogClass: "wikiEditor-toolbar-dialog",
close: function () {
$(this).dialog("destroy");
$(this).remove();
},
buttons: dlgButtons
});
var submitButton = $('.ui-dialog-buttonpane button:first');
$.each(questions, function (i, v) {
if (v.type == 'textarea') {
$('#AjaxDeleteContainer').append(v.message)
.append('<textarea rows=20 id="AjaxQuestion' + i + '"><br><br>');
} else {
$('#AjaxDeleteContainer').append(v.message)
.append('<input type="' + (v.type || 'text') + '" id="AjaxQuestion' + i + '" style="width: 98%;"><br><br>');
}
$('#AjaxQuestion' + i).keyup(function (event) {
if (v.noEmpty) {
if ($(this).val().length < 4) {
submitButton.addClass('ui-state-disabled');
} else {
submitButton.removeClass('ui-state-disabled');
}
}
if (event.keyCode == '13' && v.enterToSubmit != false) submitButton.click();
});
$('#AjaxQuestion' + i).val(v.prefill);
if (v.type == 'checkbox') $('#AjaxQuestion' + i).attr('checked', v.prefill).attr('style', 'margin-left: 5px');
$('#AjaxQuestion' + i).keyup();
});
$('#AjaxQuestion0').focus();
},
cleanFileName: function (uncleanName) {
uncleanName = uncleanName.replace(/Image:/i, 'File:');
uncleanName = uncleanName.replace(/.jpe*g/i, '.jpg');
uncleanName = uncleanName.replace(/.png/i, '.png');
uncleanName = uncleanName.replace(/.gif/i, '.gif');
// If new file name is without extension, add the one from the old name
if (uncleanName.toLowerCase().indexOf(mw.config.get('wgPageName').toLowerCase().replace(/.*./, '').replace(/jpe*g/i, 'jpg')) == -1) {
uncleanName += '.' + mw.config.get('wgPageName').toLowerCase().replace(/.*./, '').replace(/jpe*g/i, 'jpg');
}
return uncleanName;
},
cleanReason: function (uncleanReason) {
// trim whitespace
uncleanReason = uncleanReason.replace(/^\s*(.+)\s*$/, '$1');
// remove signature
uncleanReason = uncleanReason.replace(/(.+)(--)?~{3,5}$/, '$1');
return uncleanReason;
},
/**
** For display of progress messages.
**/
showProgress: function (message) {
if ($('#feedbackContainer').length) {
$('#feedbackContainer').html(message);
} else {
document.body.style.cursor = 'wait';
this.progressDialog = $('<div></div>')
.html('<div id="feedbackContainer">' + (message || this.i18n.preparingToEdit) + '</div>')
.dialog({
width: 450,
height: 'auto',
minHeight: 90,
modal: true,
resizable: false,
draggable: false,
closeOnEscape: false,
dialogClass: "ajaxDeleteFeedback"
});
$('.ui-dialog-titlebar').hide();
}
},
/**
** Submit an edited page.
**/
savePage: function (page, summary, callback) {
var edit = {
action: 'edit',
summary: summary,
watchlist: (page.watchlist || 'preferences'),
title: page.title,
token: this.edittoken
};
edit[page.editType] = page.text;
this.doAPICall(edit, callback);
},
movePage: function () {
var edit = {
action: 'move',
reason: this.reason,
from: mw.config.get('wgPageName'),
to: this.destination,
movetalk: '',
token: this.movetoken
};
// Option to not leave a redirect behind, MediaWiki default does leave one behind
// Just like movetalk, an empty parameter sets it to true (true to not leave a redirect behind)
if (this.wpLeaveRedirect === false) {
edit.noredirect = '';
}
this.showProgress(this.i18n.movingFile);
this.doAPICall(edit, 'nextTask');
},
/**
** Does a MediaWiki API request and passes the result to the supplied callback (method name).
** Uses POST requests for everything for simplicity.
**/
doAPICall: function (params, callback) {
var o = this;
params.format = 'json';
$.ajax({
url: this.apiURL,
cache: false,
dataType: 'json',
data: params,
type: 'POST',
success: function (result, status, x) {
if (!result) return o.fail("Receive empty API response:\n" + x.responseText);
// In case we get the mysterious 231 unknown error, just try again
if (result.error && result.error.info.indexOf('231') != -1) return setTimeout(function () { o.doAPICall(params, callback); }, 500);
if (result.error) return o.fail("API request failed (" + result.error.code + "): " + result.error.info);
if (result.edit && result.edit.spamblacklist) return o.fail("The edit failed because " + result.edit.spamblacklist + " is on the Spam Blacklist");
try { o[callback](result); } catch (e) { return o.fail(e); }
},
error: function (x, status, error) {
return o.fail("API request returned code " + x.status + " " + status + "Error code is " + error);
}
});
},
/**
** Simple task queue. addTask() adds a new task to the queue, nextTask() executes
** the next scheduled task. Tasks are specified as method names to call.
**/
tasks: [], // list of pending tasks
currentTask: '', // current task, for error reporting
addTask: function (task) {
this.tasks.push(task);
},
nextTask: function () {
var task = this.currentTask = this.tasks.shift();
try { this[task](); } catch (e) { this.fail(e); }
},
/**
** Once we're all done, reload the page.
**/
reloadPage: function () {
location.href = mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace("$1", encodeURIComponent(this.destination || mw.config.get('wgPageName')));
},
/**
** Crude error handler. Just throws an alert at the user and
** (if we managed to add the {{delete}} tag) reloads the page.
**/
fail: function (err) {
document.body.style.cursor = 'default';
var msg = this.i18n.taskFailure[this.currentTask] || this.i18n.genericFailure;
var fix = (this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand);
$('#feedbackContainer').html(msg + " " + fix + "<br>" + this.i18n.errorDetails + "<br>" + err + "<br><a href=" + mw.config.get('wgServer') + "/wiki/MediaWiki_talk:AjaxQuickDelete.js>" + this.i18n.errorReport + "</a>");
$('.ui-dialog-content').height('auto');
$('.ui-dialog').addClass('ajaxDeleteError');
// Allow some time to read the message
if (this.templateAdded) setTimeout(this.reloadPage(), 5000);
},
/**
** Very simple date formatter. Replaces the substrings "YYYY", "MM" and "DD" in a
** given string with the UTC year, month and day numbers respectively.
** Also replaces "MON" with the English full month name and "DAY" with the unpadded day.
**/
formatDate: function (fmt, date) {
var pad0 = function (s) { s = "" + s; return (s.length > 1 ? s : "0" + s); }; // zero-pad to two digits
if (!date) date = this.startDate;
fmt = fmt.replace(/YYYY/g, date.getUTCFullYear());
fmt = fmt.replace(/MM/g, pad0(date.getUTCMonth() + 1));
fmt = fmt.replace(/DD/g, pad0(date.getUTCDate()));
fmt = fmt.replace(/MON/g, this.months[date.getUTCMonth()]);
fmt = fmt.replace(/DAY/g, date.getUTCDate());
return fmt;
},
months: "January February March April May June July August September October November December".split(" "),
// Constants
// MediaWiki API script URL
apiURL: mw.config.get('wgServer') + mw.config.get('wgScriptPath') + "/api.php",
// Translatable strings
i18n: {
// GUI reason prompt form
toolboxLinkCloseCfd : "Close category discussion",
submitButtonLabel : "Proceed",
cancelButtonLabel : "Cancel",
// Closing category discussion
queryCloseRationale: "What is your conclusion for this discussion?",
closingCategoryDiscussion: "Closing category discussion...",
// Errors
errorDetails: "A detailed description of the error is shown below:",
errorReport: "Report the error here"
}
};
var wgUserLanguage = mw.config.get('wgUserLanguage');
if (wgUserLanguage != 'en') importScript('User:BMacZero/AjaxCfdClose.js/' + wgUserLanguage);
$(document).ready(function () { AjaxCfdClose.install(); });
} // end if (guard)
// </nowiki>