ব্যবহারকারী:রবি/common.js: সংশোধিত সংস্করণের মধ্যে পার্থক্য
অবয়ব
অসম্পাদনা সারাংশ নেই |
অসম্পাদনা সারাংশ নেই |
||
| ১ নং লাইন: | ১ নং লাইন: | ||
/** | |||
* AjaxQuickDelete from <https://commons.wikimedia.org/wiki/MediaWiki:Gadget-AjaxQuickDelete.js> | |||
* | |||
* Changelog: | |||
* - 2010: Created in 2010 by [[User:Ilmari Karonen]] as Ajax-based replacement for [[MediaWiki:Quick-delete-code.js]]. | |||
* - Rewritten by [[User:DieBuche]]. | |||
* - Botdetection and encoding fixer by [[User:Lupo]]. | |||
* - 2011-2012: Validation and further development [[User:Rillke]], 2011-2012. | |||
* - 2018: dropped IE8 support. | |||
* | |||
* TODO: | |||
* - Fix problems with moves of videos. | |||
* - Support move of timed text for videos. | |||
* | |||
* This pages has automated validation on save. Interested? See [[:commons:Commons:User scripts/reports]]. | |||
*/ | |||
// <nowiki> | |||
/* eslint indent:[error,tab,{outerIIFEBody:0}] */ | |||
/* eslint-disable one-var, vars-on-top, camelcase, no-underscore-dangle, valid-jsdoc */ | |||
/* global $:false, mw:false, importScript:false */ | |||
( function () { | |||
'use strict'; | |||
// Guard against multiple inclusions | |||
if ( window.AjaxQuickDelete ) { | |||
return; | |||
} | |||
var AQD, | |||
conf = mw.config.get( [ | |||
'wgArticleId', | |||
'wgCanonicalNamespace', | |||
'wgCanonicalSpecialPageName', | |||
'wgCategories', | |||
'wgFormattedNamespaces', | |||
'wgNamespaceNumber', | |||
'wgPageName', | |||
'wgRestrictionEdit', | |||
'wgUserGroups', | |||
'wgUserLanguage', | |||
'wgUserName', | |||
'wgIsRedirect' | |||
] ), | |||
nsNr = conf.wgNamespaceNumber, | |||
pageName = conf.wgPageName; | |||
// A bunch of helper functions | |||
function _firstItem( o ) { | |||
for ( var i in o ) { | |||
if ( Object.prototype.hasOwnProperty.call( o, i ) ) { | |||
return o[ i ]; | |||
} | |||
} | |||
} | |||
$.ucFirst = function ( s ) { | |||
return s[ 0 ].toUpperCase() + s.slice( 1 ); | |||
}; | |||
// Create the AjaxQuickDelete-singleton (object literal) | |||
AQD = window.AjaxQuickDelete = { | |||
// When maintaining this script always bump this number! | |||
version: '1.1.3', | |||
/** | |||
* Runs before document ready and before translation is available | |||
* (important event-binders should be attached as fast as possible) | |||
*/ | |||
preinstall: function () { | |||
// Promote our gadget when user opened old move page | |||
if ( conf.wgCanonicalSpecialPageName === 'Movepage' && Number( $( 'select[name="wpNewTitleNs"]' ).val() ) === 6 ) { | |||
$( '#mw-movepage-table' ).before( | |||
'<div class="mw-message-box mw-message-box-warning">Consider using <i>Move & Replace</i> from the menu on file pages (open with a single click) when moving files to care for global usage and redirects.</div>' | |||
); | |||
} | |||
this.doNothing = ( !conf.wgArticleId || nsNr < 0 || /^Commons:Deletion/.test( pageName ) ); | |||
if ( this.doNothing ) { | |||
return; | |||
} | |||
// Check user group | |||
if ( conf.wgUserGroups.indexOf( 'sysop' ) !== -1 ) { | |||
this.userRights = 'sysop'; | |||
} else if ( conf.wgUserGroups.indexOf( 'filemover' ) !== -1 ) { | |||
this.userRights = 'filemover'; | |||
} else { | |||
this.userRights = [ 'autopatrolled', 'patroller', 'image-reviewer' ].filter( function ( g ) { | |||
return conf.wgUserGroups.indexOf( g ) !== -1; | |||
} )[ 0 ]; | |||
} | |||
if ( [ 'filemover', 'sysop' ].indexOf( this.userRights ) !== -1 && nsNr === 6 ) { | |||
// Change "Move" to "Move & Replace" | |||
var $moveLink = $( '#ca-move' ), | |||
$moveLanchor = $moveLink.find( 'a' ); | |||
this.$moveLink = $moveLink = $moveLanchor.length ? $moveLanchor : $moveLink; | |||
$moveLink.text( $moveLink.text() + ' & Replace' ) | |||
.attr( 'title', 'Click in order to ' + $moveLink.attr( 'title' ) + ' and replace usage. Default form though new tab.' ) | |||
.on( 'click', function ( e ) { | |||
e.preventDefault(); | |||
AQD.moveFile(); | |||
} ); | |||
} | |||
}, | |||
/** | |||
* Set up the AjaxQuickDelete object and add the toolbox link. Called via document.ready during page loading. | |||
*/ | |||
install: function () { | |||
// Disallow performing operations on empty or special pages | |||
if ( this.doNothing ) { | |||
return; | |||
} | |||
// Check edit restrictions and do not install anything if protected | |||
if ( conf.wgRestrictionEdit && conf.wgRestrictionEdit.length && | |||
conf.wgUserGroups.indexOf( conf.wgRestrictionEdit[ 0 ] ) === -1 ) { | |||
return; | |||
} | |||
// Trigger a jQuery event for other scripts that like to know | |||
// when this script has loaded all translations and is ready to install | |||
$( document ).triggerHandler( 'scriptLoaded', [ 'AjaxQuickDelete' ] ); | |||
var link; | |||
// Set up toolbox link | |||
if ( nsNr === 14 ) { | |||
// In categories the discuss-category link | |||
link = mw.util.addPortletLink( 'p-tb', '#', this.i18n.toolboxLinkDiscuss, 't-ajaxquickdiscusscat' ); | |||
if ( link ) { | |||
link.addEventListener( 'click', function ( e ) { | |||
e.preventDefault(); | |||
mw.loader.using( 'jquery.ui' ).then( function () { | |||
AQD.discussCategory(); | |||
} ); | |||
} ); | |||
} | |||
} else { | |||
// On other pages, the nominate-for-deletion link | |||
link = mw.util.addPortletLink( 'p-tb', '#', this.i18n.toolboxLinkDelete, 't-ajaxquickdelete' ); | |||
if ( link ) { | |||
link.addEventListener( 'click', function ( e ) { | |||
e.preventDefault(); | |||
AQD.nominateForDeletion(); | |||
} ); | |||
} | |||
} | |||
// Install AjaxMoveButton for filemovers and administrators | |||
if ( this.$moveLink ) { | |||
// Change Move & Replace link to fully localized text | |||
this.$moveLink.html( $( '<span>' ).text( this.i18n.dropdownMove ) ); | |||
// Add quicklinks to template | |||
$( '#AjaxRenameLink' ).append( '<a href="javascript:AjaxQuickDelete.moveFile();">' + this.i18n.moveAndReplace + '</a>' ) | |||
.append( '<a href="javascript:AjaxQuickDelete.loadAndDeclineRequest(\'move\');" class="ajaxDeleteDeclineMove"><sup> ' + this.i18n.anyDecline + '</sup></a>' ); | |||
// Install x-To-DR. See [[Template:X-To-DR]] | |||
$( '#mw-imagepage-content .convert-to-dr' ) | |||
.find( '.ctdr-btn-convert' ).on( 'click', this._convertToDR ).show().end() | |||
// Currently filemover rights required | |||
.find( '.ctdr-btn-remove' ).on( 'click', this._removeAnyTag ).show(); | |||
} else if ( this.userRights ) { | |||
$( '#mw-imagepage-content .convert-to-dr .ctdr-btn-convert' ).on( 'click', this._convertToDR ).show(); | |||
} | |||
// Install "Process Duplicates"-Link (either in template | |||
// or if no template was detected and MediaWiki found dupes, behind the link in the dupe-section) | |||
if ( this.userRights === 'sysop' && nsNr === 6 ) { | |||
var dupeSection = $( '#AjaxDupeProcess' ); | |||
if ( dupeSection.length ) { | |||
dupeSection.append( $( '<a>', { | |||
href: '#', | |||
text: this.i18n.processDupes, | |||
style: 'font-weight:bold', | |||
click: function ( e ) { | |||
e.preventDefault(); | |||
AQD.processDupes(); | |||
} | |||
} ) ).show(); | |||
} else { | |||
dupeSection = $( '#mw-imagepage-section-duplicates .mw-imagepage-duplicates' ); | |||
if ( dupeSection.length ) { | |||
dupeSection.find( 'li:first' ) | |||
.append( $( '<span>', { | |||
style: 'display:none', | |||
id: 'AjaxDupeDestination', | |||
text: dupeSection.find( 'a' ).attr( 'title' ) | |||
} ) ) | |||
.append( ' ', $( '<sup>' ).append( $( '<a>', { | |||
href: '#', | |||
text: '[' + this.i18n.processDupes + ']', | |||
style: 'background:#CEB', | |||
click: function ( e ) { | |||
e.preventDefault(); | |||
AQD.processDupes(); | |||
} | |||
} ) ) ); | |||
} | |||
} | |||
} | |||
// Extra buttons | |||
// Wait until the user's js was loaded and executed | |||
mw.loader.using( 'user', function () { | |||
if ( mw.user.options.get( 'gadget-QuickDelete' ) ) { | |||
mw.loader.using( 'ext.gadget.QuickDelete' ).always( function () { | |||
AQD.doInsertTagButtons(); | |||
} ); | |||
} | |||
} ); | |||
}, | |||
/** | |||
* Ensure that all variables are in a good state | |||
* You must call this method before doing anything! | |||
* TODO: Never override pageName, always clean task queue | |||
*/ | |||
initialize: function () { | |||
pageName = conf.wgPageName; | |||
this.tasks = []; | |||
this.destination = undefined; | |||
this.details = undefined; | |||
this.declineReason = undefined; | |||
this.notifyUser = true; | |||
this.watchlist = 'preferences'; | |||
}, | |||
/** | |||
* If a file exists, exchange the messages (very hackish) | |||
* so the user is prompted to choose another destination | |||
* TODO: Develop an improved solution | |||
*/ | |||
fileExists: function () { | |||
this.i18n.moveDestination = this.i18n.moveOtherDestination; | |||
this.moveFile(); | |||
}, | |||
/** | |||
* For moving files | |||
*/ | |||
moveFile: function () { | |||
var o = this; | |||
o.initialize(); | |||
mw.loader.using( [ 'jquery.ui' ] ).then( function () { | |||
o.showProgress(); | |||
if ( $( '#AjaxRenameLink' ).length ) { | |||
o.possibleDestination = $( '#AjaxRenameDestination' ).text(); | |||
o.possibleReason = o.cleanReason( $( '#AjaxRenameReason' ).text() ); | |||
} | |||
// Let's be sure we have a fresh token and the latest MIME-Info | |||
o.addTask( 'getMoveToken' ); | |||
var linkstoimage = $( '#mw-imagepage-section-linkstoimage' ); | |||
if ( $( '#globalusage' ).length || ( | |||
linkstoimage.length && | |||
linkstoimage.find( 'a' ).not( '.mw-redirect' ).length - | |||
linkstoimage.find( '.mw-imagepage-linkstoimage-ns2 a[href^="/wiki/User:OgreBot/Uploads"]' ).length | |||
) ) { | |||
o.inUse = true; | |||
o.addTask( 'chkPreMoveDecline' ); | |||
} | |||
o.addTask( 'promptForMoveTarget' ); | |||
o.addTask( 'doesFileExist' ); | |||
o.fileNameExistsCB = 'fileExists'; | |||
o.addTask( 'movePage' ); | |||
o.addTask( 'removeTemplate' ); | |||
o.addTask( 'queryRedirects' ); | |||
o.addTask( 'replaceUsage' ); | |||
// finally reload the page to show changed page | |||
o.addTask( 'reloadPage' ); | |||
o.nextTask(); | |||
} ); | |||
}, | |||
promptForMoveTargetCB: function ( AQD ) { | |||
if ( AQD.inUse ) { | |||
$( '#AjaxQuestion2' ).prop( 'disabled', true ); | |||
} | |||
}, | |||
promptForMoveTarget: function () { | |||
var toAppend; | |||
this.showProgress(); | |||
if ( mw.user.options.get( 'gadget-RenameLink' ) ) { | |||
toAppend = [ $( '<a>' ) | |||
.text( this.i18n.moreInformation ) | |||
.on( 'click', function () { | |||
if ( !AQD.rGetPolicy ) { | |||
importScript( 'MediaWiki:RenameRequest.js' ); | |||
mw.hook( 'aqd.renamerequest.i18n' ).fire(); | |||
} | |||
mw.hook( 'aqd.renamerequest.run' ).fire( { exec: this } ); | |||
} ).button( { | |||
icons: { primary: 'ui-icon-script' }, | |||
showLabel: true, | |||
text: false | |||
} ).css( { | |||
fontSize: '.6em', | |||
margin: '0', | |||
width: '2.5em', | |||
'float': 'right' | |||
} ), '<br>' ]; | |||
} | |||
mw.hook( 'aqd.prompt' ).remove( this.promptForMoveTargetCB ).add( this.promptForMoveTargetCB ); | |||
this.prompt( [ { | |||
message: this.i18n.moveDestination, | |||
prefill: this.cleanFileName( this.possibleDestination || pageName ), | |||
returnvalue: 'destination', | |||
appendNode: toAppend, | |||
cleanUp: true, | |||
noEmpty: true | |||
}, { | |||
message: this.i18n.reasonForMove, | |||
prefill: ( ( this.reason || this.possibleReason || '' ).trim().replace( /'{2,}/g, '' ).replace( /\s{2,}/g, ' ' ) ), | |||
returnvalue: 'reason', | |||
cleanUp: true, | |||
noEmpty: false | |||
}, { | |||
message: this.i18n.leaveRedirect, | |||
prefill: true, | |||
returnvalue: 'wpLeaveRedirect', | |||
// cleanUp: false, | |||
noEmpty: false, | |||
type: 'checkbox' | |||
}, { | |||
message: this.i18n.useCORSForReplace, | |||
prefill: !window.aqdCORSOptOut, | |||
returnvalue: 'replaceUsingCORS', | |||
// cleanUp: false, | |||
noEmpty: false, | |||
type: 'checkbox' | |||
} | |||
], this.i18n.movingFile ); | |||
}, | |||
/* Warn other filemovers */ | |||
chkPreMoveDecline: function () { | |||
$( '#mw-imagepage-section-linkstoimage' ).find( 'a' ).each( function () { | |||
if ( $( this ).text() === 'Commons:File renaming/Recently declined rename requests' ) { | |||
// eslint-disable-next-line no-alert | |||
alert( AQD.i18n.warnRename ); | |||
return false; | |||
} | |||
} ); | |||
this.nextTask(); | |||
}, | |||
/** | |||
* For loading jquery UI to decline a request | |||
*/ | |||
loadAndDeclineRequest: function ( reason ) { | |||
var that = this; | |||
mw.loader.using('jquery.ui').then(function () { | |||
that.declineRequest.call( that, reason ); | |||
} ); | |||
}, | |||
/** | |||
* For declining a request | |||
*/ | |||
declineRequest: function ( reason ) { | |||
reason = reason || this.declineReason; | |||
// 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' ); | |||
// TODO extend the reason (for summary) | |||
switch ( reason ) { | |||
case 'move': | |||
reason = 'rename request declined: does not comply with [[COM:FR|renaming guidelines]]'; | |||
if ( window.AjaxDeclineMoveWatchFile ) { | |||
this.watchlist = 'watch'; | |||
} | |||
break; | |||
} | |||
this.prompt( [ { | |||
message: '', | |||
prefill: reason || '', | |||
returnvalue: 'declineReason', | |||
cleanUp: false, | |||
noEmpty: true, | |||
byteLimit: 250 | |||
} ], this.i18n.declineRequest ); | |||
}, | |||
// This method is generlaly used directly by user scripts without going | |||
// through other methods or setup functions. Needs to be standalone, | |||
// and ensures loading of its own dependencies. | |||
insertTagOnPage: function ( tag, img_summary, talk_tag, talk_summary, prompt_text, page, optin_notify ) { | |||
var o = this; | |||
this.initialize(); | |||
mw.loader.using( [ 'jquery.ui' ], function () { | |||
o.pageName = ( page || pageName ).replace( /_/g, ' ' ); | |||
o.tag = tag.replace( '%USER%', conf.wgUserName ) + '\n'; | |||
o.img_summary = img_summary; | |||
// first schedule some API queries to fetch the info we need… | |||
// get token | |||
o.addTask( 'findCreator' ); | |||
o.addTask( 'prependTemplate' ); | |||
if ( o.isMobile() && /(?:copyvio|nsd|npd|nld)/.test( tag ) ) { | |||
o.addTask( 'listMobileUploadSpeedy' ); | |||
} | |||
var prompt = []; | |||
// Cave: insertTagOnPage is inserted as javascript link and therefore talk_tag can be "undefined"/string | |||
if ( talk_tag && talk_tag !== 'undefined' ) { | |||
o.talk_tag = talk_tag.replace( '%FILE%', o.pageName ); | |||
o.talk_summary = talk_summary.replace( '%FILE%', '[[:' + o.pageName + ']]' ); | |||
o.usersNeeded = true; | |||
prompt.push( { | |||
message: o.i18n.notifyUser, | |||
prefill: true, | |||
returnvalue: 'notifyUser', | |||
type: 'checkbox' | |||
} ); | |||
o.addTask( 'notifyUploaders' ); | |||
} | |||
o.addTask( 'reloadPage' ); | |||
if ( tag.indexOf( '%PARAMETER%' ) !== -1 ) { | |||
prompt.push( { | |||
message: '', | |||
prefill: '', | |||
returnvalue: 'reason', | |||
cleanUp: true, | |||
noEmpty: true, | |||
minLength: 1 | |||
} ); | |||
o.prompt( prompt, prompt_text || o.i18n.reasonForDeletion ); | |||
} else if ( optin_notify && prompt.length && o.talk_summary ) { | |||
o.prompt( prompt, o.talk_summary ); | |||
} else { | |||
o.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(); | |||
// eslint-disable-next-line no-useless-escape | |||
this.tag = '{{subst:cfd}}\n'; | |||
this.img_summary = 'This category needs discussion'; | |||
// eslint-disable-next-line no-useless-escape | |||
this.talk_tag = '{{subst:cdw|1=' + 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( 'prependTemplate' ); | |||
this.addTask( 'createRequestSubpage' ); | |||
this.addTask( 'listRequestSubpage' ); | |||
this.addTask( 'notifyUploaders' ); | |||
// 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 () { | |||
o.pageName = ( page || pageName ).replace( /_/g, ' ' ); | |||
o.startDate = new Date(); | |||
// set up some page names we'll need later | |||
var requestPage = o.pageName, | |||
mwString = require( 'mediawiki.String' ); | |||
// MediaWiki has an ugly limit of 255 bytes per title, excluding the namespace | |||
while ( mwString.byteLength( requestPage ) + mwString.byteLength( o.requestPagePrefix.replace( /^.+?:/, '' ) ) >= 255 ) { | |||
requestPage = requestPage.slice( 0, requestPage.length - 1 ).trim(); | |||
} | |||
o.requestPage = o.requestPagePrefix + requestPage; | |||
o.dailyLogPage = o.requestPagePrefix + o.formatDate( 'YYYY/MM/DD' ); | |||
o.tag = '{{delete|reason=%PARAMETER%|subpage=' + requestPage + o.formatDate( '|year=YYYY|month=MON|day=DAY}}\n' ); | |||
switch ( nsNr ) { | |||
// 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; | |||
case 828: // Lua comments | |||
o.tag = '\n--[=[ ' + o.tag + ' ]=]\n'; | |||
} | |||
if ( o.templateReplace ) { | |||
o.declineReason = o.img_summary; | |||
} | |||
o.img_summary = 'Nominating for deletion'; | |||
// eslint-disable-next-line no-useless-escape | |||
o.talk_tag = '{{subst:idw|1=' + requestPage + '}}'; | |||
o.talk_summary = '[[:' + o.pageName + ']] has been nominated for deletion'; | |||
o.subpage_summary = 'Starting deletion request'; | |||
// without \n it breaks the redirect syntax | |||
if ( conf.wgIsRedirect ) { | |||
o.tag += '\n'; | |||
} | |||
// first schedule some API queries to fetch the info we need… | |||
o.addTask( 'findCreator' ); | |||
// …then schedule the actual edits | |||
o.addTask( o.templateReplace ? 'replaceTemplate' : 'prependTemplate' ); | |||
o.addTask( 'createRequestSubpage' ); | |||
o.addTask( 'listRequestSubpage' ); | |||
o.addTask( 'purge' ); | |||
o.addTask( 'notifyUploaders' ); | |||
if ( o.isMobile() ) { | |||
o.addTask( 'listMobileUpload' ); | |||
} | |||
// 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.prevDRNode = $( '<ul>' ).attr( 'id', 'AjaxDeletePrevRequests' ); | |||
o.secureCall( 'checkForFormerDR' ); | |||
var toAppend = $( '<div>' ).append( $( '<div>' ).attr( 'class', 'ajaxDeleteLazyLoad' ).css( { | |||
'max-height': Math.max( Math.round( $( window ).height() / 2 ) - 250, 100 ), | |||
'min-height': 0, | |||
overflow: 'auto' | |||
} ).append( o.prevDRNode ), '<br>', lazyLoadNode ); | |||
o.prompt( [ { | |||
message: '', | |||
prefill: o.reason || '', | |||
returnvalue: 'reason', | |||
cleanUp: true, | |||
noEmpty: true, | |||
appendNode: toAppend, | |||
parseReason: true | |||
} | |||
], o.i18n.reasonForDeletion ); | |||
} ); | |||
}, | |||
// Check whether there was a deletion request for the same title in the past | |||
checkForFormerDR: function () { | |||
// Don't search for "kept" when nominating talk pages | |||
if ( nsNr % 2 === 0 ) { | |||
this.talkPage = conf.wgFormattedNamespaces[ nsNr + 1 ] + ':' + this.pageName.replace( conf.wgCanonicalNamespace + ':', '' ); | |||
this.queryAPI( { | |||
prop: 'templates', | |||
titles: this.talkPage, | |||
tltemplates: 'Template:Kept', | |||
tllimit: 1 | |||
}, 'formerDRTalk' ); | |||
} | |||
this.queryAPI( { | |||
list: 'backlinks', | |||
bltitle: this.pageName, | |||
blnamespace: 4, | |||
blfilterredir: 'nonredirects', | |||
bllimit: 500 | |||
}, 'formerDRRequestpage' ); | |||
}, | |||
formerDRTalk: function ( r ) { | |||
var pgs = r.query.pages; | |||
$.each( pgs, function ( id, pg ) { | |||
if ( Array.isArray( pg.templates ) ) { | |||
$( '<li>' ).append( $( '<a>', { | |||
text: AQD.i18n.keptAfterDR, | |||
href: mw.util.getUrl( AQD.talkPage ) | |||
} ) ).prependTo( AQD.prevDRNode ); | |||
} else if ( pg.missing === undefined ) { | |||
$( '<li>' ).append( $( '<a>', { | |||
text: AQD.i18n.hasTalkpage, | |||
href: mw.util.getUrl( AQD.talkPage ) | |||
} ) ).appendTo( AQD.prevDRNode ); | |||
} | |||
} ); | |||
}, | |||
formerDRRequestpage: function ( r ) { | |||
var bls = r.query.backlinks, | |||
_addItem = function ( t, m, bl ) { | |||
$( '<li>' ).append( $( '<a>', { | |||
text: t.replace( '%PAGE%', bl.title ), | |||
href: mw.util.getUrl( bl.title ) | |||
} ) )[ m ]( AQD.prevDRNode ); | |||
}; | |||
$.each( bls, function ( i, bl ) { | |||
if ( this.requestPage === bl.title ) { | |||
_addItem( AQD.i18n.mentionedInDR, 'prependTo', bl ); | |||
} else if ( /^Commons:Deletion requests\/\D/.test( bl.title ) ) { | |||
_addItem( AQD.i18n.mentionedInDR, 'appendTo', bl ); | |||
} else if ( /^Commons:Village pump\//.test( bl.title ) ) { | |||
_addItem( AQD.i18n.mentionedInForum, 'appendTo', bl ); | |||
} | |||
} ); | |||
}, | |||
renderNode: function ( $node, remotecontent, selector ) { | |||
if ( selector ) { | |||
selector = ' ' + selector; | |||
} | |||
$node.load( mw.util.wikiScript() + '?' + $.param( { | |||
action: 'render', | |||
title: remotecontent, | |||
uselang: conf.wgUserLanguage | |||
} ) + ( selector || '' ), function () { | |||
$node.find( 'a' ).attr( 'href', function ( i, v ) { | |||
return v.replace( 'MediaWiki:Anoneditwarning', conf.wgPageName ); | |||
} ); | |||
} ); | |||
return $node; | |||
}, | |||
createLazyLoadNode: function ( label, page, selector ) { | |||
return $( '<div>', { style: 'min-height:40px;' } ).append( $( '<a>', { | |||
href: '#', | |||
text: label | |||
} ).on( 'click', function ( e ) { | |||
e.preventDefault(); | |||
var $content = $( this ).parent().find( '.ajaxDeleteLazyLoad' ), | |||
$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>', { | |||
// eslint-disable-next-line quote-props | |||
'class': 'ajaxDeleteLazyLoad', | |||
style: 'display:none;' | |||
} ).append( $( '<span>', { | |||
// eslint-disable-next-line quote-props | |||
'class': 'ajax-quick-delete-loading', | |||
text: this.i18n.loading | |||
} ).data( 'aqdPage', page ) ) ); | |||
}, | |||
extractFromHTML: function ( $el ) { | |||
$el = $( $el ).parent(); | |||
// …extract the regular expression from html | |||
this.templateRegExp = $el.find( '.ctdr-regex' ).text(); | |||
var m = this.templateRegExp.match( /^\/(.+)\/(i)?$/ ); | |||
if ( !m || !m[ 1 ] ) { | |||
m = new Error( this.i18n.templateRegExp ); | |||
this.fail( m ); | |||
throw m; | |||
} | |||
this.templateRegExp = new RegExp( m[ 1 ], m[ 2 ] ); | |||
// …and the decline reason | |||
this.declineReason = $el.find( '.ctdr-template-decline-reason' ).text(); | |||
// …and the template name itself | |||
m = $el.find( '.ctdr-template-name' ).text(); | |||
this.reason = 'This file was initially tagged by %USER%' + ( m ? ( ' as \'\'\'' + m + '\'\'\'' ) : '' ); | |||
}, | |||
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( e.currentTarget || 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 ) { | |||
e = e.currentTarget || this; | |||
AQD.extractFromHTML( e ); | |||
AQD.convertToDR( e ); | |||
return false; | |||
}, | |||
convertToDR: function ( el ) { | |||
// reset task list in case an earlier error left it non-empty | |||
this.initialize(); | |||
this.declineReason = 'This file does not qualify for [[COM:SPEEDY|speedy-deletion]] and a regular deletion request will be started.'; | |||
this.templateReplace = true; | |||
// first schedule a API query to fetch the info we need… | |||
this.addTask( 'findTemplateAdder' ); | |||
this.addTask( 'getMoveToken' ); | |||
// prompt before conversion for user decision | |||
this.addTask( 'removeTemplate' ); | |||
this.addTask( 'removeProgress' ); | |||
this.addTask( 'nominateForDeletion' ); | |||
// Hide the buttons to prevent attempts of duplicate removal | |||
$( el ).closest( '.convert-to-dr' ).hide(); | |||
// … and go! | |||
this.nextTask(); | |||
}, | |||
findTemplateAdder: function () { | |||
var query = { | |||
prop: 'revisions', | |||
rvprop: 'content|user', | |||
titles: pageName.replace( /_/g, ' ' ), | |||
rvlimit: 50 | |||
}; | |||
this.queryAPI( query, 'findTemplateAdderCB' ); | |||
}, | |||
findTemplateAdderCB: function ( result ) { | |||
var reason, | |||
user, | |||
pgRevs, // for debug | |||
template; | |||
$.each( result.query.pages, function ( id, pg ) { | |||
pgRevs = pg.revisions; | |||
pgRevs.forEach( function ( rv ) { | |||
var 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 ) { | |||
mw.log.warn( pgRevs ); | |||
throw new Error( this.i18n.findTemplateAdderErr ); | |||
} | |||
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' ); | |||
this.addTask( 'replaceUsage' ); | |||
this.addTask( 'queryRedirects' ); | |||
this.addTask( 'deletePage' ); | |||
this.addTask( 'redirectPage' ); | |||
this.addTask( 'reloadPage' ); | |||
this.destination = $( '#AjaxDupeDestination' ).text(); | |||
this.nextTask(); | |||
}, | |||
getDupeDetails: function () { | |||
this.queryAPI( { | |||
curtimestamp: 1, | |||
meta: 'tokens', | |||
prop: 'imageinfo|revisions|info', | |||
rvprop: 'content|timestamp', | |||
inprop: 'watched', | |||
iiprop: 'sha1|size|url', | |||
iiurlwidth: 365, | |||
redirects: 1, | |||
titles: pageName.replace( /_/g, ' ' ) + '|' + this.destination | |||
}, 'getDupeDetailsCB' ); | |||
this.showProgress( 'Fetching details' ); | |||
}, | |||
getDupeDetailsCB: function ( result ) { | |||
this.details = []; | |||
if ( result ) { | |||
var q = result.query, | |||
id, | |||
pg, | |||
ii, | |||
n, | |||
pages = q.pages; | |||
for ( id in pages ) { | |||
if ( Object.prototype.hasOwnProperty.call( pages, id ) ) { | |||
pg = pages[ id ]; | |||
if ( !pg.imageinfo ) { | |||
// Nothing we can change so prevent users reporting | |||
this.disableReport = true; | |||
throw new Error( ( ( pg.title.trim() === '{{{1}}}' ) ? | |||
this.i18n.dupeParaErr : | |||
this.i18n.dupeExistErr.replace( '%TITLE%', pg.title ) ) + ' (pg.imageinfo is undefined)' ); | |||
} | |||
ii = pg.imageinfo[ 0 ]; | |||
n = { | |||
title: pg.title, | |||
size: ii.size, | |||
width: ii.width, | |||
height: ii.height, | |||
thumburl: ii.thumburl, | |||
thumbwidth: ii.thumbwidth, | |||
thumbheight: ii.thumbheight, | |||
descriptionurl: ii.descriptionurl, | |||
sha1: ii.sha1, | |||
content: pg.revisions[ 0 ][ '*' ], | |||
starttimestamp: result.curtimestamp | |||
}; | |||
this.details.push( n ); | |||
this.csrftoken = q.tokens.csrftoken; | |||
if ( pg.watched !== undefined ) { | |||
this.pageWasWatched = true; | |||
} | |||
} | |||
} | |||
} | |||
if ( this.details.length < 2 ) { | |||
this.disableReport = true; | |||
throw new Error( this.i18n.noPageFound ); | |||
} | |||
// If order (old=0, new=1) is incorrect: Reverse | |||
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 = { | |||
title: this.pageName, | |||
text: this.tag, | |||
editType: 'prependtext', | |||
watchlist: window.AjaxDeleteWatchFile ? 'watch' : this.watchlist, | |||
minor: false | |||
}; | |||
this.showProgress( this.i18n.addingAnyTemplate ); | |||
this.savePage( page, this.img_summary, 'nextTask' ); | |||
}, | |||
/** | |||
* Edit the current page to add the specified tag and the changed content. | |||
*/ | |||
replaceTemplate: function ( text ) { | |||
var page = { | |||
title: this.destination || pageName, | |||
text: text || this.tag + this.pageContent, | |||
editType: 'text', | |||
starttimestamp: this.starttimestamp, | |||
timestamp: this.timestamp, | |||
watchlist: window.AjaxDeleteWatchFile ? 'watch' : this.watchlist, | |||
minor: false | |||
}; | |||
this.templateReplace = false; | |||
this.showProgress( this.i18n.addingAnyTemplate ); | |||
this.savePage( page, ( this.declineReason || 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 = { | |||
title: this.requestPage, | |||
// eslint-disable-next-line no-useless-escape | |||
text: '\n=== [[:' + this.pageName + ']] ===\n' + this.reason + ' ~~~~\n', | |||
watchlist: 'watch', | |||
editType: 'appendtext' | |||
}; | |||
if ( this.isMobile() ) { | |||
page.text += '\n<noinclude>[[Category:MobileUpload-related deletion requests]]</noinclude>'; | |||
} | |||
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 = 'preferences'; | |||
page.editType = 'appendtext'; | |||
this.showProgress( this.i18n.listingNomination ); | |||
this.savePage( page, 'Listing [[' + this.requestPage + ']]', 'nextTask' ); | |||
}, | |||
isMobile: function () { | |||
var isMobile = false, | |||
cats = conf.wgCategories; | |||
for ( var i = 0, len = cats.length; i < len; i++ ) { | |||
isMobile = isMobile || /^Uploaded with Mobile/.test( cats[ i ] ); | |||
} | |||
return isMobile; | |||
}, | |||
listMobileUpload: function () { | |||
var page = { | |||
title: 'Commons:Deletion requests/mobile tracking', | |||
text: '\n{{' + this.requestPage + '}}\n', | |||
watchlist: 'preferences', | |||
editType: 'appendtext' | |||
}; | |||
this.showProgress( this.i18n.listingMobile ); | |||
this.savePage( page, 'Listing [[' + this.requestPage + ']]', 'nextTask' ); | |||
}, | |||
listMobileUploadSpeedy: function () { | |||
var page = { | |||
title: 'Commons:Mobile app/deletion request tracking', | |||
text: '\n# [[:' + this.pageName + ']]', | |||
watchlist: 'preferences', | |||
editType: 'appendtext' | |||
}; | |||
this.showProgress( this.i18n.listingMobile ); | |||
this.savePage( page, 'Listing [[' + this.pageName + ']]', 'nextTask' ); | |||
}, | |||
/** | |||
* Check the users talkpage is not blocked indefinite | |||
*/ | |||
verifyUserNotify: function ( user ) { | |||
this.queryAPI( { | |||
prop: 'info', | |||
titles: this.userTalkPrefix + user, | |||
redirects: 1, | |||
inprop: 'protection' | |||
}, 'verifyUserNotifyCB' ); | |||
}, | |||
verifyUserNotifyCB: function ( result ) { | |||
var page; | |||
this.notifyUser = true; // reset | |||
if ( !result || !result.query || !result.query.pages ) { | |||
mw.log.warn( 'Verify user: result.query.pages is undefined. ', result ); | |||
return this.uploaderNotified(); | |||
} | |||
result = result.query.pages; | |||
for ( var pg in result ) { | |||
pg = result[ pg ]; | |||
page = pg.title; | |||
pg = pg.protection; | |||
if ( pg ) { | |||
for ( var p = 0; p < pg.length; p++ ) { | |||
var pt = pg[ p ]; | |||
if ( pt && pt.type === 'edit' && pt.level === 'sysop' ) { | |||
// Disable report for protected userpages | |||
this.disableReport = true; | |||
// Disable notify for indefinite protected | |||
if ( pt.expiry === 'infinity' ) { | |||
this.notifyUser = false; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
if ( this.notifyUser && page ) { | |||
page = { | |||
title: page, | |||
// eslint-disable-next-line no-useless-escape | |||
text: '\n' + this.talk_tag + ' ~~~~\n', | |||
editType: 'appendtext', | |||
redirect: true, | |||
minor: false | |||
}; | |||
if ( window.AjaxDeleteWatchUserTalk ) { | |||
page.watchlist = 'watch'; | |||
} | |||
this.savePage( page, this.talk_summary, 'uploaderNotified' ); | |||
} else { | |||
this.uploaderNotified(); | |||
} | |||
}, | |||
/** | |||
* Notify any uploaders/creators of this page using {{idw}}. | |||
*/ | |||
notifyUploaders: function () { | |||
this.uploadersToNotify = 0; | |||
if ( this.notifyUser ) { | |||
for ( var user in this.uploaders ) { | |||
if ( Object.prototype.hasOwnProperty.call( this.uploaders, user ) ) { | |||
if ( user === conf.wgUserName ) { | |||
// notifying yourself is pointless | |||
continue; | |||
} | |||
this.verifyUserNotify( user ); | |||
this.showProgress( this.i18n.notifyingUploader.replace( '%USER%', user ) ); | |||
this.uploadersToNotify++; | |||
} | |||
} | |||
} | |||
if ( !this.uploadersToNotify ) { | |||
this.nextTask(); | |||
} | |||
}, | |||
uploaderNotified: function () { | |||
this.uploadersToNotify--; | |||
if ( !this.uploadersToNotify ) { | |||
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 | |||
* DONE: notify fileimporter (2019-09-09) | |||
*/ | |||
findCreator: function () { | |||
var q = { | |||
curtimestamp: 1, | |||
meta: 'tokens', | |||
prop: 'revisions', | |||
titles: this.pageName, | |||
rvprop: 'timestamp|user', | |||
rvdir: 'newer', | |||
rvslots: 'main', | |||
rvlimit: 1 | |||
}; | |||
if ( nsNr === 6 ) { | |||
$.extend( q, { | |||
prop: q.prop + '|imageinfo', | |||
rvprop: q.rvprop + '|content', | |||
iiprop: 'user|comment|sha1', | |||
iilimit: '50', | |||
list: 'logevents', | |||
letype: 'import', | |||
leprop: 'user', | |||
letitle: this.pageName | |||
} ); | |||
} | |||
this.showProgress( this.i18n.preparingToEdit ); | |||
this.queryAPI( q, 'findCreatorCB' ); | |||
}, | |||
findCreatorCB: function ( r ) { | |||
this.uploaders = {}; | |||
if ( !r || !r.query || !r.query.pages ) { | |||
this.disableReport = true; | |||
throw new Error( this.i18n.noPageFound ); | |||
} | |||
var q = r.query, | |||
pg = _firstItem( q.pages ), | |||
rv; | |||
if ( !pg || !pg.revisions ) { | |||
throw new Error( this.i18n.noCreatorFound ); | |||
} | |||
// The csrftoken only changes between sessions | |||
this.csrftoken = q.tokens.csrftoken; | |||
rv = pg.revisions[ 0 ]; | |||
// First handle non-file pages | |||
if ( nsNr !== 6 || !pg.imageinfo ) { | |||
this.starttimestamp = r.curtimestamp; | |||
this.timestamp = rv.timestamp; | |||
if ( ( this.pageCreator = rv.user ) ) { | |||
this.uploaders[ this.pageCreator ] = true; | |||
} | |||
} else { | |||
var info = pg.imageinfo, | |||
i = info.length, | |||
content = rv.slots.main[ '*' ], | |||
seenHashes = {}; | |||
// Iterate in reverse order (sha1 check) | |||
for ( --i; i >= 0; i-- ) { | |||
var iii = info[ i ], | |||
rev = seenHashes[ iii.sha1 ]; | |||
// Skip reverts and remove users | |||
if ( iii.sha1 && rev ) { | |||
for ( rev; rev > i; rev-- ) { | |||
var u = info[ rev - 1 ].user; | |||
if ( this.uploaders[ u ] < rev ) { | |||
delete this.uploaders[ u ]; | |||
} | |||
} | |||
continue; | |||
} | |||
seenHashes[ iii.sha1 ] = i + 1; | |||
// No need to check again the whole shebang | |||
if ( this.uploaders[ iii.user ] ) { | |||
continue; | |||
} | |||
// Now exclude bots which only reupload a new version: | |||
if ( mw.libs.commons.isSmallChangesBot( iii.user ) ) { | |||
continue; | |||
} | |||
// Outsourced to MediaWiki:Gadget-libCommons.js | |||
iii = mw.libs.commons.getUploadBotUser( iii.user, content, iii.comment, rv.user ); | |||
if ( iii ) { | |||
this.uploaders[ iii ] = i + 1; | |||
} | |||
} | |||
// Get fileimporter | |||
if ( ( info = q.logevents ) && ( i = info.length ) ) { | |||
for ( --i; i >= 0; i-- ) { | |||
if ( info[ i ].user ) { | |||
this.uploaders[ info[ i ].user ] = true; | |||
} | |||
} | |||
} | |||
} | |||
this.nextTask(); | |||
}, | |||
getMoveToken: function () { | |||
this.showProgress( this.i18n.preparingToEdit ); | |||
var query = { | |||
curtimestamp: 1, | |||
prop: 'info|revisions', | |||
meta: 'tokens', | |||
rvprop: 'content|timestamp', | |||
inprop: 'watched', | |||
titles: this.pageName || pageName.replace( /_/g, ' ' ) | |||
}; | |||
if ( !this.declineReason ) { | |||
query.prop += '|imageinfo'; | |||
query.iiprop = 'mediatype|mime|timestamp'; | |||
} | |||
this.queryAPI( query, 'getMoveTokenCB' ); | |||
}, | |||
/** | |||
* @brief [callback] Prepare page for saving before | |||
* @param [in] result of query | |||
* @return csrftoken, pageContent, starttimestamp, timestamp, imagetimestamp, mimeFileExtension, pageWasWatched | |||
*/ | |||
getMoveTokenCB: function ( result ) { | |||
var q = result.query || {}, | |||
pg = _firstItem( q.pages ); | |||
if ( !pg || !pg.revisions ) { | |||
this.disableReport = true; | |||
throw new Error( this.i18n.noPageFound ); | |||
} | |||
// The csrftoken only changes between sessions | |||
$.extend( this, { | |||
csrftoken: q.tokens.csrftoken, | |||
pageContent: pg.revisions[ 0 ][ '*' ], | |||
starttimestamp: result.curtimestamp, | |||
timestamp: pg.revisions[ 0 ].timestamp | |||
} ); | |||
if ( pg.watched !== undefined ) { | |||
this.pageWasWatched = true; | |||
} | |||
var ii = pg.imageinfo; | |||
if ( ii && ii.length && ii[ 0 ].mime ) { | |||
ii = ii[ 0 ]; | |||
this.imagetimestamp = ii.timestamp; | |||
this.mimeFileExtension = ii.mime | |||
.toLowerCase() | |||
.replace( 'image/jpeg', 'jpg' ) | |||
.replace( /image\/(?:x-|vnd\.)?(png|gif|xcf|djvu|svg|tiff)(?:\+xml)?/, '$1' ) | |||
.replace( /application\/(ogg|pdf)/, '$1' ) | |||
.replace( /video\/(webm)/, '$1' ) | |||
.replace( 'audio/midi', 'mid' ) | |||
.replace( /audio\/(?:x-|vnd\.)?wave?/, 'wav' ) | |||
.replace( /audio\/(?:x-)?flac/, 'flac' ); | |||
if ( this.mimeFileExtension.length > 5 ) { | |||
this.mimeFileExtension = ''; | |||
} else if ( this.mimeFileExtension === 'ogg' ) { | |||
switch ( ii.mediatype ) { | |||
case 'AUDIO': | |||
this.mimeFileExtension = 'oga'; | |||
break; | |||
case 'VIDEO': | |||
this.mimeFileExtension = 'ogv'; | |||
break; | |||
} | |||
} | |||
} | |||
this.nextTask(); | |||
}, | |||
doesFileExist: function () { | |||
if ( !this.destination ) { | |||
// eslint-disable-next-line no-alert | |||
return alert( this.i18n.moveDestination ); | |||
} | |||
this.destination = this.cleanFileName( this.destination ); | |||
var query = { | |||
prop: 'info|revisions', | |||
titles: this.destination, | |||
rvprop: 'content', | |||
rvlimit: 2 | |||
}; | |||
// usually you would use 'redirects': 1, to detect the redirect target but | |||
// in this case you would get the revisions for the target and not the redirect | |||
this.showProgress( this.i18n.checkFileExists ); | |||
this.queryAPI( query, 'doesFileExistCB' ); | |||
}, | |||
/** | |||
* Return nextTask if the page does not exist | |||
* or it is a redirect with one revision to the source | |||
*/ | |||
doesFileExistCB: function ( result ) { | |||
if ( !result || !result.query || !result.query.pages ) { | |||
throw new Error( 'Checking filename: result.query.pages is undefined. ' + this.destination ); | |||
} | |||
var exists = true, | |||
pg = _firstItem( result.query.pages ), | |||
getRedirRegExp = function ( title ) { | |||
title = title.replace( /^(File|Image):/, '' ).replace( /_/g, ' ' ); | |||
return new RegExp( '^\\s*#REDIRECT\\s*\\[\\[File\\:[' + | |||
mw.util.escapeRegExp( title[ 0 ].toUpperCase() ) + mw.util.escapeRegExp( title[ 0 ].toLowerCase() ) + ']' + | |||
mw.util.escapeRegExp( title.slice( 1 ) ).replace( / /g, '[ _]' ) + | |||
'\\s*\\]\\]', | |||
'' ); | |||
}; | |||
if ( pg.missing !== undefined ) { | |||
exists = false; | |||
} else if ( !pg.revisions || pg.revisions.length === 1 && getRedirRegExp( pageName ) | |||
.test( pg.revisions[ 0 ][ '*' ].replace( 'Image:', 'File:' ) ) ) { | |||
// There seems to be no way to find out whether a title is a redirect | |||
// and whether the redirect only consists of one revision | |||
exists = false; | |||
} | |||
if ( exists ) { | |||
if ( this.fileNameExistsCB ) { | |||
this[ this.fileNameExistsCB ]( pg.title.replace( /^File:/, '' ) ); | |||
} | |||
return; | |||
} | |||
this.nextTask(); | |||
}, | |||
removeTemplate: function () { | |||
this.replaceWith = ( this.replaceWith || ( this.templateRegExp ? '' : '$1$2' ) ); | |||
// Remove the template from the text. In case there is an empty line before, remove this also. | |||
var newText = this.pageContent | |||
.replace( | |||
( this.templateRegExp || /(?:([^=])\n)?\{\{(?:rename|rename media|move)\s*\|.*?\}\}(?:\n([^=]))?/i ), | |||
this.replaceWith | |||
); | |||
if ( newText === this.pageContent ) { | |||
return this.nextTask(); | |||
} | |||
this.showProgress( this.i18n.removingTemplate ); | |||
newText = this.pageContent = newText.trim(); | |||
// We have another save request | |||
if ( this.templateReplace ) { | |||
this.img_summary = this.declineReason; | |||
return this.nextTask(); | |||
} | |||
if ( !this.declineReason ) { | |||
this.img_summary = 'Removing template; rename done'; | |||
} | |||
// If nothing remains, add the no-license-template (this also to prevent abuse filter blocking this edit because of page blanking) | |||
this.replaceTemplate( newText || '{{subst:nld}}' ); | |||
}, | |||
replaceUsage: function () { | |||
if ( !this.inUse ) { | |||
return this.nextTask(); | |||
} | |||
this.showProgress( this.i18n.replacingUsage ); | |||
var reasonShort = '[[COM:Duplicate|Duplicate]]:'; | |||
if ( !this.details ) { | |||
AQD.reason = AQD.reason.replace( /\[\[Commons:File[_ ]renaming[^[\]]*\]\]:? ?/i, '' ); | |||
reasonShort = '[[COM:FR|File renamed]]:'; | |||
} | |||
mw.loader.using( 'ext.gadget.libGlobalReplace', function () { | |||
if ( AQD.replaceUsingCORS ) { | |||
mw.libs.globalReplace( pageName, AQD.destination, reasonShort, AQD.reason ) | |||
.fail( function ( err ) { | |||
throw new Error( err ); | |||
} ) | |||
.done( function () { | |||
AQD.nextTask(); | |||
} ) | |||
.progress( function ( r ) { | |||
AQD.showProgress( r ); | |||
mw.log( r ); | |||
} ); | |||
} else { | |||
mw.libs.globalReplaceDelinker( pageName, AQD.destination, reasonShort + ' ' + AQD.reason, function () { | |||
AQD.nextTask(); | |||
}, function ( err ) { | |||
throw new Error( err ); | |||
} ); | |||
} | |||
} ); | |||
}, | |||
redirectPage: function () { | |||
var page = { | |||
title: pageName, | |||
text: '#REDIRECT [[' + this.destination + ']]', | |||
editType: 'text', | |||
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences' | |||
}; | |||
this.showProgress( this.i18n.redirectingFile ); | |||
this.savePage( page, 'Redirecting to duplicate file', 'nextTask' ); | |||
}, | |||
saveDescription: function () { | |||
var page = { | |||
title: this.destination, | |||
text: this.newPageText, | |||
editType: 'text', | |||
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences' | |||
}; | |||
this.showProgress( this.i18n.savingDescription ); | |||
this.savePage( page, 'Merging details from duplicate ([[' + pageName + ']])', 'nextTask' ); | |||
}, | |||
/** | |||
* Updates the redirects to the current page | |||
* when moving or processing dupes immediately | |||
* to prevent double redirects | |||
*/ | |||
queryRedirects: function () { | |||
mw.loader.using( 'mediawiki.api' ).then( function () { | |||
return new mw.Api().loadMessagesIfMissing( [ | |||
'Whatlinkshere' | |||
] ); | |||
} ).then( function () { | |||
AQD.showProgress( mw.msg( 'Whatlinkshere' ) ); | |||
// TODO: Replace also the redirects inclusions!? | |||
AQD.queryAPI( { | |||
generator: 'backlinks', | |||
gblfilterredir: 'redirects', | |||
prop: 'revisions', | |||
rvprop: 'content', | |||
gbltitle: AQD.pageName || pageName.replace( /_/g, ' ' ) | |||
}, AQD.queryRedirectsCB ? | |||
AQD.queryRedirectsCB : | |||
'updateRedirects' ); | |||
} ); | |||
}, | |||
updateRedirects: function ( result ) { | |||
AQD.redirectsToUpdate = 0; | |||
if ( result.query && result.query.pages ) { | |||
this.showProgress( this.i18n.updRedir ); | |||
$.each( result.query.pages, function ( id, pg ) { | |||
var rv = pg.revisions[ 0 ]; | |||
if ( !rv || !rv[ '*' ] ) { | |||
return; | |||
} | |||
// Update only redirects with same mimetype | |||
if ( AQD.checkFileExt( pg.title, AQD.destination, true ) ) { | |||
return mw.log( 'Redirect skipped, not same mimetype.', pg.title ); | |||
} | |||
var page = { | |||
title: pg.title, | |||
text: rv[ '*' ].replace( /#\s*REDIRECT\s*\[\[.+/, '#REDIRECT [[' + AQD.destination + ']]' ), | |||
editType: 'text', | |||
watchlist: 'preferences' | |||
}; | |||
AQD.savePage( page, 'Updating redirect while processing [[' + pageName.replace( /_/g, ' ' ) + ']]', 'updateRedirectsCB' ); | |||
AQD.redirectsToUpdate++; | |||
} ); | |||
} | |||
if ( !AQD.redirectsToUpdate ) { | |||
AQD.nextTask(); | |||
} | |||
}, | |||
updateRedirectsCB: function () { | |||
AQD.redirectsToUpdate--; | |||
if ( !AQD.redirectsToUpdate ) { | |||
AQD.nextTask(); | |||
} | |||
}, | |||
/** | |||
* Pseudo-Modal JS windows. | |||
*/ | |||
prompt: function ( questions, title, width ) { | |||
var o = this, | |||
dlgButtons = {}; | |||
dlgButtons[ this.i18n.submitButtonLabel ] = function () { | |||
questions.forEach( function ( v, i ) { | |||
var response = document.getElementById( 'AjaxQuestion' + i ); | |||
response = ( v.type === 'checkbox' ) ? response.checked : response.value; | |||
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 ) | |||
.replace( '%PARAMETER-LINKED%', '[[:' + response + ']]' ); | |||
} | |||
} ); | |||
$( this ).dialog( 'close' ); | |||
AQD.nextTask(); | |||
}; | |||
dlgButtons[ this.i18n.cancelButtonLabel ] = function () { | |||
$( this ).dialog( 'close' ); | |||
}; | |||
var $submitButton, | |||
$AjaxDeleteContainer = $( '<div>', { id: 'AjaxDeleteContainer' } ), | |||
_parseReason = function () { | |||
var $el = $( this ), | |||
$parserResultNode = $el.data( 'parserResultNode' ); | |||
if ( !$parserResultNode ) { | |||
return; | |||
} | |||
$parserResultNode.css( 'color', '#877' ); | |||
var _gotParsedText = function ( r ) { | |||
try { | |||
$parserResultNode.html( r ); | |||
$parserResultNode.css( 'color', '#000' ); | |||
} catch ( ex ) {} | |||
}; | |||
mw.loader.using( [ 'ext.gadget.libAPI' ], function () { | |||
mw.libs.commons.api.parse( $el.val(), conf.wgUserLanguage, pageName, _gotParsedText ); | |||
} ); | |||
}, | |||
_validateInput = function ( event ) { | |||
var $el = $( this ), | |||
v = $el.data( 'v' ); | |||
if ( v.noEmpty ) { | |||
$submitButton.button( 'option', 'disabled', $el.val().trim().length < ( v.minLength || 8 ) ); | |||
} | |||
if ( | |||
( $el.prop( 'nodeName' ) !== 'TEXTAREA' ) && | |||
( event.which === 13 ) && | |||
( v.enterToSubmit !== false ) && | |||
!$submitButton.button( 'option', 'disabled' ) | |||
) { | |||
$submitButton.trigger( 'click' ); | |||
} | |||
}, | |||
_convertToTextarea = function () { | |||
var $el = $( this ), | |||
$input = $el.data( 'toConvert' ), | |||
$tarea = $( '<textarea>', { | |||
id: $input.attr( 'id' ), | |||
style: 'height:10em; width:98%; display:none;' | |||
} ); | |||
$el.off().fadeOut(); | |||
$input.parent().prepend( | |||
$tarea | |||
.data( 'v', $input.data( 'v' ) ).data( 'parserResultNode', $input.data( 'parserResultNode' ) ) | |||
.val( $input.val() ).on( 'keyup', _parseReason ).on( 'keyup input', _validateInput ) ); | |||
$tarea.slideDown(); | |||
$input.remove(); | |||
}; | |||
questions.forEach( function ( v, i ) { | |||
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>' ); | |||
} | |||
$AjaxDeleteContainer.append( v.appendNode ? v.appendNode : '<br>' ); | |||
if ( typeof v.byteLimit === 'number' ) { | |||
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.prop( 'checked', v.prefill ).attr( 'style', 'margin-left: 5px' ); | |||
} | |||
} ); | |||
if ( mw.user.isAnon() ) { | |||
AQD.renderNode( $( '<div>', { id: 'ajaxDeleteAnonwarning' } ), 'MediaWiki:Anoneditwarning' ).appendTo( $AjaxDeleteContainer ); | |||
} | |||
$( '<div>' ).append( $AjaxDeleteContainer ).dialog( { | |||
width: ( width || 600 ), | |||
modal: true, | |||
title: title, | |||
dialogClass: 'wikiEditor-toolbar-dialog', | |||
close: function () { | |||
$( this ).dialog( 'destroy' ).remove(); | |||
if ( AQD.currentTask === 'formerDRRequestpage' ) { | |||
mw.util.$content.find( '.convert-to-dr' ).show(); | |||
} | |||
}, | |||
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 ).specialButton( 'proceed' ); | |||
$buttons.eq( 1 ).specialButton( 'cancel' ); | |||
} | |||
} ); | |||
questions.forEach( function ( v, i ) { | |||
var curQuestion = $AjaxDeleteContainer.find( '#AjaxQuestion' + i ); | |||
curQuestion.trigger( 'keyup' ); | |||
if ( v.type === 'text' ) { | |||
var $q = curQuestion.wrap( '<div style="position:relative;">' ).parent(), | |||
$i = $.createIcon( 'ui-icon-arrow-4-diag' ).attr( 'title', AQD.i18n.expandToTextarea ); | |||
$( '<span>', { | |||
// eslint-disable-next-line quote-props | |||
'class': 'ajaxTextareaConverter' } ).append( $i ).appendTo( $q ).data( 'toConvert', curQuestion ).on( 'click', _convertToTextarea ); | |||
} | |||
} ); | |||
$( '#AjaxQuestion0' ).trigger( 'focus' ).trigger( 'select' ); | |||
mw.hook( 'aqd.prompt' ).fire( o ); | |||
}, | |||
/** | |||
* Open a jQuery dialog with preview-images and some options | |||
* and information to compare the two files | |||
*/ | |||
compareDetails: function () { | |||
var d = this.details[ 0 ], | |||
f = this.details[ 1 ], | |||
$swapButton, | |||
$overlayButton; | |||
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 ) + ' KiB <br>' + d.width + '×' + d.height + '<br>' | |||
} ).append( | |||
$( '<a>', { | |||
href: d.descriptionurl, | |||
text: d.title, | |||
target: '_blank' | |||
} ) ) ), | |||
$imgF = $( '<div>' ).append( $( '<img>', { | |||
src: f.thumburl, | |||
height: f.thumbheight, | |||
width: f.thumbwidth | |||
} ), $( '<div>', { | |||
id: 'AjaxDeleteImgKeep', | |||
html: Math.round( f.size / 1000 ) + ' KiB <br>' + f.width + '×' + f.height + '<br>' | |||
} ).append( | |||
$( '<a>', { | |||
href: f.descriptionurl, | |||
text: f.title, | |||
target: '_blank' | |||
} ) ) ), | |||
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(); | |||
AQD.inUse = true; | |||
setTimeout( function () { | |||
AQD.compareDetails(); | |||
}, 20 ); | |||
}; | |||
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, left: pos.left - 1 | |||
} ) | |||
.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)', | |||
width: 'auto', | |||
border: '1px solid #0c9' | |||
} ).draggable(); | |||
$fClone.find( 'img' ).resizable(); | |||
// In IE, opacity is not fully inerhited | |||
$fClone.children( 'div' ).fadeTo( 0, 0.7 ); | |||
} ); | |||
} | |||
}; | |||
this.showProgress(); | |||
var $AjaxDupeContainer = $( '<div>', { id: 'AjaxDupeContainer' } ).append( $imgD, $imgF ); | |||
$( '<div>' ).append( $AjaxDupeContainer ).dialog( { | |||
width: 800, | |||
modal: true, | |||
title: this.i18n.compareDetails, | |||
draggable: false, | |||
dialogClass: 'wikiEditor-toolbar-dialog', | |||
close: function () { | |||
$( this ).dialog( 'destroy' ).remove(); | |||
}, | |||
buttons: dlgButtons, | |||
open: function () { | |||
var $buttons = $( this ).parent().find( '.ui-dialog-buttonpane button' ); | |||
$buttons.eq( 0 ).specialButton( 'proceed' ); | |||
$buttons.eq( 1 ).button( { icons: { primary: 'ui-icon-refresh' } } ); | |||
$swapButton = $buttons.eq( 2 ).button( { icons: { primary: 'ui-icon-transfer-e-w' } } ); | |||
$overlayButton = $buttons.eq( 3 ).button( { icons: { primary: 'ui-icon-newwin' } } ); | |||
$swapButton.css( 'float', ( ( $swapButton.css( 'float' ) === 'left' ) ? 'right' : 'left' ) ); | |||
$overlayButton.css( 'float', ( ( $overlayButton.css( 'float' ) === 'left' ) ? 'right' : 'left' ) ); | |||
} | |||
} ); | |||
mw.loader.load( [ 'ext.gadget.libGlobalReplace', 'ext.gadget.libWikiDOM' ] ); | |||
}, | |||
mergeDescriptions: function () { | |||
var newPageText = this.details[ 1 ].content; | |||
mw.loader.using( [ 'ext.gadget.libGlobalReplace', 'ext.gadget.libWikiDOM' ], function () { | |||
newPageText = mw.libs.wikiDOM.nowikiEscaper( newPageText ).doCleanUp(); | |||
AQD.showProgress(); | |||
AQD.prompt( [ { | |||
message: '', | |||
prefill: AQD.details[ 0 ].content, | |||
returnvalue: 'discard', | |||
cleanUp: false, | |||
noEmpty: false, | |||
type: 'textarea', | |||
enterToSubmit: false | |||
}, { | |||
message: '', | |||
prefill: newPageText, | |||
returnvalue: 'newPageText', | |||
cleanUp: false, | |||
noEmpty: false, | |||
type: 'textarea', | |||
enterToSubmit: false | |||
}, { | |||
message: AQD.i18n.useCORSForReplace, | |||
prefill: !window.aqdCORSOptOut, | |||
returnvalue: 'replaceUsingCORS', | |||
// cleanUp: false, | |||
noEmpty: false, | |||
type: 'checkbox' | |||
} | |||
], AQD.i18n.mergeDescription, 800 ); | |||
AQD.destination = AQD.details[ 1 ].title; | |||
AQD.reason = 'Exact or scaled-down duplicate: [[:' + AQD.destination + ']]'; | |||
} ); | |||
}, | |||
/** | |||
* Correct the MIME-Type; Accepts only valid filenames (with extension) | |||
* Either a filename is passed or the destination property is used | |||
*/ | |||
correctMIME: function ( fn ) { | |||
// If the current mime-type is available to the script, check it; | |||
// MediaWiki sometimes allows uploading mismatching mimetypes but not moving | |||
var f = fn || this.destination; | |||
if ( this.mimeFileExtension ) { | |||
f = f.replace( /\.\w{2,5}$/, '.' + this.mimeFileExtension ); | |||
} | |||
if ( !fn ) { | |||
this.destination = f; | |||
return this.nextTask(); | |||
} else { | |||
return f; | |||
} | |||
}, | |||
cleanFileName: function ( fn, ignoreMIME ) { | |||
// Remove Namespace | |||
fn = fn.replace( /^(?:Image|File):/i, '' ) | |||
// Convert extension to lower case | |||
.replace( /(\.\w{2,5})+$/, function ( $e ) { | |||
return $e.toLowerCase(); | |||
} ) | |||
// jpeg -> jpg | |||
.replace( /\.jpe*g$/, '.jpg' ) | |||
// First cleanUp from Flinfo (FlinfoOut.php) by Flominator and Lupo | |||
.replace( /~{3,}/g, '' ) // "signature" | |||
.replace( /[\u00A0\u1680\u180E\u2000-\u200B\u2028\u2029\u202F\u205F\u3000]/, ' ' ) // remove NBSP and other unusual spaces | |||
.replace( /\s+|_/g, ' ' ) // (multiple) whitespace | |||
// eslint-disable-next-line no-control-regex | |||
.replace( /[\x00-\x1f\x7f]/g, '' ) | |||
.replace( /%([0-9A-Fa-f]{2})/g, '% $1' ) // URL encoding stuff | |||
.replace( /&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g, '& $1' ) // URL-params? | |||
.replace( /''/g, '"' ) | |||
.replace( /[:/|#]/g, '-' ) | |||
.replace( /[\]}>]/g, ')' ) | |||
.replace( /[[{<]/g, '(' ); | |||
fn = this.checkFileExt( pageName, fn, ignoreMIME ) || fn; | |||
// Capitalize the first letter and prefix the namespace | |||
return 'File:' + $.ucFirst( fn ); // own prototype | |||
}, | |||
/** | |||
* @brief Compare mimetype | |||
* @param [in] of old filename | |||
* @param [in] fn new filename | |||
* @param [in] boolean | |||
* @return false if same, otherwise new file with old extension | |||
*/ | |||
checkFileExt: function ( of, fn, ignoreMIME ) { | |||
var currentExt = ( !ignoreMIME && this.mimeFileExtension ) ? | |||
this.mimeFileExtension : | |||
of.replace( /.*?\.(\w{2,5})$/, '$1' ).toLowerCase().replace( 'jpeg', 'jpg' ), | |||
reCurrentExt = new RegExp( '\\.' + mw.util.escapeRegExp( currentExt ) + '$', 'i' ), | |||
reDestExt = new RegExp( '\\.' + mw.util.escapeRegExp( fn.replace( /.*?\.(\w{2,5})$/, '$1' ) ) + '$', 'i' ); | |||
// If new filename is without (same) extension, add the one from the old name | |||
if ( !reCurrentExt.test( fn ) ) { | |||
// First, try to replace the old extension | |||
fn = fn.replace( reDestExt, '.' + currentExt ); | |||
if ( !reCurrentExt.test( fn ) ) { | |||
// If this did not work, then simply append the old extension | |||
fn += '.' + currentExt; | |||
} | |||
} else { | |||
fn = false; | |||
} // is equal | |||
return fn; | |||
}, | |||
cleanReason: function ( uncleanReason ) { | |||
return uncleanReason | |||
.trim() // whitespace | |||
.replace( /(?:--|–|—)? ?~{3,5} ?/, '' ) // remove signature | |||
.replace( /\|\s/g, '| ' ); // pipes | |||
}, | |||
/** | |||
* 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>' ).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', | |||
open: function () { | |||
$( this ).parent().find( '.ui-dialog-titlebar' ).hide(); | |||
}, | |||
close: function () { | |||
$( this ).dialog( 'destroy' ).remove(); | |||
} | |||
} ); | |||
} | |||
}, | |||
/** | |||
* Submit an edited page. | |||
*/ | |||
savePage: function ( page, summary, callback ) { | |||
if ( AQD.csrftoken ) { | |||
mw.user.tokens.set( 'csrfToken', AQD.csrftoken ); | |||
} | |||
$.extend( true, page, { | |||
cb: function ( r ) { | |||
AQD.secureCall( callback, r ); | |||
}, | |||
// text, result, query | |||
errCb: function ( t, r ) { | |||
if ( AQD.uploadersToNotify ) { | |||
// If user notify fails don't break next task (e.q. redirect to protected page, very rare) | |||
AQD.secureCall( callback, r ); | |||
} | |||
AQD.fail( t, r ); | |||
}, | |||
summary: summary | |||
} ); | |||
mw.loader.using( [ 'ext.gadget.libAPI' ], function () { | |||
mw.libs.commons.api.editPage( page ); | |||
} ); | |||
}, | |||
movePage: function () { | |||
mw.user.tokens.set( 'csrfToken', AQD.csrftoken ); | |||
// Some users don't get it: They want to move pages to themselves. | |||
if ( pageName.replace( /_/g, ' ' ) === AQD.destination ) { | |||
return AQD.nextTask(); | |||
} | |||
mw.loader.using( [ 'ext.gadget.libAPI' ], function () { | |||
var moveArgs = { | |||
cb: function () { | |||
AQD.nextTask(); | |||
}, | |||
// text, r-result, query | |||
errCb: function ( t, r ) { | |||
if ( r && r.error && /articleexists/.test( r.error.code ) ) { | |||
AQD.disableReport = true; | |||
} | |||
AQD.fail( t, r ); | |||
}, | |||
from: pageName, | |||
to: AQD.destination, | |||
reason: AQD.reason, | |||
movetalk: true, | |||
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences' | |||
}; | |||
// Option to not leave a redirect behind, MediaWiki default does leave one behind | |||
// Just like movetalk, an empty parameter sets it to true | |||
if ( AQD.wpLeaveRedirect === false ) { | |||
moveArgs.noredirect = true; | |||
} | |||
AQD.showProgress( AQD.i18n.movingFile ); | |||
mw.libs.commons.api.movePage( moveArgs ); | |||
} ); | |||
}, | |||
deletePage: function () { | |||
mw.user.tokens.set( 'csrfToken', AQD.csrftoken ); | |||
mw.loader.using( [ 'ext.gadget.libAPI' ], function () { | |||
AQD.showProgress( AQD.i18n.deletingFile ); | |||
mw.libs.commons.api.deletePage( { | |||
cb: function () { | |||
AQD.nextTask(); | |||
}, | |||
// text, result, query | |||
errCb: function ( t, r ) { | |||
AQD.fail( t, r ); | |||
}, | |||
title: pageName, | |||
reason: AQD.reason | |||
} ); | |||
} ); | |||
}, | |||
purge: function () { | |||
// No need for checking success, showing progress, nor for waiting for task to complete | |||
this.nextTask(); | |||
$.post( this.apiURL, { | |||
format: 'json', | |||
action: 'purge', | |||
forcelinkupdate: 1, | |||
titles: pageName | |||
} ); | |||
}, | |||
/** | |||
* Does a MediaWiki API request and passes the result to the supplied callback (method name). | |||
*/ | |||
queryAPI: function ( params, callback ) { | |||
mw.loader.using( [ 'ext.gadget.libAPI' ], function () { | |||
params.action = params.action || 'query'; | |||
mw.libs.commons.api.query( params, { | |||
method: 'GET', | |||
cache: false, | |||
cb: function ( r ) { | |||
AQD.secureCall( callback, r ); | |||
}, | |||
// text, result, query | |||
errCb: function ( t, r ) { | |||
AQD.fail( t, r ); | |||
} | |||
} ); | |||
} ); | |||
}, | |||
/** | |||
* Method to catch errors and report where they occurred | |||
*/ | |||
secureCall: function ( fn, r ) { | |||
var o = AQD; | |||
try { | |||
o.currentTask = arguments[ 0 ]; | |||
if ( typeof fn === 'function' ) { | |||
// arguments is not of type array so we can't take .slice | |||
return fn.apply( o, Array.prototype.slice.call( arguments, 1 ) ); | |||
} else if ( typeof fn === 'string' ) { | |||
return o[ fn ].apply( o, Array.prototype.slice.call( arguments, 1 ) ); | |||
} else { | |||
mw.log.warn( fn, this.tasks ); | |||
o.fail( 'This is not a function!' ); | |||
} | |||
} catch ( ex ) { | |||
o.fail( ex, r ); | |||
} | |||
}, | |||
/** | |||
* 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 () { | |||
this.secureCall( this.tasks.shift() ); | |||
}, | |||
retryTask: function () { | |||
this.secureCall( this.currentTask ); | |||
}, | |||
/** | |||
* Once we're all done, reload the page. | |||
*/ | |||
reloadPage: function () { | |||
this.showProgress(); | |||
if ( this.pageName && this.pageName.replace( / /g, '_' ) !== pageName ) { | |||
return; | |||
} | |||
location.href = mw.util.getUrl( this.destination || pageName ); | |||
}, | |||
/** | |||
* Error handler. Throws an alert at the user and give him | |||
* the possibility to retry or autoreport the error-message. | |||
*/ | |||
fail: function ( err, r ) { | |||
var o = this, | |||
msg, | |||
dlgButtons = {}; | |||
if ( typeof err === 'object' ) { | |||
msg = err.message + ' \n\n ' + err.name; | |||
if ( err.lineNumber ) { | |||
msg += ' @line' + err.lineNumber; | |||
} | |||
err = msg; | |||
} | |||
// Mostly the same as err | |||
if ( typeof r === 'object' ) { | |||
if ( r.error && /tpt-target-page|readonly/.test( r.error.code ) ) { | |||
this.disableReport = true; | |||
} | |||
} | |||
// err += '\n' + JSON.stringify( r ); | |||
msg = this.i18n.taskFailure[ this.currentTask ] || this.i18n.genericFailure; | |||
// TODO: Needs cleanup | |||
if ( this.img_summary === 'Nominating for deletion' ) { | |||
msg += ' ' + ( this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand ); | |||
} | |||
dlgButtons[ this.i18n.retryButtonLabel ] = function () { | |||
$( this ).remove(); | |||
o.retryTask(); | |||
}; | |||
if ( [ 'movePage', 'deletePage', 'notifyUploaders' ].indexOf( o.currentTask ) !== -1 && | |||
( /code 50\d|missingtitle/.test( err ) ) ) { | |||
dlgButtons[ this.i18n.ignoreButtonLabel ] = function () { | |||
$( this ).remove(); | |||
o.nextTask(); | |||
}; | |||
} | |||
if ( !this.disableReport ) { | |||
dlgButtons[ this.i18n.reportButtonLabel ] = function () { | |||
var randomId = Math.round( Math.random() * 1099511627776 ), | |||
toSend = '\n== Autoreport by AjaxQuickDelete ' + randomId + ' ==\n' + err + | |||
'\nAQD version: ' + o.version + | |||
'\n++++\n:Task: ' + o.currentTask + '\n:NextTask: ' + o.tasks[ 0 ] + '\n:LastTask: ' + o.tasks[ o.tasks.length - 1 ] + | |||
'\n:Page: {{Page|1=' + ( o.pageName || pageName ) + '}}\n:Skin: ' + mw.user.options.get( 'skin' ) + | |||
'\n:[{{fullurl:Special:Contributions|target={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Contribs] ' + | |||
'[{{fullurl:Special:Log|user={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Log] ' + | |||
'before error [[User:{{subst:REVISIONUSER}}|]] ~~~~~\n\n'; | |||
$( '#feedbackContainer' ).contents().remove().end() | |||
.append( $( '<img>', { src: '//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif' } ) ).css( 'text-align', 'center' ); | |||
$.post( o.apiURL, { | |||
action: 'edit', | |||
format: 'json', | |||
title: 'MediaWiki talk:Gadget-AjaxQuickDelete.js/auto-errors', | |||
summary: '/*Autoreport by AjaxQuickDelete ' + randomId + '*/ error with random id', | |||
appendtext: toSend, | |||
token: ( o.csrftoken || 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 + '<br>' + this.i18n.errorDetails + '<br>' + mw.html.escape( err ) + '<br>' + | |||
( this.tag ? ( this.i18n.tagWas + this.tag ) : '' ) + '<br><a href="' + mw.util.getUrl( 'MediaWiki talk:AjaxQuickDelete.js' ) + | |||
'" >' + this.i18n.errorReport.replace( /%BUTTON%/, '<tt>' + this.i18n.reportButtonLabel + '</tt>' ) + '</a>' ) | |||
} ) ).dialog( { | |||
width: 550, | |||
modal: true, | |||
closeOnEscape: false, | |||
title: this.i18n.errorDlgTitle, | |||
dialogClass: 'ajaxDeleteError', | |||
buttons: dlgButtons, | |||
close: function () { | |||
$( this ).dialog( 'destroy' ).remove(); | |||
} | |||
} ); | |||
if ( mw.log.warn ) { | |||
mw.log.warn( err ); | |||
} | |||
}, | |||
/** | |||
* 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 ) { | |||
return mw.libs.commons.formatDate( fmt, date, | |||
( mw.libs.commons.api && mw.libs.commons.api.getCurrentDate() || new Date() ) | |||
); | |||
}, | |||
// Constants | |||
// DR subpage prefix | |||
requestPagePrefix: 'Commons:Deletion requests/', | |||
// user talk page prefix | |||
userTalkPrefix: conf.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: '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?', | |||
moreInformation: 'More information', | |||
loading: 'Loading…', | |||
keptAfterDR: 'This page was kept after a deletion request. Please contact the administrator who kept it before re-nominating.', | |||
hasTalkpage: 'There is a talk page. Consider reading it or adding your remarks.', | |||
mentionedInDR: 'Consider reading the deletion debate –%PAGE%– that links to this page.', | |||
mentionedInForum: 'On %PAGE%, this page is part of a discussion.', | |||
// 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', | |||
expandToTextarea: 'Expand to textarea', | |||
notifyUser: 'Notify users', | |||
// GUI progress messages | |||
preparingToEdit: 'Preparing to edit pages… ', | |||
creatingNomination: 'Creating nomination page… ', | |||
listingNomination: 'Adding nomination page to daily list… ', | |||
addingAnyTemplate: 'Adding template to ' + conf.wgCanonicalNamespace.toLowerCase() + ' page… ', | |||
notifyingUploader: 'Notifying %USER%… ', | |||
listingMobile: 'Listing mobile upload', | |||
updRedir: 'Updating redirects', | |||
// 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 filename?', | |||
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', | |||
warnRename: 'File renaming was recently declined, be prudent!', | |||
// For declining any request | |||
removingTemplate: 'Removing template', | |||
declineRequest: 'Why do you want to decline the request?', | |||
anyDecline: 'Decline request', | |||
// For Duplicates | |||
useCORSForReplace: 'Try to replace usage immediately using your user account:', | |||
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', | |||
processDupes: 'Process Duplicates', | |||
// Errors | |||
errorDlgTitle: 'Error', | |||
genericFailure: 'An error occurred while trying to do the requested action. ', | |||
taskFailure: { | |||
listUploaders: 'An error occurred while determining the ' + ( nsNr === 6 ? ' uploader(s) of this file' : 'creator of this page' ) + '.', | |||
loadPages: 'An error occurred while preparing to nominate this ' + conf.wgCanonicalNamespace.toLowerCase() + ' for deletion.', | |||
prependDeletionTemplate: 'An error occurred while adding the {{delete}} template to this ' + conf.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 ' + ( nsNr === 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 ' + conf.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: 'Manually report the error here or click on %BUTTON% to send an automatic error-report.', | |||
tagWas: 'The tag to be inserted into this page was ', | |||
// Minor errors/warnings | |||
templateRegExp: 'The template does not expose a valid regular expression for {{X-To-DR}}. Go the the template and fix it there.', | |||
findTemplateAdderErr: '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.', | |||
dupeParaErr: 'Error in the duplicate-template, check your language version!', | |||
dupeExistErr: 'Retrieving information about %TITLE% failed. It is possible that it is deleted, the last revision is corrupt or the file is a redirect.', | |||
noCreatorFound: 'The page you are attempting to add a tag to was deleted or moved. Unable to retrieve the content.', | |||
noPageFound: 'The page you are attempting to modify or move is corrupted, was deleted or moved: Unable to retrieve history and contents.' | |||
} | |||
}; | |||
AQD.preinstall(); | |||
if ( conf.wgUserLanguage !== 'en' ) { | |||
$.when( mw.loader.getScript( mw.util.wikiScript() + | |||
'?title=MediaWiki:Gadget-AjaxQuickDelete.js/' + | |||
conf.wgUserLanguage + '.js&action=raw&ctype=text/javascript' | |||
), $.ready ).always( function () { AQD.install(); } ); | |||
} else { | |||
$( function () { | |||
AQD.install(); | |||
} ); | |||
} | |||
}() ); | |||
// </nowiki> | |||
// This script aligns dates into one of two formats allowed by [[WP:MOSNUM]]. | // This script aligns dates into one of two formats allowed by [[WP:MOSNUM]]. | ||
// PLEASE READ THE DOCUMENTATION at [[User:Ohconfucius/script/MOSNUM dates]] (click on the link above) before using. | // PLEASE READ THE DOCUMENTATION at [[User:Ohconfucius/script/MOSNUM dates]] (click on the link above) before using. | ||
১৬:০৬, ২৭ এপ্রিল ২০২২ তারিখে সংশোধিত সংস্করণ
/**
* AjaxQuickDelete from <https://commons.wikimedia.org/wiki/MediaWiki:Gadget-AjaxQuickDelete.js>
*
* Changelog:
* - 2010: Created in 2010 by [[User:Ilmari Karonen]] as Ajax-based replacement for [[MediaWiki:Quick-delete-code.js]].
* - Rewritten by [[User:DieBuche]].
* - Botdetection and encoding fixer by [[User:Lupo]].
* - 2011-2012: Validation and further development [[User:Rillke]], 2011-2012.
* - 2018: dropped IE8 support.
*
* TODO:
* - Fix problems with moves of videos.
* - Support move of timed text for videos.
*
* This pages has automated validation on save. Interested? See [[:commons:Commons:User scripts/reports]].
*/
// <nowiki>
/* eslint indent:[error,tab,{outerIIFEBody:0}] */
/* eslint-disable one-var, vars-on-top, camelcase, no-underscore-dangle, valid-jsdoc */
/* global $:false, mw:false, importScript:false */
( function () {
'use strict';
// Guard against multiple inclusions
if ( window.AjaxQuickDelete ) {
return;
}
var AQD,
conf = mw.config.get( [
'wgArticleId',
'wgCanonicalNamespace',
'wgCanonicalSpecialPageName',
'wgCategories',
'wgFormattedNamespaces',
'wgNamespaceNumber',
'wgPageName',
'wgRestrictionEdit',
'wgUserGroups',
'wgUserLanguage',
'wgUserName',
'wgIsRedirect'
] ),
nsNr = conf.wgNamespaceNumber,
pageName = conf.wgPageName;
// A bunch of helper functions
function _firstItem( o ) {
for ( var i in o ) {
if ( Object.prototype.hasOwnProperty.call( o, i ) ) {
return o[ i ];
}
}
}
$.ucFirst = function ( s ) {
return s[ 0 ].toUpperCase() + s.slice( 1 );
};
// Create the AjaxQuickDelete-singleton (object literal)
AQD = window.AjaxQuickDelete = {
// When maintaining this script always bump this number!
version: '1.1.3',
/**
* Runs before document ready and before translation is available
* (important event-binders should be attached as fast as possible)
*/
preinstall: function () {
// Promote our gadget when user opened old move page
if ( conf.wgCanonicalSpecialPageName === 'Movepage' && Number( $( 'select[name="wpNewTitleNs"]' ).val() ) === 6 ) {
$( '#mw-movepage-table' ).before(
'<div class="mw-message-box mw-message-box-warning">Consider using <i>Move & Replace</i> from the menu on file pages (open with a single click) when moving files to care for global usage and redirects.</div>'
);
}
this.doNothing = ( !conf.wgArticleId || nsNr < 0 || /^Commons:Deletion/.test( pageName ) );
if ( this.doNothing ) {
return;
}
// Check user group
if ( conf.wgUserGroups.indexOf( 'sysop' ) !== -1 ) {
this.userRights = 'sysop';
} else if ( conf.wgUserGroups.indexOf( 'filemover' ) !== -1 ) {
this.userRights = 'filemover';
} else {
this.userRights = [ 'autopatrolled', 'patroller', 'image-reviewer' ].filter( function ( g ) {
return conf.wgUserGroups.indexOf( g ) !== -1;
} )[ 0 ];
}
if ( [ 'filemover', 'sysop' ].indexOf( this.userRights ) !== -1 && nsNr === 6 ) {
// Change "Move" to "Move & Replace"
var $moveLink = $( '#ca-move' ),
$moveLanchor = $moveLink.find( 'a' );
this.$moveLink = $moveLink = $moveLanchor.length ? $moveLanchor : $moveLink;
$moveLink.text( $moveLink.text() + ' & Replace' )
.attr( 'title', 'Click in order to ' + $moveLink.attr( 'title' ) + ' and replace usage. Default form though new tab.' )
.on( 'click', function ( e ) {
e.preventDefault();
AQD.moveFile();
} );
}
},
/**
* Set up the AjaxQuickDelete object and add the toolbox link. Called via document.ready during page loading.
*/
install: function () {
// Disallow performing operations on empty or special pages
if ( this.doNothing ) {
return;
}
// Check edit restrictions and do not install anything if protected
if ( conf.wgRestrictionEdit && conf.wgRestrictionEdit.length &&
conf.wgUserGroups.indexOf( conf.wgRestrictionEdit[ 0 ] ) === -1 ) {
return;
}
// Trigger a jQuery event for other scripts that like to know
// when this script has loaded all translations and is ready to install
$( document ).triggerHandler( 'scriptLoaded', [ 'AjaxQuickDelete' ] );
var link;
// Set up toolbox link
if ( nsNr === 14 ) {
// In categories the discuss-category link
link = mw.util.addPortletLink( 'p-tb', '#', this.i18n.toolboxLinkDiscuss, 't-ajaxquickdiscusscat' );
if ( link ) {
link.addEventListener( 'click', function ( e ) {
e.preventDefault();
mw.loader.using( 'jquery.ui' ).then( function () {
AQD.discussCategory();
} );
} );
}
} else {
// On other pages, the nominate-for-deletion link
link = mw.util.addPortletLink( 'p-tb', '#', this.i18n.toolboxLinkDelete, 't-ajaxquickdelete' );
if ( link ) {
link.addEventListener( 'click', function ( e ) {
e.preventDefault();
AQD.nominateForDeletion();
} );
}
}
// Install AjaxMoveButton for filemovers and administrators
if ( this.$moveLink ) {
// Change Move & Replace link to fully localized text
this.$moveLink.html( $( '<span>' ).text( this.i18n.dropdownMove ) );
// Add quicklinks to template
$( '#AjaxRenameLink' ).append( '<a href="javascript:AjaxQuickDelete.moveFile();">' + this.i18n.moveAndReplace + '</a>' )
.append( '<a href="javascript:AjaxQuickDelete.loadAndDeclineRequest(\'move\');" class="ajaxDeleteDeclineMove"><sup> ' + this.i18n.anyDecline + '</sup></a>' );
// Install x-To-DR. See [[Template:X-To-DR]]
$( '#mw-imagepage-content .convert-to-dr' )
.find( '.ctdr-btn-convert' ).on( 'click', this._convertToDR ).show().end()
// Currently filemover rights required
.find( '.ctdr-btn-remove' ).on( 'click', this._removeAnyTag ).show();
} else if ( this.userRights ) {
$( '#mw-imagepage-content .convert-to-dr .ctdr-btn-convert' ).on( 'click', this._convertToDR ).show();
}
// Install "Process Duplicates"-Link (either in template
// or if no template was detected and MediaWiki found dupes, behind the link in the dupe-section)
if ( this.userRights === 'sysop' && nsNr === 6 ) {
var dupeSection = $( '#AjaxDupeProcess' );
if ( dupeSection.length ) {
dupeSection.append( $( '<a>', {
href: '#',
text: this.i18n.processDupes,
style: 'font-weight:bold',
click: function ( e ) {
e.preventDefault();
AQD.processDupes();
}
} ) ).show();
} else {
dupeSection = $( '#mw-imagepage-section-duplicates .mw-imagepage-duplicates' );
if ( dupeSection.length ) {
dupeSection.find( 'li:first' )
.append( $( '<span>', {
style: 'display:none',
id: 'AjaxDupeDestination',
text: dupeSection.find( 'a' ).attr( 'title' )
} ) )
.append( ' ', $( '<sup>' ).append( $( '<a>', {
href: '#',
text: '[' + this.i18n.processDupes + ']',
style: 'background:#CEB',
click: function ( e ) {
e.preventDefault();
AQD.processDupes();
}
} ) ) );
}
}
}
// Extra buttons
// Wait until the user's js was loaded and executed
mw.loader.using( 'user', function () {
if ( mw.user.options.get( 'gadget-QuickDelete' ) ) {
mw.loader.using( 'ext.gadget.QuickDelete' ).always( function () {
AQD.doInsertTagButtons();
} );
}
} );
},
/**
* Ensure that all variables are in a good state
* You must call this method before doing anything!
* TODO: Never override pageName, always clean task queue
*/
initialize: function () {
pageName = conf.wgPageName;
this.tasks = [];
this.destination = undefined;
this.details = undefined;
this.declineReason = undefined;
this.notifyUser = true;
this.watchlist = 'preferences';
},
/**
* If a file exists, exchange the messages (very hackish)
* so the user is prompted to choose another destination
* TODO: Develop an improved solution
*/
fileExists: function () {
this.i18n.moveDestination = this.i18n.moveOtherDestination;
this.moveFile();
},
/**
* For moving files
*/
moveFile: function () {
var o = this;
o.initialize();
mw.loader.using( [ 'jquery.ui' ] ).then( function () {
o.showProgress();
if ( $( '#AjaxRenameLink' ).length ) {
o.possibleDestination = $( '#AjaxRenameDestination' ).text();
o.possibleReason = o.cleanReason( $( '#AjaxRenameReason' ).text() );
}
// Let's be sure we have a fresh token and the latest MIME-Info
o.addTask( 'getMoveToken' );
var linkstoimage = $( '#mw-imagepage-section-linkstoimage' );
if ( $( '#globalusage' ).length || (
linkstoimage.length &&
linkstoimage.find( 'a' ).not( '.mw-redirect' ).length -
linkstoimage.find( '.mw-imagepage-linkstoimage-ns2 a[href^="/wiki/User:OgreBot/Uploads"]' ).length
) ) {
o.inUse = true;
o.addTask( 'chkPreMoveDecline' );
}
o.addTask( 'promptForMoveTarget' );
o.addTask( 'doesFileExist' );
o.fileNameExistsCB = 'fileExists';
o.addTask( 'movePage' );
o.addTask( 'removeTemplate' );
o.addTask( 'queryRedirects' );
o.addTask( 'replaceUsage' );
// finally reload the page to show changed page
o.addTask( 'reloadPage' );
o.nextTask();
} );
},
promptForMoveTargetCB: function ( AQD ) {
if ( AQD.inUse ) {
$( '#AjaxQuestion2' ).prop( 'disabled', true );
}
},
promptForMoveTarget: function () {
var toAppend;
this.showProgress();
if ( mw.user.options.get( 'gadget-RenameLink' ) ) {
toAppend = [ $( '<a>' )
.text( this.i18n.moreInformation )
.on( 'click', function () {
if ( !AQD.rGetPolicy ) {
importScript( 'MediaWiki:RenameRequest.js' );
mw.hook( 'aqd.renamerequest.i18n' ).fire();
}
mw.hook( 'aqd.renamerequest.run' ).fire( { exec: this } );
} ).button( {
icons: { primary: 'ui-icon-script' },
showLabel: true,
text: false
} ).css( {
fontSize: '.6em',
margin: '0',
width: '2.5em',
'float': 'right'
} ), '<br>' ];
}
mw.hook( 'aqd.prompt' ).remove( this.promptForMoveTargetCB ).add( this.promptForMoveTargetCB );
this.prompt( [ {
message: this.i18n.moveDestination,
prefill: this.cleanFileName( this.possibleDestination || pageName ),
returnvalue: 'destination',
appendNode: toAppend,
cleanUp: true,
noEmpty: true
}, {
message: this.i18n.reasonForMove,
prefill: ( ( this.reason || this.possibleReason || '' ).trim().replace( /'{2,}/g, '' ).replace( /\s{2,}/g, ' ' ) ),
returnvalue: 'reason',
cleanUp: true,
noEmpty: false
}, {
message: this.i18n.leaveRedirect,
prefill: true,
returnvalue: 'wpLeaveRedirect',
// cleanUp: false,
noEmpty: false,
type: 'checkbox'
}, {
message: this.i18n.useCORSForReplace,
prefill: !window.aqdCORSOptOut,
returnvalue: 'replaceUsingCORS',
// cleanUp: false,
noEmpty: false,
type: 'checkbox'
}
], this.i18n.movingFile );
},
/* Warn other filemovers */
chkPreMoveDecline: function () {
$( '#mw-imagepage-section-linkstoimage' ).find( 'a' ).each( function () {
if ( $( this ).text() === 'Commons:File renaming/Recently declined rename requests' ) {
// eslint-disable-next-line no-alert
alert( AQD.i18n.warnRename );
return false;
}
} );
this.nextTask();
},
/**
* For loading jquery UI to decline a request
*/
loadAndDeclineRequest: function ( reason ) {
var that = this;
mw.loader.using('jquery.ui').then(function () {
that.declineRequest.call( that, reason );
} );
},
/**
* For declining a request
*/
declineRequest: function ( reason ) {
reason = reason || this.declineReason;
// 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' );
// TODO extend the reason (for summary)
switch ( reason ) {
case 'move':
reason = 'rename request declined: does not comply with [[COM:FR|renaming guidelines]]';
if ( window.AjaxDeclineMoveWatchFile ) {
this.watchlist = 'watch';
}
break;
}
this.prompt( [ {
message: '',
prefill: reason || '',
returnvalue: 'declineReason',
cleanUp: false,
noEmpty: true,
byteLimit: 250
} ], this.i18n.declineRequest );
},
// This method is generlaly used directly by user scripts without going
// through other methods or setup functions. Needs to be standalone,
// and ensures loading of its own dependencies.
insertTagOnPage: function ( tag, img_summary, talk_tag, talk_summary, prompt_text, page, optin_notify ) {
var o = this;
this.initialize();
mw.loader.using( [ 'jquery.ui' ], function () {
o.pageName = ( page || pageName ).replace( /_/g, ' ' );
o.tag = tag.replace( '%USER%', conf.wgUserName ) + '\n';
o.img_summary = img_summary;
// first schedule some API queries to fetch the info we need…
// get token
o.addTask( 'findCreator' );
o.addTask( 'prependTemplate' );
if ( o.isMobile() && /(?:copyvio|nsd|npd|nld)/.test( tag ) ) {
o.addTask( 'listMobileUploadSpeedy' );
}
var prompt = [];
// Cave: insertTagOnPage is inserted as javascript link and therefore talk_tag can be "undefined"/string
if ( talk_tag && talk_tag !== 'undefined' ) {
o.talk_tag = talk_tag.replace( '%FILE%', o.pageName );
o.talk_summary = talk_summary.replace( '%FILE%', '[[:' + o.pageName + ']]' );
o.usersNeeded = true;
prompt.push( {
message: o.i18n.notifyUser,
prefill: true,
returnvalue: 'notifyUser',
type: 'checkbox'
} );
o.addTask( 'notifyUploaders' );
}
o.addTask( 'reloadPage' );
if ( tag.indexOf( '%PARAMETER%' ) !== -1 ) {
prompt.push( {
message: '',
prefill: '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true,
minLength: 1
} );
o.prompt( prompt, prompt_text || o.i18n.reasonForDeletion );
} else if ( optin_notify && prompt.length && o.talk_summary ) {
o.prompt( prompt, o.talk_summary );
} else {
o.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();
// eslint-disable-next-line no-useless-escape
this.tag = '{{subst:cfd}}\n';
this.img_summary = 'This category needs discussion';
// eslint-disable-next-line no-useless-escape
this.talk_tag = '{{subst:cdw|1=' + 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( 'prependTemplate' );
this.addTask( 'createRequestSubpage' );
this.addTask( 'listRequestSubpage' );
this.addTask( 'notifyUploaders' );
// 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 () {
o.pageName = ( page || pageName ).replace( /_/g, ' ' );
o.startDate = new Date();
// set up some page names we'll need later
var requestPage = o.pageName,
mwString = require( 'mediawiki.String' );
// MediaWiki has an ugly limit of 255 bytes per title, excluding the namespace
while ( mwString.byteLength( requestPage ) + mwString.byteLength( o.requestPagePrefix.replace( /^.+?:/, '' ) ) >= 255 ) {
requestPage = requestPage.slice( 0, requestPage.length - 1 ).trim();
}
o.requestPage = o.requestPagePrefix + requestPage;
o.dailyLogPage = o.requestPagePrefix + o.formatDate( 'YYYY/MM/DD' );
o.tag = '{{delete|reason=%PARAMETER%|subpage=' + requestPage + o.formatDate( '|year=YYYY|month=MON|day=DAY}}\n' );
switch ( nsNr ) {
// 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;
case 828: // Lua comments
o.tag = '\n--[=[ ' + o.tag + ' ]=]\n';
}
if ( o.templateReplace ) {
o.declineReason = o.img_summary;
}
o.img_summary = 'Nominating for deletion';
// eslint-disable-next-line no-useless-escape
o.talk_tag = '{{subst:idw|1=' + requestPage + '}}';
o.talk_summary = '[[:' + o.pageName + ']] has been nominated for deletion';
o.subpage_summary = 'Starting deletion request';
// without \n it breaks the redirect syntax
if ( conf.wgIsRedirect ) {
o.tag += '\n';
}
// first schedule some API queries to fetch the info we need…
o.addTask( 'findCreator' );
// …then schedule the actual edits
o.addTask( o.templateReplace ? 'replaceTemplate' : 'prependTemplate' );
o.addTask( 'createRequestSubpage' );
o.addTask( 'listRequestSubpage' );
o.addTask( 'purge' );
o.addTask( 'notifyUploaders' );
if ( o.isMobile() ) {
o.addTask( 'listMobileUpload' );
}
// 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.prevDRNode = $( '<ul>' ).attr( 'id', 'AjaxDeletePrevRequests' );
o.secureCall( 'checkForFormerDR' );
var toAppend = $( '<div>' ).append( $( '<div>' ).attr( 'class', 'ajaxDeleteLazyLoad' ).css( {
'max-height': Math.max( Math.round( $( window ).height() / 2 ) - 250, 100 ),
'min-height': 0,
overflow: 'auto'
} ).append( o.prevDRNode ), '<br>', lazyLoadNode );
o.prompt( [ {
message: '',
prefill: o.reason || '',
returnvalue: 'reason',
cleanUp: true,
noEmpty: true,
appendNode: toAppend,
parseReason: true
}
], o.i18n.reasonForDeletion );
} );
},
// Check whether there was a deletion request for the same title in the past
checkForFormerDR: function () {
// Don't search for "kept" when nominating talk pages
if ( nsNr % 2 === 0 ) {
this.talkPage = conf.wgFormattedNamespaces[ nsNr + 1 ] + ':' + this.pageName.replace( conf.wgCanonicalNamespace + ':', '' );
this.queryAPI( {
prop: 'templates',
titles: this.talkPage,
tltemplates: 'Template:Kept',
tllimit: 1
}, 'formerDRTalk' );
}
this.queryAPI( {
list: 'backlinks',
bltitle: this.pageName,
blnamespace: 4,
blfilterredir: 'nonredirects',
bllimit: 500
}, 'formerDRRequestpage' );
},
formerDRTalk: function ( r ) {
var pgs = r.query.pages;
$.each( pgs, function ( id, pg ) {
if ( Array.isArray( pg.templates ) ) {
$( '<li>' ).append( $( '<a>', {
text: AQD.i18n.keptAfterDR,
href: mw.util.getUrl( AQD.talkPage )
} ) ).prependTo( AQD.prevDRNode );
} else if ( pg.missing === undefined ) {
$( '<li>' ).append( $( '<a>', {
text: AQD.i18n.hasTalkpage,
href: mw.util.getUrl( AQD.talkPage )
} ) ).appendTo( AQD.prevDRNode );
}
} );
},
formerDRRequestpage: function ( r ) {
var bls = r.query.backlinks,
_addItem = function ( t, m, bl ) {
$( '<li>' ).append( $( '<a>', {
text: t.replace( '%PAGE%', bl.title ),
href: mw.util.getUrl( bl.title )
} ) )[ m ]( AQD.prevDRNode );
};
$.each( bls, function ( i, bl ) {
if ( this.requestPage === bl.title ) {
_addItem( AQD.i18n.mentionedInDR, 'prependTo', bl );
} else if ( /^Commons:Deletion requests\/\D/.test( bl.title ) ) {
_addItem( AQD.i18n.mentionedInDR, 'appendTo', bl );
} else if ( /^Commons:Village pump\//.test( bl.title ) ) {
_addItem( AQD.i18n.mentionedInForum, 'appendTo', bl );
}
} );
},
renderNode: function ( $node, remotecontent, selector ) {
if ( selector ) {
selector = ' ' + selector;
}
$node.load( mw.util.wikiScript() + '?' + $.param( {
action: 'render',
title: remotecontent,
uselang: conf.wgUserLanguage
} ) + ( selector || '' ), function () {
$node.find( 'a' ).attr( 'href', function ( i, v ) {
return v.replace( 'MediaWiki:Anoneditwarning', conf.wgPageName );
} );
} );
return $node;
},
createLazyLoadNode: function ( label, page, selector ) {
return $( '<div>', { style: 'min-height:40px;' } ).append( $( '<a>', {
href: '#',
text: label
} ).on( 'click', function ( e ) {
e.preventDefault();
var $content = $( this ).parent().find( '.ajaxDeleteLazyLoad' ),
$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>', {
// eslint-disable-next-line quote-props
'class': 'ajaxDeleteLazyLoad',
style: 'display:none;'
} ).append( $( '<span>', {
// eslint-disable-next-line quote-props
'class': 'ajax-quick-delete-loading',
text: this.i18n.loading
} ).data( 'aqdPage', page ) ) );
},
extractFromHTML: function ( $el ) {
$el = $( $el ).parent();
// …extract the regular expression from html
this.templateRegExp = $el.find( '.ctdr-regex' ).text();
var m = this.templateRegExp.match( /^\/(.+)\/(i)?$/ );
if ( !m || !m[ 1 ] ) {
m = new Error( this.i18n.templateRegExp );
this.fail( m );
throw m;
}
this.templateRegExp = new RegExp( m[ 1 ], m[ 2 ] );
// …and the decline reason
this.declineReason = $el.find( '.ctdr-template-decline-reason' ).text();
// …and the template name itself
m = $el.find( '.ctdr-template-name' ).text();
this.reason = 'This file was initially tagged by %USER%' + ( m ? ( ' as \'\'\'' + m + '\'\'\'' ) : '' );
},
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( e.currentTarget || 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 ) {
e = e.currentTarget || this;
AQD.extractFromHTML( e );
AQD.convertToDR( e );
return false;
},
convertToDR: function ( el ) {
// reset task list in case an earlier error left it non-empty
this.initialize();
this.declineReason = 'This file does not qualify for [[COM:SPEEDY|speedy-deletion]] and a regular deletion request will be started.';
this.templateReplace = true;
// first schedule a API query to fetch the info we need…
this.addTask( 'findTemplateAdder' );
this.addTask( 'getMoveToken' );
// prompt before conversion for user decision
this.addTask( 'removeTemplate' );
this.addTask( 'removeProgress' );
this.addTask( 'nominateForDeletion' );
// Hide the buttons to prevent attempts of duplicate removal
$( el ).closest( '.convert-to-dr' ).hide();
// … and go!
this.nextTask();
},
findTemplateAdder: function () {
var query = {
prop: 'revisions',
rvprop: 'content|user',
titles: pageName.replace( /_/g, ' ' ),
rvlimit: 50
};
this.queryAPI( query, 'findTemplateAdderCB' );
},
findTemplateAdderCB: function ( result ) {
var reason,
user,
pgRevs, // for debug
template;
$.each( result.query.pages, function ( id, pg ) {
pgRevs = pg.revisions;
pgRevs.forEach( function ( rv ) {
var 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 ) {
mw.log.warn( pgRevs );
throw new Error( this.i18n.findTemplateAdderErr );
}
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' );
this.addTask( 'replaceUsage' );
this.addTask( 'queryRedirects' );
this.addTask( 'deletePage' );
this.addTask( 'redirectPage' );
this.addTask( 'reloadPage' );
this.destination = $( '#AjaxDupeDestination' ).text();
this.nextTask();
},
getDupeDetails: function () {
this.queryAPI( {
curtimestamp: 1,
meta: 'tokens',
prop: 'imageinfo|revisions|info',
rvprop: 'content|timestamp',
inprop: 'watched',
iiprop: 'sha1|size|url',
iiurlwidth: 365,
redirects: 1,
titles: pageName.replace( /_/g, ' ' ) + '|' + this.destination
}, 'getDupeDetailsCB' );
this.showProgress( 'Fetching details' );
},
getDupeDetailsCB: function ( result ) {
this.details = [];
if ( result ) {
var q = result.query,
id,
pg,
ii,
n,
pages = q.pages;
for ( id in pages ) {
if ( Object.prototype.hasOwnProperty.call( pages, id ) ) {
pg = pages[ id ];
if ( !pg.imageinfo ) {
// Nothing we can change so prevent users reporting
this.disableReport = true;
throw new Error( ( ( pg.title.trim() === '{{{1}}}' ) ?
this.i18n.dupeParaErr :
this.i18n.dupeExistErr.replace( '%TITLE%', pg.title ) ) + ' (pg.imageinfo is undefined)' );
}
ii = pg.imageinfo[ 0 ];
n = {
title: pg.title,
size: ii.size,
width: ii.width,
height: ii.height,
thumburl: ii.thumburl,
thumbwidth: ii.thumbwidth,
thumbheight: ii.thumbheight,
descriptionurl: ii.descriptionurl,
sha1: ii.sha1,
content: pg.revisions[ 0 ][ '*' ],
starttimestamp: result.curtimestamp
};
this.details.push( n );
this.csrftoken = q.tokens.csrftoken;
if ( pg.watched !== undefined ) {
this.pageWasWatched = true;
}
}
}
}
if ( this.details.length < 2 ) {
this.disableReport = true;
throw new Error( this.i18n.noPageFound );
}
// If order (old=0, new=1) is incorrect: Reverse
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 = {
title: this.pageName,
text: this.tag,
editType: 'prependtext',
watchlist: window.AjaxDeleteWatchFile ? 'watch' : this.watchlist,
minor: false
};
this.showProgress( this.i18n.addingAnyTemplate );
this.savePage( page, this.img_summary, 'nextTask' );
},
/**
* Edit the current page to add the specified tag and the changed content.
*/
replaceTemplate: function ( text ) {
var page = {
title: this.destination || pageName,
text: text || this.tag + this.pageContent,
editType: 'text',
starttimestamp: this.starttimestamp,
timestamp: this.timestamp,
watchlist: window.AjaxDeleteWatchFile ? 'watch' : this.watchlist,
minor: false
};
this.templateReplace = false;
this.showProgress( this.i18n.addingAnyTemplate );
this.savePage( page, ( this.declineReason || 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 = {
title: this.requestPage,
// eslint-disable-next-line no-useless-escape
text: '\n=== [[:' + this.pageName + ']] ===\n' + this.reason + ' ~~~~\n',
watchlist: 'watch',
editType: 'appendtext'
};
if ( this.isMobile() ) {
page.text += '\n<noinclude>[[Category:MobileUpload-related deletion requests]]</noinclude>';
}
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 = 'preferences';
page.editType = 'appendtext';
this.showProgress( this.i18n.listingNomination );
this.savePage( page, 'Listing [[' + this.requestPage + ']]', 'nextTask' );
},
isMobile: function () {
var isMobile = false,
cats = conf.wgCategories;
for ( var i = 0, len = cats.length; i < len; i++ ) {
isMobile = isMobile || /^Uploaded with Mobile/.test( cats[ i ] );
}
return isMobile;
},
listMobileUpload: function () {
var page = {
title: 'Commons:Deletion requests/mobile tracking',
text: '\n{{' + this.requestPage + '}}\n',
watchlist: 'preferences',
editType: 'appendtext'
};
this.showProgress( this.i18n.listingMobile );
this.savePage( page, 'Listing [[' + this.requestPage + ']]', 'nextTask' );
},
listMobileUploadSpeedy: function () {
var page = {
title: 'Commons:Mobile app/deletion request tracking',
text: '\n# [[:' + this.pageName + ']]',
watchlist: 'preferences',
editType: 'appendtext'
};
this.showProgress( this.i18n.listingMobile );
this.savePage( page, 'Listing [[' + this.pageName + ']]', 'nextTask' );
},
/**
* Check the users talkpage is not blocked indefinite
*/
verifyUserNotify: function ( user ) {
this.queryAPI( {
prop: 'info',
titles: this.userTalkPrefix + user,
redirects: 1,
inprop: 'protection'
}, 'verifyUserNotifyCB' );
},
verifyUserNotifyCB: function ( result ) {
var page;
this.notifyUser = true; // reset
if ( !result || !result.query || !result.query.pages ) {
mw.log.warn( 'Verify user: result.query.pages is undefined. ', result );
return this.uploaderNotified();
}
result = result.query.pages;
for ( var pg in result ) {
pg = result[ pg ];
page = pg.title;
pg = pg.protection;
if ( pg ) {
for ( var p = 0; p < pg.length; p++ ) {
var pt = pg[ p ];
if ( pt && pt.type === 'edit' && pt.level === 'sysop' ) {
// Disable report for protected userpages
this.disableReport = true;
// Disable notify for indefinite protected
if ( pt.expiry === 'infinity' ) {
this.notifyUser = false;
}
}
}
}
}
if ( this.notifyUser && page ) {
page = {
title: page,
// eslint-disable-next-line no-useless-escape
text: '\n' + this.talk_tag + ' ~~~~\n',
editType: 'appendtext',
redirect: true,
minor: false
};
if ( window.AjaxDeleteWatchUserTalk ) {
page.watchlist = 'watch';
}
this.savePage( page, this.talk_summary, 'uploaderNotified' );
} else {
this.uploaderNotified();
}
},
/**
* Notify any uploaders/creators of this page using {{idw}}.
*/
notifyUploaders: function () {
this.uploadersToNotify = 0;
if ( this.notifyUser ) {
for ( var user in this.uploaders ) {
if ( Object.prototype.hasOwnProperty.call( this.uploaders, user ) ) {
if ( user === conf.wgUserName ) {
// notifying yourself is pointless
continue;
}
this.verifyUserNotify( user );
this.showProgress( this.i18n.notifyingUploader.replace( '%USER%', user ) );
this.uploadersToNotify++;
}
}
}
if ( !this.uploadersToNotify ) {
this.nextTask();
}
},
uploaderNotified: function () {
this.uploadersToNotify--;
if ( !this.uploadersToNotify ) {
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
* DONE: notify fileimporter (2019-09-09)
*/
findCreator: function () {
var q = {
curtimestamp: 1,
meta: 'tokens',
prop: 'revisions',
titles: this.pageName,
rvprop: 'timestamp|user',
rvdir: 'newer',
rvslots: 'main',
rvlimit: 1
};
if ( nsNr === 6 ) {
$.extend( q, {
prop: q.prop + '|imageinfo',
rvprop: q.rvprop + '|content',
iiprop: 'user|comment|sha1',
iilimit: '50',
list: 'logevents',
letype: 'import',
leprop: 'user',
letitle: this.pageName
} );
}
this.showProgress( this.i18n.preparingToEdit );
this.queryAPI( q, 'findCreatorCB' );
},
findCreatorCB: function ( r ) {
this.uploaders = {};
if ( !r || !r.query || !r.query.pages ) {
this.disableReport = true;
throw new Error( this.i18n.noPageFound );
}
var q = r.query,
pg = _firstItem( q.pages ),
rv;
if ( !pg || !pg.revisions ) {
throw new Error( this.i18n.noCreatorFound );
}
// The csrftoken only changes between sessions
this.csrftoken = q.tokens.csrftoken;
rv = pg.revisions[ 0 ];
// First handle non-file pages
if ( nsNr !== 6 || !pg.imageinfo ) {
this.starttimestamp = r.curtimestamp;
this.timestamp = rv.timestamp;
if ( ( this.pageCreator = rv.user ) ) {
this.uploaders[ this.pageCreator ] = true;
}
} else {
var info = pg.imageinfo,
i = info.length,
content = rv.slots.main[ '*' ],
seenHashes = {};
// Iterate in reverse order (sha1 check)
for ( --i; i >= 0; i-- ) {
var iii = info[ i ],
rev = seenHashes[ iii.sha1 ];
// Skip reverts and remove users
if ( iii.sha1 && rev ) {
for ( rev; rev > i; rev-- ) {
var u = info[ rev - 1 ].user;
if ( this.uploaders[ u ] < rev ) {
delete this.uploaders[ u ];
}
}
continue;
}
seenHashes[ iii.sha1 ] = i + 1;
// No need to check again the whole shebang
if ( this.uploaders[ iii.user ] ) {
continue;
}
// Now exclude bots which only reupload a new version:
if ( mw.libs.commons.isSmallChangesBot( iii.user ) ) {
continue;
}
// Outsourced to MediaWiki:Gadget-libCommons.js
iii = mw.libs.commons.getUploadBotUser( iii.user, content, iii.comment, rv.user );
if ( iii ) {
this.uploaders[ iii ] = i + 1;
}
}
// Get fileimporter
if ( ( info = q.logevents ) && ( i = info.length ) ) {
for ( --i; i >= 0; i-- ) {
if ( info[ i ].user ) {
this.uploaders[ info[ i ].user ] = true;
}
}
}
}
this.nextTask();
},
getMoveToken: function () {
this.showProgress( this.i18n.preparingToEdit );
var query = {
curtimestamp: 1,
prop: 'info|revisions',
meta: 'tokens',
rvprop: 'content|timestamp',
inprop: 'watched',
titles: this.pageName || pageName.replace( /_/g, ' ' )
};
if ( !this.declineReason ) {
query.prop += '|imageinfo';
query.iiprop = 'mediatype|mime|timestamp';
}
this.queryAPI( query, 'getMoveTokenCB' );
},
/**
* @brief [callback] Prepare page for saving before
* @param [in] result of query
* @return csrftoken, pageContent, starttimestamp, timestamp, imagetimestamp, mimeFileExtension, pageWasWatched
*/
getMoveTokenCB: function ( result ) {
var q = result.query || {},
pg = _firstItem( q.pages );
if ( !pg || !pg.revisions ) {
this.disableReport = true;
throw new Error( this.i18n.noPageFound );
}
// The csrftoken only changes between sessions
$.extend( this, {
csrftoken: q.tokens.csrftoken,
pageContent: pg.revisions[ 0 ][ '*' ],
starttimestamp: result.curtimestamp,
timestamp: pg.revisions[ 0 ].timestamp
} );
if ( pg.watched !== undefined ) {
this.pageWasWatched = true;
}
var ii = pg.imageinfo;
if ( ii && ii.length && ii[ 0 ].mime ) {
ii = ii[ 0 ];
this.imagetimestamp = ii.timestamp;
this.mimeFileExtension = ii.mime
.toLowerCase()
.replace( 'image/jpeg', 'jpg' )
.replace( /image\/(?:x-|vnd\.)?(png|gif|xcf|djvu|svg|tiff)(?:\+xml)?/, '$1' )
.replace( /application\/(ogg|pdf)/, '$1' )
.replace( /video\/(webm)/, '$1' )
.replace( 'audio/midi', 'mid' )
.replace( /audio\/(?:x-|vnd\.)?wave?/, 'wav' )
.replace( /audio\/(?:x-)?flac/, 'flac' );
if ( this.mimeFileExtension.length > 5 ) {
this.mimeFileExtension = '';
} else if ( this.mimeFileExtension === 'ogg' ) {
switch ( ii.mediatype ) {
case 'AUDIO':
this.mimeFileExtension = 'oga';
break;
case 'VIDEO':
this.mimeFileExtension = 'ogv';
break;
}
}
}
this.nextTask();
},
doesFileExist: function () {
if ( !this.destination ) {
// eslint-disable-next-line no-alert
return alert( this.i18n.moveDestination );
}
this.destination = this.cleanFileName( this.destination );
var query = {
prop: 'info|revisions',
titles: this.destination,
rvprop: 'content',
rvlimit: 2
};
// usually you would use 'redirects': 1, to detect the redirect target but
// in this case you would get the revisions for the target and not the redirect
this.showProgress( this.i18n.checkFileExists );
this.queryAPI( query, 'doesFileExistCB' );
},
/**
* Return nextTask if the page does not exist
* or it is a redirect with one revision to the source
*/
doesFileExistCB: function ( result ) {
if ( !result || !result.query || !result.query.pages ) {
throw new Error( 'Checking filename: result.query.pages is undefined. ' + this.destination );
}
var exists = true,
pg = _firstItem( result.query.pages ),
getRedirRegExp = function ( title ) {
title = title.replace( /^(File|Image):/, '' ).replace( /_/g, ' ' );
return new RegExp( '^\\s*#REDIRECT\\s*\\[\\[File\\:[' +
mw.util.escapeRegExp( title[ 0 ].toUpperCase() ) + mw.util.escapeRegExp( title[ 0 ].toLowerCase() ) + ']' +
mw.util.escapeRegExp( title.slice( 1 ) ).replace( / /g, '[ _]' ) +
'\\s*\\]\\]',
'' );
};
if ( pg.missing !== undefined ) {
exists = false;
} else if ( !pg.revisions || pg.revisions.length === 1 && getRedirRegExp( pageName )
.test( pg.revisions[ 0 ][ '*' ].replace( 'Image:', 'File:' ) ) ) {
// There seems to be no way to find out whether a title is a redirect
// and whether the redirect only consists of one revision
exists = false;
}
if ( exists ) {
if ( this.fileNameExistsCB ) {
this[ this.fileNameExistsCB ]( pg.title.replace( /^File:/, '' ) );
}
return;
}
this.nextTask();
},
removeTemplate: function () {
this.replaceWith = ( this.replaceWith || ( this.templateRegExp ? '' : '$1$2' ) );
// Remove the template from the text. In case there is an empty line before, remove this also.
var newText = this.pageContent
.replace(
( this.templateRegExp || /(?:([^=])\n)?\{\{(?:rename|rename media|move)\s*\|.*?\}\}(?:\n([^=]))?/i ),
this.replaceWith
);
if ( newText === this.pageContent ) {
return this.nextTask();
}
this.showProgress( this.i18n.removingTemplate );
newText = this.pageContent = newText.trim();
// We have another save request
if ( this.templateReplace ) {
this.img_summary = this.declineReason;
return this.nextTask();
}
if ( !this.declineReason ) {
this.img_summary = 'Removing template; rename done';
}
// If nothing remains, add the no-license-template (this also to prevent abuse filter blocking this edit because of page blanking)
this.replaceTemplate( newText || '{{subst:nld}}' );
},
replaceUsage: function () {
if ( !this.inUse ) {
return this.nextTask();
}
this.showProgress( this.i18n.replacingUsage );
var reasonShort = '[[COM:Duplicate|Duplicate]]:';
if ( !this.details ) {
AQD.reason = AQD.reason.replace( /\[\[Commons:File[_ ]renaming[^[\]]*\]\]:? ?/i, '' );
reasonShort = '[[COM:FR|File renamed]]:';
}
mw.loader.using( 'ext.gadget.libGlobalReplace', function () {
if ( AQD.replaceUsingCORS ) {
mw.libs.globalReplace( pageName, AQD.destination, reasonShort, AQD.reason )
.fail( function ( err ) {
throw new Error( err );
} )
.done( function () {
AQD.nextTask();
} )
.progress( function ( r ) {
AQD.showProgress( r );
mw.log( r );
} );
} else {
mw.libs.globalReplaceDelinker( pageName, AQD.destination, reasonShort + ' ' + AQD.reason, function () {
AQD.nextTask();
}, function ( err ) {
throw new Error( err );
} );
}
} );
},
redirectPage: function () {
var page = {
title: pageName,
text: '#REDIRECT [[' + this.destination + ']]',
editType: 'text',
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences'
};
this.showProgress( this.i18n.redirectingFile );
this.savePage( page, 'Redirecting to duplicate file', 'nextTask' );
},
saveDescription: function () {
var page = {
title: this.destination,
text: this.newPageText,
editType: 'text',
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences'
};
this.showProgress( this.i18n.savingDescription );
this.savePage( page, 'Merging details from duplicate ([[' + pageName + ']])', 'nextTask' );
},
/**
* Updates the redirects to the current page
* when moving or processing dupes immediately
* to prevent double redirects
*/
queryRedirects: function () {
mw.loader.using( 'mediawiki.api' ).then( function () {
return new mw.Api().loadMessagesIfMissing( [
'Whatlinkshere'
] );
} ).then( function () {
AQD.showProgress( mw.msg( 'Whatlinkshere' ) );
// TODO: Replace also the redirects inclusions!?
AQD.queryAPI( {
generator: 'backlinks',
gblfilterredir: 'redirects',
prop: 'revisions',
rvprop: 'content',
gbltitle: AQD.pageName || pageName.replace( /_/g, ' ' )
}, AQD.queryRedirectsCB ?
AQD.queryRedirectsCB :
'updateRedirects' );
} );
},
updateRedirects: function ( result ) {
AQD.redirectsToUpdate = 0;
if ( result.query && result.query.pages ) {
this.showProgress( this.i18n.updRedir );
$.each( result.query.pages, function ( id, pg ) {
var rv = pg.revisions[ 0 ];
if ( !rv || !rv[ '*' ] ) {
return;
}
// Update only redirects with same mimetype
if ( AQD.checkFileExt( pg.title, AQD.destination, true ) ) {
return mw.log( 'Redirect skipped, not same mimetype.', pg.title );
}
var page = {
title: pg.title,
text: rv[ '*' ].replace( /#\s*REDIRECT\s*\[\[.+/, '#REDIRECT [[' + AQD.destination + ']]' ),
editType: 'text',
watchlist: 'preferences'
};
AQD.savePage( page, 'Updating redirect while processing [[' + pageName.replace( /_/g, ' ' ) + ']]', 'updateRedirectsCB' );
AQD.redirectsToUpdate++;
} );
}
if ( !AQD.redirectsToUpdate ) {
AQD.nextTask();
}
},
updateRedirectsCB: function () {
AQD.redirectsToUpdate--;
if ( !AQD.redirectsToUpdate ) {
AQD.nextTask();
}
},
/**
* Pseudo-Modal JS windows.
*/
prompt: function ( questions, title, width ) {
var o = this,
dlgButtons = {};
dlgButtons[ this.i18n.submitButtonLabel ] = function () {
questions.forEach( function ( v, i ) {
var response = document.getElementById( 'AjaxQuestion' + i );
response = ( v.type === 'checkbox' ) ? response.checked : response.value;
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 )
.replace( '%PARAMETER-LINKED%', '[[:' + response + ']]' );
}
} );
$( this ).dialog( 'close' );
AQD.nextTask();
};
dlgButtons[ this.i18n.cancelButtonLabel ] = function () {
$( this ).dialog( 'close' );
};
var $submitButton,
$AjaxDeleteContainer = $( '<div>', { id: 'AjaxDeleteContainer' } ),
_parseReason = function () {
var $el = $( this ),
$parserResultNode = $el.data( 'parserResultNode' );
if ( !$parserResultNode ) {
return;
}
$parserResultNode.css( 'color', '#877' );
var _gotParsedText = function ( r ) {
try {
$parserResultNode.html( r );
$parserResultNode.css( 'color', '#000' );
} catch ( ex ) {}
};
mw.loader.using( [ 'ext.gadget.libAPI' ], function () {
mw.libs.commons.api.parse( $el.val(), conf.wgUserLanguage, pageName, _gotParsedText );
} );
},
_validateInput = function ( event ) {
var $el = $( this ),
v = $el.data( 'v' );
if ( v.noEmpty ) {
$submitButton.button( 'option', 'disabled', $el.val().trim().length < ( v.minLength || 8 ) );
}
if (
( $el.prop( 'nodeName' ) !== 'TEXTAREA' ) &&
( event.which === 13 ) &&
( v.enterToSubmit !== false ) &&
!$submitButton.button( 'option', 'disabled' )
) {
$submitButton.trigger( 'click' );
}
},
_convertToTextarea = function () {
var $el = $( this ),
$input = $el.data( 'toConvert' ),
$tarea = $( '<textarea>', {
id: $input.attr( 'id' ),
style: 'height:10em; width:98%; display:none;'
} );
$el.off().fadeOut();
$input.parent().prepend(
$tarea
.data( 'v', $input.data( 'v' ) ).data( 'parserResultNode', $input.data( 'parserResultNode' ) )
.val( $input.val() ).on( 'keyup', _parseReason ).on( 'keyup input', _validateInput ) );
$tarea.slideDown();
$input.remove();
};
questions.forEach( function ( v, i ) {
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>' );
}
$AjaxDeleteContainer.append( v.appendNode ? v.appendNode : '<br>' );
if ( typeof v.byteLimit === 'number' ) {
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.prop( 'checked', v.prefill ).attr( 'style', 'margin-left: 5px' );
}
} );
if ( mw.user.isAnon() ) {
AQD.renderNode( $( '<div>', { id: 'ajaxDeleteAnonwarning' } ), 'MediaWiki:Anoneditwarning' ).appendTo( $AjaxDeleteContainer );
}
$( '<div>' ).append( $AjaxDeleteContainer ).dialog( {
width: ( width || 600 ),
modal: true,
title: title,
dialogClass: 'wikiEditor-toolbar-dialog',
close: function () {
$( this ).dialog( 'destroy' ).remove();
if ( AQD.currentTask === 'formerDRRequestpage' ) {
mw.util.$content.find( '.convert-to-dr' ).show();
}
},
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 ).specialButton( 'proceed' );
$buttons.eq( 1 ).specialButton( 'cancel' );
}
} );
questions.forEach( function ( v, i ) {
var curQuestion = $AjaxDeleteContainer.find( '#AjaxQuestion' + i );
curQuestion.trigger( 'keyup' );
if ( v.type === 'text' ) {
var $q = curQuestion.wrap( '<div style="position:relative;">' ).parent(),
$i = $.createIcon( 'ui-icon-arrow-4-diag' ).attr( 'title', AQD.i18n.expandToTextarea );
$( '<span>', {
// eslint-disable-next-line quote-props
'class': 'ajaxTextareaConverter' } ).append( $i ).appendTo( $q ).data( 'toConvert', curQuestion ).on( 'click', _convertToTextarea );
}
} );
$( '#AjaxQuestion0' ).trigger( 'focus' ).trigger( 'select' );
mw.hook( 'aqd.prompt' ).fire( o );
},
/**
* Open a jQuery dialog with preview-images and some options
* and information to compare the two files
*/
compareDetails: function () {
var d = this.details[ 0 ],
f = this.details[ 1 ],
$swapButton,
$overlayButton;
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 ) + ' KiB <br>' + d.width + '×' + d.height + '<br>'
} ).append(
$( '<a>', {
href: d.descriptionurl,
text: d.title,
target: '_blank'
} ) ) ),
$imgF = $( '<div>' ).append( $( '<img>', {
src: f.thumburl,
height: f.thumbheight,
width: f.thumbwidth
} ), $( '<div>', {
id: 'AjaxDeleteImgKeep',
html: Math.round( f.size / 1000 ) + ' KiB <br>' + f.width + '×' + f.height + '<br>'
} ).append(
$( '<a>', {
href: f.descriptionurl,
text: f.title,
target: '_blank'
} ) ) ),
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();
AQD.inUse = true;
setTimeout( function () {
AQD.compareDetails();
}, 20 );
};
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, left: pos.left - 1
} )
.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)',
width: 'auto',
border: '1px solid #0c9'
} ).draggable();
$fClone.find( 'img' ).resizable();
// In IE, opacity is not fully inerhited
$fClone.children( 'div' ).fadeTo( 0, 0.7 );
} );
}
};
this.showProgress();
var $AjaxDupeContainer = $( '<div>', { id: 'AjaxDupeContainer' } ).append( $imgD, $imgF );
$( '<div>' ).append( $AjaxDupeContainer ).dialog( {
width: 800,
modal: true,
title: this.i18n.compareDetails,
draggable: false,
dialogClass: 'wikiEditor-toolbar-dialog',
close: function () {
$( this ).dialog( 'destroy' ).remove();
},
buttons: dlgButtons,
open: function () {
var $buttons = $( this ).parent().find( '.ui-dialog-buttonpane button' );
$buttons.eq( 0 ).specialButton( 'proceed' );
$buttons.eq( 1 ).button( { icons: { primary: 'ui-icon-refresh' } } );
$swapButton = $buttons.eq( 2 ).button( { icons: { primary: 'ui-icon-transfer-e-w' } } );
$overlayButton = $buttons.eq( 3 ).button( { icons: { primary: 'ui-icon-newwin' } } );
$swapButton.css( 'float', ( ( $swapButton.css( 'float' ) === 'left' ) ? 'right' : 'left' ) );
$overlayButton.css( 'float', ( ( $overlayButton.css( 'float' ) === 'left' ) ? 'right' : 'left' ) );
}
} );
mw.loader.load( [ 'ext.gadget.libGlobalReplace', 'ext.gadget.libWikiDOM' ] );
},
mergeDescriptions: function () {
var newPageText = this.details[ 1 ].content;
mw.loader.using( [ 'ext.gadget.libGlobalReplace', 'ext.gadget.libWikiDOM' ], function () {
newPageText = mw.libs.wikiDOM.nowikiEscaper( newPageText ).doCleanUp();
AQD.showProgress();
AQD.prompt( [ {
message: '',
prefill: AQD.details[ 0 ].content,
returnvalue: 'discard',
cleanUp: false,
noEmpty: false,
type: 'textarea',
enterToSubmit: false
}, {
message: '',
prefill: newPageText,
returnvalue: 'newPageText',
cleanUp: false,
noEmpty: false,
type: 'textarea',
enterToSubmit: false
}, {
message: AQD.i18n.useCORSForReplace,
prefill: !window.aqdCORSOptOut,
returnvalue: 'replaceUsingCORS',
// cleanUp: false,
noEmpty: false,
type: 'checkbox'
}
], AQD.i18n.mergeDescription, 800 );
AQD.destination = AQD.details[ 1 ].title;
AQD.reason = 'Exact or scaled-down duplicate: [[:' + AQD.destination + ']]';
} );
},
/**
* Correct the MIME-Type; Accepts only valid filenames (with extension)
* Either a filename is passed or the destination property is used
*/
correctMIME: function ( fn ) {
// If the current mime-type is available to the script, check it;
// MediaWiki sometimes allows uploading mismatching mimetypes but not moving
var f = fn || this.destination;
if ( this.mimeFileExtension ) {
f = f.replace( /\.\w{2,5}$/, '.' + this.mimeFileExtension );
}
if ( !fn ) {
this.destination = f;
return this.nextTask();
} else {
return f;
}
},
cleanFileName: function ( fn, ignoreMIME ) {
// Remove Namespace
fn = fn.replace( /^(?:Image|File):/i, '' )
// Convert extension to lower case
.replace( /(\.\w{2,5})+$/, function ( $e ) {
return $e.toLowerCase();
} )
// jpeg -> jpg
.replace( /\.jpe*g$/, '.jpg' )
// First cleanUp from Flinfo (FlinfoOut.php) by Flominator and Lupo
.replace( /~{3,}/g, '' ) // "signature"
.replace( /[\u00A0\u1680\u180E\u2000-\u200B\u2028\u2029\u202F\u205F\u3000]/, ' ' ) // remove NBSP and other unusual spaces
.replace( /\s+|_/g, ' ' ) // (multiple) whitespace
// eslint-disable-next-line no-control-regex
.replace( /[\x00-\x1f\x7f]/g, '' )
.replace( /%([0-9A-Fa-f]{2})/g, '% $1' ) // URL encoding stuff
.replace( /&(([A-Za-z0-9\x80-\xff]+|#[0-9]+|#x[0-9A-Fa-f]+);)/g, '& $1' ) // URL-params?
.replace( /''/g, '"' )
.replace( /[:/|#]/g, '-' )
.replace( /[\]}>]/g, ')' )
.replace( /[[{<]/g, '(' );
fn = this.checkFileExt( pageName, fn, ignoreMIME ) || fn;
// Capitalize the first letter and prefix the namespace
return 'File:' + $.ucFirst( fn ); // own prototype
},
/**
* @brief Compare mimetype
* @param [in] of old filename
* @param [in] fn new filename
* @param [in] boolean
* @return false if same, otherwise new file with old extension
*/
checkFileExt: function ( of, fn, ignoreMIME ) {
var currentExt = ( !ignoreMIME && this.mimeFileExtension ) ?
this.mimeFileExtension :
of.replace( /.*?\.(\w{2,5})$/, '$1' ).toLowerCase().replace( 'jpeg', 'jpg' ),
reCurrentExt = new RegExp( '\\.' + mw.util.escapeRegExp( currentExt ) + '$', 'i' ),
reDestExt = new RegExp( '\\.' + mw.util.escapeRegExp( fn.replace( /.*?\.(\w{2,5})$/, '$1' ) ) + '$', 'i' );
// If new filename is without (same) extension, add the one from the old name
if ( !reCurrentExt.test( fn ) ) {
// First, try to replace the old extension
fn = fn.replace( reDestExt, '.' + currentExt );
if ( !reCurrentExt.test( fn ) ) {
// If this did not work, then simply append the old extension
fn += '.' + currentExt;
}
} else {
fn = false;
} // is equal
return fn;
},
cleanReason: function ( uncleanReason ) {
return uncleanReason
.trim() // whitespace
.replace( /(?:--|–|—)? ?~{3,5} ?/, '' ) // remove signature
.replace( /\|\s/g, '| ' ); // pipes
},
/**
* 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>' ).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',
open: function () {
$( this ).parent().find( '.ui-dialog-titlebar' ).hide();
},
close: function () {
$( this ).dialog( 'destroy' ).remove();
}
} );
}
},
/**
* Submit an edited page.
*/
savePage: function ( page, summary, callback ) {
if ( AQD.csrftoken ) {
mw.user.tokens.set( 'csrfToken', AQD.csrftoken );
}
$.extend( true, page, {
cb: function ( r ) {
AQD.secureCall( callback, r );
},
// text, result, query
errCb: function ( t, r ) {
if ( AQD.uploadersToNotify ) {
// If user notify fails don't break next task (e.q. redirect to protected page, very rare)
AQD.secureCall( callback, r );
}
AQD.fail( t, r );
},
summary: summary
} );
mw.loader.using( [ 'ext.gadget.libAPI' ], function () {
mw.libs.commons.api.editPage( page );
} );
},
movePage: function () {
mw.user.tokens.set( 'csrfToken', AQD.csrftoken );
// Some users don't get it: They want to move pages to themselves.
if ( pageName.replace( /_/g, ' ' ) === AQD.destination ) {
return AQD.nextTask();
}
mw.loader.using( [ 'ext.gadget.libAPI' ], function () {
var moveArgs = {
cb: function () {
AQD.nextTask();
},
// text, r-result, query
errCb: function ( t, r ) {
if ( r && r.error && /articleexists/.test( r.error.code ) ) {
AQD.disableReport = true;
}
AQD.fail( t, r );
},
from: pageName,
to: AQD.destination,
reason: AQD.reason,
movetalk: true,
watchlist: AQD.pageWasWatched ? 'watch' : 'preferences'
};
// Option to not leave a redirect behind, MediaWiki default does leave one behind
// Just like movetalk, an empty parameter sets it to true
if ( AQD.wpLeaveRedirect === false ) {
moveArgs.noredirect = true;
}
AQD.showProgress( AQD.i18n.movingFile );
mw.libs.commons.api.movePage( moveArgs );
} );
},
deletePage: function () {
mw.user.tokens.set( 'csrfToken', AQD.csrftoken );
mw.loader.using( [ 'ext.gadget.libAPI' ], function () {
AQD.showProgress( AQD.i18n.deletingFile );
mw.libs.commons.api.deletePage( {
cb: function () {
AQD.nextTask();
},
// text, result, query
errCb: function ( t, r ) {
AQD.fail( t, r );
},
title: pageName,
reason: AQD.reason
} );
} );
},
purge: function () {
// No need for checking success, showing progress, nor for waiting for task to complete
this.nextTask();
$.post( this.apiURL, {
format: 'json',
action: 'purge',
forcelinkupdate: 1,
titles: pageName
} );
},
/**
* Does a MediaWiki API request and passes the result to the supplied callback (method name).
*/
queryAPI: function ( params, callback ) {
mw.loader.using( [ 'ext.gadget.libAPI' ], function () {
params.action = params.action || 'query';
mw.libs.commons.api.query( params, {
method: 'GET',
cache: false,
cb: function ( r ) {
AQD.secureCall( callback, r );
},
// text, result, query
errCb: function ( t, r ) {
AQD.fail( t, r );
}
} );
} );
},
/**
* Method to catch errors and report where they occurred
*/
secureCall: function ( fn, r ) {
var o = AQD;
try {
o.currentTask = arguments[ 0 ];
if ( typeof fn === 'function' ) {
// arguments is not of type array so we can't take .slice
return fn.apply( o, Array.prototype.slice.call( arguments, 1 ) );
} else if ( typeof fn === 'string' ) {
return o[ fn ].apply( o, Array.prototype.slice.call( arguments, 1 ) );
} else {
mw.log.warn( fn, this.tasks );
o.fail( 'This is not a function!' );
}
} catch ( ex ) {
o.fail( ex, r );
}
},
/**
* 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 () {
this.secureCall( this.tasks.shift() );
},
retryTask: function () {
this.secureCall( this.currentTask );
},
/**
* Once we're all done, reload the page.
*/
reloadPage: function () {
this.showProgress();
if ( this.pageName && this.pageName.replace( / /g, '_' ) !== pageName ) {
return;
}
location.href = mw.util.getUrl( this.destination || pageName );
},
/**
* Error handler. Throws an alert at the user and give him
* the possibility to retry or autoreport the error-message.
*/
fail: function ( err, r ) {
var o = this,
msg,
dlgButtons = {};
if ( typeof err === 'object' ) {
msg = err.message + ' \n\n ' + err.name;
if ( err.lineNumber ) {
msg += ' @line' + err.lineNumber;
}
err = msg;
}
// Mostly the same as err
if ( typeof r === 'object' ) {
if ( r.error && /tpt-target-page|readonly/.test( r.error.code ) ) {
this.disableReport = true;
}
}
// err += '\n' + JSON.stringify( r );
msg = this.i18n.taskFailure[ this.currentTask ] || this.i18n.genericFailure;
// TODO: Needs cleanup
if ( this.img_summary === 'Nominating for deletion' ) {
msg += ' ' + ( this.templateAdded ? this.i18n.completeRequestByHand : this.i18n.addTemplateByHand );
}
dlgButtons[ this.i18n.retryButtonLabel ] = function () {
$( this ).remove();
o.retryTask();
};
if ( [ 'movePage', 'deletePage', 'notifyUploaders' ].indexOf( o.currentTask ) !== -1 &&
( /code 50\d|missingtitle/.test( err ) ) ) {
dlgButtons[ this.i18n.ignoreButtonLabel ] = function () {
$( this ).remove();
o.nextTask();
};
}
if ( !this.disableReport ) {
dlgButtons[ this.i18n.reportButtonLabel ] = function () {
var randomId = Math.round( Math.random() * 1099511627776 ),
toSend = '\n== Autoreport by AjaxQuickDelete ' + randomId + ' ==\n' + err +
'\nAQD version: ' + o.version +
'\n++++\n:Task: ' + o.currentTask + '\n:NextTask: ' + o.tasks[ 0 ] + '\n:LastTask: ' + o.tasks[ o.tasks.length - 1 ] +
'\n:Page: {{Page|1=' + ( o.pageName || pageName ) + '}}\n:Skin: ' + mw.user.options.get( 'skin' ) +
'\n:[{{fullurl:Special:Contributions|target={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Contribs] ' +
'[{{fullurl:Special:Log|user={{subst:urlencode:{{subst:REVISIONUSER}}}}&offset={{subst:REVISIONTIMESTAMP}}}} Log] ' +
'before error [[User:{{subst:REVISIONUSER}}|]] ~~~~~\n\n';
$( '#feedbackContainer' ).contents().remove().end()
.append( $( '<img>', { src: '//upload.wikimedia.org/wikipedia/commons/d/de/Ajax-loader.gif' } ) ).css( 'text-align', 'center' );
$.post( o.apiURL, {
action: 'edit',
format: 'json',
title: 'MediaWiki talk:Gadget-AjaxQuickDelete.js/auto-errors',
summary: '/*Autoreport by AjaxQuickDelete ' + randomId + '*/ error with random id',
appendtext: toSend,
token: ( o.csrftoken || 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 + '<br>' + this.i18n.errorDetails + '<br>' + mw.html.escape( err ) + '<br>' +
( this.tag ? ( this.i18n.tagWas + this.tag ) : '' ) + '<br><a href="' + mw.util.getUrl( 'MediaWiki talk:AjaxQuickDelete.js' ) +
'" >' + this.i18n.errorReport.replace( /%BUTTON%/, '<tt>' + this.i18n.reportButtonLabel + '</tt>' ) + '</a>' )
} ) ).dialog( {
width: 550,
modal: true,
closeOnEscape: false,
title: this.i18n.errorDlgTitle,
dialogClass: 'ajaxDeleteError',
buttons: dlgButtons,
close: function () {
$( this ).dialog( 'destroy' ).remove();
}
} );
if ( mw.log.warn ) {
mw.log.warn( err );
}
},
/**
* 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 ) {
return mw.libs.commons.formatDate( fmt, date,
( mw.libs.commons.api && mw.libs.commons.api.getCurrentDate() || new Date() )
);
},
// Constants
// DR subpage prefix
requestPagePrefix: 'Commons:Deletion requests/',
// user talk page prefix
userTalkPrefix: conf.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: '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?',
moreInformation: 'More information',
loading: 'Loading…',
keptAfterDR: 'This page was kept after a deletion request. Please contact the administrator who kept it before re-nominating.',
hasTalkpage: 'There is a talk page. Consider reading it or adding your remarks.',
mentionedInDR: 'Consider reading the deletion debate –%PAGE%– that links to this page.',
mentionedInForum: 'On %PAGE%, this page is part of a discussion.',
// 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',
expandToTextarea: 'Expand to textarea',
notifyUser: 'Notify users',
// GUI progress messages
preparingToEdit: 'Preparing to edit pages… ',
creatingNomination: 'Creating nomination page… ',
listingNomination: 'Adding nomination page to daily list… ',
addingAnyTemplate: 'Adding template to ' + conf.wgCanonicalNamespace.toLowerCase() + ' page… ',
notifyingUploader: 'Notifying %USER%… ',
listingMobile: 'Listing mobile upload',
updRedir: 'Updating redirects',
// 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 filename?',
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',
warnRename: 'File renaming was recently declined, be prudent!',
// For declining any request
removingTemplate: 'Removing template',
declineRequest: 'Why do you want to decline the request?',
anyDecline: 'Decline request',
// For Duplicates
useCORSForReplace: 'Try to replace usage immediately using your user account:',
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',
processDupes: 'Process Duplicates',
// Errors
errorDlgTitle: 'Error',
genericFailure: 'An error occurred while trying to do the requested action. ',
taskFailure: {
listUploaders: 'An error occurred while determining the ' + ( nsNr === 6 ? ' uploader(s) of this file' : 'creator of this page' ) + '.',
loadPages: 'An error occurred while preparing to nominate this ' + conf.wgCanonicalNamespace.toLowerCase() + ' for deletion.',
prependDeletionTemplate: 'An error occurred while adding the {{delete}} template to this ' + conf.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 ' + ( nsNr === 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 ' + conf.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: 'Manually report the error here or click on %BUTTON% to send an automatic error-report.',
tagWas: 'The tag to be inserted into this page was ',
// Minor errors/warnings
templateRegExp: 'The template does not expose a valid regular expression for {{X-To-DR}}. Go the the template and fix it there.',
findTemplateAdderErr: '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.',
dupeParaErr: 'Error in the duplicate-template, check your language version!',
dupeExistErr: 'Retrieving information about %TITLE% failed. It is possible that it is deleted, the last revision is corrupt or the file is a redirect.',
noCreatorFound: 'The page you are attempting to add a tag to was deleted or moved. Unable to retrieve the content.',
noPageFound: 'The page you are attempting to modify or move is corrupted, was deleted or moved: Unable to retrieve history and contents.'
}
};
AQD.preinstall();
if ( conf.wgUserLanguage !== 'en' ) {
$.when( mw.loader.getScript( mw.util.wikiScript() +
'?title=MediaWiki:Gadget-AjaxQuickDelete.js/' +
conf.wgUserLanguage + '.js&action=raw&ctype=text/javascript'
), $.ready ).always( function () { AQD.install(); } );
} else {
$( function () {
AQD.install();
} );
}
}() );
// </nowiki>
// This script aligns dates into one of two formats allowed by [[WP:MOSNUM]].
// PLEASE READ THE DOCUMENTATION at [[User:Ohconfucius/script/MOSNUM dates]] (click on the link above) before using.
// Feedback and constructive criticism are welcome
/**
* TemplateScript adds configurable templates and scripts to the sidebar, and adds an example regex editor.
* @see https://meta.wikimedia.org/wiki/TemplateScript
*/
// <pre>
if ($.inArray(mw.config.get('wgAction'), ['edit', 'submit']) !== -1) {
$.ajax(
'//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js',
{dataType: 'script', cache: true}
).done(function () {
$.when(
mw.loader.using(['mediawiki.util']),
$.ajax('//en.wikipedia.org/w/index.php?title=User:Ohconfucius/script/MOSNUM_utils.js&action=raw&ctype=text/javascript',
{dataType: 'script', cache: true}),
$.ready
).done(function () {
var add = function() {
return mw.util.addPortletLink.apply(mw.util, arguments);
};
$(add('p-tb', '#', 'DATES to dmy', 'dmy-unitfixer', 'Align all dates to dmy', '', '')).click(ohc_all_to_dmy_driver);
$(add('p-tb', '#', 'DATES to mdy', 'mdy-unitfixer', 'Align all dates to mdy', '', '')).click(ohc_all_to_mdy_driver);
// $(add('p-tb', '#', 'Expand ref dates', 't-expandref', 'Expand month names within refs', '', '')).click(ohc_expand_ref_dates_driver);
// $(add('p-tb', '#', 'Expand all dates', 't-expandall', 'Expand month names throughout', '', '')).click(ohc_expand_all_dates_driver);
$( add('p-tb', '#', 'US-slash dates', 't-US', 'US-slash', '', '') ).click(ohc_US_slash_dates_driver);
$( add('p-tb', '#', 'UK-slash dates', 't-UK', 'UK-slash', '', '') ).click(ohc_UK_slash_dates_driver);
});
});
}
function ohc_fix_unambiguous_dates()
{
// resolvable ambiguous date formats
// UK style
ohc_regex(/([^-\w/:\.])@DD\.@MM\.@YYYY(?=[^-–/\w&])/gi, "$1@Day @Month @YYYY", function(d) {
if (d.d == d.m) return true;
if (d.d > 12) return true;
return false;
});
ohc_regex(/([^-\w/:\.])@DD\/@MM\/@YYYY(?=[^-–/\w&])/gi, "$1@Day @Month @YYYY", function(d) {
if (d.d == d.m) return true;
if (d.d > 12) return true;
return false;
});
// US style
ohc_regex(/([^-\w/:\.])@MM\.@DD\.@YYYY(?=[^-–/\w&])/gi, "$1@Month @Day, @YYYY", function(d) {
if (d.d > 12) return true;
return false;
});
ohc_regex(/([^-\w/:\.])@MM\/@DD\/@YYYY(?=[^-–/\w&])/gi, "$1@Month @Day, @YYYY", function(d) {
if (d.d > 12) return true;
return false;
});
}
function ohc_US_slash_dates_to_mdy()
{
//ranges
ohc_regex(/([^-\d/:\.])@MM\/@DD\/@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@MM\/@DD\/@YYNN(?=[^-–/\w&])/gi, "$1@Mon1 @Day1, @Year1$2 – @Mon2 @Day2, @Year2");
ohc_regex(/([^-\d/:\.])@MM\.@DD\.@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@MM\.@DD\.@YYNN(?=[^-–/\w&])/gi, "$1@Mon1 @Day1, @Year1$2 – @Mon2 @Day2, @Year2");
ohc_regex(/(\| ?)@MM\/@DD\/@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@MM\/@DD\/@YYNN(?=\s*\|)/gi, "$1@Mon1 @Day1, @Year1$2 – @Mon2 @Day2, @Year2");
ohc_regex(/(\| ?)@MM\.@DD\.@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@MM\.@DD\.@YYNN(?=\s*\|)/gi, "$1@Mon1 @Day1, @Year1$2 – @Mon @Day2, @Year2");
// resolvable ambiguous date formats
ohc_regex(/([^-\d/:\.])@MM\/@DD\/@YYNN(?=[^-–/\w&])/gi, '$1@Month @Day, @YYYY');
ohc_regex(/([^-\d/:\.])@MM\.@DD\.@YYNN(?=[^-–/\w&])/gi, '$1@Month @Day, @YYYY');
ohc_regex(/([^-\d/:\.])@MM[-–]@DD[-–]@YYNN(?=[^-–/\w&])/gi, '$1@Month @Day, @YYYY');
ohc_regex(/(\| ?)@MM\/@DD\/@YYNN(?=\s*\|)/gi, '$1@Month @Day, @YYYY');
ohc_regex(/(\| ?)@MM\.@DD\.@YYNN(?=\s*\|)/gi, '$1@Month @Day, @YYYY');
// ohc_regex(/(\| ?)@MM[-–]@DD[-–]@YYNN(?=\s*\|)/gi, '$1@Month @Day, @YYYY');
}
function ohc_UK_slash_dates_to_dmy()
{
//ranges
ohc_regex(/([^-\d/:\.])@DD\/@MM\/@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@DD\/@MM\/@YYNN(?=[^-–/\w&])/gi, "$1@Day1 @Mon1 @Year1$2 – @Day2 @Mon2 @Year2");
ohc_regex(/([^-\d/:\.])@DD\.@MM\.@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@DD\.@MM\.@YYNN(?=[^-–/\w&])/gi, "$1@Day1 @Mon1 @Year1$2 – @Day2 @Mon2 @Year2");
ohc_regex(/(\| ?)@DD\/@MM\/@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@DD\/@MM\/@YYNN(?=\s*\|)/gi, "$1@Day1 @Mon1 @Year1$2 – @Day2 @Mon2 @Year2");
ohc_regex(/(\| ?)@DD\.@MM\.@YYNN( |\s)?(?:(?:[-–—]|&[mn]dash;)(?: |\s)?)@DD\.@MM\.@YYNN(?=\s*\|)/gi, "$1@Day1 @Mon1 @Year1$2 – @Day2 @Mon2 @Year2");
// resolvable ambiguous date formats
ohc_regex(/([^-\d/:\.])@DD\/@MM\/@YYNN(?=[^-–/\w&])/gi, '$1@Day @Month @YYYY');
ohc_regex(/([^-\d/:\.])@DD\.@MM\.@YYNN(?=[^-–/\w&])/gi, '$1@Day @Month @YYYY');
ohc_regex(/([^-\d/:\.])@DD[-–]@MM[-–]@YYNN(?=[^-–/\w&])/gi, '$1@Day @Month @YYYY');
ohc_regex(/(\| ?)@DD\/@MM\/@YYNN(?=\s*\|)/gi, '$1@Day @Month @YYYY');
ohc_regex(/(\| ?)@DD\.@MM\.@YYNN(?=\s*\|)/gi, '$1@Day @Month @YYYY');
// ohc_regex(/(\| ?)@DD[-–]@MM[-–]@YYNN(?=\s*\|)/gi, '$1@Day @Month @YYYY');
}
function ohc_remove_leading_zeroes()
{
ohc_regex(/(\D[^\w\/])@Month\s@ZD@th?,?\s@YYYY(?=\W\D)/gi, "$1@LMonth @Day, @LYear");
ohc_regex(/(\D[^\w\/])@Month\s@ZD@th?(?=\W\D)/gi, "$1@LMonth @Day");
ohc_regex(/(\D[^\w\/])@ZD@th?\s@Month\s@YYYY(?=\W\D)/gi, "$1@Day @LMonth @LYear");
ohc_regex(/(\D[^\w\/])@ZD@th?\s@Month(?=\W\D)/gi, "$1@Day @LMonth");
}
function ohc_delink_dates()
{
//add missing space between wikilinks
regex(/(\]\])(\[\[)/gi, '$1 $2'); //remove (?!file:) to ensure a space between all links
// rem redundant quote marks and parentheses
regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)\'\'\'([^|}\[\]]*)\'\'\'(?=\s*[|}])/gi, "$1$2");
regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)\'\'([^|}\[\]]*)\'\'(?=\s*[|}])/gi, "$1$2");
regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)\"([^|}\[\]]*)\"(?=\s*[|}])/gi, "$1$2");
regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)\(([^|}\[\]]*)\)(?=\s*[|}])/gi, "$1$2");
//delink unpiped dates
//delink full dates
ohc_regex(/\[\[@Day[\s_](?:of[\s_])?@Month(?:\]\]\s?\[\[\| )@YYYY\]\]/gi, "@Day @LMonth @YYYY");
ohc_regex(/\[\[@Month (?:the\s)?@Day(?:\]\],? \[\[|, )@YYYY\]\]/gi, "@LMonth @Day, @YYYY");
//delink yyyy-mm-dd dates
ohc_regex(/\[\[@YYYY(?:\]\]-\[\[|-)@MM-@DD\]\]/gi, "@YYYY-@MM-@DD");
ohc_regex(/@YYYY-\[\[@MM-@DD\]\]/gi, "@YYYY-@MM-@DD");
//underscore and nbsp in linked dates
ohc_regex(/\[\[@DD@th?(?:\s|_| )@Month\]\]/gi, "@Day @LMonth");
ohc_regex(/\[\[@Month(?:\s|_| )@DD@th?\]\]@th?/gi, "@LMonth @Day"); //consolidated regex with flexible removal of ordinal simple date
ohc_regex(/\[\[@Day @Month\]\]/gi, "@Day @LMonth");
ohc_regex(/\[\[@Month @YYYY\]\]/gi, "@LMonth @YYYY");
ohc_regex(/\[\[@YYYY\]\]/gi, "@YYYY");
//remove leading zero and links from linked date
ohc_regex(/\[\[@Month(?:[ _]| )@ZD@th\]\]/gi, "@LMonth @Day");
ohc_regex(/\[\[@ZD@th(?:[ _]| )@Month\]\]/gi, "@Day @LMonth");
// remove nowrap template from dm and md dates
ohc_regex(/(date[ ]*=[ ]*)\{\{(?:j|no ?(?:break|wrap))\|(?:@DD(?:[ _]| )@Month)( @yyyy|)\}\}/gi, '$1@DD @LMonth$2');
ohc_regex(/(date[ ]*=[ ]*)\{\{(?:j|no ?(?:break|wrap))\|(?:@Month(?:[ _]| )@DD)( @yyyy|)\}\}/gi, '$1@LMonth @DD$2');
//delink single dm or 'dth the m'
ohc_regex(/(?:the\s)?\[\[@Day@th?[\s_](?:of[\s_])?@Month\]\]/gi, "@Day @LMonth");
//delink single md or 'm the dth'
ohc_regex(/\[\[@Month[\s_](?:the[\s_])?@Day@th?\]\](?=\W)/gi, "@LMonth @Day");
//month+day piped
ohc_regex(/\[\[(?:@dd@th?[\s_](?:of[\s_])?@month|@month[\s_]@dd@th?)\|((?:@month |)@day)@th?\]\]/gi, "$1");
ohc_regex(/\[\[(?:@dd@th?[\s_](?:of[\s_])?@month|@month[\s_]@dd@th?)\|@Day@th?[\s_](@month|)\]\]/gi, "@Day $1");
// ohc_regex(/\[\[@month @dd(?:#[^|]+|)\|([^|\]]+)@th?\]\]/gi, "$1");
// ohc_regex(/\[\[@dd @month(?:#[^|]+|)\|([^|\]]+)@th?\]\]/gi, "$1");
ohc_regex(/\[\[@month @yyyy(?:#[^|]+|)\|([^|\]]+)\]\]/gi, "$1");
//month+day+year pseudo-ISO dates
ohc_regex(/\[\[@Month @DD\|\d\d-\d\d\]\]-(?:\[\[)?(?:@yyyy[^|]*\|)?@YYYY(?:\]\])/gi, "@Day @LMonth @YYYY");
ohc_regex(/\[\[@DD\s@Month\|\d\d-\d\d\]\]-(?:\[\[)?(?:@yyyy[^|]*\|)?@YYYY(?:\]\])/gi, "@Day @LMonth @YYYY");
//'[[month day|xxXxx]]Xyyyy ' to 'month day, year'
ohc_regex(/\[\[@Month\s@Day\|@dd.@dd\]\].@YYYY/gi, "@LMonth @Day, @YYYY");
//'[[day month|xxXxx]]Xyyyy' to 'day month year'
ohc_regex(/\[\[@Day\s@Month\|@dd.@dd\]\].@YYYY/gi, "@Day @LMonth @YYYY");
// century
regex(/\[\[((?:first|second|third|(?:four|fif|six|seven|eigh|nin|ten|eleven|twelf|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twentie)th|twenty[\s\-]first[\s\-])centur(?:y|ies)(?:\s(?:AD|BC|CE|BCE))?)\]\]/gi, '$1');
regex(/\[\[(?:first|second|third|(?:four|fif|six|seven|eigh|nin|ten|eleven|twelf|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twentie)th|twenty[\s\-]first)[\s\-_]centur(?:y|ies)(?:\s(?:AD|BC|CE|BCE))?\|([^\]]{1,30})\]\]/gi, '$1');
regex(/\[\[(\d{1,2}(?:st|[nr]d|th))[\s\-_](centur(?:y|ies))(\s(?:AD|BC|CE|BCE)|)\]\]/gi, '$1 $2$3');
ohc_regex(/\[\[\d{1,2}@th[\s\-_]centur(?:y|ies)(?:\s(?:AD|BC|CE|BCE)|)\|([^\]]{1,30})\]\]/gi, '$1');
// months
ohc_regex(/\[\[@Month\]\]/gi, "@LMonth");
ohc_regex(/\[\[@FullMonth\|([^\]]{1,30})\]\]/gi, "$1");
// decades and years
regex(/(\d{1,3}0)[‘`´’′]?s/g, '$1s');
regex(/\[\[(\d{1,3}0)\'?s\]\]/g, '$1s');
regex(/\[\[\d{1,3}0\'?s?\|([^\]]{1,30})\]\]/g, '$1');
regex(/\[\[(\d{1,3}0)\'?(s)?\s(AD|BC|CE|BCE)\]\]/gi, '$1$2 $3');
regex(/\[\[(\d{1,4}[\s_]?)(AD|BC|CE|BCE)\]\]/gi, '$1$2');
regex(/\[\[(AD|BC|CE|BCE)([\s_]?)(\d{1,4})\]\]/gi, '$1$2$3');
ohc_regex(/([-–])\[\[\s?@yyyy\s?\|\s?(\d{2,4})\s?\]\]/gi, '$1$2'); //piped year
regex(/\[\[([12]\d{3}|[1-9]\d{0,2})\]\]/gi, '$1');
regex(/\[\[\d{1,3}0\'?s?\s(?:AD|BC|CE|BCE)\|([^\]]{1,30})\]\]/gi, '$1');
regex(/\[\[\d{1,3}0\'?s?\s\(decade\)\|([^\]]{1,30})\]\]/gi, '$1');
//month+year
//Identify surprise or 'Easter egg' diversions linking month+years to year articles. Turn them into month+year links to be dealt with below
ohc_regex(/\[\[@yyyy in[^|\]]+\|@Day @Month @YYYY\]\]/gi, "@Day @LMonth @YYYY");
ohc_regex(/\[\[@yyyy in[^|\]]+\|@Month @Day,? @YYYY\]\]/gi, "@LMonth @Day, @YYYY");
ohc_regex(/\[\[@yyyy#[^|\]]+\|(@month\s|)(@yyyy)\]\]/gi, "$1$2");
ohc_regex(/\[\[@yyyy#[^|\]]+\|(@month|@yyyy)\]\]/gi, "$1");
ohc_regex(/\[\[(@month\s@yyyy)\]\]/gi, "$1");
ohc_regex(/\[\[@month\s@yyyy\|([^\]]{1,30})\]\]/gi, "$1");
ohc_regex(/\[\[(?:List of |)Bollywood films of @yyyy\|@YYYY\]\](?= [^\[]{2}^B)/gi, "@YYYY");
ohc_regex(/(\[\[@yyyy[^|\]]+\|@yyyy)(\]\])( season)/gi, "$1$3$2");
//removed piped years when in full date (excepting disambiguated parentheses - updated 24/2/2012)
// ohc_regex(/\[\[[^|\]\(\)]{1,32}\|@YYYY\]\]/gi, '@Year'); // 23/6/2021 – disabled delinking "other" year-in articles
//Identify surprise or 'Easter egg' diversions linking months to year or "year in" articles.
ohc_regex(/\[\[\d{1,4}#[^|\]]+\|@Month\]\]/gi, "@LMonth");
ohc_regex(/\[\[@yyyy \w[^|\]]{3,12}\|@Year\]\]/gi, '@Year');
// month and day piped
ohc_regex(/\[\[@month[\s_]@day\|@MM-@DD\]\]/gi, "@MM-@DD");
ohc_regex(/\[\[@month[\s_]@day\|([^\]]{1,30})\]\]/gi, "$1");
ohc_regex(/\[\[@day(?:\s|_|@th )(?:of[\s_]|)?@month\|([^\]]{1,30})\]\]/gi, "$1");
//years piped
regex(/\[\[\d{1,4}\|([^\]]{1,30})\]\]/gi, '$1');
}
function ohc_fix_common_errors()
{
ohc_regex(/(\d)<sup>@th<\/sup>/gi, '$1th');
// remove dashbot comment from dates
regex(/<!-- ?DASHBot ?-->/gi, '');
ohc_regex(/(\|\s*(?:date|year)\s*=)\s*c(?:irca|a\.)?\s?((?:@day |)@month @year)(?=[\s\n]*[<|}])/gi, '$1c. $2'); //add space
//common cs1 errors
regex(/(\|\s*(?:date|year)\s*=)\s*(1[7-9]\d|20[0-1])\?(?=[\s\n]*[<|}])/gi, '$1c. $20');
regex(/(\|\s*(?:date|year)\s*=\s*(1[7-9])\d{2})[-–](\d{2})(?=[\s\n]*[<|}])/gi, '$1–$2$3');
// regex(/(\|\s*(?:date|year)\s*=\s*(20)[0-1]\d)[-–]([0-2]\d)(?=[\s\n]*[<|}])/gi, '$1–$2$3');
regex(/(\|\s*(?:date|year)\s*=\s*)@YYYY\s*[-–/]\s*@YYYY(?=[\s\n]*[<|}])/gi, '$1@YYYY1–@YYYY2'); //common cs1 error
regex(/(\|\s*(?:date)\s*=)\s*s(pring|ummer),?\s*((?:1[7-9]\d|20[0-1])\d)(?=[\s\n]*[<|}])/gi, '$1S$2 $3'); //common cs1 error
regex(/(\|\s*(?:date)\s*=)\s*a(utumn),?\s*((?:1[7-9]\d|20[0-1])\d)(?=[\s\n]*[<|}])/gi, '$1A$2 $3'); //common cs1 error
regex(/(\|\s*(?:date)\s*=)\s*f(all),?\s*((?:1[7-9]\d|20[0-1])\d)(?=[\s\n]*[<|}])/gi, '$1F$2 $3'); //common cs1 error
regex(/(\|\s*(?:date)\s*=)\s*w(inter),?\s*((?:1[7-9]\d|20[0-1])\d)(?=[\s\n]*[<|}])/gi, '$1W$2 $3'); //common cs1 error
ohc_regex(/(\|\s*access-?date\s*=)\s*(@month @year)(?=[\s\n]*[<|}])/gi, '$11 $2'); //common cs1 error
// ohc_regex(/(\|\s*date\s*=\s*)@year\?(?=[\s\n]*[<|}])/gi, '$1@Year'); //common cs1 error
// ohc_regex(/(\|\s*(?:date|year)\s*=)\s*@year\?(?=[\s\n]*[<|}])/gi, '$1c. @Year'); //common cs1 error
// ohc_regex(/(\|\s*(?:date|year)\s*=)\s*(@month @year)\?(?=[\s\n]*[<|}])/gi, '$1c. $2'); //common cs1 error
ohc_regex(/(\|\s*access-?date\s*=)\s*@month @day, (1[7-9]\d{2}|2000)(?=[\s\n]*[<|}])/gi, '$1'); //rem access date before Wikipedia (জানুয়ারি 2001)
ohc_regex(/(\|\s*access-?date\s*=)\s*@day @month.? (1[7-9]\d{2}|2000)(?=[\s\n]*[<|}])/gi, '$1'); //rem access date before Wikipedia (জানুয়ারি 2001)
ohc_regex(/(\|\s*access-?date\s*=)\s*31( (?:এপ্রিল|জুন|সেপ্টেম্বর|নভেম্বর) @year)(?=[\s\n]*[<|}])/gi, '$130$2'); //rem nonsense access date (short months)
ohc_regex(/(\|\s*access-?date\s*=)\s*29( ফেব্রুয়ারি (?:200[1235679]|201[1345789]))(?=[\s\n]*[<|}])/gi, '$128$2'); //rem nonsense access date 29 ফেব্রুয়ারি (non-leap year)
ohc_regex(/(\|\s*access-?date\s*=)\s*(?:3[01])( ফেব্রুয়ারি @yyyy)(?=[\s\n]*[<|}])/gi, '$128$2'); //rem nonsense access dates ফেব্রুয়ারি
regex(/(\|\s*access-?date\s*=)\s*(\d{4})(?=[\s\n]*[<|}])/gi, ''); //common cs1 error
//month+day+year pseudo-ISO dates
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*@dd)-@Mon-@YYYY/gi, "$1 @FullMonth @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@YYYY-@DD-@Mon/gi, "$1 @DD @FullMonth @YYYY");
regex(/(\|\s*(?:date|year)\s*=)\s*(?:(?:date |)unknown|(?:not? |non-|un)date[ds])(?=[\s\n]*[<|}])/gi, '$1n.d.'); //common cs1 error
regex(/(\|\s*(?:date|year)\s*=)\s*n\.?d(?=[\s\n]*[<|}])/gi, '$1n.d.'); //common cs1 error
regex(/(\|\s*)year(\s*=\s*)(?=n\.?d\.)(?=[\s\n]*[<|}])/gi, '$1date$2'); //common cs1 error
// remove parasitic metadata - days of the week/descriptives
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)(?:[^|}\d]*|)@Day\s+@Month\s+@YYYY((?:[a-z]\b|)\s?)[^|<}]*(?=[\s\n]*[<|}])/gi, "$1@Day @Month @YYYY$2"); //rem negate "<", "hyphens" "dashes" 26/6/2020
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)[^|}]*@Month\s+@Day,\s+@YYYY((?:[a-z]\b|)\s?)[^|<}]*(?=[\s\n]*[<|}])/gi, "$1@Month @Day, @YYYY$2"); //rem negate "<", "hyphens" "dashes" 26/6/2020
// ohc_regex(/(\|\s*date\s*=\s*)(?:[^|}–<]*\D|)@Day\s+@Month\s+@YYYY ?– ?@Day\s+@Month\s+@YYYY[^|}–<]*(?=[\s\n]*[<|}])/gi, "$1@Day1 @Mon1 @Year1 – @Day2 @Mon2 @Year2");
// ohc_regex(/(\|\s*date\s*=\s*)[^|}–<]*@Month\s+@Day,\s+@YYYY ?– ?@Month @Day, @YYYY[^|}–<]*(?=[\s\n]*[<|}])/gi, "$1@Mon1 @Day1, @Year1 – @@Mon2 Day2, @Year2");
// regex(/(\|\s*author\s*=\s*)(?:posted|published)(?: by\b| on\b|)[\s:](?=\w)/gi, "$1");
// regex(/(\|\s*(?:date|archive-?date|access-?date|author|year)\s*=\s*)(?:accessed|retrieved|entered|posted|published|(?:last |)updated?|©)(?: by\b| on\b|)[\s:]*(?=\w)/gi, "$1");
regex(/(\|\s*(?:author|first|last)\s*=\s*)(?:by\b|on\b)[\s:]*(?=\w)/gi, "$1");
regex(/(\|\s*(?:date|archive-?date|access-?date|author|first|last)\s*=\s*)(?:(?:Mon|Tues|Wednes|Thurs|Fri|Satur|Sun)day,?)\s/gi, "$1");
regex(/(\|\s*(?:date|archive-?date|access-?date|author|first|last)\s*=\s*)(?:Mon|Tues?|Wed|Thur?|Fri|Sat)[\.,]?\s/gi, "$1"); //<!-- rem "Sun" - false positives -->
// remove deprecated parameters (|day= and |month)
ohc_regex(/(\|\s*)day(?:\s*=\s*)@DD ?\| ?month(?:\s*=\s*)@Month ?\| ?year(?:\s*=\s*)@YYYY(?=[|}\s])/gi, '$1date=@Day @Month @Year');
ohc_regex(/(\|\s*)year(?:\s*=\s*)@YYYY ?\| ?month(?:\s*=\s*)@Month ?\| ?day(?:\s*=\s*)@DD(?=[|}\s])/gi, '$1date=@Month @Day, @Year');
// typos
ohc_regex(/(date *= *)@Day @Month ?@th/gi, '$1@Day @Month');
ohc_regex(/(date *= *)@Day @Month\w(?= \d{3,4})/gi, '$1@Day @Month');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )(জানুয়ারি|Febr)[\w ]?[aur]{3,4}\w{0,2}(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1$2uary');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )J[anu]{3,5}r\w{0,2}(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1জানুয়ারি');
ohc_regex(/(date *= *(?:[12]?\d) )Febua?r\w{0,2}(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1ফেব্রুয়ারি');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )M\w{2,3}ch(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1মার্চ');
ohc_regex(/(date *= *(?:[12]?\d|30) )A[bpv\[]\w{2,3}l\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1এপ্রিল');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )Ma[tui](?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1মে');
ohc_regex(/(date *= *(?:[12]?\d|30) )J\wn\w(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুন');
ohc_regex(/(date *= *(?:[12]?\d|30) )J\w{1,2}e(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুন');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )জুলাই\w{1,2}(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুলাই');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )A[oug]{2,3}\w{1,4}t(?:\w|us)?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1আগষ্ট');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )(সেপ্টেম্বর|নভেম্বর|ডিসেম্বর)\w{2,4}b[er]{1,2}\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1$2ember');
ohc_regex(/(date *= *(?:[12]?\d|30) )S\w{0,2}p[ \[]?\w{2,5}[er]{2,3}\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1সেপ্টেম্বর');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )O\w{0,2}t\w{1,3}[er]{2,3}\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1অক্টোবর');
ohc_regex(/(date *= *(?:[12]?\d|30) )[MN]\w{1,2}[bv] ?\w{1,3}b[er]{1,2}\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1নভেম্বর');
ohc_regex(/(date *= *(?:[12]?\d|30|31) )D[ie]?[cs] ?\w{1,4}b[er]{1,2}\w?(?= (?:1[6-9]\d|20[01])\d\b)/gi, '$1ডিসেম্বর');
ohc_regex(/(date *= *)@Month\w(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1@Month'); //rem stray digit at the end of year string
ohc_regex(/(date *= *)(জানুয়ারি|Febr)[\w ]?[aur]{3,4}\w{0,2}(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1$2uary');
ohc_regex(/(date *= *)J[anu]{3,5}r\w{0,2}(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1জানুয়ারি');
ohc_regex(/(date *= *)Febua?r\w{0,2}(?=(?: (?:[12]?\d),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1ফেব্রুয়ারি');
ohc_regex(/(date *= *)M\w{2,3}ch(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1মার্চ');
ohc_regex(/(date *= *)A[bpv\[]\w{2,3}l\w?(?=(?: (?:[12]?\d|30),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1এপ্রিল');
ohc_regex(/(date *= *)Ma[tui](?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1মে');
ohc_regex(/(date *= *)J\wn\w(?=(?: (?:[12]?\d|30),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুন');
ohc_regex(/(date *= *)J\w{1,2}e(?=(?: (?:[12]?\d|30),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুন');
ohc_regex(/(date *= *)জুলাই\w{1,2}(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1জুলাই');
ohc_regex(/(date *= *)A[oug]{2,3}\w{1,4}t(?:\w|us)?(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1আগষ্ট');
ohc_regex(/(date *= *)(সেপ্টেম্বর|নভেম্বর|ডিসেম্বর)\w{2,4}b[er]{1,2}\w?(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1$2ember');
ohc_regex(/(date *= *)S\w{0,2}p[ \[]?\w{2,5}[er]{2,3}\w?(?=(?: (?:[12]?\d|30),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1সেপ্টেম্বর');
ohc_regex(/(date *= *)O\w{0,2}t\w{1,3}[er]{2,3}\w?(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1অক্টোবর');
ohc_regex(/(date *= *)[MN]\w{1,2}[bv] ?\w{1,3}b[er]{1,2}\w?(?=(?: (?:[12]?\d|30),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1নভেম্বর');
ohc_regex(/(date *= *)D[ie]?[cs] ?\w{1,4}b[er]{1,2}\w?(?=(?: (?:[12]?\d|30|31),?|) (?:1[6-9]\d|20[01])\d\b)/gi, '$1ডিসেম্বর');
//insert comma to separate date from army unit
ohc_regex(/@FullMonth ((?:the |)\d\d*@th (?:Air(?:borne|)|Arm(?:ou?red|y)|Artillery|Battalion|Brigade|Co(?:mpan|)y|Division|Fleet|Group|Infantry|Land|Panzer|Regiment|Squadron|Sqn)\b)/g, "@Month, $1");
// remove parasitic metadata - time
ohc_regex(/(\|\s*\w*date\s*=\s*@yyyy-@mm-@dd)\s?(?:T[0-2]\d:[0-5]\d:[0-5]\d(?:\s*| |)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|Z))(?=\s*[|}])/g, "$1");
ohc_regex(/(\|\s*\w*date\s*=\s*@month @day,? @yyyy)\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|))(?=\|)/g, "$1");
ohc_regex(/(\|\s*\w*date\s*=\s*@day @month,? @yyyy)\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|))(?=\|)/g, "$1");
ohc_regex(/(\|\s*\w*date\s*=\s*\w[\w\s]+@month @day,? @yyyy)\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|))(?=\|)/g, "$1");
ohc_regex(/(\|\s*\w*date\s*=\s*\w[\w\s]+@day @month,? @yyyy)\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|))(?=\|)/g, "$1");
ohc_regex(/(\|\s*\w*date\s*=[^:|}]*)\s?(?:T?[0-2]\d:[0-5]\d:[0-5]\d(?:\s*| |)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|Z))/g, "$1");
// add back spaces or comma or remove other single artefact after date string
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)(?:on |)@Day\s{,2}@Month\s{,2}@YYYY[^a-z{}]?(?=\s*[|}\n])/gi, "$1@Day @Month @YYYY$2");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)(?:on |)@Month\s{,2}@Day,?\s{,2}@YYYY[^a-z{}]?(?=\s*[|}\n])/gi, "$1@Month @Day, @YYYY$2");
//long-hand palliative for observed failure to insert spaces
ohc_regex(/(\|\s*(?:archive|access|air)-?date\s*=\s*)(?:on |)([0-2]?\d|30|31)\s*(জানুয়ারি|ফেব্রুয়ারি|মার্চ|এপ্রিল|মে|জুন|জুলাই|আগষ্ট|সেপ্টেম্বর|অক্টোবর|নভেম্বর|ডিসেম্বর)\s*((?:19\d|20[0-1])\d\s?)[^|{}\s<>]*(?=\s*[|}])/gi, "$1$2 $3 $4");
// ohc_regex(/(\|\s*(?:publication|)-?date\s*=\s*)(?:on |)@Day\s*@Month\s*@YYYY([a-z]\s?|\s?)\w*[^|{}<>]*(?=[|}])/gi, "$1@Day @Month @YYYY$2");
// ohc_regex(/(\|\s*(?:publication|)-?date\s*=\s*)(?:on |)@Month\s*@Day,\s*@YYYY([a-z]\s?|\s?)\w*[^|{}<>]*(?=[|}])/gi, "$1@Month @Day, @YYYY$2");
//remove extraneous bracket //from Batty
ohc_regex(/{{(\s*[Cc]it(?:e|ation))([^}]+)(\s*\|\s*(?:archive-?|access-?|publication-?)?date\s*=\s*)([\w\s-]+)\](?=\s*[\|}<])/gi, '{{$1$2$3$4');
//fix bda template redirect
// regex(/\{\{bda\|([^}]+)\}\}/gi, '{{birth date and age|$1}}');
// regex(/\{\{dda\|([^}]+)\}\}/gi, '{{death date and age|$1}}');
//zap redundant {{date}}, {{accessdate}} {{retrieved}}, {{monthname}} and {{#dateformat}}
// regex(/({{(?:end|start)[-–]date)\|df=y(?:es|)/gi, "$1"); //rem deprecated parameter - disabled: doesn't work here, so ejected to the formatting script
ohc_regex(/{{#formatdate:@YYYY-@MM-@DD(?:[^\}]*)}}/gi, "@Day @Month @YYYY");
regex(/{{#formatdate:([^}]+)}}/gi, "$1");
regex(/\{\{#dateformat:([^}\|]+)(?:\|dmy|\|mdy)?\}\}/gi, '$1');
// ohc_regex(/(date[ ]*=[ ]*)\{\{(?:Start|End) ?date(?:\|df=y(?:es|)|)\|@YYYY\|@MM\|@DD(?:\|df=y(?:es|)|)\}\}/gi, '$1@Month @DD, @YYYY'); //stripping start/end template notes inside "|date=" parameter (line 72)
ohc_regex(/{{#formatdate:@YYYY-@MM-@DD(?:\|(?:[dmy]{3}|iso)|)}}/gi, "@Day @Month @YYYY");
ohc_regex(/\{\{date\|@YYYY-@MM-@DD\}\}/gi, "@Day @Month @YYYY");
ohc_regex(/\{\{date\|(@DD[\-\s]@Mon|@Mon[\-\s]@DD)\}\}/gi, "@Day @Month");
regex(/\{\{date\|([^}\|]+)(?:\|l?(?:[dmy]{3}|iso|none|link))?\}\}/gi, '$1');
ohc_regex(/[ ]*\{\{accessdate\|@YYYY-@MM-@DD[ ]*\}\}/gi, " Retrieved @YYYY-@MM-@DD");
ohc_regex(/[ ]*\{\{retrieved[ ]*\|[ ]accessdate=([\-\w, ]*)\}\}/gi, " Retrieved $1");
// ohc_regex(/(date[ ]*=[ ]*|\w{2,}[ ]*)\{\{(?:start|end).date[^|}]*(?:\|df=y(?:es|)|)\|@YYYY(?:\|df=y(?:es|)|)\}\}/gi, '$1@YYYY'); //stripping start/end template (disabling to placate Pigsonthewing)
// ohc_regex(/\{\{(?:Start|End) date(?:\|df=y(?:es|)|)\|@YYYY\|@MM\}\}/gi, '@Month @YYYY') //stripping start/end template notes
// ohc_regex(/<!--(?:Use)? \{\{(?:Start|End) date\|@YYYY\|@MM\|@DD\}\} -->/gi, ''); //stripping start/end template notes
ohc_regex(/(\{\{MONTHNAME\|@MM\}\})/gi, "@Month")
//expand one and two-digit years in ranges
regex(/(\()@Month @YYYY[-–]@Month @YYNN(\))/gi, '$1@Month1 @YYYY1 – @Month2 @YYYY2$2');
regex(/(\(1[789])(\d)(1)[-–]([2-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(2)[-–]([3-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(3)[-–]([4-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(4)[-–]([5-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(5)[-–]([6-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(6)[-–]([7-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(7)[-–]([8-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(1[789])(\d)(8)[-–](9\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(1)[-–]([2-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(2)[-–]([3-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(3)[-–]([4-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(4)[-–]([5-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(5)[-–]([6-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(6)[-–]([7-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(7)[-–]([8-9]\))/gi, '$1$2$3–$2$4');
regex(/(\(20)([01])(8)[-–](9\))/gi, '$1$2$3–$2$4');
ohc_regex(/(\W)@DD@th?, @DD@th?((?:[ ]| )(?:and|&|to|or)(?:[ ]| ))@DD@th?(?:\sof\s?)?([ ]| )@Month(?=\W)/gi, '$1@Day1, @Day2$2@Day3$3@LMonth');
ohc_regex(/(\W)@DD@th?((?:[ ]| )(?:and|&|to|or)(?:[ ]| ))@DD@th?(?:\sof\s?|)([ ]| )@Month(?=\W)/gi, '$1@Day1$2@Day2$3@LMonth');
ohc_regex(/(\W)the @DD@th(?:\sof|)\s@Month,? @YYYY(?=\W\D)/g, '$1@Day @LMonth @YYYY');
ohc_regex(/(\W)the @DD@th(?:\sof|)\s@Month(?=\W\D)/g, '$1@Day @LMonth');
ohc_regex(/(\W)@Day@th?(?:\sof|)([ _]| |{{(?:break|nbsp)}})@Month(?=[^|\]\w])/gi, '$1@Day$2@LMonth');
ohc_regex(/(\W)@Month\s(?:the\s)?@DD@th?,(?:\sthe\s)?(?: | )@DD@th?(\s(?:and|&|to|or)(?:[ _]| ))(?:the\s)?@DD@th?(?=[^|\]\w])/gi, '$1@LMonth @Day1, @Day2$2@Day3');
ohc_regex(/(\W)(@month)\s(@day)((?:,\s@day){0,6}),?(\/|\s?[-–]\s?|\s(?:and|&|to|or)\s+?)@Day@th?(?:,?(?:[ _]| )|\sof\s)?(\d{3,4}\W\D)/gi, '$1$2 $3$4$5@Day, $6');
ohc_regex(/(\W)(@month)\s(@day)((?:,\s@day){0,6}),?(\/|\s?[-–]\s?|\s(?:and|&|to|or)\s+?)@Day@th?(?=\W\D)/gi, '$1$2 $3$4$5@Day');
ohc_regex(/(\W)@Month([ _]| |{{(?:break|nbsp)}})(?:the\s)?@DD@th?,? @Year/gi, '$1@LMonth$2@Day, @Year');
ohc_regex(/(\W)@Month @Day(?:\sof|,|)([ _]| |{{(?:break|nbsp)}})@Year(?=[^|\]\w])/gi, '$1@Month @Day,$2@Year');
// consolidating/amalgamating date fields
ohc_regex(/(\|[ ]*date[ ]*=[ ]*@day)\s*\|[ ]*month=[ ]*(@month)\s*\|[ ]*year=[ ]*(@yyyy[ ]*)(?=[|}\s])/gi, "$1 $2 $3");
ohc_regex(/(\|[ ]*date[ ]*=[ ]*@day.?@month)\s*\|[ ]*year=[ ]*(@yyyy[ ]*)(?=[|}\s])/gi, "$1 $2");
ohc_regex(/(\|[ ]*date[ ]*=[ ]*@month.?@day)\s*\|[ ]*year=[ ]*(@yyyy[ ]*)(?=[|}\s])/gi, "$1 $2");
// eliminating dates and time placed in author parameter
//removing artefacts from Indian news sites (TNN আগষ্ট 30, 2012, 05.46AM)
ohc_regex(/(\|\s*author\s*=\s*(?:[A-Z]{2,3}\b))(?:[^|}]*)\d{4}, [\d\.:]{4,5}( |)[AP]M IST(?=[ ]*\|)/g, '$1$2');
// ohc_regex(/(\|\s*author=(?:[^\[\]|{}]*))(TNN|PTI) @day @month @yyyy, [\d\.:]{4,5}( |)[AP]M IST(?=[ ]*\|)/g, '$1$2');
ohc_regex(/(\|\s*author\s*=\s*)@month @day,? @yyyy\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|)|)(?=\|)/g, "$1");
ohc_regex(/(\|\s*author\s*=\s*)@day @month,? @yyyy\s?(?:,? [0-2]?\d[\.:][0-5]\d(?:\s*| )(?:[apAP][mM]\s*|)(?:GMT\s*|UTC\s*|[A-Z]{1,2}[DS]T\s*|)|)(?=\|)/g, "$1");
ohc_regex(/(\|\s*author\s*=\s*\w[\w\s]+)@month @day,? @yyyy(?:[^\[\]|{}]*)(?=[|}])/g, "$1");
ohc_regex(/(\|\s*author\s*=\s*\w[\w\s]+)@day @month,? @yyyy(?:[^\[\]|{}]*)(?=[|}])/g, "$1");
ohc_regex(/(\|\s*author\s*=\s*(?:\w[\w\s]+)?)@dd-@mm-@yyyy/gi, "$1");
ohc_regex(/(\|\s*author\s*=\s*(?:\w[\w\s]+)?)@YYYY-@MM-@DD ?/gi, "$1");
regex(/(\|\s*(?:date|archive-?date|access-?date|author)\s*=\s*(?:\w[\w\s]+)?)[0-2]\d:[0-5]\d(?:[ ]| )?(?:[ap]m|[ap]\.m\.|)(?: ?(?:[A-Z]{1,2}T|UTC)[\.,]?|)/gi, "$1");
// ohc_regex(/(\|\s*author\s*=\s*(?:\w[\w\s]+)?)\s*[0-2]\d[\.:][0-5]\d(?:[\.:][0-5]\d|)/gi, "$1"); //let's see later if we need this
// eliminating mm-dd-yyyy dates
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@MM[-–\/\.=\s](1[3-9]|2\d|3[01])[-–\/\.=\s]@YYYY/gi, "$1@Month $2, @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Mon[-–\/\.=\s](1[3-9]|2\d|3[01])[-–\/\.=\s]@YYYY/gi, "$1@Month $2, @YYYY");
ohc_regex(/[ ](?:Accessed|Retrieved)(?:(?: online|) on|:?)[ ]@MM[-–\/\.=\s](1[3-9]|2\d|3[01])[-–\/\.=\s]@YYYY(?=\D)/gi, " Retrieved @Month $1, @YYYY");
// ohc_regex(/[ ](?:Accessed|Retrieved)(?:(?: online|) on|:?)[ ]@YYYY,? @Month @DD(?=\D)/gi, " Retrieved @Day @Month @YYYY");
ohc_regex(/([ =|])@Mon[-–](1[3-9]|2\d|3[01])[-–]@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@Mon\/(1[3-9]|2\d|3[01])\/@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@Mon\.(1[3-9]|2\d|3[01])\.@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@Mon (1[3-9]|2\d|3[01]) @YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@MM[-–](1[3-9]|2\d|3[01])[-–]@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@MM\/(1[3-9]|2\d|3[01])\/@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@MM\.(1[3-9]|2\d|3[01])\.@YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
ohc_regex(/([ =|])@MM (1[3-9]|2\d|3[01]) @YYYY(?=[^-–\d/])/gi, "$1@Mon $2, @YYYY");
// eliminating dd-mm-yyyy dates (assumed default)
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@DD[-–\/\.=\s]@MM[-–\/\.=\s]@YYYY/gi, "$1@Day @Month @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@DD[-–\/\.=\s]@Mon[-–\/\.=\s]@YYYY/gi, "$1@Day @Month @YYYY");
ohc_regex(/[ ](?:Accessed|Retrieved)(?:(?: online|) on|:?)[ ]@DD[-–\/\.=\s]@MM[-–\/\.=\s]@YYYY(?=\D)/gi, " Retrieved @Day @Month @YYYY");
ohc_regex(/[ ](?:Accessed|Retrieved)(?:(?: online|) on|:?)[ ]@YYYY,? @Month @DD(?=\D)/gi, " Retrieved @Day @Month @YYYY");
ohc_regex(/([ =|])@DD[-–]@Mon[-–]@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD\/@Mon\/@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD\.@Mon\.@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD @Mon @YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD[-–]@MM[-–]@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD\/@MM\/@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD\.@MM\.@YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/([ =|])@DD @MM @YYYY(?=[^-–\d/])/gi, "$1@Day @Month @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@DD[-–\/\.=\s]@Month[-–\/\.=\s]@YYNN(?=\s*[|}\n])/gi, '$1@Day @Month @YYYY');
//convert yyyy-dd-mm to yyyy-mm-dd
ohc_regex(/{{(\s*[Cc]it(?:e|ation))([^}]+)(\s*\|\s*(?:publication|archive|access|air|)-?date\s*=\s*(?:1[0-9]|20)\d{2})[-/\.=\s](1[3-9]|2\d|3[01])[-/\.=\s](0?\d|1[0-2])(\s*[\|}<])/gi, '{{$1$2$3-$5-$4$6'); //from Batty
// eliminating other errant formats
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)([0-2]?\d|30|31) @Month \2,? @YYYY(?=\s*[|}])/gi, "$1$2 @Month @YYYY"); //repeated digits
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Day ?@Month ?200([01]\d)[^\s|}]?(?=\s*[|}])/gi, "$1@Day @Month 20$2");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month ?@Day,? ?200([01]\d)[^\s|}]?(?=\s*[|}])/gi, "$1@Month @Day, 20$2");
ohc_regex(/([ ]\()@DD-@MM-@YYYY(\)\W)/gi, '$1@Day @Month @YYYY$2');
ohc_regex(/([ ]\()@DD-@Month-@YYYY(\)\W)/gi, '$1@Day @Month @YYYY$2');
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@Month, @DD, @YYYY(?=\s*[|}])/gi, "$1@Day @Month @YYYY");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@DD[\.,] @Month,? @YYYY(?=\s*[|}])/gi, "$1@Day @Month @YYYY");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@YYYY,? @Month @DD(?=\s*[|}])/gi, "$1@Day @Month @YYYY");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@YYYY,? @DD @Month(?=\s*[|}])/gi, "$1@Day @Month @YYYY");
ohc_regex(/[ ]\(@YYYY,? @Month @DD\)/gi, " (@Day @Month @YYYY)");
ohc_regex(/[ ]\(@YYYY @MM @DD\)/gi, " (@Day @Month @YYYY)");
ohc_regex(/[ ]\(@YYYY, @DD @Month\)/gi, " (@Day @Month @YYYY)");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@YYYY,? @Month(?=\s*[|}])/gi, "$1@Month @YYYY");
ohc_regex(/[ ]\(@YYYY, @Month\)/gi, " (@Month @YYYY)");
ohc_regex(/[ ]\(@Mon@YYYY\)/gi, " (@Mon @YYYY)");
//CS1 errors ISO-like dates (too few or too many digits)
ohc_regex(/(date\s*=\s*)(?:\[\[)?@YYYY(?:\]\][–—‐]\[\[|[–—‐])@MM[\-–—‐]@DD(?:\]\])?(?=\s*[|}])/gi, "$1@YYYY-@ZM-@ZD"); //ndashes and emdashes
ohc_regex(/(\|\s*access-?date\s*=)\s*@YYYY-0[–—]@DD(?=\s*[|}])/gi, ""); //rem nonsense access date
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@YYYY[-–—]@MM[-–—]@DD(?=\s*[|}])/gi, "$1@YYYY-@ZM-@ZD"); //ndashes and emdashes
// ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)@YYYY[\-–—‐]@MM(?=\s*[|}])/gi, "$1@Month @YYYY"); //transform "yyyy-mm" to "month year"
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[–—]@MM[–—]@DD([^-\w\/%,<][^<]*?<\/ref>)/gi, "$1$2@Day @Month @YYYY$3");
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[-]@MM[-]([1-9])([^-\w\/%,<][^<]*?<\/ref>)/gi, "$1$2$3 @Month @YYYY$4");
// spaces and commas between month and year
ohc_regex(/@Month(?:,?|\s+of|)(\s| )@YYYY(?=\D)/gi, "@LMonth$1@YYYY");
// fix month names (capitalize, remove dots)
ohc_regex(/(\W)@DD\s@FullMonth(\s+|\s*,)/gi, "$1@Day @FullMonth$2");
ohc_regex(/(\W)@DD\s@Mon(\s+|\s*,)/gi, "$1@Day @Mon$2");
ohc_regex(/(\W)@FullMonth\s@DD(\s+|\s*,)/gi, "$1@FullMonth @Day$2");
ohc_regex(/(\W)@Mon\s@DD(\s+|\s*,)/gi, "$1@Mon @Day$2");
regex(/(\d(?:st|nd|rd|th) )C(entur(?:ies|y))(?=[ ]*(=|BC|AD|CE))/g, '$1c$2');
// regex(/(\d(?:st|nd|rd|th) )C(entur(?:ies|y))(?![ \-][A-Z]\w*|''|")/g, '$1c$2');
//spaces and commas - in date formats
ohc_regex(/(\D\W\[?\[?@dd)[ ]+(@month)(\]\])?[ ]*,[ ]*(\[?\[?@yyyy(\sAD|\sBC|\sCE|\sBCE|)\]?\]?\W\D)/gi, "$1 $2$3 $4");
ohc_regex(/(\D\W\[?\[?)(@month)[ ]+(@dd)(\]?\]?)(?:[ ]*,[ ]*)(\[?\[?@yyyy(\sAD|\sBC|\sCE|\sBCE|)\]?\]?\W\D)/gi, "$1$2 $3$4, $5");
ohc_regex(/(\D\W\[?\[?)(@month)[ ]+(@dd)(\]?\]?)[ ]+(\[?\[?@yyyy(\sAD|\sBC|\sCE|\sBCE|)\]?\]?\W\D)/gi, "$1$2 $3$4, $5");
ohc_regex(/(@month)(?:, | of )(@yyyy\w)/gi, "$1 $2"); //repeat after delinking
// spaces and commas - md-md and dm-dm date ranges
ohc_regex(/(\D\W)@Month\s@DD( |\s)(?:[-–—]|&[mn]dash;)(?: |\s)?@Month\s@DD(?=\W\D)/gi, "$1@LMonth1 @Day1$2– @LMonth2 @Day2");
ohc_regex(/(\D\W)@Month\s@DD(?:[-–—]|&[mn]dash;)(?: |\s)?@Month\s@DD(?=\W\D)/gi, "$1@LMonth1 @Day1 – @LMonth2 @Day2");
ohc_regex(/(\D\W)@DD\s@Month( |\s)(?:[-–—]|&[mn]dash;)(?: |\s)?@DD\s@Month(?=\W\D)/gi, "$1@Day1 @LMonth1$2– @Day2 @LMonth2");
ohc_regex(/(\D\W)@DD\s@Month(?:[-–—]|&[mn]dash;)(?: |\s)?@DD\s@Month(?=\W\D)/gi, "$1@Day1 @LMonth1 – @Day2 @LMonth2");
// spaces and commas - d-dm and md-d date ranges
ohc_regex(/(\D\W)@Month(\s| )@DD@th?(?:(?:\s*| |)[-–](?:\s*| |))@DD@th?(?=\W\D)/gi, "$1@LMonth1$2@Day1–@Day2");
ohc_regex(/(\D\W)@DD@th?(?:(?:\s*| |)[-–](?:\s*| |))@DD@th?(\s| )@Month(?=\W\D)/gi, "$1@Day1–@Day2$2@LMonth1");
// spaces and commas between month and year (again)
ohc_regex(/@Month(?:,\s+|\s+of\s+)@YYYY(\w)/gi, "@LMonth @YYYY$1");
}
/** ------------------------------------------------------------------------- */
/// ISO to long format
function ohc_ISO_to_dmy_in_citations()
{
//ISO dates within single citation
ohc_regex(/(\|\s*(?:publication|archive|access|air|designation\d?_|)-?date\s*=\s*)@YYYY[-–]@MM[-–]@DD(?=\W\D)/gi, "$1@Day @Month @YYYY");
}
function ohc_ISO_to_mdy_in_citations()
{
//ISO dates within single citation
ohc_regex(/(\|\s*(?:publication|archive|access|air|designation\d?_|)-?date\s*=\s*)@YYYY[-–]@MM[-–]@DD(?=\W\D)/gi, "$1@Month @Day, @YYYY");
}
function ohc_ISO_to_dmy_in_references()
{
//multiple calls for multiple dates within single citation
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[-–]@MM[-–]@DD([^-\w\/%,<][^<]*?<\/ref>)/gi, "$1$2@Day @Month @YYYY$3");
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[-–]@MM[-–]@DD(<\/ref>)/gi, "$1$2@Day @Month @YYYY$3");
}
function ohc_ISO_to_mdy_in_references()
{
//multiple calls for multiple dates within single citation
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[-–]@MM[-–]@DD([^-\w\/%,<][^<]*?<\/ref>)/gi, "$1$2@Month @Day, @YYYY$3");
ohc_regex(/(<ref[^>]*>[^<]+?)([\s\(])@YYYY[-–]@MM[-–]@DD(<\/ref>)/gi, "$1$2@Month @Day, @YYYY$3");
}
// currently unused
function ohc_ISO_to_dmy_anywhere()
{
//multiple calls for multiple dates within single citation
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Day @Month @YYYY$2');
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Day @Month @YYYY$2');
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Day @Month @YYYY$2');
}
// currently unused
function ohc_ISO_to_mdy_anywhere()
{
//multiple calls for multiple dates within single citation
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Month @Day, @YYYY$2');
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Month @Day, @YYYY$2');
ohc_regex(/([^-\w/])@YYYY[-–]@MM[-–]@DD([^-\w/])/gi, '$1@Month @Day, @YYYY$2');
}
function ohc_delink_ISO_to_dmy()
{
ohc_ISO_to_dmy_in_citations();
ohc_ISO_to_dmy_in_references();
regex(/([^\w\/\-%,])@YYYY-@MM-@DD(<\s?\/ref.*?>)/g, '$1@Day @Month @Year$2');
ohc_regex(/(\|\|[ ]*)@YYYY[-–]@MM[-–]@DD([ ]*\|\|)/gi, '$1{{dts|format=dmy|@YYYY|@MM|@DD}}$2'); //dts template for sortable tables
ohc_regex(/(\{\{dts\|format=)mdy\|/gi, '$1dmy|'); //flip dts template formats
}
function ohc_delink_ISO_to_mdy()
{
ohc_ISO_to_mdy_in_citations();
ohc_ISO_to_mdy_in_references();
regex(/([^\w\/\-%,])@YYYY-@MM-@DD(<\s?\/ref.*?>)/g, '$1@Month @Day, @Year$2');
ohc_regex(/(\|\|[ ]*)@YYYY[-–]@MM[-–]@DD([ ]*\|\|)/gi, '$1{{dts|@YYYY|@MM|@DD}}$2'); //dts template for sortable tables (mdy is default)
ohc_regex(/(\{\{dts\|)format=(?:mdy|dmy|) ?\|/gi, '$1'); //flip dts template formats
}
function ohc_delink_year_in_X()
{
// ohc_regex(/\{\{(?:avyear|by|baseball\syear|fy|ly|mlby|mlb\syear|scy|sdy)\|(@year)\}\}/gi, "$1"); //disabled 3/5/21 discussion at https://en.wikipedia.org/w/index.php?title=Wikipedia_talk:WikiProject_Baseball&oldid=1021203365#Links_to_baseball_year_articles_in_infobox?
// ohc_regex(/\{\{(?:avyear|by|baseball\syear|fy|ly|mlby|mlb\syear|scy|sdy)\|@year\|(@year)\}\}/gi, "$1");
ohc_regex(/\[\[@yyyy\sin\s[^|\]]+\|((?:(?:@day |)@month |)@yyyy)\]\]/gi, '$1');
regex(/\[\[(?:\d{4}[-–]\d{2} in English football)\|((?:(?:@Day |)@Month |)@YYYY)\]\]/gi, '$1');
regex(/\[\[\d{4} Major League Baseball season\|(\d{4})(\sseason)?\]\]/gi, '$1$2');
}
// format parameter must be 'dmy' or 'mdy'
function ohc_fix_dts_template(format)
{
// dts - format=dmy in this case (per D12000)
// per {{dts}} doc, remove the obsolete link=off param
regex(/(\{\{dts[^}]*)(?:\|link=off)/gi, '$1');
ohc_regex(/(\{\{dts\s*\|\s*@year\s*\|\s*@month\s*\|\s*@day\s*\|\s*format=)(?:dmy|mdy)(\}\})/gi, '$1'+format+'$2');
ohc_regex(/(\{\{dts\s*\|\s*format=)(?:dmy|mdy)(\s*\|\s*@year\s*\|\s*@month\s*\|\s*@day\s*\}\})/gi, '$1'+format+'$2');
ohc_regex(/(\{\{dts\s*\|\s*@year\s*\|\s*@month\s*\|\s*@day\s*)(\}\})/gi, '$1|format='+format+'$2');
// stripping – {{dts}} serves no purpose inside date parameters
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)\{\{dts\s*\|\s*format=(?:dmy|mdy)\s*\|\s*([|}]*)\}\}(?=\s*[|}])/gi, "$1$2");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)\{\{dts\s*\|\s*([|}]*)(?:\|\s*format=(?:dmy|mdy)\s*|)\}\}(?=\s*[|}])/gi, "$1$2");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)\{\{dts\s*\|\s*format=(?:dmy|mdy)\s*\|\s*@YYYY(\s*\|\s*|-)@MM(\s*\|\s*|-)@DD\s*\}\}(?=\s*[|}])/gi, "$1@YYYY-@MM-@DD ");
ohc_regex(/(\|(?:[\w ]*)date\s*=\s*)\{\{dts\s*\|\s*@YYYY(\s*\|\s*|-)@MM(\s*\|\s*|-)@DD\s*\|\s*format=(?:dmy|mdy)\s*\}\}(?=\s*[|}])/gi, "$1@YYYY-@MM-@DD ");
}
// format parameter must be 'dmy' or 'mdy'
function ohc_fix_gr_template(format)
{
// GR - date formatting parameter
// per {GR} doc, insert date formatting param
regex(/(\{\{GR\|\d\d?)(?!\|date)/gi, '$1|dateform=' + format);
}
function ohc_dates_to_dmy()
{
regex(/\|df=[mdy]{3}(?:-all|)(?=\s*[|}])/gi, '');
regex(/\|format=mdy/gi, '|format=dmy');
//change start and end templates; vgrelease new v=2 or mdy is default
regex(/(\{\{(?:start date|end date)[^|}]*\|)df=ye?s?\|([^}]*df=ye?s?)/gi, '$1$2');
regex(/(\{\{vgrelease new[^|}]*\|)(?:v=1\||)([A-Z]{2,3}\|)/g, '$1v=2|$2');
ohc_regex(/(\|\s*(?:begin|end|first|last)\s*=\s*)@YYYY[-–]@MM[-–]@DD(?=\W\D)/gi, "$1@Day @Month @YYYY"); //spotted at [[Template:Infobox recurring event]]
//change birth and death templates
regex(/(\{\{(?:(?:Birth|Death) date(?: and age|)|launch|release|start)[^}]*\|)m(f=ye?s?)/gi, '$1d$2'); // substitute df for mf
regex(/(\{\{(?:(?:Birth|Death) date(?: and age|)|launch|release|start|film ?date|Wayback)[\|\d]+)(\}\})/gi, '$1|df=y$2'); //insert df parameter
regex(/(\{\{(?:(?:Birth|Death) date(?: and age|)|launch|release|start|film ?date|Wayback)[\|\d]+\|)df=ye?s?\|([^}]*df=ye?s?)/gi, '$1$2'); //put date format parameter at the end
regex(/(\{\{(?:start date|end date)\|)df=ye?s?\|((?:1[789]|20)\d{2}\}\})/gi, '$1$2'); //date format parameter not needed – year only
//convert date ranges (md,d,d-dy to d,d,d-dmy; md,d,d-d to d,d,d-dm; md,d,d to d,d,dm)
ohc_regex(/(\D\W)@Month((?:\s@day?,?){1,6}),?(\/|\s?(?:[-–]|–)\s?|(?:[ _]| )(?:and|&|to|or)(?:[ _]| )+?)@Day,?\s(?:of\s)?(@yyyy\W\D)/gi, "$1$2$3@Day @LMonth $4");
ohc_regex(/(\D\W)@Month((?:\s@day?,?){1,6}),?(\/|\s?(?:[-–]|–)\s?|(?:[ _]| )(?:and|&|to|or)(?:[ _]| )+?)@Day(?=\W\D)/gi, "$1$2$3@Day @LMonth");
// ohc_regex(/(\D\W)@Month((?:\s@day?,?){1,6}) @Day(?=\W\D)/gi, "$1$2 @Day @LMonth"); //disabling as unlikely and ungrammatical constructions causing false positives
//'md, md,' to 'dm, dm,'
ohc_regex(/(\D\W)@Month @Day, @Month @Day(?=[,\.]\W\D)/gi, "$1@Day1 @LMonth1, @Day2 @LMonth2");
//convert simple mdy dates to dmy
ohc_regex(/(p(?:g?\.?|age) ?\d+|p(?:p\.?|ages) ?\d+[-–]\d+), @Month((?:\s@day?,?){1,6}) @Day(?=\W\D)/gi, "$1. $2 @Day @LMonth");
ohc_regex(/(\D\W)@Month\s@Day,?\s@Year(\s?(?:AD|BC|CE|BCE)\W\D)/gi, "$1@Day @LMonth @Year$2");
ohc_regex(/(\|\s*term_(?:end|start)\d\s*=\s*|\D\W)@Month\s@Day,?\s@YYYY(?=\W\D)/gi, "$1@Day @LMonth @Year");
ohc_regex(/(\|\s*term_(?:end|start)\d\s*=\s*|\D\W)@Month\s@Day(?=\W\D)/gi, "$1@Day @LMonth");
//Month+day_number " 7th মার্চ" -> "7 মার্চ"
ohc_regex(/(\D\W)@Day\s@Month\s@YYYY(?=\W\D)/gi, "$1@Day @LMonth @Year");
ohc_regex(/(\D\W)@Day\s@Month(?=\W\D)/gi, "$1@Day @LMonth");
ohc_regex(/(\D\W)@Month\s@Day,?\s@Year(?=\W\D)/gi, "$1@Day @LMonth @Year");
//dates with embedded nbsp
ohc_regex(/(\D\W)@Month([ _]| )(\d{1,2},(?: | )\d{1,2}(?:\s(?:and|&|to|or)(?:[ _]| ))\d{1,2})(?=[^|\]\w])/gi, '$1$3$2@LMonth ');
ohc_regex(/(\D\W)@Month([ _]| |{{(?:break|nbsp)}})@Day,([ _]| |{{(?:break|nbsp)}})@YYYY(?=[^|\]\w])/gi, '$1@Day$2@Month$3@Year');
//remove comma
ohc_regex(/(\D\W)@Day\s@Month,\s?@Year(?=\W\D)/gi, "$1@Day @LMonth @Year");
// remove "the" from "on the dd month"
ohc_regex(/(on |by )the @Day\s@Month(?=[ ,\)\.])/gi, "$1@Day @LMonth");
//remove space from date range conversion
ohc_regex(/(\D ) @Day(\/|\s?(?:[-–]|–)\s?|(?:[ _]| )(?:and|&|to|or)(?:[ _]| )+?)/gi, "$1@Day$2");
}
function ohc_dates_to_mdy()
{
//change birth and death templates – mf=y is the default; vgrelease new v=2 or mdy is default
regex(/\|df=[mdy]{3}(?:-all|)(?=\s*[|}])/gi, '');
regex(/\|df=y(?:es|)(?=[|}\s])/gi, '');
regex(/(mf=y(?:es|)\|[^}]*\|)(?:mf=y(?:es|)\|)/gi, '$1');
regex(/(\{\{vgrelease new[^|}]*\|)(?:v=2\||)([A-Z]{2,3}\|)/g, '$1$2');
ohc_regex(/(\|\s*(?:begin|end|first|last)\s*=\s*)@YYYY[-–]@MM[-–]@DD(?=\W\D)/gi, "$1@Month @Day, @YYYY");
//convert date ranges (d,d,d-dmy to md,d,d-dy; d,d,d-dm to md,d,d-d; dm,d,d to md,d,d)
ohc_regex(/(\D\W)@Day((?:, @day){0,5})(\/|\s?(?:[-–]|–)\s?|\s(?:and|&|to|or)\s+?)@Day\s@Month,? @YYYY(?=\W\D)/gi, "$1@LMonth @Day1$2$3@Day2, @YYYY");
ohc_regex(/(\D\W)@Day((?:, @day){0,5})(\/|\s?(?:[-–]|–)\s?|\s(?:and|&|to|or)\s+?)@Day\s@Month(?=\W\D)/gi, "$1@LMonth @Day1$2$3@Day2");
// ohc_regex(/(\D\W)@Day((?:, @day){0,5})\s@Month,? @YYYY(?=\W\D)/gi, "$1@LMonth @Day1$2, @YYYY"); //disabling as unlikely and ungrammatical constructions causing false positives
// ohc_regex(/(\D\W)@Day((?:, @day){0,5})\s@Month(?=\W\D)/gi, "$1@LMonth @Day1$2"); //disabling as unlikely and ungrammatical constructions causing false positives
//'dm, dm,' to 'md, md,'
ohc_regex(/(\D\W)@Day @Month, @Day @Month(?=[,\.]?\W\D)/gi, "$1@LMonth1 @Day1, @LMonth2 @Day2");
//convert simple dmy dates to mdy
ohc_regex(/(p(?:g?\.?|age) ?\d+|p(?:p\.?|ages) ?\d+[-–]\d+), @Month((?:\s@day?,?){1,6}) @Day(?=\W\D)/gi, "$1. @LMonth $2 @Day");
ohc_regex(/(\D\W)@Day(\s| )@Month,?(\s| )@Year(?=\W\D)/gi, "$1@LMonth$2@Day,$3@Year");
ohc_regex(/(\|\s*term_(?:end|start)\d\s*=\s*|\D\W)@Day[ ]+@Month,?\s@YYYY(?=\W\D)/gi, "$1@LMonth @Day, @Year");
ohc_regex(/(\|\s*term_(?:end|start)\d\s*=\s*|\D\W)@Day[ ]+@Month(?=\W\D)/gi, "$1@LMonth @Day");
//Month+day_number "মার্চ 7th" -> "মার্চ 7"
ohc_regex(/(\D\W)@Month\s@Day@th?,?\s@YYYY(?=\W\D)/gi, "$1@LMonth @Day, @YYYY");
ohc_regex(/(\D\W)@Day@th?(\s| )@Month(\s| )@YYYY(?=\W\D)/gi, "$1@LMonth$2@Day,$3@YYYY");
ohc_regex(/(\D\W)@Month(\s| )@Day(?=\W\D)/gi, "$1@LMonth$2@Day");
ohc_regex(/(\D\W)@Day@th?(\s| )@Month([,\.])(?=\W\D)/gi, "$1@LMonth$2@Day$3");
//add comma where missing
ohc_regex(/(\D\W)@Month\s@Day,?\s@YYYY,?(?=\s\w)/gi, "$1@LMonth @Day, @YYYY,");
//rem superfluous comma where applicable
ohc_regex(/(\D[^\w\/])@Month\s@ZD,,(?=\W\D|\b)/gi, "$1@LMonth @Day,");
}
function ohc_fix_dmy_redundancies()
{
//month-range redundancies (repeating month names)
ohc_regex(/([\D]@dd)@th? @Month[ ]{1,2}((?:to|and|-|–)[ ]{1,2}@dd)@th? @Month/, "$1 $2 @LMonth", function(d1, d2) {
if (d1.m == d2.m) return true;
return false;
});
}
//not currently used
function ohc_fix_mdy_redundancies()
{
//month-range redundancies (repeating month names)
ohc_regex(/(@Month[ ]{1,2}@dd)@th? (to|and|-|–)[ ]{1,2}@Month[ ]{1,2}@DD@th?/, "$1 $2 @LDay", function(d1, d2) {
if (d1.m == d2.m) return true;
return false;
});
}
function ohc_expand_ref_dates()
{
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month @YYYY(?=\s*[|}])/gi, "$1@FullMonth @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Day @Month @YYYY(?=\s*[|}])/gi, "$1@Day @FullMonth @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month @Day, @YYYY(?=\s*[|}])/gi, "$1@FullMonth @Day, @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)((?:[01-2]?\d|30|31) |)@Month @YYYY ?(?:-|–|–) ?((?:[01-3]?\d|30|31) |)@Month @YYYY(?=\s*[|}])/gi, "$1$2 @FullMonth1 @YYYY1 – $3@FullMonth2 @YYYY2");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month @Day(, (?:19|20)\d\d) ?(?:-|–|–) ?@Month @Day, @YYYY(?=\s*[|}])/gi, "$1@FullMonth1 @Day1$2 – @FullMonth2 @Day2, @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Day @Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Day @Month @YYYY(?=\s*[|}])/gi, "$1@Day1 @FullMonth1$2 – @Day2 @FullMonth2 @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Month,? @YYYY(?=\s*[|}])/gi, "$1@FullMonth1$2 – @FullMonth2 @YYYY");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)((?:19|20)\d\d) ?(?:-|–|–) ?@Month @YYYY(?=\s*[|}])/gi, "$1@$2 – @FullMonth @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))((?:[01-2]?\d|30|31) |)@Month @YYYY ?(?:-|–|–) ?((?:[01-3]?\d|30|31) |)@Month @YYYY(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1$2@FullMonth1 @YYYY1 – $3@FullMonth2 @YYYY2");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))@Month @Day(, (?:19|20)\d\d) ?(?:-|–|–) ?@Month @Day, @YYYY(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1@FullMonth1 @Day1$2 – @FullMonth2 @Day2, @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))@Day @Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Day @Month @YYYY(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1@Day1 @FullMonth1$2 – @Day2 @FullMonth2 @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))@Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Month,? @YYYY(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1@FullMonth1$2 – @FullMonth2 @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))((?:19|20)\d\d) ?(?:-|–|–) ?@Month @YYYY(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1$2 – @FullMonth @YYYY");
}
function ohc_expand_all_dates()
{
ohc_regex(/([^-\w/])@Month @YYYY(?=[^-\w/])/gi, '$1@FullMonth @YYYY');
ohc_regex(/([^-\w/])@Day @Month @YYYY(?=[^-\w/])/gi, '$1@Day @FullMonth @YYYY');
ohc_regex(/([^-\w/])@Month @Day, @YYYY(?=[^-\w/])/gi, '$1@FullMonth @Day, @YYYY');
ohc_regex(/([^-\w/])((?:[01-2]?\d|30|31) |)@Month @YYYY ?(?:-|–|–) ?((?:[01-3]?\d|30|31) |)@Month @YYYY(?=[^-\w/])/gi, "$1$2@FullMonth1 @YYYY1 – $3@FullMonth2 @YYYY2");
ohc_regex(/([^-\w/])@Month @Day(, (?:19|20)\d\d) ?(?:-|–|–) ?@Month @Day, @YYYY(?=[^-\w/])/gi, "$1@FullMonth1 @Day1$2 – @FullMonth2 @Day2, @YYYY");
ohc_regex(/([^-\w/])@Day @Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Day @Month @YYYY(?=[^-\w/])/gi, "$1@Day1 @FullMonth1$2 – @Day2 @FullMonth2 @YYYY");
ohc_regex(/([^-\w/])@Month,?( (?:19|20)\d\d) ?(?:-|–|–) ?@Month,? @YYYY(?=[^-\w/])/gi, "$1@FullMonth1$2 – @FullMonth2 @YYYY");
ohc_regex(/([^-\w/])((?:19|20)\d\d) ?(?:-|–|–) ?@Month @YYYY(?=[^-\w/])/gi, "$1$2 – @FullMonth @YYYY");
ohc_regex(/([ ]\(|\|\s*)@Day @Month @YYYY(?=\)\W|\s*[|\n])/gi, "$1@Day @FullMonth @YYYY");
ohc_regex(/([ ]\(|\|\s*)@Month @Day,? @YYYY(?=\)\W|\s*[|\n])/gi, "$1@FullMonth @Day, @YYYY");
ohc_regex(/([ ]\((?:c\. |))@Month @YYYY ?(?:-|–| ) ?@Day @Month @YYYY(?=\)\W)/gi, "$1@FullMonth1 @YYYY1 – @Day @FullMonth2 @YYYY2");
ohc_regex(/([ ]\((?:c\. |))@Month @YYYY ?(?:-|–| ) ?@Month @YYYY(?=\)\W)/gi, "$1@FullMonth1 @YYYY1 – @FullMonth2 @YYYY2");
ohc_regex(/([ ]\((?:c\. |))@YYYY ?(?:-|–| ) ?@Month @YYYY(?=\)\W)/gi, "$1@YYYY1 – @FullMonth @YYYY2");
ohc_regex(/([ ]\((?:c\. |))@Month ?(?:-|–| ) ?@Month @YYYY(?=\)\W)/gi, "$1@FullMonth1 – @FullMonth2 @YYYY");
ohc_regex(/([ ]\((?:c\. |))@Month @YYYY(?=\)\W)/gi, "$1@FullMonth @YYYY");
}
function ohc_abbrev_ref_dates()
{
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Day @Month @YYYY(?=\s*[|}])/gi, "$1@Day @Mon @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))([12]?\d|30|31) (জানুয়ারি|ফেব্রুয়ারি|মার্চ|এপ্রিল|মে|জুন|জুলাই|আগষ্ট|অক্টোবর|Oct|নভেম্বর|ডিসেম্বর)(?:r?uary|ch|il|[ey]|ust|(?:t?em|o)ber)( (?:19\d|20[01])\d)(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1$2 $3$4");
ohc_regex(/([ ]\()@Day @Month @YYYY([^-\w\/])/gi, "$1@Day @Mon @YYYY$2");
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month @Day, @YYYY(?=\s*[|}])/gi, "$1@Mon @Day, @YYYY");
ohc_regex(/(<ref[^>]*>(?:[^<]+?[\s\(]|))(জানুয়ারি|ফেব্রুয়ারি|মার্চ|এপ্রিল|মে|জুন|জুলাই|আগষ্ট|অক্টোবর|Oct|নভেম্বর|ডিসেম্বর)(?:r?uary|ch|il|[ey]|ust|(?:t?em|o)ber) ([12]?\d|30|31),( (?:19\d|20[01])\d)(?=(?:[^-\w\/%,<][^<]*?|)<\/ref>)/gi, "$1$2 $3,$4");
ohc_regex(/([^-\w\/])@Month @Day, @YYYY([^-\w\/])/gi, '$1@Mon @Day, @YYYY$2');
}
//not currently used
function ohc_abbrev_all_dates()
{
ohc_regex(/([^-\w/])@Day @Month @YYYY(?=[^-\w/])/gi, '$1@Day @Mon @YYYY');
ohc_regex(/([^-\w/])@Month @Day, @YYYY(?=[^-\w/])/gi, '$1@Mon @Day, @YYYY');
}
//not currently used
function ohc_dmy_publication_dates()
{
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Month @Day,? @YYYY/gi, "$1@Day @Month @YYYY"); // conversion of dates in references outside of the protection loop (eg for quotations etc.)
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)(?:\[\[)?@YYYY(?:\]\][-–]\[\[|[-–])@MM[-–]@DD(?:\]\])?(?=\s*[|}])/gi, "$1@Day @Month @YYYY");
//replace ISO dates (within parentheses) only within refs
ohc_regex(/(<ref[^>]*>[^<]+?)([ ]\()@YYYY-@MM-@DD(\))(\W[^<]*?<\/ref>)/gi, "$1$2@Day @Month @YYYY$3$4");
}
function ohc_mdy_publication_dates()
{
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)@Day @Month @YYYY/gi, "$1@Month @Day, @YYYY"); // conversion of dates in references outside of the protection loop (eg for quotations etc.)
ohc_regex(/(\|\s*(?:publication|archive|access|air|)-?date\s*=\s*)(?:\[\[)?@YYYY(?:\]\][-–]\[\[|[-–])@MM[-–]@DD(?:\]\])?(?=\s*[|}])/gi, "$1@Month @Day, @YYYY");
//replace ISO dates (within parentheses) only within refs
ohc_regex(/(<ref[^>]*>[^<]+?)([ ]\()@YYYY-@MM-@DD(\))(\W[^<]*?<\/ref>)/gi, "$1$2@Month @Day, @YYYY$3$4");
}
function ohc_Bigendian_ref_dates() {
// Add a tag to the summary box
regex(/(\{\{use (?:mdy|dmy) dates)(?:\|cs1-dates=\w{1,2}|)(\|[^\}\|]+\}\})/gi, '$1|cs1-dates=y$2');
}
/** ------------------------------------------------------------------------ **/
/// EDIT SUMMARIES & USE DMY TEMPLATE
// this function modified 2019-03-27 from [[Special:Permalink/846525463]]
// format parameter must be 'dmy' or 'mdy'
function ohc_use_dates_template(format)
{
var txt = document.editform.wpTextbox1;
// current month-year
var currentDate = new Date();
var currmonth = currentDate.getMonth();
var curryear = currentDate.getFullYear();
var myMonths = ["জানুয়ারি", "ফেব্রুয়ারি", "মার্চ", "এপ্রিল", "মে", "জুন", "জুলাই", "আগষ্ট", "সেপ্টেম্বর", "অক্টোবর", "নভেম্বর", "ডিসেম্বর"];
var curryyyymm = myMonths[currmonth] + ' ' + curryear;
// step 1: normalize redirect template names to canonical template names 'Use dmy dates' or 'Use mdy dates' according to 'format' argument
// rename redirects 'dmy', 'mdy', 'usedmydates', 'usemdydates' ('usedmydates' and 'usemdydates' are not currently listed as redirects but might be encountered?)
regex(/\{\{ *(?:use(?:dmy|mdy)dates|dmy|mdy) *([\|\}])/gi, '{{Use ' + format + ' dates$1');
// rename redirects 'usedmy', 'usemdy', 'use dmy', 'use mdy' to canonical form
regex(/\{\{ *use *(?:dmy|mdy) *([\|\}])/gi, '{{Use ' + format + ' dates$1');
// strip leading / trailing whitespace from 'use xxx dates'
regex(/\{\{ *use (?:dmy|mdy) dates *([\|\}])/gi, '{{Use ' + format + ' dates$1');
// step 2: look for a canonical template name (the above might not have had anthing to work on) ; if found continue, write new template else
var rxpdd = new RegExp("{{Use " + format + " dates", "gi"); // here we search for canonical form {{use xxx dates}} that has current date
var dflagfound = txt.value.search(rxpdd);
if (dflagfound === -1) {
txt.value = '{{Use ' + format + ' dates|date=' + curryyyymm + '}}\r\n' + txt.value; // not found so insert new template at top of wikitext
return; // and done
}
// step 3: rewrite existing canonical templates; add or refresh |date= to current month year; preserve |cs1-dates= as is
regex(/(\{\{use (?:dmy|mdy) dates)\| *date *=[^\|]*\| *cs1\-dates *= *([^\|\}]*?) *\}\}/gi, '$1|date=' + curryyyymm + '|cs1-dates=$2}}'); // with |date= followed by |cs1-dates=
regex(/(\{\{use (?:dmy|mdy) dates)\| *cs1\-dates *= *([^\|\}]*?) *\| *date *=[^\}]*\}\}/gi, '$1|date=' + curryyyymm + '|cs1-dates=$2}}'); // with |cs1-dates= followed by |date=
regex(/(\{\{use (?:dmy|mdy) dates)\| *date *=[^\|\}]*\}\}/gi, '$1|date=' + curryyyymm + '}}'); // with |date=
regex(/(\{\{use (?:dmy|mdy) dates)\| *cs1\-dates *= *([^\|\}]*?) \}\}/gi, '$1|date=' + curryyyymm + '|cs1-dates=$2}}'); // with |cs1-dates
regex(/(\{\{use (?:dmy|mdy) dates)\}\}/gi, '$1|date=' + curryyyymm + '}}'); // no parameters so add |date=curryyyymm
regex(/(\{\{[\t_ ]*use[\t_ ]+(?:dmy|mdy)[\t_ ]+dates[^{}]*\}\})([\r\n\t ]*)(\{\{[\t ]*short description[^{}]*\}\})/gi, '$3$2$1'); // repositioning "short description" template at the top
}
/** ------------------------------------------------------------------------ **/
/// DATE PROTECTION
var linkmap = [];
function ohc_protect_dates()
{
// protects dates within links, quotes, etc
// the sensitive part is stored and replaced with a unique identifier,
// which is later replaced with the stored part.
var protect_function = function(s, begin, replace, end) {
linkmap.push(replace);
return begin + "⍌"+(linkmap.length-1)+"⍍" + end;
};
//\|x=
regex(/(<timeline>)([\s\S]*?)(<\/timeline>)/gi, protect_function);
regex(/(<!--)([^<\{\}]*?)(-->)/gi, protect_function);
regex(/(<math>)([\s\S]*?)(<\/math>)/gi, protect_function);
regex(/((?:{(?:Wikimedia |)Commons ?|C ?))(Cat(?:egory|))\|[^{}*](})/gi, protect_function);
regex(/(<ref)([^>]+)(>)/gi, protect_function);
regex(/((?:Category|Image|File):)([^|\]]*)([|\]])/gi, protect_function);
regex(/(\{(?:See ?also|Main))(\|[^}]*)(\})/gi, protect_function);
regex(/(\{\{(?:double ?|external ?|wide ?)image\s?\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:harvnb|r|wikisource)\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:Canadian federal by-election))([^}]+)(\}\})/gi, protect_function);
regex(/(\[(?:https?:|ftp:))([^\]]*)(\])/gi, protect_function);
regex(/(>[ ]*(?:https?:|ftp:))([^< >\]]*)([ ]*<)/gi, protect_function);
// regex(/(https?:|ftp:)([^\s\]]*)([\s\]])/gi, protect_function);
regex(/(<blockquote>)([\s\S]*?)(<\/blockquote>)/gi, protect_function);
regex(/(<noinclude>)([\s\S]*?)(<\/noinclude>)/gi, protect_function);
regex(/(<gallery)([\s\S]*?)(<\/gallery>)/gi, protect_function);
regex(/(<poem>)([\s\S]*?)(<\/poem>)/gi, protect_function);
regex(/(\{[^\{]{0,6}(?:quot[^|]{1,7}\s?|sic)\|)([^}]+)(\})/gi, protect_function);
regex(/((?:image\d?|image_skyline|image[ _]location\d?|image[ _]name|image[ _]file|img|map\d|pic)\s*=)([^|}]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|(?:trans-?|)title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|(?:trans-?|)title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:season)\s*=)(\s*\[\[[^\]]*\]\]\s*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:[xy])\s*=)(\s*[^\|]+\s*)(\|)/gi, protect_function);
regex(/(\{\{navsource\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:singlechart)\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:defaultsort|graph):)([^}]+)(\})/gi, protect_function);
regex(/(["“])([^"”\n]*)(["”])/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}[-–]\{\{by\|)([12]\d{3}[ ]*\}\}\))/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}(?:[-–]present|))(\))/gi, protect_function);
regex(/(\[\[)(4AD)(\]\]|\|)/gi, protect_function);
//retraining redirects containing a date
regex(/(\[\[)(?:সেপ্টেম্বর 3 Society)(\]\]|\|)/gi, '$1Jiusan Society$2');
regex(/(\[\[)(?:জুন 4th incident)(\]\]|\|)/gi, '$1Tiananmen Square protests of 1989$2');
regex(/(\[\[)(?:5@th? অক্টোবর (?:\(Serbia\)|overthrow))(\]\]|\|)/gi, '$1Overthrow of Slobodan Milošević$2');
regex(/(\[\[)(?:অক্টোবর 5@th?(?: \(Serbia\)| overthrow|, 2000))(\]\]|\|)/gi, '$1Overthrow of Slobodan Milošević$2');
//protect alternate meanings of 'মে' and 'মার্চ' (note: not case insensitive)
// ohc_regex(/([^\w])(@day,?\s{0,3}মে)([^\w][^\d])/g, protect_function); //disabled 26 মে – not case senstitve: false negatives in converting dates in মে
ohc_regex(/([^\w])(মার্চ\s@day)(\s(?:mile|kilomet|met))/g, protect_function);
//protect all links containing a date and some text
ohc_regex(/(\[\[)(\s*\w[^\]\|\n]+?@day[^\]\|\n]+?@fullmonth[^\]\|\n]*)(\]\]|\|)/gi, protect_function);
ohc_regex(/(\[\[)([^\]\|\n]*?@day[^\]\|\n]+?@fullmonth(?:,? @year|) \D[^\]\|\n]+\w\s*)(\]\]|\|)/gi, protect_function);
ohc_regex(/(\[\[)(\s*\w[^\]\|\n]+?@fullmonth[^\]\|\n]+?@day[^\]\|\n]*)(\|)/gi, protect_function);
ohc_regex(/(\[\[)([^\]\|\n]*?@fullmonth[^\]\|\n]+?@day(?:, @year|) \D[^\]\|\n]+\w\s*)(\]\]|\|)/gi, protect_function);
//protects certain dates
regex(/(\[\[)(4AD)([\s\S])/gi, protect_function);
regex(/([\s\S])(মার্চ (?:8|14) Alliance)([\s\S])/gi, protect_function);
regex(/([\s\S])(23rd মার্চ 1931: Shaheed)([\s\S])/gi, protect_function);
regex(/([\s\S])(Long মার্চ [1-7])([\s\S])/gi, protect_function);
regex(/([\s\S])(National সেপ্টেম্বর 11 Memorial & Museum)([\s\S])/gi, protect_function);
regex(/([\s\S])(Queen Elizabeth II সেপ্টেম্বর 11th Garden)([\s\S])/gi, protect_function);
regex(/([\s\S])(Bombay মার্চ 12)([\s\S])/gi, protect_function);
regex(/([\s\S])(movement 2 জুন)([\s\S])/gi, protect_function);
regex(/([\s\S])(6th\sof অক্টোবর City)([\s\S])/gi, protect_function);
regex(/([\s\S])(Party Workers' Liberation Front 30th of মে)([\s\S])/gi, protect_function);
regex(/([\s\S])(4th\sof আগষ্ট regime)([\s\S])/gi, protect_function);
regex(/([\s\S])(19th\sof এপ্রিল movement)([\s\S])/gi, protect_function);
regex(/([\s\S])(জুন 4th (?:incident|museum))([\s\S])/gi, protect_function);
regex(/([\s\S])(The Battle Of Marston Moor \(জুলাই 2nd,? 1644\))([\s\S])/gi, protect_function);
regex(/([\s\S])(Night of জানুয়ারি 16th)([\s\S])/gi, protect_function);
regex(/([\s\S])(Observance of 5th নভেম্বর Act)([\s\S])/gi, protect_function);
regex(/(\[\[)(13 মে incident)(\|)/gi, protect_function);
regex(/(\'\'\')(22nd\sof মে)(\'\'\')/gi, protect_function);
regex(/(\[\[)(22nd\sof মে \(film\))([|\]\'])/gi, protect_function);
regex(/(\[\[)(জুলাই 12, 2007, Baghdad airstrike)(\|)/gi, protect_function);
regex(/(\[\[)(নভেম্বর 1828)(\]\])/gi, protect_function);
regex(/(.)(4th\sof জুলাই)(.)/gi, protect_function);
regex(/(\[\[)((?:2300|2000)\s?AD)(\]\])/gi, protect_function);
regex(/(\[\[)(29 ফেব্রুয়ারি)(\]\])/gi, protect_function);
regex(/(\[\[)(ফেব্রুয়ারি 29)(\]\])/gi, protect_function);
// works names
// regex(/(.)(protect string)(.)/gi, protect_function);
regex(/(.)(11\/22\/63)(.)/gi, protect_function);
regex(/(.)(1\.1\.1994)(.)/gi, protect_function);
regex(/([\s\S])(2\.13\.61)([\s\S])/gi, protect_function);
// regex(/(.)(4th of জুলাই, Asbury)(.)/gi, protect_function); //protected above globally
regex(/(.)(26 অক্টোবর 1993)(.)/gi, protect_function);
regex(/(\[\[|\'\')(জুন 1, 1974)(.)/gi, protect_function);
regex(/(\[\[|\'\')(জুলাই 15, 1972)(.)/gi, protect_function);
regex(/(\[\[|\'\')(আগষ্ট 1914)(.)/gi, protect_function);
ohc_regex(/(\[\[|\'\')(@fullmonth @dd@th? \((?:@yyyy |)film\)(?:\|@fullmonth @dd@th?|))([\s\S])/gi, protect_function);
ohc_regex(/(\[\[|\'\')(@dd@th? @fullmonth \((?:@yyyy |)film\)(?:\|@dd@th? @fullmonth|))([\s\S])/gi, protect_function);
// regex(/(Les Bains Douches 18)( ডিসেম্বর 1979)(.)/gi, protect_function);
regex(/(আগষ্ট)( 7, 4:15)(.)/gi, protect_function);
regex(/(In Concert, Zürich, অক্টোবর)( 28, 1979)(.)/gi, protect_function);
regex(/(CBGB OMFUG Masters: Live জুন)( 29, 2001)(.)/gi, protect_function);
ohc_regex(/(Live at [\w ]*,? [1-3]\d)( @fullmonth)(.)/gi, protect_function);
ohc_regex(/(Live at [\w ]*,? @fullmonth)( @dd)(.)/gi, protect_function);
ohc_regex(/(Bootmoon Series: [\w ]*[-–] @fullmonth)( @day, @YYYY)(.)/gi, protect_function);
// expressions and grammatical strings including modal "মে"
regex(/(\W)((?:\d{1,3}) মে)((?: have(?: \w+|)|) be)/g, protect_function);
regex(/(\W)((?:\d{3}|3[2-9]|[4-9]\d)\/\d ডিসেম্বর(?:l\.?|))(\W)/gi, protect_function);
regex(/(\W)((?:\d{2,3})\/\d ডিসেম্বর(?:l\.?|))( \(\d\d? overs)/gi, protect_function);
}
/** ------------------------------------------------------------------------ **/
/// DATE PROTECTION FOR ALL REF SECTION
var linkmap = [];
function ohc_protect_dates_ref()
{
// protects dates within links, quotes, etc
// the sensitive part is stored and replaced with a unique identifier,
// which is later replaced with the stored part.
var protect_function = function (s, begin, replace, end) {
linkmap.push(replace);
return begin + "⍌" + (linkmap.length - 1) + "⍍" + end;
};
regex(/(<ref\s?(?:>|name=[^>]+))([^<]+)(<\/ref>)/gi, protect_function);
regex(/(<timeline>)([\s\S]*?)(<\/timeline>)/gi, protect_function);
regex(/(<math>)([\s\S]*?)(<\/math>)/gi, protect_function);
regex(/((?:{(?:Wikimedia |)Commons ?|C ?))(Cat(?:egory|))\|[^{}*](})/gi, protect_function);
regex(/((?:Category|Image|File):)([^|\]]*)([|\]])/gi, protect_function);
regex(/(\{(?:See ?also|Main))(\|[^}]*)(\})/gi, protect_function);
regex(/(\{\{(?:double ?|external ?|wide ?)image\s?\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:harvnb|wikisource)\|)([^}]+)(\})/gi, protect_function);
regex(/(\[(?:https?:|ftp:))([^\]]*)(\])/gi, protect_function);
regex(/(>[ ]*(?:https?:|ftp:))([^< >\]]*)([ ]*<)/gi, protect_function);
//regex(/(https?:|ftp:)([^\s\]]*)([\s\]])/gi, protect_function);
regex(/(<blockquote>)([\s\S]*?)(<\/blockquote>)/gi, protect_function);
regex(/(\{[^\{]{0,6}(?:quot[^|]{1,7}\s?|sic)\|)([^}]+)(\})/gi, protect_function);
regex(/((?:image\d?|image_skyline|image[ _]location\d?|image[ _]name|image[ _]file|img|pic)\s*=)([^|}]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:season)\s*=)(\s*\[\[[^\]]*\]\]\s*)([|}])/gi, protect_function);
regex(/(\{\{navsource\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{singlechart\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{defaultsort:)([^}]+)(\})/gi, protect_function);
regex(/([\s\(>]")([^"\n]*)(")/gi, protect_function);
regex(/([\s\(>]“)([^”\n]*)(”)/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}[-–]\{\{by\|)([12]\d{3}[ ]*\}\}\))/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}(?:[-–]present|))(\))/gi, protect_function);
//retraining redirects containing a date
regex(/(\[\[)(?:সেপ্টেম্বর 3 Society)(\]\]|\|)/gi, '$1Jiusan Society$2');
regex(/(\[\[)(?:জুন 4th incident)(\]\]|\|)/gi, '$1Tiananmen Square protests of 1989$2');
ohc_regex(/(\[\[)(?:5@th? অক্টোবর (?:\(Serbia\)|overthrow))(\]\]|\|)/gi, '$1Overthrow of Slobodan Milošević$2');
ohc_regex(/(\[\[)(?:অক্টোবর 5@th?(?: \(Serbia\)| overthrow|, 2000))(\]\]|\|)/gi, '$1Overthrow of Slobodan Milošević$2');
//protect alternate meanings of 'মে' and 'মার্চ' (note: not case insensitive)
ohc_regex(/([^\w])(@day,?\s{0,3}মে)([^\w][^\d])/g, protect_function);
ohc_regex(/([^\w])(মার্চ\s@day)(\s(?:mile|kilomet|met))/g, protect_function);
//protect all links containing a date and some text
ohc_regex(/(\[\[)(\s*\w[^\]\|\n]+?@day[^\]\|\n]+?@fullmonth[^\]\|\n]*)(\]\]|\|)/gi, protect_function);
ohc_regex(/(\[\[)([^\]\|\n]*?@day[^\]\|\n]+?@fullmonth(?:,? @year|) \D[^\]\|\n]+\w\s*)(\]\]|\|)/gi, protect_function);
ohc_regex(/(\[\[)(\s*\w[^\]\|\n]+?@fullmonth[^\]\|\n]+?@day[^\]\|\n]*)(\|)/gi, protect_function);
ohc_regex(/(\[\[)([^\]\|\n]*?@fullmonth[^\]\|\n]+?@day(?:, @year|) \D[^\]\|\n]+\w\s*)(\]\]|\|)/gi, protect_function);
//protects certain dates
regex(/(\[\[)(4AD)([\s\S])/gi, protect_function);
regex(/([\s\S])(মার্চ (?:8|14) Alliance)([\s\S])/gi, protect_function);
regex(/([\s\S])(23rd মার্চ 1931: Shaheed)([\s\S])/gi, protect_function);
regex(/([\s\S])(Long মার্চ [1-7])([\s\S])/gi, protect_function);
regex(/([\s\S])(Bombay মার্চ 12)([\s\S])/gi, protect_function);
regex(/([\s\S])(movement 2 জুন)([\s\S])/gi, protect_function);
regex(/([\s\S])(6th\sof অক্টোবর City)([\s\S])/gi, protect_function);
regex(/([\s\S])(Party Workers' Liberation Front 30th of মে)([\s\S])/gi, protect_function);
regex(/([\s\S])(4th\sof আগষ্ট regime)([\s\S])/gi, protect_function);
regex(/([\s\S])(19th\sof এপ্রিল movement)([\s\S])/gi, protect_function);
regex(/([\s\S])(জুন 4th incident)([\s\S])/gi, protect_function);
regex(/([\s\S])(The Battle Of Marston Moor \(জুলাই 2nd,? 1644\))([\s\S])/gi, protect_function);
regex(/([\s\S])(Night of জানুয়ারি 16th)([\s\S])/gi, protect_function);
regex(/([\s\S])(Observance of 5th নভেম্বর Act)([\s\S])/gi, protect_function);
regex(/(\[\[)(13 মে incident)(\|)/gi, protect_function);
regex(/(\'\'\')(22nd\sof মে)(\'\'\')/gi, protect_function);
regex(/(\[\[)(22nd\sof মে \(film\))([|\]\'])/gi, protect_function);
regex(/(\[\[)(জুলাই 12, 2007, Baghdad airstrike)(\|)/gi, protect_function);
regex(/(\[\[)(নভেম্বর 1828)(\]\])/gi, protect_function);
regex(/(.)(4th\sof জুলাই)(.)/gi, protect_function);
regex(/(\[\[)((?:2300|2000)\s?AD)(\]\])/gi, protect_function);
regex(/(\[\[)(29 ফেব্রুয়ারি)(\]\])/gi, protect_function);
regex(/(\[\[)(ফেব্রুয়ারি 29)(\]\])/gi, protect_function);
// works names
//regex(/(.)(protect string)(.)/gi, protect_function);
regex(/(.)(11\/22\/63)(.)/gi, protect_function);
regex(/(.)(1\.1\.1994)(.)/gi, protect_function);
regex(/([\s\S])(2\.13\.61)([\s\S])/gi, protect_function);
//regex(/(.)(4th of জুলাই, Asbury)(.)/gi, protect_function); //protected above globally
regex(/(.)(26 অক্টোবর 1993)(.)/gi, protect_function);
regex(/(\[\[|\'\')(জুন 1, 1974)(.)/gi, protect_function);
regex(/(\[\[|\'\')(জুলাই 15, 1972)(.)/gi, protect_function);
regex(/(\[\[|\'\')(আগষ্ট 1914)(.)/gi, protect_function);
ohc_regex(/(\[\[|\'\')(@fullmonth @dd@th? \((?:@yyyy |)film\)(?:\|@fullmonth @dd@th?|))([\s\S])/gi, protect_function);
ohc_regex(/(\[\[|\'\')(@dd@th? @fullmonth \((?:@yyyy |)film\)(?:\|@dd@th? @fullmonth|))([\s\S])/gi, protect_function);
//regex(/(Les Bains Douches 18)( ডিসেম্বর 1979)(.)/gi, protect_function);
regex(/(আগষ্ট)( 7, 4:15)(.)/gi, protect_function);
regex(/(In Concert, Zürich, অক্টোবর)( 28, 1979)(.)/gi, protect_function);
regex(/(CBGB OMFUG Masters: Live জুন)( 29, 2001)(.)/gi, protect_function);
ohc_regex(/(Live at [\w ]*,? [1-3]\d)( @fullmonth)(.)/gi, protect_function);
ohc_regex(/(Live at [\w ]*,? @fullmonth)( @dd)(.)/gi, protect_function);
ohc_regex(/(Bootmoon Series: [\w ]*[-–] @fullmonth)( @day, @YYYY)(.)/gi, protect_function);
regex(/(\W)((?:\d{3}|3[2-9]|[4-9]\d)\/\d ডিসেম্বর(?:l\.?|))(\W)/gi, protect_function);
regex(/(\W)((?:\d{2,3})\/\d ডিসেম্বর(?:l\.?|))( \(\d\d? overs)/gi, protect_function);
}
/** ------------------------------------------------------------------------ **/
/// DATE PROTECTION FOR SLASH modules
var linkmap = [];
function ohc_protect_dates_slash()
{
// protects dates within links, quotes, etc
// the sensitive part is stored and replaced with a unique identifier,
// which is later replaced with the stored part.
var protect_function = function (s, begin, replace, end) {
linkmap.push(replace);
return begin + "⍌" + (linkmap.length - 1) + "⍍" + end;
};
regex(/(<ref\s?(?:>|name=[^>]+))([^<]+)(<\/ref>)/gi, protect_function);
regex(/(<timeline>)([\s\S]*?)(<\/timeline>)/gi, protect_function);
regex(/(<math>)([\s\S]*?)(<\/math>)/gi, protect_function);
regex(/((?:{(?:Wikimedia |)Commons ?|C ?))(Cat(?:egory|))\|[^{}*](})/gi, protect_function);
regex(/((?:Category|Image|File):)([^|\]]*)([|\]])/gi, protect_function);
regex(/(\{(?:See ?also|Main))(\|[^}]*)(\})/gi, protect_function);
regex(/(\{\{(?:double ?|external ?|wide ?)image\s?\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{(?:harvnb|wikisource)\|)([^}]+)(\})/gi, protect_function);
regex(/(\[(?:https?:|ftp:))([^\]]*)(\])/gi, protect_function);
regex(/(>[ ]*(?:https?:|ftp:))([^< >\]]*)([ ]*<)/gi, protect_function);
//regex(/(https?:|ftp:)([^\s\]]*)([\s\]])/gi, protect_function);
regex(/(<blockquote>)([\s\S]*?)(<\/blockquote>)/gi, protect_function);
regex(/(\{[^\{]{0,6}(?:quot[^|]{1,7}\s?|sic)\|)([^}]+)(\})/gi, protect_function);
regex(/((?:image\d?|image_skyline|image[ _]location\d?|image[ _]name|image[ _]file|img|pic)\s*=)([^|}]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:file(?:name\d?|)|image\d?|image location\d?|img|pic|Cover|title|quote|chapter|journal|url|archiveurl|work|doi|club|at|volume|reporter)\s*=)([^|\}⍍]*)([|}])/gi, protect_function);
regex(/([\|\{]\s*(?:season)\s*=)(\s*\[\[[^\]]*\]\]\s*)([|}])/gi, protect_function);
regex(/(\{\{navsource\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{singlechart\|)([^}]+)(\})/gi, protect_function);
regex(/(\{\{defaultsort:)([^}]+)(\})/gi, protect_function);
regex(/([\s\(>]")([^"\n]*)(")/gi, protect_function);
regex(/([\s\(>]“)([^”\n]*)(”)/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}[-–]\{\{by\|)([12]\d{3}[ ]*\}\}\))/gi, protect_function);
regex(/(.)(\(\{\{by\|[12]\d{3}\}\}(?:[-–]present|))(\))/gi, protect_function);
}
function ohc_unprotect_dates()
{
//unprotect all dates
regex(/[♫]/g, '');
regex(/⍌([0-9]+)⍍/g, function (x, n) {
var res = linkmap[n];
res = res.replace(/⍌([0-9]+)⍍/g, function (x, n) {
var res = linkmap[n];
res = res.replace(/⍌([0-9]+)⍍/g, function (x, n) {
var res = linkmap[n];
res = res.replace(/⍌([0-9]+)⍍/g, function (x, n) {
return linkmap[n];
});
return res;
});
return res;
});
return res;
});
}
function ohc_MOSNUM_edit_summary()
{
//Add a tag to the summary box
setoptions(minor = 'true');
setreason('date formats per [[MOS:DATEFORMAT]] by [[WP:MOSNUMscript|script]]', 'append');
doaction('diff');
}
function ohc_bigendien_edit_summary()
{
//Add a tag to the summary box
setoptions(minor = 'true');
setreason('ymd ref dates', 'append');
doaction('diff');
}
function ohc_expand_edit_summary()
{
//Add a tag to the summary box
setoptions(minor = 'true');
setreason('expand month names', 'append');
doaction('diff');
}
function ohc_slash_edit_summary()
{
//Add a tag to the summary box
setoptions(minor = 'true');
setreason('resolved slash dates', 'append');
doaction('diff');
}
function ohc_abbrev_edit_summary()
{
//Add a tag to the summary box
setoptions(minor = 'true');
setreason('abbrev month names', 'append');
doaction('diff');
}
/** ------------------------------------------------------------------------ **/
/// CUSTOMIZATION POINTS
function ohc_customize_all_to_dmy() {
}
function ohc_customize_all_to_mdy() {
}
function ohc_customize_body_to_dmy() {
}
function ohc_customize_body_to_mdy() {
}
function ohc_customize_ISO_to_dmy() {
}
function ohc_customize_ISO_to_mdy() {
}
/** ------------------------------------------------------------------------ **/
function ohc_body_dates_to_dmy()
{
ohc_use_dates_template('dmy');
ohc_fix_common_errors();
ohc_fix_dts_template('dmy');
ohc_fix_gr_template('dmy');
ohc_dates_to_dmy();
ohc_remove_leading_zeroes();
ohc_fix_dmy_redundancies();
}
function ohc_body_dates_to_mdy()
{
ohc_use_dates_template('mdy');
ohc_fix_common_errors();
ohc_fix_dts_template('mdy');
ohc_fix_gr_template('mdy');
ohc_dates_to_mdy();
ohc_remove_leading_zeroes();
ohc_fix_mdy_redundancies();
}
/** ------------------------------------------------------------------------ **/
function ohc_all_to_dmy_driver(e)
{
e.preventDefault();
ohc_delink_dates();
ohc_protect_dates();
ohc_fix_unambiguous_dates();
ohc_body_dates_to_dmy();
ohc_delink_ISO_to_dmy();
ohc_delink_year_in_X();
ohc_customize_all_to_dmy();
ohc_ISO_to_dmy_in_citations();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_all_to_mdy_driver(e)
{
e.preventDefault();
ohc_delink_dates();
ohc_protect_dates();
ohc_fix_unambiguous_dates();
ohc_body_dates_to_mdy();
ohc_delink_ISO_to_mdy();
ohc_delink_year_in_X();
ohc_customize_all_to_mdy();
ohc_ISO_to_mdy_in_citations();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_dmy_publication_dates_driver(e)
{
e.preventDefault();
// ohc_delink_dates();
ohc_protect_dates();
ohc_body_dates_to_dmy();
ohc_customize_body_to_dmy();
ohc_dmy_publication_dates();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_mdy_publication_dates_driver(e)
{
e.preventDefault();
// ohc_delink_dates();
ohc_protect_dates();
ohc_body_dates_to_mdy();
ohc_customize_body_to_mdy();
ohc_mdy_publication_dates();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_body_to_dmy_driver(e)
{
e.preventDefault();
// ohc_delink_dates();
ohc_protect_dates_ref();
ohc_body_dates_to_dmy();
ohc_customize_body_to_dmy();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_body_to_mdy_driver(e)
{
e.preventDefault();
// ohc_delink_dates();
ohc_protect_dates_ref();
ohc_body_dates_to_mdy();
ohc_customize_body_to_mdy();
ohc_unprotect_dates();
ohc_MOSNUM_edit_summary();
}
function ohc_ISO_to_dmy_driver(e)
{
e.preventDefault();
ohc_protect_dates();
ohc_delink_ISO_to_dmy();
ohc_customize_ISO_to_dmy();
ohc_unprotect_dates();
// ohc_MOSNUM_edit_summary();
}
function ohc_ISO_to_mdy_driver(e) {
e.preventDefault();
ohc_protect_dates();
ohc_delink_ISO_to_mdy();
ohc_customize_ISO_to_mdy();
ohc_unprotect_dates();
// ohc_MOSNUM_edit_summary();
}
function ohc_delink_year_in_X_driver(e)
{
e.preventDefault();
ohc_delink_year_in_X();
}
function ohc_expand_ref_dates_driver(e) {
e.preventDefault();
ohc_expand_ref_dates();
ohc_protect_dates();
ohc_unprotect_dates();
ohc_expand_edit_summary();
}
function ohc_expand_all_dates_driver(e)
{
e.preventDefault();
ohc_expand_ref_dates();
ohc_protect_dates();
ohc_expand_all_dates();
ohc_unprotect_dates();
ohc_expand_edit_summary();
}
function ohc_abbrev_ref_dates_driver(e)
{
e.preventDefault();
ohc_protect_dates();
ohc_abbrev_ref_dates();
ohc_unprotect_dates();
ohc_slash_edit_summary();
}
function ohc_UK_slash_dates_driver(e)
{
e.preventDefault();
ohc_expand_ref_dates();
ohc_protect_dates_slash();
ohc_UK_slash_dates_to_dmy();
ohc_unprotect_dates();
ohc_slash_edit_summary();
}
function ohc_US_slash_dates_driver(e)
{
e.preventDefault();
ohc_expand_ref_dates();
ohc_protect_dates_slash();
ohc_US_slash_dates_to_mdy();
ohc_unprotect_dates();
ohc_expand_edit_summary();
}
// </pre>
//সংশ্লিষ্ট পাতা ত্যাগ না করেই অন্য পাতা থেকে পুনর্নির্দেশ করা যাবে।
//<nowiki>
( function () {
var redir = $(mw.util.addPortletLink('p-tb','#','পুনর্নির্দেশক','পুনর্নির্দেশ তৈরিকারী','পুনর্নির্দেশ তৈরিকারী','','#pt-mytalk'));
$( redir ).click( function() {
var redirec = prompt("যে পাতা থেকে এই পাতায় পুনর্নির্দেশ করতে চান তার নাম লিখুন:", "");
if (redirec === null || redirec === "") {
alert( 'আপনি কোনও নাম প্রবেশ করান নি। দয়া করে আবার চেষ্টা করুন।' );
} else {
var params = {
action: 'edit',
title: redirec ,
createonly: 1,
prependtext: '#পুনর্নির্দেশ [[' + mw.config.get("wgPageName") + ']]',
appendtext: " \n" + '{{একটি পুনর্নির্দেশ}}',
summary: '[[' + mw.config.get("wgPageName") + ']] পাতায় পুনর্নির্দেশ করা হল' ,
format: 'json'
};
api = new mw.Api();
api.postWithToken( 'csrf', params ).done( function ( data ) {
alert( 'সফলভাবে ' + redirec + ' পাতা থেকে একটি পুনর্নির্দেশ তৈরি করা হয়েছে!' );
});
}
}
);
}() );
//</nowiki>
//<nowiki>
( function () {
var talkp = $(mw.util.addPortletLink('p-tb','#','স্বাগত জানান','স্বাগত জানান','স্বাগত জানান','','#pt-mytalk'));
$( talkp ).click( function() {
var talk = prompt("কোন ধরনের স্বাগত বার্তা যুক্ত করতে চান এমন টেমপ্লেটটি লিখুন {{Subst:}} সাথে, অন্যথায় পূর্বনির্ধারিত {{Subst:স্বাগতম}} যুক্ত হবে:", "");
if (talk === null || talk === "") {
var params1 = {
action: 'edit',
title: 'User talk:' + mw.config.get("wgRelevantUserName"),
prependtext: '{{Subst:স্বাগতম}}'
,
summary: 'ভারতপিডিয়ায় স্বাগতম!',
format: 'json'
};
api = new mw.Api();
api.postWithToken( 'csrf', params1 ).done( function ( data ) {
alert( 'সফলভাবে স্বাগত জানানো হয়েছে!' );
} );
} else {
var params = {
action: 'edit',
title: 'User talk:' + mw.config.get("wgRelevantUserName"),
prependtext: talk
,
summary: 'ভারতপিডিয়ায় স্বাগত!',
format: 'json'
};
api = new mw.Api();
api.postWithToken( 'csrf', params ).done( function ( data ) {
alert( 'সফলভাবে স্বাগত জানানো হয়েছে!' );
} );
}
}
);
}() );
//</nowiki>
//<nowiki>
console.time('mobileUndo');
$.when(mw.loader.using(['mediawiki.util', 'mediawiki.api', 'oojs-ui-windows', 'oojs-ui-core', 'oojs-ui.styles.icons-editing-core'])).then(function() {
return new mw.Api().getMessages(['editundo', 'Undo-nochange']);
}).then(function(globalmessages) {
var mobileUndo, i18n_lang = mw.config.get('wgUserLanguage');
var i18n = {
"en": {
"mu-success": "Successful",
"mu-fail": "Failed",
"mu-loading": "Loading...",
"editsummary-ad": "",
"mu-undo-successful": "Revert successful.... Reloading in a sec",
"mu-empty-reponse-from-server": "An empty response was received from the server",
"mu-edit-summary-prompt": "Add a reason to your edit summary !",
"mu-edit-summary-prompt-placeholder": "Reason...",
"mu-thank-success": "Thanked",
"mu-thank-confirm": "Send public thanks ?",
"mu-thank-loading": "Thanking...",
"mu-thank-successful": "You thanked $1.",
"mu-thank-failed": "Failed to thank $1.",
"mu-http-error":"HTTP error: "
},
"bn": {
"mu-success": "সফল",
"mu-fail": "ব্যর্থ হয়েছে",
"mu-loading": "লোড করা হচ্ছে...",
"editsummary-ad": "",
"mu-undo-successful": "পূর্বাবস্থায় ফেরত যাওয়া সফল হয়েছে.... পুনঃলোড করা হচ্ছে",
"mu-empty-reponse-from-server": "সার্ভার থেকে একটি খালি প্রতিক্রিয়া পাওয়া গেছে।",
"mu-edit-summary-prompt": "সম্পাদনা সারাংশে কারণ উল্লেখ করুন!",
"mu-edit-summary-prompt-placeholder": "কারণ...",
"mu-thank-success": "ধন্যবাদ দিয়েছেন",
"mu-thank-confirm": "প্রকাশ্য ধন্যবাদ জানাবেন?",
"mu-thank-loading": "ধন্যবাদ জানানো হচ্ছে...",
"mu-thank-successful": "আপনি $1 কে ধন্যবাদ জানিয়েছেন।",
"mu-thank-failed": "$1 কে ধন্যবাদ জানানো ব্যর্থ হয়েছে।",
"mu-http-error":"HTTP ত্রুটি: "
}
};
if(undefined === i18n[i18n_lang]){
i18n_lang = 'en';
}
if (mw.config.get('wgCanonicalSpecialPageName') === 'MobileDiff') {
mw.util.addCSS('#mw-mf-userinfo .mobileUndo-ui .mw-ui-icon.mw-ui-icon-before:before{display:none;}\n#mobileUndo > a > span.oo-ui-iconElement-icon.oo-ui-icon-editUndo.oo-ui-image-invert{display:inline-block; position:initial;}\n#mobileUndo > a{padding:0.57142857em 0.9375em;}'+
'#mw-mf-userinfo > div > div.mobileUndo-ui > a > div{line-height:normal;}');
if (mw.config.get('wgVisualEditor').pageLanguageDir === 'ltr') {
$('#mw-mf-userinfo .post-content').append($('<div>').attr('class', 'mobileUndo-ui').css({
'display': 'inline-block',
'float': 'right'
}));
} else {
$('#mw-mf-userinfo .post-content').append($('<div>').attr('class', 'mobileUndo-ui').css({
'display': 'inline-block',
'float': 'left'
}));
}
$('.mobileUndo-ui').append($('.mw-mf-action-button').remove().css({
'clear': 'none'
}));
if (!$('.warningbox').length && mw.config.get('wgRelevantPageIsProbablyEditable')) {
mobileUndo = new OO.ui.ButtonWidget({
label: (globalmessages.editundo).charAt(0).toUpperCase() + (globalmessages.editundo).slice(1),
icon: 'editUndo',
title: 'mobileUndo',
flags: ['destructive', 'primary'],
id: 'mobileUndo'
});
$('.mobileUndo-ui').append(mobileUndo.$element);
if (mw.config.get('wgVisualEditor').pageLanguageDir === 'ltr') {
$('#mobileUndo').css('margin-right', '0.3em');
} else {
$('#mobileUndo').css('margin-left', '0.3em');
}
}
window.wgRelevantUser = function() {
var user;
if ($('.mw-mf-user-link').text() !== '') {
user = $('.mw-mf-user-link').text();
} else {
user = $('#mw-mf-userinfo .post-content div:nth-child(2)').text();
}
return user;
};
window.wgRevId = ($('#mw-mf-diffarea > div.mw-mf-diff-info > h3 > a').attr('href')).split('&oldid=')[1];
var revId = wgRevId;
console.log(revId);
var username = wgRelevantUser();
$('#mw-mf-userinfo .post-content .mobileUndo-ui .mw-mf-action-button').click(function(e) {
e.preventDefault();
OO.ui.confirm(i18n[i18n_lang]['mu-thank-confirm']).done(function(confirmed) {
if (confirmed) {
$('.mw-mf-action-button').css('pointer-events', 'none').text(i18n[i18n_lang]['mu-thank-loading']);
var api = new mw.Api();
api.postWithToken('csrf', {
action: "thank",
rev: revId,
}).done(function(result) {
mw.notify((i18n[i18n_lang]['mu-thank-successful']).replace(/\$1/g, username));
$('.mw-mf-action-button').text(i18n[i18n_lang]['mu-thank-success']);
}).fail(function() {
mw.notify((i18n[i18n_lang]['mu-thank-failed']).replace(/\$1/g, username));
$('.mw-mf-action-button').text(i18n[i18n_lang]['mu-failed']);
});
}
});
});
console.timeEnd('mobileUndo');
mobileUndo.on('click', function() {
OO.ui.prompt(i18n[i18n_lang]['mu-edit-summary-prompt'], {
textInput: {
placeholder: i18n[i18n_lang]['mu-edit-summary-prompt-placeholder']
}
}).done(function(customEditSummary) {
$.when(mw.loader.using('mediawiki.api')).then(function() {
return new mw.Api().getMessages(['undo-summary'], {
amlang: mw.config.get('wgServerName').split('.wik')[0]
});
}).then(function(message) {
var editSummaryMessage = message['undo-summary'];
var editSummary = editSummaryMessage.replace(/\$1/g, revId);
editSummary = editSummary.replace(/\$2/g, username);
if (customEditSummary !== null) {
mobileUndo.setDisabled(true);
editSummary = editSummary + ' ' + customEditSummary + (i18n[i18n_lang]['editsummary-ad']);
//call the API
var api = new mw.Api({
ajax: {
headers: {
'Api-User-Agent': 'mobileUndo/2.0.2(https://en.wikipedia.org/wiki/User:FR30799386/undo.js)'
}
}
});
//Feedback UI
mobileUndo.setLabel(i18n[i18n_lang]['mu-loading']);
api.postWithToken('csrf', {
action: "edit",
title: mw.config.get('wgRelevantPageName'),
summary: editSummary,
undo: revId,
}).done(function(result) {
if (result.edit.code === 'abusefilter-warning' || result.edit.code === 'abusefilter-disallowed') {
mobileUndo.setLabel(i18n[i18n_lang]['mu-fail']);
OO.ui.alert($('<p>').html(result.edit.warning), {
title: result.edit.info
}).done(function() {});
return;
} else if (typeof result.edit.nochange !== 'undefined') {
mobileUndo.setLabel(i18n[i18n_lang]['mu-fail']);
OO.ui.alert($('<p>').html(globalmessages['Undo-nochange'])).done(function() {});
return;
}
console.log(result);
mobileUndo.setLabel(i18n[i18n_lang]['mu-success']);
mw.notify(i18n[i18n_lang]['mu-undo-successful']);
setTimeout(function() {
location.href = mw.util.getUrl('Special:MobileDiff/' + result.edit.newrevid);
}, 1500);
}).fail(function(code, jqxhr) {
mobileUndo.setLabel(i18n[i18n_lang]['mu-fail']);
var details;
if (code === 'http' && jqxhr.textStatus === 'error') {
details = i18n[i18n_lang]['mu-http-error'] + jqxhr.xhr.status;
} else if (code === 'http') {
details = i18n[i18n_lang]['mu-http-error'] + jqxhr.textStatus;
} else if (code === 'ok-but-empty') {
details = i18n[i18n_lang]['mu-empty response-from-server'];
} else {
details = jqxhr.error.info;
}
OO.ui.alert($('<p>').html(details)).done(function() {});
});
}
});
});
});
}
});
//End of code
//</nowiki>
window.wgRelevantUser = function() {
var user;
if ($('.mw-mf-user-link').text() !== '') {
user = $('.mw-mf-user-link').text();
} else {
user = $('#mw-mf-userinfo .post-content div:nth-child(2)').text();
}
return user;
};
var href = ($('#mw-mf-diffarea > div.mw-mf-diff-info > h3 > a').attr('href')) || '';
window.wgRevId = href.split('&oldid=')[1];
if( $('#mw-mf-diffarea > ul > li.revision-history-next').length ) {
$('#mw-mf-diffarea > div.mw-mf-diff-info > h3').append(
$('<a>').attr('id','mobileRestore').attr('href','javascript:void(0)').append(
$('<b>').text('(পুনরুদ্ধার)')
)
);
}
$('#mobileRestore').click(function(e){
e.preventDefault();
$('#mobileRestore b').text('(লোড হচ্ছে....)');
$('#mobileRestore').css('pointer-events','none');
var customEditSummary = prompt('পুনরুদ্ধারের কারণ (ঐচ্ছিক)');
if(customEditSummary === null) {
$('#mobileRestore b').text('(পুনরুদ্ধার)');
$('#mobileRestore').css('pointer-events','auto');
return;
}
var username =wgRelevantUser();
var editSummary = '[[Special:Contributions/'+username+'|'+username+']] ([[User talk:'+username+'|আলাপ]])-এর করা ' +wgRevId+ 'নং সংস্করণ পুনরুদ্ধার করা হয়েছে';
editSummary = editSummary + customEditSummary + ' (পুনরুদ্ধারায়ন)';
$.when(mw.loader.load('mediawiki.api')).then(function(){
var api = new mw.Api();
api.get({
'action':'query',
'prop':'revisions',
'titles':mw.config.get('wgRelevantPageName'),
'rvprop':'ids'
}).done(function(result){
if(result.query.pages) {
var StartRevId = result.query.pages[mw.config.get('wgRelevantArticleId')].revisions[0].revid;
console.log(StartRevId);
api.postWithToken('csrf', {
action: "edit",
title: mw.config.get('wgRelevantPageName'),
summary: editSummary,
undo:StartRevId,
undoafter:wgRevId,
}).done(function(result) {
if (typeof result.edit.nochange !== 'undefined') {
$('#mobileRestore b').text('(Failed)');
alert( 'No change occured');
return;
}
console.log(result);
$('#mobileRestore b').text('(সফলভাবে হয়েছে)')
mw.notify('সংস্করণ পুনরুদ্ধার সফল হয়েছে.... পুনঃলোড করা হচ্ছে');
setTimeout(function() {
location.href = mw.util.getUrl('Special:MobileDiff/' + result.edit.newrevid);
}, 1500);
}).fail(function(code, jqxhr) {
$('#mobileRestore b').text('(পুনরুদ্ধার কার্য বাতিল করা হয়েছে)');
var details;
if (code === 'http' && jqxhr.textStatus === 'error') {
details = 'HTTP error: ' + jqxhr.xhr.status;
} else if (code === 'http') {
details = 'HTTP error: ' + jqxhr.textStatus;
} else if (code === 'ok-but-empty') {
details = 'An empty response was given the server';
} else {
details = jqxhr.error.info;
}
alert( details );
});
} else {
alert('Failed to retrieve page details');
}
});
});
});
if(mw.config.get('wgCanonicalSpecialPageName') === 'MobileDiff') {
mw.loader.using('mediawiki.util', function() {
var ele_a = document.createElement('a');
ele_a.setAttribute('href', mw.util.getUrl(mw.config.get('wgRelevantPageName'), {action: 'history'}));
ele_a.innerHTML = '(ইতিহাস)';
document.getElementsByTagName('h2')[0].appendChild(ele_a);
});
}
//my common.js
$.when(mw.loader.using(['mediawiki.util'], $.ready)).done( function() {
mw.util.addPortletLink("p-tb", mw.config.get('wgArticlePath').replace('$1', "ব্যবহারকারী:রবি/common.js"), "common.js", "tb-awbscript", "Personal common.js");
});
if (mw.config.get('wgCanonicalNamespace')+':'+mw.config.get('wgTitle') === 'ব্যবহারকারী:রবি/common.js' && mw.config.get('wgAction') == 'view');
//শীর্ষক
(function($, mw) {
var scrollDownButtonId = 'scrollDownButton',
scrollUpButtonId = 'scrollUpButton';
if (mw.config.get('wgServerName').match(/wikimirror\.org$/)) {
scrollDownButtonId += '-zhwiki';
scrollUpButtonId += '-zhwiki'
}
var scrollButtonIcon = '//upload.wikimedia.org/wikipedia/commons/7/7f/Font_Awesome_5_regular_arrow-circle-up_green.svg';
if (!document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#Image', '1.1'))
scrollButtonIcon = '//upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Font_Awesome_5_regular_arrow-circle-up_green.svg/32px-Font_Awesome_5_regular_arrow-circle-up_green.svg.png';
$scrollDownButton = $('<img>', {
src: scrollButtonIcon,
id: scrollDownButtonId
}).css({
cursor: 'pointer',
opacity: 0.7,
position: 'fixed',
display: 'none',
right: '18px',
transform: 'rotate(180deg)',
'-webkit-transform': 'rotate(180deg)',
'-moz-transform': 'rotate(180deg)',
'-o-transform': 'rotate(180deg)',
'-ms-transform': 'rotate(180deg)'
}).on('click', function() {
$('html, body').animate({
scrollTop: $(document).height() - $(window).height()
}, 660)
}).on('mouseenter mouseleave', function(e) {
this.style.opacity = e.type === 'mouseenter' ? 1 : 0.7
}).appendTo('body');
$scrollUpButton = $('<img>', {
src: scrollButtonIcon,
id: scrollUpButtonId
}).css({
cursor: 'pointer',
opacity: 0.7,
position: 'fixed',
display: 'none',
right: '18px'
}).on('click', function() {
$('html, body').animate({
scrollTop: 0
}, 660)
}).on('mouseenter mouseleave', function(e) {
this.style.opacity = e.type === 'mouseenter' ? 1 : 0.7
}).appendTo('body');
var scrollButtonTimer;
$(window).on('scroll', function() {
var dingHeight = $('#bluedeck_ding>div').height() ? $('#bluedeck_ding>div').height() : 0;
$('#mw-ge-help-panel-cta-button').length > 0 ? $scrollDownButton.css('bottom', dingHeight + 75 + 'px') && $scrollUpButton.css('bottom', dingHeight + 116 + 'px') : $scrollDownButton.css('bottom', dingHeight + 24 + 'px') && $scrollUpButton.css('bottom', dingHeight + 65 + 'px');
$('#cat_a_lot').length > 0 || $('#proveit').length > 0 || $('.wordcount').length > 0 ? $scrollDownButton.css('left', '10px') && $scrollUpButton.css('left', '10px') : $scrollDownButton.css('left', 'unset') && $scrollUpButton.css('left', 'unset');
$(this).scrollTop() > 60 ? $scrollDownButton.fadeIn('slow') && $scrollUpButton.fadeIn('slow') : $scrollDownButton.fadeOut('slow') && $scrollUpButton.fadeOut('slow');
this.clearTimeout(scrollButtonTimer);
scrollButtonTimer = this.setTimeout(function() {
$scrollDownButton.fadeOut('slow');
$scrollUpButton.fadeOut('slow')
}, 2000)
});
$scrollDownButton.on('mouseenter', function() {
window.clearTimeout(scrollButtonTimer)
});
$scrollUpButton.on('mouseenter', function() {
window.clearTimeout(scrollButtonTimer)
})
})(jQuery, mw);
/**
* Add "Purge" content action link.
*
* Dependencies: mediawiki.util, mediawiki.api
*
* @source https://www.mediawiki.org/wiki/Snippets/Purge_action
* @revision 2016-05-22
*/
$( function () {
if ( $( '#ca-purge' ).length || !mw.config.get( 'wgIsArticle' ) ) return;
var node = mw.util.addPortletLink(
'p-cactions',
mw.util.getUrl( null, { action: 'purge' } ),
mw.config.get( 'skin' ) === 'vector' ? 'শোধন' : 'শোধন',
'ca-purge',
'এই পাতার সার্ভার ক্যাশে পরিস্কার করুন',
'শোধন'
);
$(node).on( 'click', function (e) {
new mw.Api().post( { action: 'purge', titles: mw.config.get( 'wgPageName' ) } ).then(function () {
location.reload();
}, function () {
mw.notify( 'শোধন ব্যর্থ হয়েছে', { type: 'error' } );
});
e.preventDefault();
});
});
//rollback confirmation
//<nowiki>
(function() {
if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
$(".mw-rollback-link").on('click', function(e) {
var linkText = $(e.target).text(),
count = linkText.match(/\d/) ? linkText.match(/\d+/)[0] : null,
message = 'রোলব্যাক ' + 'করছেন ' + mw.util.getParamValue('from',e.target.href) + ' কতৃক সম্পাদিত ' + (count ? count + ' edits' : 'সম্পাদনা') + '?';
if(!confirm(message)) return e.preventDefault();
});
}
}());
//</nowiki>
/** Quick Edit **/
// Edit sections of a page without leaving the article
// [[en:w:User:BrandonXLF/QuickEdit]]
// By [[en:w:User:BrandonXLF]]
(function() {
var mobile = mw.config.get('skin') === 'minerva',
apiSingleton,
titleRegexp = new RegExp(
mw.config.get('wgArticlePath').replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\$1/, '([^?]+)') +
'|[?&]title=([^&#]*)'
);
function api(func, params) {
if (!apiSingleton) apiSingleton = new mw.Api();
$.extend(params, {
errorformat: 'html',
errorlang: mw.config.get('wgUserLanguage'),
errorsuselocal: true
});
return apiSingleton[func](params).fail(function(_, data) {
mw.notify(apiSingleton.getErrorMessage(data), {
type: 'error',
tag: 'quickedit'
});
});
}
function getPageInfo(title, sectionID) {
return api('get', {
action: 'query',
curtimestamp: 1,
prop: 'revisions',
indexpageids: 1,
titles: title,
rvprop: ['timestamp', 'content'],
rvslots: 'main',
rvsection: sectionID
}).then(function(res) {
var rev = res.query.pages[res.query.pageids[0]].revisions[0];
return {
start: res.curtimestamp,
base: rev.timestamp,
full: rev.slots.main['*']
};
});
}
function getPreviewCallback(editor) {
editor.children('.preview').remove();
new OO.ui.ProgressBarWidget().$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none',
margin: '8px 0'
}).addClass('preview').appendTo(editor);
return function(html) {
editor.children('.preview').remove();
$('<div>').html(html).css({
margin: '8px 0',
border: '1px solid #a2a9b1',
padding: '8px',
overflowX: 'hidden'
}).addClass('preview').appendTo(editor);
};
}
function showCompare(editor, title, from, to) {
mw.loader.load('mediawiki.diff.styles');
api('post', {
action: 'compare',
fromslots: 'main',
'fromtext-main': from,
fromtitle: title,
frompst: 'true',
toslots: 'main',
'totext-main': to,
totitle: title,
topst: 'true'
}).then(function(r) {
return r.compare['*'] ? $('<table>').addClass('diff').append(
$('<colgroup>').append(
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content'),
$('<col>').addClass('diff-marker'),
$('<col>').addClass('diff-content')
)
).append(r.compare['*']) : 'কোন পার্থক্য নেই';
}).then(getPreviewCallback(editor));
}
// Parts taken from EditPage::extractSectionTitle and Parser::stripSectionName
function getSectionSummary(text) {
var match = text.match(/^(=+)(.+)\1\s*(\n|$)/);
return !match ? '' : '/* ' + match[2].trim()
// Strip internal link markup
.replace(/\[\[:?([^[|]+)\|([^[]+)\]\]/g, '$2')
.replace(/\[\[:?([^[]+)\|?\]\]/g, '$1')
// Strip external link markup
.replace(new RegExp('\\[(?:' + mw.config.get('wgUrlProtocols') + ')([^ ]+?) ([^\\[]+)\\]', 'ig'), '$2')
// Remove wikitext quotes
.replace(/(''|'''|''''')(?!')/g, '')
// Strip HTML tags
.replace(/<[^>]+?>/g, '') + ' */ ';
}
function showEditor(el) {
var progress = new OO.ui.ProgressBarWidget(),
heading = el.closest(':header'),
matcher = heading.nextUntil.bind(heading),
inserter = heading.after.bind(heading),
targetEl = el.siblings('.quickedit-target').last(),
titleMatch = targetEl.attr('href').match(titleRegexp),
title = decodeURIComponent(titleMatch[1] || titleMatch[2]),
sectionID = /[?&]v?e?section=T?-?(\d*)/.exec(targetEl.attr('href'))[1];
if (!heading.closest('.mw-parser-output').length) {
var articleContent = $('#mw-content-text .mw-parser-output');
matcher = function(selector) {
var child = articleContent.children(selector).first();
if (child.length) return child.prevAll();
return articleContent.children();
};
inserter = articleContent.prepend.bind(articleContent);
}
inserter(progress.$element.css({
maxWidth: '100%',
borderRadius: '0',
boxShadow: 'none'
}));
el.addClass('quickedit-loading');
$('.quickedit-hide').removeClass('quickedit-hide');
$('.quickedit-heading').removeClass('quickedit-heading');
$('#quickedit-editor').remove();
getPageInfo(title, sectionID).then(function(r) {
var start = r.start,
base = r.base,
full = r.full,
saving = false,
expanded = false,
remainderStart = full.match(/\n=+.+=+(?:\n|$)/),
part = remainderStart ? full.substring(0, remainderStart.index) : full,
remainder = remainderStart ? full.substring(remainderStart.index) : '',
level = 0,
editor;
full.replace(/^(=+).+?(=+)(?:\n|$)/, function(m, a, b) {
level = Math.min(a.length, b.length);
return m;
});
var levelMatch = 'h1';
for (var i = 2; i <= level; i++) levelMatch += ',h' + i + ':has(*)';
var partSection = matcher(':header:has(*)'),
fullSection = matcher(levelMatch),
textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: part
}),
summary = new OO.ui.TextInputWidget({
value: getSectionSummary(part)
}),
minor = new OO.ui.CheckboxInputWidget(),
save = new OO.ui.ButtonInputWidget({
label: 'পরিবর্তন প্রকাশ করুন',
title: 'Save your changes',
flags: ['primary', 'progressive']
}),
preview = new OO.ui.ButtonInputWidget({
label: 'প্রাকদর্শন',
title: 'Preview the new wikitext'
}),
compare = new OO.ui.ButtonInputWidget({
label: 'পরিবর্তনসমূহ',
title: 'View the difference between the current revision and your revision'
}),
cancel = new OO.ui.ButtonInputWidget({
useInputTag: true,
label: 'বাতিল',
title: 'Close the edit form and discard changes',
flags: ['secondary', 'destructive']
}),
more = new OO.ui.ButtonInputWidget({
label: '+',
title: 'Edit the entire section (including subsections)'
}),
buttons = new OO.ui.HorizontalLayout({
items: [save, preview, compare, cancel]
});
if (part != full) {
buttons.addItems([more], 3);
}
partSection.addClass('quickedit-hide');
heading.addClass('quickedit-heading');
el.removeClass('quickedit-loading');
progress.$element.remove();
textarea.$input.css({
borderRadius: '0'
});
summary.on('enter', function() {
save.emit('click');
});
save.on('click', function() {
if (saving) return;
var fullText = textarea.getValue() + (expanded ? '' : remainder);
saving = true;
save.setLabel('সংরক্ষণ করা হচ্ছে...');
compare.setDisabled(true);
preview.setDisabled(true);
cancel.setDisabled(true);
more.setDisabled(true);
api('postWithEditToken', {
action: 'edit',
title: title,
section: sectionID,
summary: summary.getValue(),
text: fullText,
minor: minor.isSelected() ? true : undefined,
notminor: minor.isSelected() ? undefined : true,
starttimestamp: start,
basetimestamp: base
}).then(function() {
api('get', {
action: 'parse',
page: mw.config.get('wgPageName'),
prop: ['text', 'categorieshtml']
}).then(function(r) {
var contentText = $('#mw-content-text'),
catLinks = $('#catlinks');
contentText.find('.mw-parser-output').replaceWith(r.parse.text['*']);
mw.hook('wikipage.content').fire(contentText);
catLinks.replaceWith(r.parse.categorieshtml['*']);
mw.hook('wikipage.categories').fire(catLinks);
saving = false;
});
}, function(code) {
if (code == 'editconflict') {
showEditConflict(editor, title, sectionID, fullText).then(function(r) {
start = r.start;
base = r.base;
textarea = r.textarea;
expanded = true;
});
}
compare.setDisabled(false);
preview.setDisabled(false);
cancel.setDisabled(false);
more.setDisabled(expanded);
saving = false;
save.setLabel('Save');
});
});
preview.on('click', function() {
api('post', {
action: 'parse',
title: title,
prop: 'text',
pst: 'true',
disablelimitreport: 'true',
disableeditsection: 'true',
sectionpreview: 'true',
disabletoc: 'true',
text: textarea.getValue()
}).then(function(r) {
return r.parse.text['*'] + '<div style="clear:both;"></div>';
}).then(getPreviewCallback(editor));
});
compare.on('click', function() {
showCompare(editor, title, part + (expanded ? remainder : ''), textarea.getValue());
});
cancel.on('click', function() {
editor.remove();
heading.removeClass('quickedit-heading');
fullSection.removeClass('quickedit-hide');
});
more.on('click', function() {
expanded = true;
textarea.setValue(textarea.getValue() + remainder);
fullSection.addClass('quickedit-hide');
more.setDisabled(true);
});
editor = $('<div id="quickedit-editor">').css({
overflowX: 'hidden'
}).append(
$('<div>').css({
backgroundColor: '#eaecf0',
borderBottom: '1px solid #a2a9b1',
marginBottom: '8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
}).addClass('quickedit-textarea'),
$('<div>').css({
border: '1px solid #a2a9b1',
borderWidth: '0 1px'
}).append(
$('<div>').css({
padding: '8px 4px 8px 8px',
display: 'table-cell',
verticalAlign: 'middle'
}).html('সারাংশ:'),
summary.$element.css({
width: '100%',
maxWidth: '100%',
padding: '8px 0px',
display: 'table-cell',
verticalAlign: 'middle'
}),
new OO.ui.FieldLayout(minor, {
label: new OO.ui.HtmlSnippet('🤏?'),
align: 'inline'
}).$element.css({
padding: '8px 8px 8px 4px',
display: 'table-cell',
verticalAlign: 'middle'
})
),
buttons.$element.css({
border: '1px solid #a2a9b1',
borderWidth: '0 1px',
padding: '0px 8px 0'
}),
title !== mw.config.get('wgPageName') ? $('<div>').css({
border: '1px solid #a2a9b1',
borderWidth: '0 1px',
padding: '0px 8px 8px'
}).append(
'Editing page: ',
$('<a>').attr('href', mw.config.get('wgArticlePath').replace('$1', title)).css({
fontWeight: 'bold'
}).text(title.replace(/_/g, ' '))
) : undefined
)
);
inserter(editor);
}, function() {
el.removeClass('quickedit-loading');
progress.$element.remove();
});
}
function showEditConflict(editor, title, sectionID, text) {
return getPageInfo(title, sectionID).then(function(r) {
var textarea = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: r.full
}),
textarea2 = new OO.ui.MultilineTextInputWidget({
rows: 1,
maxRows: 20,
autosize: true,
value: text,
});
function syncSize() {
textarea.styleHeight = -1;
textarea.adjustSize(true);
textarea2.styleHeight = -1;
textarea2.adjustSize(true);
var height = Math.max(textarea.$input.height(), textarea2.$input.height());
textarea.$input.height(height);
textarea2.$input.height(height);
}
textarea.$input.css({
borderRadius: '0'
});
editor.find('> :first-child > :first-child').remove();
$('<table>').css({
width: '100%',
border: '1px solid #a2a9b1',
borderBottom: 'none',
borderSpacing: '0',
margin: '0 !important'
}).append(
$('<tr>').append(
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Their version (to be saved)'),
$('<th>').css({
width: '50%',
paddingTop: '4px'
}).text('Your version')
),
$('<tr>').append(
$('<td>').css({
width: '50%',
padding: '4px 4px 0 8px'
}).append(
textarea.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
),
$('<td>').css({
width: '50%',
padding: '4px 8px 0 4px'
}).append(
textarea2.$element.css({
width: '100%',
maxWidth: '100%',
fontFamily: 'monospace, monospace'
})
)
)
).prependTo(editor.find('> :first-child'));
textarea.on('change', syncSize);
textarea2.on('change', syncSize);
syncSize();
showCompare(editor, title, text, r.full);
r.textarea = textarea;
return r;
});
}
function clickHandler(e) {
var el = $(e.target);
if (!el.hasClass('quickedit-editlink') || el.hasClass('quickedit-loading')) return;
e.preventDefault();
showEditor(el);
}
function addLinksToChildren(element) {
element.find('#quickedit-editor, .quickedit-section').remove();
element.find('.mw-editsection').each(function() {
$('[href*="section="]', this).last().after(
mobile ? '' : '<span class="quickedit-section"> | </span>',
$('<a>').html(mobile ? ' 💨' : 'দ্রুত সম্পাদনা').addClass('quickedit-section quickedit-editlink')
).addClass('quickedit-target');
});
}
$.when(mw.loader.using('oojs-ui-core'), $.ready).done(function() {
var body = $(document.body);
body.on('click', clickHandler);
addLinksToChildren(body);
mw.hook('wikipage.content').add(addLinksToChildren);
});
mw.loader.addStyleTag(
'.skin-minerva .mw-editsection { white-space: nowrap; }' +
'.skin-minerva .content .collapsible-heading .quickedit-section { visibility: hidden; }' +
'.skin-minerva .content .collapsible-heading.open-block .quickedit-section { visibility: visible; }' +
'.quickedit-hide { display: none !important; }' +
'.quickedit-loading, .quickedit-heading { color: #777; }'
);
})();
//খেলাঘর
$( document ).ready( function() {
mw.util.addPortletLink(
'p-personal',
mw.util.getUrl( 'Special:MyPage/খেলাঘর' ), //No need for a variable.
'খেলাঘর',
'pt-sandbox2',
'খেলাঘর',
null,
'#pt-sandbox'
);
});
//article creation
(function() {
$(document).ready(function() {
mw.loader.using( ['mediawiki.util'], function() {
{
var L=mw.util.addPortletLink("p-navigation", "#", "নতুন পাতা তৈরি");L.addEventListener("click", create);
return;
}
});
});
}());
function create() {
var art = prompt ("যে নিবন্ধটি তৈরি করতে চান তার নাম লিখুন:");
switch (art) {
case null:
case undefined:
case '':
//Do nothing
break;
default:
{
window.open('/index.php?preload=Special:New_page&editintro=&title=' + art + '&create=Create+page#/editor/all', '_blank');
}
}
}