મીડિયાવિકિ:Gadget-AjaxQuickDelete.js
Appearance
નોંધ: પાનું પ્રકાશિત કર્યા પછી, તમારે તમારા બ્રાઉઝરની કૅશ બાયપાસ કરવાની આવશ્યકતા પડી શકે છે.
- ફાયરફોક્સ / સફારી: શીફ્ટ દબાવેલી રાખીને રિલોડ પર ક્લિક કરો, અથવા તો Ctrl-F5 કે Ctrl-R દબાવો (મેક પર ⌘-R)
- ગુગલ ક્રોમ: Ctrl-Shift-R દબાવો (મેક પર ⌘-Shift-R)
- ઈન્ટરનેટ એક્સપ્લોરર/એજ: Ctrl દબાવેલી રાખીને રિફ્રેશ પર ક્લિક કરો, અથવા Ctrl-F5 દબાવો
- Opera: Ctrl-F5 દબાવો
// Original code written by [[User:Ilmari Karonen]]
// Rewritten & extended by [[User:DieBuche]]. Botdetection and encoding fixer by [[User:Lupo]]
// Validation and further development [[User:Rillke]], 2011-2012
//
// Ajax-based replacement for [[MediaWiki:Quick-delete-code.js]]
//
// TODO: Fix problems with moves of videos
// TODO: Delete talk
//<nowiki>
/*global jQuery:false, mediaWiki:false */
/*jshint curly:false, laxbreak:true, scripturl:true, onecase:true, */
(function($, mw) {
'use strict';
var namespaceNumber = mw.config.get('wgNamespaceNumber');
var pageName = mw.config.get('wgPageName');
var canonicalNS = mw.config.get('wgCanonicalNamespace');
var AjaxQuickDelete, AQD;
if (typeof AjaxQuickDelete !== 'undefined' || namespaceNumber < 0) return;
// utility method: Should be moved out into some global site code since used everywhere
$.createIcon = function (iconClass) {
return $('<span>', { 'class': 'ui-icon ' + iconClass + ' ajaxInlineIcon', text: ' ' });
};
AjaxQuickDelete = AQD = window.AjaxQuickDelete = {
/**
** Set up the AjaxQuickDelete object and add the toolbox link. Called via $(document).ready() during page loading.
**/
install: function() {
// Disallow performing operations on empty pages
if (0 === mw.config.get('wgArticleId')) return;
// Check edit restrictions and do not install anything if protected
if (mw.config.get('wgRestrictionEdit') && mw.config.get('wgRestrictionEdit').length) {
if ($.inArray(mw.config.get('wgRestrictionEdit')[0], mw.config.get('wgUserGroups')) === -1) {
return;
}
}
// wait for document.readyState
$(function() {
$(document).triggerHandler('scriptLoaded', ['AjaxQuickDelete']);
// Set up toolbox link
if (namespaceNumber !== 14) {
mw.util.addPortletLink('p-tb', 'javascript:AjaxQuickDelete.nominateForDeletion();', AQD.i18n.toolboxLinkDelete, 't-ajaxquickdelete', null);
} else {
mw.util.addPortletLink('p-tb', 'javascript:AjaxQuickDelete.discussCategory();', AQD.i18n.toolboxLinkDiscuss, 't-ajaxquickdiscusscat', null);
}
// Check user group.
if ($.inArray('sysop', mw.config.get('wgUserGroups')) !== -1) {
AQD.userRights = 'sysop';
} else if ($.inArray('filemover', mw.config.get('wgUserGroups')) !== -1) {
AQD.userRights = 'filemover';
}
// Install AjaxMoveButton
if ((AQD.userRights === 'filemover' || AQD.userRights === 'sysop') && namespaceNumber === 6) {
// Also add a "Move & Replace" button to dropdown menu
mw.util.addPortletLink('p-cactions', 'javascript:AjaxQuickDelete.moveFile("", "");', AQD.i18n.dropdownMove, 'ca-quickmove', 'ca-move');
//Add quicklinks to template
if ($('#AjaxRenameLink').length) {
$('#AjaxRenameLink').append('<a href="javascript:AjaxQuickDelete.moveFile();">' + AQD.i18n.moveAndReplace + '</a>').append('<a href="javascript:AjaxQuickDelete.declineRequest(\'move\');" class="ajaxDeleteDeclineMove"><sup> ' + AQD.i18n.anyDecline + '</sup></a>');
}
// Install x-To-DR
$('.ctdr-btn-convert').click(AQD._convertToDR);
$('.ctdr-btn-remove').click(AQD._removeAnyTag);
$('.convert-to-dr').show();
}
if (AQD.userRights === 'sysop' && namespaceNumber === 6) {
if ($('#AjaxDupeProcess').length) {
$('#AjaxDupeProcess').append('<a href="javascript:AjaxQuickDelete.processDupes();">Process Duplicates</a>').show();
}
}
// Extra buttons
if ("1" === mw.user.options.get('gadget-QuickDelete')) {
// Wait until the user's js was loaded and executed
mw.loader.using(['ext.gadget.QuickDelete', 'user'], function() {
AQD.doInsertTagButtons();
});
}
});
},
/**
** Ensure that all variables are in a good state
** You must call this method before doing anything!
**/
initialize: function(undefined) {
pageName = mw.config.get('wgPageName');
this.tasks = [];
this.destination = undefined;
this.details = undefined;
},
fileExists: function() {
this.i18n.moveDestination = this.i18n.moveOtherDestination;
this.moveFile();
},
/**
** For moving files
**/
moveFile: function() {
this.initialize();
this.showProgress();
if ($('#AjaxRenameLink').length) {
this.possibleDestination = this.cleanFileName($('#AjaxRenameDestination').text());
this.possibleReason = this.cleanReason($('#AjaxRenameReason').text());
}
if ($('#globalusage').length || !$('#mw-imagepage-nolinkstoimage').length) this.inUse = true;
this.addTask('doesFileExist');
this.fileNameExistsCB = 'fileExists';
this.addTask('getMoveToken');
this.addTask('movePage');
this.addTask('removeTemplate');
if (this.inUse) this.addTask('replaceUsage');
// finally reload the page to show changed page
this.addTask('reloadPage');
this.prompt([{
message: this.i18n.moveDestination,
prefill: (this.possibleDestination || this.cleanFileName(pageName)),
returnvalue: 'destination',
cleanUp: true,
noEmpty: true
}, {
message: this.i18n.reasonForMove,
prefill: (this.reason || this.possibleReason || ''),
returnvalue: 'reason',
cleanUp: true,
noEmpty: false
}, {
message: this.i18n.leaveRedirect,
prefill: true,
returnvalue: 'wpLeaveRedirect',
cleanUp: false,
noEmpty: false,
type: 'checkbox'
}], this.i18n.movingFile);
if (this.inUse || this.userRights === 'filemover') $('#AjaxQuestion2').attr('disabled', true);
},
/**
** For declining a request
**/
declineRequest: function(reason) {
// No valid reason stated, see the rename guidelines or not an exact duplicate
this.initialize();
this.addTask('getMoveToken');
this.addTask('removeTemplate');
// finally reload the page to show the template was removed
this.addTask('reloadPage');
// extend the reason
switch (reason) {
case 'move':
reason = 'No valid reason stated, see the [[COM:MOVE|rename guidelines]]';
break;
}
this.prompt([{
message: '',
prefill: reason || this.declineReason || '',
returnvalue: 'declineReason',
cleanUp: false,
noEmpty: true,
byteLimit: 250
}], this.i18n.declineRequest);
},
insertTagOnPage: function(tag, img_summary, talk_tag, talk_summary, prompt_text, page) {
this.initialize();
this.pageName = (page === undefined) ? pageName.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...
// get token
this.addTask('findCreator');
this.addTask('prependTemplate');
// Cave: insertTagOnPage is inserted as javascript link and therefore talk_tag can be "undefined"/string
if (talk_tag && 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,
minLength: 1
}], prompt_text || this.i18n.reasonForDeletion);
} else {
this.nextTask();
}
},
discussCategory: function() {
// reset task list in case an earlier error left it non-empty
this.initialize();
this.pageName = pageName.replace(/_/g, ' ');
this.startDate = new Date();
this.tag = '{' + '{subst:cfd}}';
this.img_summary = 'This category needs discussion';
this.talk_tag = '{' + '{subst:cdw|' + pageName + '}}';
this.talk_summary = "[[:" + pageName + "]] needs discussion";
this.subpage_summary = 'Starting category discussion';
// set up some page names we'll need later
this.requestPage = 'Commons:Categories for discussion/' + this.formatDate("YYYY/MM/") + pageName;
this.dailyLogPage = 'Commons:Categories for discussion/' + this.formatDate("YYYY/MM");
// first schedule some API queries to fetch the info we need...
this.addTask('findCreator');
// ...then schedule the actual edits
this.addTask('notifyUploaders');
this.addTask('prependTemplate');
this.addTask('createRequestSubpage');
this.addTask('listRequestSubpage');
// finally reload the page to show the deletion tag
this.addTask('reloadPage');
var lazyLoadNode = this.createLazyLoadNode(this.i18n.moreInformation, 'MediaWiki:Gadget-AjaxQuickDelete.js/DiscussCategoryInfo', '#AjaxQuickDeleteCatInfo');
this.prompt([{
message: '',
prefill: '',
returnvalue: 'reason',
cleanUp: true,
appendNode: lazyLoadNode,
noEmpty: true,
parseReason: true
}], this.i18n.reasonForDiscussion);
},
nominateForDeletion: function(page) {
var o = this;
// reset task list in case an earlier error left it non-empty
this.initialize();
mw.loader.using(['mediawiki.String', 'jquery.ui'], function(require) {
var mwStr = require('mediawiki.String');
o.pageName = (page === undefined) ? pageName.replace(/_/g, ' ') : page.replace(/_/g, ' ');
o.startDate = new Date();
// set up some page names we'll need later
var requestPage = o.pageName;
// MediaWiki has an ugly limit of 255 bytes per title, excluding the namespace
while (mwStr.byteLength(requestPage) + mwStr.byteLength(o.requestPagePrefix.replace(/^.+?\:/, '')) >= 255) {
requestPage = $.trim(requestPage.slice(0, requestPage.length-1));
}
o.requestPage = o.requestPagePrefix + requestPage;
o.dailyLogPage = o.requestPagePrefix + o.formatDate("YYYY/MM/DD");
o.tag = "{{delete|કારણ=%PARAMETER%|subpage=" + requestPage + o.formatDate("|year=YYYY|month=MON|day=DAY}}\n");
switch (namespaceNumber) {
// On MediaWiki pages, wrap inside comments (for css and js)
case 8:
o.tag = '/*' + o.tag + '*/';
break;
// On templates and creator/institution-templates: Wrap inside <noinclude>s.
case 10:
case 100:
case 106:
o.tag = '<noinclude>' + o.tag + '</noinclude>';
break;
}
o.img_summary = 'Nominating for deletion';
o.talk_tag = '{' + '{subst:idw|' + requestPage + '}}';
o.talk_summary = "[[:" + o.pageName + "]] has been nominated for deletion";
o.subpage_summary = 'Starting deletion request';
// first schedule some API queries to fetch the info we need...
o.addTask('findCreator');
// ...then schedule the actual edits
o.addTask('prependTemplate');
o.addTask('createRequestSubpage');
o.addTask('listRequestSubpage');
o.addTask('notifyUploaders');
// finally reload the page to show the deletion tag
o.addTask('reloadPage');
var lazyLoadNode = o.createLazyLoadNode(o.i18n.moreInformation, 'MediaWiki:Gadget-AjaxQuickDelete.js/DeleteInfo', '#AjaxQuickDeleteDeleteInfo');
o.prompt([{
message: '',
prefill: o.reason || '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true,
appendNode: lazyLoadNode,
parseReason: true
}], o.i18n.reasonForDeletion);
});
},
renderNode: function($node, remotecontent, selector) {
if (selector) selector = ' ' + selector;
$node.load(mw.config.get('wgScript') + '?' + $.param({
'action': 'render',
'title': remotecontent,
'uselang': mw.config.get('wgUserLanguage')
}) + (selector || ''), function() {
$node.find('a').each(function(i, el) {
var $el = $(el);
$el.attr('href', $el.attr('href').replace('MediaWiki:Anoneditwarning', mw.config.get('wgPageName')));
});
});
return $node;
},
createLazyLoadNode: function(label, page, selector) {
return $('<div>', {
style: 'min-height:40px;'
}).append($('<a>', {
'href': '#',
'text': label
}).click(function(e) {
e.preventDefault();
var $content = $(this).parent().find('.ajaxDeleteLazyLoad');
var $contentInner = $content.find('.ajax-quick-delete-loading');
if ($contentInner.length) {
// first time invoked, do the XHR to load the content
AQD.renderNode($content, $contentInner.data('aqdPage'), selector);
}
$content.toggle('fast');
}), $('<div>', {
'class': 'ajaxDeleteLazyLoad',
'style': 'display:none;'
}).append($('<span>', {
'class': 'ajax-quick-delete-loading',
'text': this.i18n.loading
}).data('aqdPage', page)));
},
extractFromHTML: function(DOMElement) {
var $el = $(DOMElement);
// ...extract the regular expression from html
this.templateRegExp = $el.parent().find('.ctdr-regex').text();
var m = this.templateRegExp.match(/^\/(.+)\/(i)?$/);
if (!m || !m[1]) {
var err = new Error('The template does not expose a valid regular expression for {{X-To-DR}}. Go the the template and fix it there.');
this.fail(err);
throw err;
}
this.templateRegExp = new RegExp(m[1], m[2]);
// ...and the template name itself
var template = $el.parent().find('.ctdr-template-name').text();
this.reason = "This file was initially tagged by %USER%" + (template ? (" as '''" + template + "'''") : "");
// ...and the decline reason
this.declineReason = $el.parent().find('.ctdr-template-decline-reason').text();
},
removeProgress: function() {
this.showProgress();
return this.nextTask();
},
/**
** Remove any tag
** @context DOM-Element
** This function must be called with the DOM-Element as this-arg!
**/
_removeAnyTag: function(e) {
AQD.extractFromHTML(this);
AQD.removeAnyTag();
return false;
},
removeAnyTag: function() {
this.initialize();
this.addTask('declineRequest');
this.nextTask();
},
/**
** Convert any tag to a deletion request
** @context DOM-Element
** This function must be called with the DOM-Element as this-arg!
**/
_convertToDR: function(e) {
AQD.extractFromHTML(this);
AQD.convertToDR();
return false;
},
convertToDR: function() {
// reset task list in case an earlier error left it non-empty
this.initialize();
// first schedule a API query to fetch the info we need...
this.addTask('findTemplateAdder');
this.addTask('getMoveToken');
// ...then schedule the actual edits
this.addTask('removeTemplate');
this.addTask('removeProgress');
this.addTask('nominateForDeletion');
this.declineReason = "This file does not qualify for [[COM:SPEEDY|speedy-deletion]] and a regular deletion request will be started.";
// Hide the buttons to prevent attempts of duplicate removal
$('.convert-to-dr').hide();
// ... and go!
this.nextTask();
},
findTemplateAdder: function() {
var query = {
action: 'query',
prop: 'revisions',
rvprop: 'user|content',
titles: pageName.replace(/_/g, ' '),
rvlimit: 50
};
this.doAPICall(query, 'findTemplateAdderCB');
},
findTemplateAdderCB: function(result) {
var m, reason, user, template;
$.each(result.query.pages, function(id, pg) {
$.each(pg.revisions, function(iRv, rv) {
m = rv['*'].match(AQD.templateRegExp);
if (m) {
user = rv.user;
if (m.length > 1 && !template) template = m[1];
if (m.length > 2 && !reason) reason = m[2];
} else {
return false;
}
});
});
if (!user) throw new Error("Unable to find the person who added the template. This can occur if the template was already removed, the page is deleted or a redirect to the template is used. In this case you must add the redirect to the RegExp of the target template.");
this.reason = this.reason.replace('%USER%', "[[User:" + user + "|" + user + "]]");
if (template) this.reason += " (" + template + ")";
if (reason) this.reason += " and the most recent rationale was: <tt>" + reason + "</tt>";
this.nextTask();
},
processDupes: function() {
// reset task list in case an earlier error left it non-empty
this.initialize();
if ($('#globalusage').length || !$('#mw-imagepage-nolinkstoimage').length) this.inUse = true;
this.addTask('getDupeDetails');
this.addTask('compareDetails');
this.addTask('mergeDescriptions');
this.addTask('saveDescription');
if (this.inUse) this.addTask('replaceUsage');
this.addTask('deletePage');
this.addTask('redirectPage');
this.addTask('reloadPage');
this.destination = $('#AjaxDupeDestination').text();
this.nextTask();
},
getDupeDetails: function() {
var query = {
action: 'query',
prop: 'imageinfo|revisions|info',
rvprop: 'content|timestamp',
intoken: 'edit|delete',
iiprop: 'size|sha1|url',
iiurlwidth: 365,
titles: pageName.replace(/_/g, ' ') + '|' + this.destination
};
this.doAPICall(query, 'getDupeDetailsCB');
this.showProgress('Fetching details');
},
getDupeDetailsCB: function(result) {
var pages, id, v, ii, n;
pages = result.query.pages;
this.details = [];
for (id in pages) {
if (pages.hasOwnProperty(id)) {
v = pages[id];
if (!v.imageinfo) {
// Nothing we can change so prevent users reporting
this.disableReport = true;
if ($.trim(v.title) === '{{{1}}}') {
throw new Error("Error in the duplicate-template, check your language version! (v.imageinfo is undefined)");
} else {
throw new Error("Retrieving information about " + v.title + " failed. It is possible that it is deleted, the last revision is corrupt or the file is a redirect. (v.imageinfo is undefined)");
}
}
ii = v.imageinfo[0];
n = {};
this.details.push(n);
n.title = v.title;
n.size = ii.size;
n.width = ii.width;
n.height = ii.height;
n.thumburl = ii.thumburl;
n.thumbwidth = ii.thumbwidth;
n.thumbheight = ii.thumbheight;
n.descriptionurl = ii.descriptionurl;
n.sha1 = ii.sha1;
n.content = v.revisions[0]['*'];
n.starttimestamp = v.starttimestamp;
this.edittoken = v.edittoken;
this.deletetoken = v.deletetoken;
}
}
//If ordner (old=0, new=1) not correct: Reverse the order
if (this.details[0].title !== pageName.replace(/_/g, ' ')) this.details.reverse();
this.nextTask();
},
/**
** 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=== [[:" + 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 (this.uploaders.hasOwnProperty(user)) {
if (user === mw.config.get('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() {
var query;
if (namespaceNumber === 6) {
query = {
action: 'query',
prop: 'imageinfo|revisions|info',
rvprop: 'content|timestamp',
intoken: 'edit',
iiprop: 'user|sha1|comment',
iilimit: 50,
titles: this.pageName
};
} else {
query = {
action: 'query',
prop: 'info|revisions',
rvprop: 'user|timestamp',
rvlimit: 1,
rvdir: 'newer',
intoken: 'edit',
titles: this.pageName
};
}
this.showProgress(this.i18n.preparingToEdit);
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
if (pages.hasOwnProperty(id)) {
// The edittoken only changes between sessions
this.edittoken = pages[id].edittoken;
if (!pages[id].revisions) {
this.disableReport = true;
throw new Error('The page you are attempting to add a tag to was deleted or moved. Unable to retrieve the content.');
}
//First handle non-file pages
if (namespaceNumber !== 6 || !pages[id].imageinfo) {
this.pageCreator = pages[id].revisions[0].user;
this.starttimestamp = pages[id].starttimestamp;
this.timestamp = pages[id].revisions[0].timestamp;
if (typeof this.pageCreator !== 'undefined') {
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', 'Reedy RotateBot'];
if (-1 !== $.inArray(info[i].user, this.excludedBots)) continue;
// outsourced to [[MediaWiki:Gadget-libCommons.js]]
var match = mw.libs.commons.getUploadBotUser(info[i].user, content, info[i].comment);
if (match) {
this.uploaders[match] = true;
}
}
}
}
}
this.nextTask();
},
getMoveToken: function() {
var query = {
action: 'query',
prop: 'info|revisions|imageinfo',
rvprop: 'content|timestamp',
iiprop: 'mime',
intoken: 'edit|move',
titles: pageName
};
this.showProgress(this.i18n.preparingToEdit);
this.doAPICall(query, 'getMoveTokenCB');
},
getMoveTokenCB: function(result) {
var pages = result.query.pages;
for (var id in pages) { // there should be only one, but we don't know its ID
if (pages.hasOwnProperty(id)) {
var pg = pages[id];
if (!pg.revisions) {
this.disableReport = true;
throw new Error('The page you are attempting to modify or move was deleted or moved. Unable to history and contents.');
}
// The edittoken only changes between sessions
this.edittoken = pg.edittoken;
this.movetoken = pg.movetoken;
this.pageContent = pg.revisions[0]['*'];
this.starttimestamp = pg.starttimestamp;
this.timestamp = pg.revisions[0].timestamp;
if (pg.imageinfo && pg.imageinfo.length && pg.imageinfo[0].mime) {
this.fileMime = pg.imageinfo[0].mime
.replace('image/jpeg', 'jpg')
.replace(/image\/(?:(png)|(gif)|x-(xcf)|vnd\.(djvu)|(svg)\+xml|(tif)f)/, '$1')
.replace(/application\/(ogg|pdf)/, '$1')
.replace('audio\/midi', 'mid');
if (this.fileMime.length > 5) this.fileMime = '';
}
}
}
this.nextTask();
},
doesFileExist: function() {
var toCheck = this.cleanFileName(this.destination).replace(/^File:/, '');
var query = {
'action': 'query',
'list': 'allpages',
'apfrom': toCheck,
'apto': toCheck,
'apnamespace': 6
};
this.showProgress(this.i18n.checkFileExists);
this.doAPICall(query, 'doesFileExistCB');
},
doesFileExistCB: function(result) {
if (!result || !result.query || !result.query.allpages) throw new Error('Checking file name: result.query.allpages is undefined.');
if (result.query.allpages[0]) {
if (this.fileNameExistsCB) this[this.fileNameExistsCB](result.query.allpages[0].title.replace(/^File:/, ''));
return;
}
this.nextTask();
},
removeTemplate: function() {
var page = {};
this.replaceWith = (this.replaceWith || (this.templateRegExp ? '' : '$1$2' ));
page.title = (this.destination || pageName);
page.text = $.trim(this.pageContent.replace((this.templateRegExp || /(?:([^\=])\n)?\{\{(?:rename|rename media|move)\|.*?\}\}(?:\n([^\=]))?/i), this.replaceWith));
page.editType = 'text';
page.starttimestamp = this.starttimestamp;
page.timestamp = this.timestamp;
this.showProgress(this.i18n.removingTemplate);
this.savePage(page, (this.declineReason || "Removing template; rename done"), 'nextTask');
},
replaceUsage: function() {
var page = {};
page.title = 'User:CommonsDelinker/commands';
if (this.userRights === 'filemover') {
page.title = 'User:CommonsDelinker/commands/filemovers';
this.reason = this.reason.replace(/\{/g, '{').replace(/\}/g, '}').replace(/\=/g, '=');
}
if (!this.details) this.reason = '[[COM:FR|File renamed]]: ' + this.reason.replace(/\[\[Commons:File[_ ]renaming[^\[\]]*\]\]:? ?/i, '');
page.text = '\n{{universal replace|' + pageName.replace('File:', '') + '|' + this.destination.replace('File:', '') + '|reason=' + this.reason + '}}';
page.editType = 'appendtext';
page.watchlist = 'nochange';
this.showProgress(this.i18n.replacingUsage);
this.savePage(page, 'universal replace: [[:' + pageName + ']] → [[:' + this.destination + ']]', 'nextTask');
},
redirectPage: function() {
var page = {};
page.title = pageName;
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 ([[' + pageName + ']])', 'nextTask');
},
/**
** Pseudo-Modal JS windows.
**/
prompt: function(questions, title, width) {
var o = this;
var dlgButtons = {};
dlgButtons[this.i18n.submitButtonLabel] = function() {
$.each(questions, function(i, v) {
var response = $('#AjaxQuestion' + i).val();
if (v.type === 'checkbox') response = $('#AjaxQuestion' + i).attr('checked');
if (v.cleanUp) {
if (v.returnvalue === 'reason') response = AQD.cleanReason(response);
if (v.returnvalue === 'destination') response = AQD.cleanFileName(response);
}
AQD[v.returnvalue] = response;
if (v.returnvalue === 'reason' && AQD.tag) {
AQD.tag = AQD.tag.replace('%PARAMETER%', response);
if (AQD.talk_tag) AQD.talk_tag = AQD.talk_tag.replace('%PARAMETER%', response);
AQD.img_summary = AQD.img_summary.replace('%PARAMETER%', response);
AQD.img_summary = AQD.img_summary.replace('%PARAMETER-LINKED%', '[[:' + response + ']]');
}
});
$(this).dialog('close');
AQD.nextTask();
};
dlgButtons[this.i18n.cancelButtonLabel] = function() {
$(this).dialog('close');
};
var $submitButton, $cancelButton;
var $AjaxDeleteContainer = $('<div>', {
id: 'AjaxDeleteContainer'
});
var _convertToTextarea = function(e) {
var $el = $(this),
$input = $el.data('toConvert'),
$tarea = $('<textarea>', { id: $input.attr('id'), style: 'height:10em; width:98%; display:none;' });
$el.off();
$el.fadeOut();
$input.parent().prepend(
$tarea
.data('v', $input.data('v')).data('parserResultNode', $input.data('parserResultNode'))
.val($input.val()).keyup(_parseReason).on('keyup input', _validateInput));
$tarea.slideDown();
$input.remove();
};
var _parseReason = function(event) {
var $el = $(this),
parsertimeout = $el.data('parsertimeout'),
parserjqXHR = $el.data('parserjqXHR'),
$parserResultNode = $el.data('parserResultNode'),
delay = 1000;
if (!$parserResultNode) return;
$parserResultNode.css('color', '#877');
parsertimeout = parsertimeout || 0;
if (parserjqXHR) parserjqXHR.abort();
var gotJSON = function(d) {
try {
$parserResultNode.html(d.parse.text['*']);
$parserResultNode.css('color', '#000');
} catch (ex) {}
};
var parseIt = function() {
var toParse = $el.val();
if (!toParse || !/(?:<|\/\/|\[|\'\{|~~)/.test(toParse)) {
gotJSON({
parse: {
text: {
'*': toParse || ''
}
}
});
return;
}
var query = {
format: 'json',
action: 'parse',
uselang: mw.config.get('wgUserLanguage'),
redirects: true,
prop: 'text',
pst: true,
text: toParse
};
$el.data('parserjqXHR',
$.getJSON(mw.util.wikiScript('api'), query, function(text) {
gotJSON(text);
delay += 65;
})
);
};
clearTimeout(parsertimeout);
$el.data('parsertimeout',
setTimeout(parseIt, Math.min(3500, delay))
);
};
var _validateInput = function(event) {
var $el = $(this),
v = $el.data('v');
if (v.noEmpty) {
if ($.trim($el.val()).length < (v.minLength || 10)) {
$submitButton.button('option', 'disabled', true);
} else {
$submitButton.button('option', 'disabled', false);
}
}
if (('TEXTAREA' !== $el.prop('nodeName')) &&
((event.keyCode - 0) === 13) &&
(v.enterToSubmit !== false) &&
!$submitButton.button('option', 'disabled')
) $submitButton.click();
};
$.each(questions, function(i, v) {
v.type = (v.type || 'text');
if (v.type === 'textarea') {
$AjaxDeleteContainer.append('<label for="AjaxQuestion' + i + '">' + v.message + '</label>').append('<textarea rows=20 id="AjaxQuestion' + i + '">');
} else {
$AjaxDeleteContainer.append('<label for="AjaxQuestion' + i + '">' + v.message + '</label>').append('<input type="' + v.type + '" id="AjaxQuestion' + i + '" style="width:97%;">');
}
var curQuestion = $AjaxDeleteContainer.find('#AjaxQuestion' + i);
if (v.parseReason) {
var $parserResultNode = $('<div>', {
id: 'AjaxQuestionParse' + i,
html: ' '
});
$AjaxDeleteContainer.append('<br><label for="AjaxQuestionParse' + i + '">' + o.i18n.previewLabel + '</label>').append($parserResultNode);
curQuestion.data('parserResultNode', $parserResultNode).keyup(_parseReason);
}
if (v.type !== 'textarea') $AjaxDeleteContainer.append('<br><br>');
if (v.appendNode) {
$AjaxDeleteContainer.append(v.appendNode);
}
if ('number' === typeof v.byteLimit) {
mw.loader.using('jquery.lengthLimit', function() {
curQuestion.byteLimit(v.byteLimit);
});
}
curQuestion.data('v', v);
curQuestion.on('keyup input', _validateInput);
// SECURITY: prefill could contain evil jsCode. Never use it unescaped!
// Use .val() or { value: prefill } or '<input value="' + mw.html.escape() + '" ...>
curQuestion.val(v.prefill);
if (v.type === 'checkbox') curQuestion.attr('checked', v.prefill).attr('style', 'margin-left: 5px');
});
if (mw.user.isAnon()) {
AQD.renderNode($('<div>', { id: 'ajaxDeleteAnonwarning' }), 'MediaWiki:Anoneditwarning').appendTo($AjaxDeleteContainer);
}
var $dialog = $('<div></div>').append($AjaxDeleteContainer).dialog({
width: (width || 600),
modal: true,
title: title,
dialogClass: "wikiEditor-toolbar-dialog",
close: function() {
$(this).dialog("destroy");
$(this).remove();
},
buttons: dlgButtons,
open: function() {
// Look out for http://bugs.jqueryui.com/ticket/6830 / jQuery UI 1.9
var $buttons = $(this).parent().find('.ui-dialog-buttonpane button');
$submitButton = $buttons.eq(0).button({ icons: { primary: 'ui-icon-circle-check' } }).addClass('ui-button-green');
$cancelButton = $buttons.eq(1).button({ icons: { primary: 'ui-icon-circle-close' } }).addClass('ui-button-red');
}
});
$.each(questions, function(i, v) {
var curQuestion = $AjaxDeleteContainer.find('#AjaxQuestion' + i);
curQuestion.keyup();
if (v.type === 'text') {
var $q = curQuestion.wrap('<div style="position:relative;">').parent();
var $i = $.createIcon('ui-icon-arrow-4-diag').attr('title', 'Expand to textarea');
$('<span>', { 'class': 'ajaxTextareaConverter' }).append($i).appendTo($q).data('toConvert', curQuestion).click(_convertToTextarea);
}
});
$('#AjaxQuestion0').focus().select();
},
/**
** Pseudo-Modal JS windows.
**/
compareDetails: function() {
var d = this.details[0],
f = this.details[1],
$submitButton, $inverseButton, $cancelButton, $swapButton, $overlayButton;
this.showProgress();
if (d.sha1 === f.sha1) {
this.exactDupes = true;
this.nextTask();
return;
}
var $imgD = $('<div>').append($('<img>', {
src: d.thumburl,
height: d.thumbheight,
width: d.thumbwidth
}), $('<div>', {
id: 'AjaxDeleteImgDel',
html: Math.round(d.size / 1000) + ' KB <br>' + d.width + 'x' + d.height + '<br>'
}).append(
$('<a>', {
href: d.descriptionurl,
text: d.title,
target: '_blank'
})));
var $imgF = $('<div>').append($('<img>', {
src: f.thumburl,
height: f.thumbheight,
width: f.thumbwidth
}), $('<div>', {
id: 'AjaxDeleteImgKeep',
html: Math.round(f.size / 1000) + ' KB <br>' + f.width + 'x' + f.height + '<br>'
}).append(
$('<a>', {
href: f.descriptionurl,
text: f.title,
target: '_blank'
})));
var dlgButtons = {};
dlgButtons[this.i18n.submitButtonLabel] = function() {
$(this).dialog("close");
AQD.nextTask();
};
dlgButtons[this.i18n.inverseButtonLabel] = function() {
$(this).dialog("close");
AQD.destination = pageName.replace(/_/g, ' ');
pageName = f.title;
AQD.details.reverse();
setTimeout(function() {
AQD.compareDetails();
}, 10);
};
dlgButtons[this.i18n.cancelButtonLabel] = function() {
$(this).dialog("close");
};
dlgButtons[this.i18n.swapImagesButtonLabel] = function() {
if ($imgD[0].nextSibling === $imgF[0]) {
$imgD.before($imgF);
} else {
$imgF.before($imgD);
}
};
var $fClone;
dlgButtons[this.i18n.overlayButtonLabel] = function() {
if ($fClone) {
$fClone.remove();
$fClone = 0;
} else {
$fClone = $imgF.clone().appendTo($imgF.parent());
$fClone.css('position', 'absolute');
var pos = $imgD.position();
$fClone.css('top', pos.top - 1);
$fClone.css('left', pos.left - 1);
$fClone.fadeTo(0, 0.65);
// These modules should be already loaded for the dialog but let's be sure
mw.loader.using(['jquery.ui'], function() {
// Set width to auto because AjaxQuickDelete.css sets it to a fixed size
$fClone.css('background', 'rgba(200, 200, 200, 0.5)').css('width', 'auto').css('border', '1px solid #0c9').draggable();
$fClone.find('img').resizable();
// In IE, opacity is not fully inerhited
$fClone.children('div').fadeTo(0, 0.7);
});
}
};
var $AjaxDupeContainer = $('<div>', {
id: 'AjaxDupeContainer'
}).append($imgD, $imgF);
var $dialog = $('<div></div>').append($AjaxDupeContainer).dialog({
width: 800,
modal: true,
title: this.i18n.compareDetails,
draggable: false,
dialogClass: "wikiEditor-toolbar-dialog",
close: function() {
$(this).dialog("destroy");
$(this).remove();
},
buttons: dlgButtons,
open: function() {
var $buttons = $(this).parent().find('.ui-dialog-buttonpane button');
$submitButton = $buttons.eq(0).button({ icons: { primary: 'ui-icon-circle-check' } }).addClass('ui-button-green');
$inverseButton = $buttons.eq(1).button({ icons: { primary: 'ui-icon-refresh' } });
$cancelButton = $buttons.eq(2).button({ icons: { primary: 'ui-icon-circle-close' } }).addClass('ui-button-red');
$swapButton = $buttons.eq(3).button({ icons: { primary: 'ui-icon-transfer-e-w' } });
$overlayButton = $buttons.eq(4).button({ icons: { primary: 'ui-icon-newwin' } });
$swapButton.css('float', (('left' === $swapButton.css('float')) ? 'right' : 'left'));
$overlayButton.css('float', (('left' === $overlayButton.css('float')) ? 'right' : 'left'));
}
});
},
mergeDescriptions: function() {
this.prompt([{
message: '',
prefill: this.details[0].content,
returnvalue: 'discard',
cleanUp: false,
noEmpty: false,
type: 'textarea',
enterToSubmit: false
}, {
message: '',
prefill: this.details[1].content,
returnvalue: 'newPageText',
cleanUp: false,
noEmpty: false,
type: 'textarea',
enterToSubmit: false
}], this.i18n.mergeDescription, 800);
this.destination = this.details[1].title;
this.reason = 'Exact or scaled-down duplicate: [[:' + this.destination + ']]';
},
cleanFileName: function(uncleanName) {
// Remove Namespace
uncleanName = uncleanName.replace(/^(?:Image|File):/i, '');
// Convert extension to lower case
uncleanName = uncleanName.replace(/\.\w{3,4}$/, function($e) { return $e.toLowerCase(); });
// jpeg -> jpg
uncleanName = uncleanName.replace(/\.jpe*g$/, '.jpg');
// First cleanUp from Flinfo (FlinfoOut.php) by Flominator and Lupo
uncleanName = uncleanName.replace(/~{3,}/g, '')
.replace(/\s+|_/g, ' ')
.replace(/[\x00-\x1f\x7f]/g, '')
.replace(/%([0-9A-Fa-f]{2})/g, '% $1')
.replace(/&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g, '& $1')
.replace(/[:\/|#]/g, '-')
.replace(/[\]\}>]/g, ')')
.replace(/[\[\{<]/g, '(');
var currentExt = pageName.toLowerCase().replace(/.*?\.(\w{3,4})$/, '$1').replace('jpeg', 'jpg');
// If the current mime-type is available to the script, check it;
// MediaWiki sometimes allows uploading mismatching mimetypes but not moving
if (this.fileMime) {
currentExt = ('ogg' === this.fileMime && ('oga' === currentExt || 'ogv' === currentExt)) ? currentExt : this.fileMime;
}
var reCurrentExt = new RegExp('\\.' + currentExt + '$', 'i');
// If new file name is without extension, add the one from the old name
if (!reCurrentExt.test(uncleanName.toLowerCase())) uncleanName += '.' + currentExt;
// Capitalize the first letter and prefix the namespace
return 'File:' + uncleanName.replace(/^\w/, function($0) { return $0.toUpperCase(); });
},
cleanReason: function(uncleanReason) {
// trim whitespace
uncleanReason = uncleanReason.replace(/^\s*(.+)\s*$/, '$1');
// remove signature
uncleanReason = uncleanReason.replace(/(?:\-\-|–|—)? ?~{3,5}$/, '').replace(/^~{3,5} ?/, '');
return uncleanReason;
},
/**
** For display of progress messages.
**/
showProgress: function(message) {
if (!message) {
if (this.progressDialog) this.progressDialog.remove();
this.progressDialog = 0;
document.body.style.cursor = 'default';
return;
}
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',
open: function() {
$(this).parent().find('.ui-dialog-titlebar').hide();
},
close: function() {
$(this).dialog("destroy");
$(this).remove();
}
});
}
},
/**
** Submit an edited page.
**/
savePage: function(page, summary, callback) {
var edit = {
action: 'edit',
summary: summary,
watchlist: (page.watchlist || 'preferences'),
title: page.title
};
if (page.redirect) edit.redirect = '';
edit[page.editType] = page.text;
this.doAPICall(edit, callback);
},
movePage: function() {
// Some users don't get it: They want to move pages to itself.
if (AQD.cleanFileName(pageName) === AQD.destination) return AQD.nextTask();
mw.loader.using(['ext.gadget.libAPI'], function() {
mw.user.tokens.set('moveToken', AQD.movetoken);
var moveArgs = {
cb: function() {
AQD.nextTask();
},
// r-result, query, text
errCb: function(r, q, t) {
AQD.fail(t);
},
from: pageName,
to: AQD.destination,
reason: AQD.reason,
movetalk: true
};
// 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 (AQD.wpLeaveRedirect === false) {
moveArgs.noredirect = true;
}
AQD.showProgress(AQD.i18n.movingFile);
mw.libs.commons.api.movePage(moveArgs);
});
},
deletePage: function() {
var edit = {
action: 'delete',
reason: this.reason,
title: pageName,
token: this.deletetoken,
recreate: ''
};
this.showProgress(this.i18n.deletingFile);
this.doAPICall(edit, 'nextTask');
},
setCurrentDate: function(x) {
var shortNames = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
try {
var dat = x.getResponseHeader('date').match(/\D+(\d\d) (\D{3}) (\d{4}) (\d\d):(\d\d):(\d\d)/);
this.currentDate = new Date(dat[3], $.inArray(dat[2], shortNames), dat[1], dat[4], dat[5], dat[6]);
// The date is initialized/ constructed in local time but the server returned GMT-Time, so remove the offset
// According to w3c under- and overflow (<0, >60) are handled by the date-object itself
this.currentDate.setMinutes(this.currentDate.getMinutes() - this.currentDate.getTimezoneOffset());
} catch (ex) {
this.currentDate = this.startDate || new Date();
}
},
/**
** 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,
newParams = { format: 'json' };
// At least let's try to send the format first and the token last
// If the POST-request is cut off, we get "invalid token" or other errors
$.extend(newParams, params);
if ('edit' === newParams.action) newParams.token = this.edittoken;
var retry = function(timeout, errText) {
o.apiErrorThreshold--;
if (0 === o.apiErrorThreshold) {
return o.fail(errText);
} else {
return setTimeout(function () {
o.doAPICall(params, callback);
}, timeout);
}
};
$.ajax({
url: this.apiURL,
cache: false,
dataType: 'json',
data: newParams,
type: 'POST',
success: function(result, status, x) {
if (!o.currentDate && x && x.getResponseHeader) o.setCurrentDate(x);
if (!result && 'query' === newParams.action) return retry(1500, "Received empty API response:\n" + x.responseText);
if (!result) return o.fail("Received 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 retry(500, "mysterious 231 unknown error");
if (result.error && 'editconflict' === result.error.code && (params.prependtext || params.appendtext)) return retry(750, "edit conflict");
if (result.error) {
// In some cases, we just don't want to know. If users have protected their talk-page it's their problem.
if (-1 !== $.inArray(result.error.code, ['protectedpage', 'missingtitle'])) this.disableReport = true;
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) {
if ('query' === newParams.action) return retry(1500, "API request returned code " + x.status + " " + status + ". Error code is " + 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);
}
},
retryTask: function() {
try {
this[this.currentTask]();
} catch (e) {
this.fail(e);
}
},
/**
** Once we're all done, reload the page.
**/
reloadPage: function() {
this.showProgress();
if (this.pageName && this.pageName.replace(/ /g, '_') !== pageName) return;
var encTitle = (this.destination || pageName);
encTitle = encodeURIComponent(encTitle.replace(/ /g, '_')).replace(/%2F/ig, '/').replace(/%3A/ig, ':');
location.href = mw.config.get('wgServer') + mw.config.get('wgArticlePath').replace("$1", encTitle);
},
/**
** Error handler. Throws an alert at the user and give him
** the possibility to retry or autoreport the error-message.
**/
fail: function(err) {
var o = this;
if (typeof err === 'object') {
var stErr = err.message + ' \n\n ' + err.name;
if (err.lineNumber) stErr += ' @line' + err.lineNumber;
err = stErr;
}
var msg = this.i18n.taskFailure[this.currentTask] || this.i18n.genericFailure;
//TODO: Needs cleanup
var fix = '';
if (this.img_summary === 'Nominating for deletion') {
fix = (this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand);
}
var dlgButtons = {};
dlgButtons[this.i18n.retryButtonLabel] = function() {
$(this).remove();
o.retryTask();
};
if (-1 !== $.inArray(o.currentTask, ['movePage', 'deletePage', 'notifyUploaders']) && (/code 50\d/.test(err) || /missingtitle/.test(err))) {
dlgButtons[this.i18n.ignoreButtonLabel] = function() {
$(this).remove();
o.nextTask();
};
}
if (!this.disableReport) {
dlgButtons[this.i18n.reportButtonLabel] = function() {
$('#feedbackContainer').contents().remove();
$('#feedbackContainer').append($('<img>', {
src: '/w/skins/common/images/ajax-loader.gif'
})).css('text-align', 'center');
var randomId = Math.round(Math.random()*1099511627776);
var toSend = '\n== Autoreport by AjaxQuickDelete ' + randomId + ' ==\n' + err + '\n++++\n:Task: ' + o.currentTask + '\n:NextTask: ' + o.tasks[0] + '\n:LastTask: ' + o.tasks[o.tasks.length - 1] +
'\n:Page: ' + (o.pageName || pageName) + '\n:Skin: ' + mw.user.options.get('skin') + '\n:[{{fullurl:Special:Contributions|target={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Contribs before error]';
$.post(o.apiURL, {
'action': 'edit',
'format': 'json',
'title': 'MediaWiki talk:Gadget-AjaxQuickDelete.js/auto-errors',
'summary': '[[#Autoreport by AjaxQuickDelete ' + randomId + '|Reporting an AjaxQuickDelete error.]] Random ID=' + randomId,
'appendtext': toSend,
'token': (o.edittoken || mw.user.tokens.get('csrfToken'))
}, function() {
o.reloadPage();
});
};
}
dlgButtons[this.i18n.abortButtonLabel] = function() {
$(this).remove();
};
this.disableReport = false;
this.showProgress();
this.progressDialog = $('<div>').append($('<div>', {
id: 'feedbackContainer',
html: (msg + ' ' + fix + '<br>' + this.i18n.errorDetails + '<br>' + mw.html.escape(err) + '<br>' + (this.tag ? (this.i18n.tagWas + this.tag) : '') + '<br><a href="' + mw.config.get('wgServer') + '/wiki/MediaWiki_talk:AjaxQuickDelete.js" >' + this.i18n.errorReport + '</a>')
})).dialog({
width: 550,
modal: true,
closeOnEscape: false,
title: this.i18n.errorDlgTitle,
dialogClass: "ajaxDeleteError",
buttons: dlgButtons,
close: function() {
$(this).dialog("destroy");
$(this).remove();
}
});
},
/**
** 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.currentDate || 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, mw.config.get('wgMonthNames')[date.getUTCMonth() + 1]);
fmt = fmt.replace(/DAY/g, date.getUTCDate());
return fmt;
},
// Constants
// DR subpage prefix
requestPagePrefix: "વિકિપીડિયા:દૂર કરવા વિનંતી/",
// user talk page prefix
userTalkPrefix: mw.config.get('wgFormattedNamespaces')[3] + ":",
// MediaWiki API script URL
apiURL: mw.util.wikiScript('api'),
// Max number of errors that are allowed for silent retry
apiErrorThreshold: 10,
// Translatable strings
i18n: {
toolboxLinkDelete: "પાનું દૂર કરવા વિનંતી",
toolboxLinkDiscuss: "Nominate category for discussion",
// GUI reason prompt form
reasonForDeletion: "શા માટે આ પાનું દૂર કરવું?",
reasonForDiscussion: "Why does this category need discussion?",
moreInformation: "More information",
loading: "Loading...",
// Labels
previewLabel: "Preview:",
submitButtonLabel: "Proceed",
cancelButtonLabel: "Cancel",
abortButtonLabel: "Abort",
reportButtonLabel: "Report automatically",
retryButtonLabel: "Retry",
ignoreButtonLabel: "Ignore and continue",
inverseButtonLabel: "Inverse. Keep this delete other",
swapImagesButtonLabel: "Swap to compare",
overlayButtonLabel: "Overlay to compare",
// GUI progress messages
preparingToEdit: "Preparing to edit pages... ",
creatingNomination: "Creating nomination page... ",
listingNomination: "Adding nomination page to daily list... ",
addingAnyTemplate: "Adding template to " + canonicalNS.toLowerCase() + " page... ",
notifyingUploader: "Notifying %USER%... ",
// Extended version
toolboxLinkSource: "No source",
toolboxLinkLicense: "No license",
toolboxLinkPermission: "No permission",
toolboxLinkCopyvio: "Report copyright violation",
reasonForCopyvio: "Why is this file a copyright violation?",
// For moving files
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?",
moveOtherDestination: "The name you have specified exists. Choose a new name, please.",
checkFileExists: "Checking whether file exists",
movingFile: "Moving file",
replacingUsage: "Ordering CommonsDelinker to replace all usage",
dropdownMove: "Move & Replace",
leaveRedirect: "Leave a redirect behind:",
moveAndReplace: "Move file and replace all usage",
// For declining any request
removingTemplate: "Removing template",
declineRequest: "Why do you want to decline the request?",
anyDecline: "Decline request",
//For Duplicates
deletingFile: "Deleting file",
compareDetails: "Please compare the images before merging the descriptions. The image with the bold text will be deleted.",
mergeDescription: "Please now merge the file descriptions",
redirectingFile: "Redirecting file",
savingDescription: "Saving new details",
// Errors
errorDlgTitle: "Error",
genericFailure: "An error occurred while trying to do the requested action. ",
taskFailure: {
listUploaders: "An error occurred while determining the " + (namespaceNumber === 6 ? " uploader(s) of this file" : "creator of this page") + ".",
loadPages: "An error occurred while preparing to nominate this " + canonicalNS.toLowerCase() + " for deletion.",
prependDeletionTemplate: "An error occurred while adding the {{delete}} template to this " + canonicalNS.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 " + (namespaceNumber === 6 ? " uploader(s) of this file" : "creator of this page") + ".",
movePage: "Error while moving the page.",
deletePage: "Error deleting the page."
},
addTemplateByHand: "To nominate this " + canonicalNS.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: "Manually report the error here or click on <tt>Report automatically</tt> to send an automatic error-report.",
tagWas: "The tag to be inserted into this page was "
}
};
if (mw.config.get('wgUserLanguage') !== 'en') {
$.ajax({
url: mw.util.wikiScript(),
dataType: 'script',
data: {
title: 'MediaWiki:Gadget-AjaxQuickDelete.js/' + mw.config.get('wgUserLanguage') + '.js',
action: 'raw',
ctype: 'text/javascript',
// Allow caching for 28 days
maxage: 2419200,
smaxage: 2419200
},
cache: true,
success: AQD.install,
error: AQD.install
});
} else {
AQD.install();
}
}(jQuery, mediaWiki));
// </nowiki>