MediaWiki:Gadget-ajaxQuickDelete.js
JS-код ниже относится к гаджету «Добавить ссылки для пометки проблемных файлов (бета)» (править описание). Связанный CSS-файл: MediaWiki:Gadget-ajaxQuickDelete.css. Его использует около 2000 учётных записей.
После сохранения или недавних изменений очистите кэш браузера.
// Original code written by [[User:Ilmari Karonen]]
// Rewritten & extended by [[User:DieBuche]]. Botdetection and encoding fixer by [[User:Lupo]]
//
// Ajax-based replacement for [[MediaWiki:Quick-delete-code.js]]
//<nowiki>
if (typeof(AjaxQuickDelete) == 'undefined') {
window.AjaxQuickDelete = {
/**
** Set up the AjaxQuickDelete object and add the toolbox link. Called via $(document).ready() during page loading.
**/
install: function () {
this.insertTagButtons = [{
label: this.i18n.toolboxLinkAuthor,
tag: '{\{subst:nad}}',
talk_tag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
img_summary: 'не указан автор файла',
talk_summary: 'уведомление об отсутствии автора для %FILE%'
}, {
label: this.i18n.toolboxLinkSource,
tag: '{\{subst:nsd}}',
talk_tag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
img_summary: 'не указан источник файла',
talk_summary: 'уведомление об отсутствии источника для %FILE%'
}, {
label: this.i18n.toolboxLinkLicense,
tag: '{\{subst:nld}}',
talk_tag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
img_summary: 'не указана лицензия файла',
talk_summary: 'уведомление об отсутствии лицензии для %FILE%'
}, {
label: this.i18n.toolboxLinkPermission,
tag: '{\{subst:npd}}',
talk_tag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
img_summary: 'отсутствует разрешение на использование файла',
talk_summary: 'уведомление об отсутствии разрешения на использование %FILE%'
}, {
label: this.i18n.toolboxLinkDisputed,
tag: '{\{subst:dd}}',
talk_tag: '{\{subst:Запрос о статусе файла|1=%FILE%}}',
img_summary: 'сомнительный статус файла',
talk_summary: 'уведомление о сомнительном статусе %FILE%'
}, {
label: this.i18n.toolboxLinkOrphanedFairuse,
tag: '{\{subst:ofud}}',
img_summary: 'неиспользуемый несвободный файл',
}];
this.insertTagButtons = this.insertTagButtons.concat(window.AjaxDeleteExtraButtons);
},
insertTagOnPage: function (tag, img_summary, talk_tag, talk_summary, prompt_text, page) {
this.pageName = (page === undefined) ? wgPageName.replace(/_/g,' ') : page.replace(/_/g,' ');
this.tag = tag + '\n';
this.img_summary = img_summary;
// first schedule some API queries to fetch the info we need...
this.tasks = [];
// get token
this.addTask('findCreator');
this.addTask('prependTemplate');
if (talk_tag != "undefined") {
this.talk_tag = talk_tag.replace('%FILE%', this.pageName);
this.talk_summary = talk_summary.replace('%FILE%', '[[:' + this.pageName + ']]');
this.usersNeeded = true;
this.addTask('notifyUploaders');
}
this.addTask('reloadPage');
if (tag.indexOf("%PARAMETER%") != -1) {
this.prompt([{
message: '',
prefill: '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true
}], prompt_text || this.i18n.reasonForDeletion);
} else {
this.nextTask();
}
},
nominateForDeletion: function (page) {
this.pageName = (page === undefined) ? wgPageName : page;
this.startDate = new Date();
// set up some page names we'll need later
this.requestPage = this.requestPagePrefix + this.pageName;
this.dailyLogPage = this.requestPagePrefix + this.formatDate("YYYY/MM/DD");
this.tag = "{{delete|reason=%PARAMETER%|subpage=" + this.pageName + this.formatDate("|year=YYYY|month=MON|day=DAY}}\n");
// On templates: Wrap inside <noinclude>s. Thanks Rillke
if ( mw.config.get("wgNamespaceNumber") == 10 ) {
this.tag = '<noinclude>' + this.tag + '</noinclude>';
}
this.img_summary = 'Nominating for deletion';
this.talk_tag = "{\{subst:idw|" + this.pageName + "}}";
this.talk_summary = "[[:" + this.pageName + "]] has been nominated for deletion";
this.subpage_summary = 'Starting deletion request';
// first schedule some API queries to fetch the info we need...
this.tasks = []; // reset task list in case an earlier error left it non-empty
this.addTask('findCreator');
// ...then schedule the actual edits
this.addTask('prependTemplate');
this.addTask('createRequestSubpage');
this.addTask('listRequestSubpage');
this.addTask('notifyUploaders');
// finally reload the page to show the deletion tag
this.addTask('reloadPage');
this.prompt([{
message: '',
prefill: '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true
}], this.i18n.reasonForDeletion);
},
/**
** Edit the current page to add the specified tag. Assumes that the page hasn't
** been tagged yet; if it is, a duplicate tag will be added.
**/
prependTemplate: function () {
var page = [];
page.title = this.pageName;
page.text = this.tag;
page.editType = 'prependtext';
if (window.AjaxDeleteWatchFile) page.watchlist = 'watch';
this.showProgress(this.i18n.addingAnyTemplate);
this.savePage(page, this.img_summary, 'nextTask');
},
/**
** Create the DR subpage (or append a new request to an existing subpage).
** The request page will always be watchlisted.
**/
createRequestSubpage: function () {
this.templateAdded = true; // we've got this far; if something fails, user can follow instructions on template to finish
var page = [];
page.title = this.requestPage;
page.text = "\n\n=== [[:" + this.pageName + "]] ===\n" + this.reason + " ~~" + "~~\n";
page.watchlist = 'watch';
page.editType = 'appendtext';
this.showProgress(this.i18n.creatingNomination);
this.savePage(page, this.subpage_summary, 'nextTask');
},
/**
** Transclude the nomination page onto today's DR log page, creating it if necessary.
** The log page will never be watchlisted (unless the user is already watching it).
**/
listRequestSubpage: function () {
var page = [];
page.title = this.dailyLogPage;
// Impossible when using appendtext. Shouldn't not be severe though, since DRBot creates those pages before they are needed.
// if (!page.text) page.text = "{{"+"subst:" + this.requestPagePrefix + "newday}}"; // add header to new log pages
page.text = "\n{{" + this.requestPage + "}}\n";
page.watchlist = 'nochange';
page.editType = 'appendtext';
this.showProgress(this.i18n.listingNomination);
this.savePage(page, "Listing [[" + this.requestPage + "]]", 'nextTask');
},
/**
** Notify any uploaders/creators of this page using {{idw}}.
**/
notifyUploaders: function () {
this.uploadersToNotify = 0;
for (var user in this.uploaders) {
if (user == wgUserName) continue; // notifying yourself is pointless
var page = [];
page.title = this.userTalkPrefix + user;
page.text = "\n" + this.talk_tag + " ~~" + "~~\n";
page.editType = 'appendtext';
page.redirect = true;
if (window.AjaxDeleteWatchUserTalk) page.watchlist = 'watch';
this.savePage(page, this.talk_summary, 'uploaderNotified');
this.showProgress(this.i18n.notifyingUploader.replace('%USER%', user));
this.uploadersToNotify++;
}
if (this.uploadersToNotify === 0) this.nextTask();
},
uploaderNotified: function () {
this.uploadersToNotify--;
if (this.uploadersToNotify === 0) this.nextTask();
},
/**
** Compile a list of uploaders to notify. Users who have only reverted the file to an
** earlier version will not be notified.
** DONE: notify creator of non-file pages
**/
findCreator: function () {
if (wgNamespaceNumber == 6) {
var query = {
action: 'query',
prop: 'imageinfo|revisions|info',
rvprop: 'content|timestamp',
intoken: 'edit',
iiprop: 'user|sha1|comment',
iilimit: 50,
titles: this.pageName
};
} else {
var query = {
action: 'query',
prop: 'info|revisions',
rvprop: 'user|timestamp',
rvlimit: 1,
rvdir: 'newer',
intoken: 'edit',
titles: this.pageName
};
}
this.showProgress();
this.doAPICall(query, 'findCreatorCB');
},
findCreatorCB: function (result) {
this.uploaders = {};
var pages = result.query.pages;
for (var id in pages) { // there should be only one, but we don't know its ID
// The edittoken only changes between logins
this.edittoken = pages[id].edittoken;
//First handle non-file pages
if (wgNamespaceNumber != 6) {
this.pageCreator = pages[id].revisions[0].user;
this.starttimestamp = pages[id].starttimestamp;
this.timestamp = pages[id].revisions[0].timestamp;
this.uploaders[this.pageCreator] = true;
} else {
var info = pages[id].imageinfo;
var content = pages[id].revisions[0]['*'];
var seenHashes = {};
for (var i = info.length - 1; i >= 0; i--) { // iterate in reverse order
if (info[i].sha1 && seenHashes[info[i].sha1]) continue; // skip reverts
seenHashes[info[i].sha1] = true;
// Now exclude bots which only reupload a new version:
this.excludedBots = 'FlickreviewR, Rotatebot, Cropbot, Picasa Review Bot';
if (this.excludedBots.indexOf(info[i].user) != -1) continue;
// Handle some special cases, most of the code by [[User:Lupo]]
if (info[i].user == 'File Upload Bot (Magnus Manske)') {
// CommonsHelper
match = /transferred to Commons by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec(info[i].comment);
// geograph_org2commons, regex accounts for typo ("transferd") and it's possible future correction
if (!match) {
match = /geograph.org.uk\]; transferr?e?d by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\] using/.exec(info[i].comment);
}
// flickr2commons
if (!match) match = /\* Uploaded by \[\[User:([^\]\|]*)(\|([^\]]*))?\]\]/.exec(content);
if (match) match = match[1];
// Really necessary?
match = this.fixDoubleEncoding(match);
} else if (info[i].user == 'FlickrLickr') {
match = /\n\|reviewer=\s*(.*)\n/.exec(content);
if (match) match = match[1];
} else if (info[i].user == 'Flickr upload bot') {
// Check for the bot's upload template
match = /\{\{User:Flickr upload bot\/upload(\|[^\|\}]*)?\|reviewer=([^\}]*)\}\}/.exec(content);
if (match) match = match[2];
} else {
// No special case applies, just continue;
this.uploaders[info[i].user] = true;
continue;
}
if (match) {
// Make sure the username is in canonical form
match = match.replace(/^[\s_]+/, "").replace(/[\s_]+$/, "").replace(/[\s_]+/g, " ");
match = match.substr(0, 1).toUpperCase() + match.substr(1);
this.uploaders[match] = true;
}
}
}
}
this.nextTask();
},
removeTemplate: function () {
var page = [];
page.title = (this.destination || wgPageName);
page.text = this.pageContent.replace(/\{\{(rename|rename media|move)\|.*?\}\}/i, "");
page.editType = 'text';
page.starttimestamp = this.starttimestamp;
page.timestamp = this.timestamp;
this.showProgress(this.i18n.removingTemplate);
this.savePage(page, (this.declineReason || this.i18n.renameDone), 'nextTask');
},
redirectPage: function () {
var page = [];
page.title = wgPageName;
page.text = '#REDIRECT [[' + this.destination + ']]';
page.editType = 'text';
this.showProgress(this.i18n.redirectingFile);
this.savePage(page, 'Redirecting to duplicate file', 'nextTask');
},
saveDescription: function () {
var page = [];
page.title = this.destination;
page.text = this.newPageText;
page.editType = 'text';
this.showProgress(this.i18n.savingDescription);
this.savePage(page, 'Merging details from duplicate', '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 = AjaxQuickDelete.cleanReason(response);
if (v.returnvalue == 'destination') response = AjaxQuickDelete.cleanFileName(response);
}
AjaxQuickDelete[v.returnvalue] = response;
if (v.returnvalue == 'reason' && AjaxQuickDelete.tag) {
AjaxQuickDelete.tag = AjaxQuickDelete.tag.replace('%PARAMETER%', response);
AjaxQuickDelete.img_summary = AjaxQuickDelete.img_summary.replace('%PARAMETER%', response);
AjaxQuickDelete.img_summary = AjaxQuickDelete.img_summary.replace('%PARAMETER-LINKED%', '[[:' + response + ']]');
}
});
$(this).dialog('close');
AjaxQuickDelete.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>');
}
var curQuestion = $('#AjaxQuestion' + i);
curQuestion.keyup(function (event) {
if (v.noEmpty) {
if ($(this).val().length < ( v.minLength || 4 )) {
submitButton.addClass('ui-state-disabled');
} else {
submitButton.removeClass('ui-state-disabled');
}
}
if (event.keyCode == '13' && v.enterToSubmit != false) submitButton.click();
});
curQuestion.val(v.prefill);
if (v.type == 'checkbox') $('#AjaxQuestion' + i).attr('checked', v.prefill).attr('style', 'margin-left: 5px');
curQuestion.keyup();
});
$('#AjaxQuestion0').focus();
},
/**
** Pseudo-Modal JS windows.
**/
compareDetails: function () {
d = this.details[0];
f = this.details[1];
document.body.style.cursor = 'default';
this.progressDialog.remove();
if (d.sha1 == f.sha1) {
this.exactDupes = true;
this.nextTask();
return;
}
var dlgButtons = {};
dlgButtons[this.i18n.submitButtonLabel] = function () {
$(this).dialog("close");
AjaxQuickDelete.nextTask();
};
dlgButtons[this.i18n.cancelButtonLabel] = function () {
$(this).dialog("close");
};
var $dialog = $('<div></div>').html('<div id="AjaxDupeContainer"></div>').dialog({
width: 800,
modal: true,
title: 'title',
draggable: false,
dialogClass: "wikiEditor-toolbar-dialog",
close: function () {
$(this).dialog("destroy");
$(this).remove();
},
buttons: dlgButtons
});
$('#AjaxDupeContainer')
.append('<div><img src="' + d.thumburl + '" ></div><div><img src="' + f.thumburl + '"></div><br>')
.append('<div>' + Math.round(d.size / 1000) + ' KB <br>' + d.width + 'x' + d.height + '</div>')
.append('<div>' + Math.round(f.size / 1000) + ' KB <br>' + f.width + 'x' + f.height + '</div>');
},
/**
** Double encoding fixer by Lupo. This is necessary for some older uploads of Magnus' bot.
**/
fixDoubleEncoding: function (match) {
if (!match) return match;
var utf8 = /[u00C2-u00F4][u0080-u00BF][u0080-u00BF]?[u0080-u00BF]?/g;
if (!utf8.test(match)) return match;
// Looks like we have a double encoding. At least it contains character
// sequences that might be legal UTF-8 encodings. Translate them into %-
// syntax and try to decode again.
var temp = "",
curr = 0,
m, hex_digit = "0123456789ABCDEF";
var str = match.replace(/%/g, '%25');
utf8.lastIndex = 0;
// Reset regexp to beginning of string
try {
while ((m = utf8.exec(str)) != null) {
temp += str.substring(curr, m.index);
m = m[0];
for (var i = 0; i < m.length; i++) {
temp += '%' + hex_digit.charAt(m.charCodeAt(i) / 16) + hex_digit.charAt(m.charCodeAt(i) % 16);
}
curr = utf8.lastIndex;
}
if (curr < str.length) temp += str.substring(curr);
temp = decodeURIComponent(temp);
return temp;
} catch (e) {}
return match;
},
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');
currentExtension = wgPageName.toLowerCase().replace(/.*?\.(\w{3,4})$/, '$1').replace('jpeg', 'jpg');
// If new file name is without extension, add the one from the old name
if (uncleanName.toLowerCase().indexOf(currentExtension) == -1) uncleanName += '.' + currentExtension;
if (uncleanName.indexOf('File:') == -1) uncleanName = 'File:' + uncleanName;
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: 90,
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
};
if (page.redirect) edit.redirect = '';
edit[page.editType] = page.text;
this.doAPICall(edit, callback);
},
/**
** 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 () {
this.progressDialog.remove();
if (this.pageName && this.pageName.replace(/ /g, '_') != wgPageName) return;
var encTitle = (this.destination || wgPageName);
encTitle = encodeURIComponent(encTitle.replace(/ /g, '_')).replace(/%2F/ig, '/').replace(/%3A/ig, ':');
location.href = wgServer + wgArticlePath.replace("$1", encTitle);
},
/**
** 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) {
if ('object' === typeof err) {
var stErr = err.message + '<br/>' + err.name
if (err.lineNumber) stErr += ' @line' + err.lineNumber;
err = stErr;
}
document.body.style.cursor = 'default';
var msg = this.i18n.taskFailure[this.currentTask] || this.i18n.genericFailure;
//TODO: Needs cleanup
if (this.img_summary == 'Nominating for deletion') {
fix = (this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand);
}
else fix = '';
$('#feedbackContainer').html(msg + " " + fix + "<br>" + this.i18n.errorDetails + "<br>" + err
+ "<br><a href=" + 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
// DR subpage prefix
requestPagePrefix: "Commons:Deletion requests/",
// user talk page prefix
userTalkPrefix: wgFormattedNamespaces[3] + ":",
// MediaWiki API script URL
apiURL: (/^\/\//.test(mw.config.get("wgServer")) ? document.location.protocol : '') + mw.config.get("wgServer") + mw.util.wikiScript( 'api' ),
// Translatable strings
i18n: {
toolboxLinkDelete: "Nominate for deletion",
toolboxLinkDiscuss: "Nominate category for discussion",
// GUI reason prompt form
reasonForDeletion: "Why should this file be deleted?",
reasonForDiscussion: "Why does this category need discussion?",
submitButtonLabel: "Подтвердить",
cancelButtonLabel: "Отменить",
// GUI progress messages
preparingToEdit: "Подготовка к редактированию страниц… ",
creatingNomination: "Creating nomination page... ",
listingNomination: "Adding nomination page to daily list... ",
addingAnyTemplate: "Добавление шаблона на страницу описания" + (wgNamespaceNumber == 6 ? " файла" : "") + "… ",
notifyingUploader: "Уведомление участника %USER%… ",
// Extended version
toolboxLinkAuthor: "Нет автора",
toolboxLinkLicense: "Нет лицензии",
toolboxLinkSource: "Нет источника",
toolboxLinkPermission: "Нет разрешения",
toolboxLinkDisputed: "Сомнительный",
toolboxLinkOrphanedFairuse: "Неисп. несвободный",
toolboxLinkCopyvio: "Report copyright violation",
reasonForCopyvio: "Why is this file a copyright violation?",
// For moving files
renameDone: "Removing template; rename done",
removingTemplate: "Removing rename template",
notAllowed: "You do not have the neccessary rights to move files",
reasonForMove: "Why do you want to move this file?",
moveDestination: "What should be the new file name?",
movingFile: "Moving file",
replacingUsage: "Ordering CommonsDelinker to replace all usage",
declineMove: "Why do you want to decline the request?",
leaveRedirect: "Leave a redirect behind:",
//For Duplicates
deletingFile: "Deleting file",
mergeDescription: "Please now merge the file descriptions",
redirectingFile: "Redirecting file",
savingDescription: "Saving new details",
// Errors
genericFailure: "An error occurred while trying to do the requested action. ",
taskFailure: {
listUploaders: "An error occurred while determining the "
+ (6 == wgNamespaceNumber ? " uploader(s) of this file" : "creator of this page") + ".",
loadPages: "An error occurred while preparing to nominate this "
+ wgCanonicalNamespace.toLowerCase() + " for deletion.",
prependDeletionTemplate: "An error occurred while adding the {{delete}} template to this "
+ wgCanonicalNamespace.toLowerCase() + ".",
createRequestSubpage: "An error occurred while creating the request subpage.",
listRequestSubpage: "An error occurred while adding the deletion request to today's log.",
notifyUploaders: "An error occurred while notifying the "
+ (6 == wgNamespaceNumber ? " uploader(s) of this file" : "creator of this page") + "."
},
addTemplateByHand: "To nominate this " + wgCanonicalNamespace.toLowerCase()
+ " for deletion, please edit the page to add the {{delete}} template and follow the instructions shown on it.",
completeRequestByHand: "Please follow the instructions on the deletion notice to complete the request.",
errorDetails: "A detailed description of the error is shown below:",
errorReport: "Report the error here"
}
};
mediaWiki.loader.using('jquery.ui.dialog', function () {
$(document).ready(function () {
AjaxQuickDelete.install();
});
});
} // end if (guard)
// </nowiki>