/******************************************************************************
Companion file for MoveToDraft
******************************************************************************/
/* jshint laxbreak: true, undef: true, maxerr:999 */
/* globals console, window, document, $, mw */
// <nowiki>
// Wikitext strings
window.mtd.config.wikitext = {
"editsummary": window.m2d_editsummary || window.m2d_rationale ||
"[[WP:AFC|AFC]] draft",
"logMsg": "#[[$1]] moved to [[$2]] at ~~~~~",
"notificationHeading": "[[Draft:$1|$1]] moved to draftspace",
"notificationTemplate": window.m2d_notification ||
"Thanks for your contributions to [[Draft:$1|$1]]. Unfortunately, I do not think it is ready for publishing at this time$3.\nI have converted your article to a draft which you can improve, undisturbed for a while.\n\nPlease see more information at [[Help:Unreviewed new page]].\nWhen the article is ready for publication, please click on the \"Submit your draft for review!\" button at the top of the page OR move the page back. ~~~~",
"rationale": window.m2d_rationale ||
"[[WP:DRAFTIFY|Not ready]] for mainspace, incubate in draftspace."
};
// Custom change tag to be applied to all M2D actions, create at Special:Tags
window.mtd.config.changeTags = 'moveToDraft';
window.mtd.config.draftReasons = [
{ "long" : "it has no sources", "short" : "no sources" },
{ "long" : "it needs more sources to establish notability",
"short" : "more sources needed" },
{ "long" : "it has too many problems of language or grammar",
"short" : "language/grammar problems" },
{ "long" : "it is a poor translation", "short" : "poor translation" },
{ "long" : "it is promotional and reads like an advertisement",
"short" : "promotional/ad" },
{ "long" : "you may have a possible Conflict of Interest",
"short" : "possible COI" }
];
window.mtd.config.minimumAgeInMinutes = 60;
window.mtd.config.maximumAgeInDays = 90;
window.mtd.config.doNotLog = (
window.m2d_doNotLog ? true : false );
// Page data -- to be retreived later from api
window.mtd.config.pagedata = {};
window.mtd.config.pagedata.previousDraftification = false;
window.mtd.config.pagedata.contributors = {};
window.mtd.config.pagedata.notifyList = [];
window.mtd.config.processTimer = [];
// Helper functions
// - prettify an encoded page title (or at least replace underscores with
// spaces)
var getPageText = function(p) {
var t = mw.Title.newFromText( decodeURIComponent(p) );
if (t) {
return t.getPrefixedText();
} else {
return p.replace(/_/g, " ");
}
};
/**
* makeApiErrorMsg
*
* Makes an error message, suitable for displaying to a user, from the values
* that the MediaWiki Api passes to the failure callback function, e.g.
* `new mw.Api.get(queryObject}).done(successCallback).fail(failureCallback)`
*
* @param {string} code
* First paramater passed to failure callback function.
* @param {jQuery.jqXHR} jqxhr
* Second paramater passed to failure callback function.
* @return {string} Error message details, with in a format like
* "(API|HTTP) error: details"
*/
var makeErrorMsg = function(code, jqxhr) {
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 = 'Error: Got an empty response from the server';
} else {
details = 'API error: ' + code;
}
return details;
};
/**
* makeLink
*
* Makes a link to a en.Wikipedia page that opens in a new tab/window.
*
* @requires {Module} mediawiki.util
* @param {string} linktarget
* The target page.
* @param {string} linktext
* Text to display in the link. Optional, if not provided the target will be used.
* @return {jQuery} jQuery object containing the `<a>` element.
*/
var makeLink = function(linktarget, linktext) {
if ( linktext == null ) {
linktext = linktarget;
}
return $('<a>').attr({
'href': mw.util.getUrl(linktarget),
'target':'_blank'
}).text(linktext);
};
/* ========== Tasks ======================================================== */
// Grab page data - initial author, current wikitext, any redirects, if Draft:
// page already exists
var grabPageData = function() {
var isNonSignificantEdit = function ( revision ) {
//edits marked as minor
if ( revision.minor === "" ) {
return true;
}
if ( revision.anon === "" ) {
return true;
}
const tagsToIgnore = [ "pagetriage", "mw-undo", "AWB", "twinkle",
"shortdesc helper", "mw-manual-revert", "mw-undo", "mw-rollback",
"mw-reverted", window.mtd.config.changeTags ];
const intersectionArray = revision.tags.filter(
(element) => tagsToIgnore.includes(element));
if ( intersectionArray.length > 0 ) {
return true;
}
return false;
};
/* ---------- Initial author ------------------------------------------- */
/* Try making an api call for just the first revision - but if that is a
redirect, then get 'max' number of revisions, and look for first
non-redirect revision - use this as the initial author, not the creator
of the redirect.
*/
var processMaxRvAuthorQuery = function (result) {
var revisions = result.query.pages[window.mtd.config.mw.wgArticleId].revisions;
var patternForRedirect = /^\s*#redirect/i;
// blanking the contributor array
window.mtd.config.pagedata.contributors = {};
window.mtd.config.pagedata.lastEditTimestamp = revisions[revisions.length - 1].timestamp;
for ( let i = 0; i < revisions.length; i++ ) {
if ( !patternForRedirect.test(revisions[i]["*"]) ) {
if (window.mtd.config.pagedata.author === undefined) {
window.mtd.config.pagedata.author = revisions[i].user;
window.mtd.config.pagedata.creationTimestamp = revisions[i].timestamp;
}
// Find if there is a comment which indicates previous draftification
if( revisions[i].comment.search(/moved page \[\[.*\]\] to \[\[Draft:/) !== -1) {
window.mtd.config.pagedata.previousDraftification = true;
}
if( isNonSignificantEdit( revisions[i] ) ) {
continue;
}
// Ignoring edits made by the current user
// and non-significant edits (previous if condition)
if( revisions[i].user !== window.mtd.config.mw.wgUserName ) {
window.mtd.config.pagedata.lastEditTimestamp = revisions[i].timestamp;
}
// Calculating contribs per editor
if (window.mtd.config.pagedata.contributors[revisions[i].user] === undefined) {
window.mtd.config.pagedata.contributors[revisions[i].user] = 1;
} else {
window.mtd.config.pagedata.contributors[revisions[i].user]++;
}
}
}
// Check that we actually found an author (i.e. not all revisions were
// redirects)
if ( window.mtd.config.pagedata.author == null ) {
window.API.abort();
const retry = confirm("Could not retrieve page author.\n\nTry again?");
if ( retry ) {
screen0();
} else {
$("#M2D-modal").remove();
}
}
return removeBotsfromContributorList()
.then( sortContributorsByEdits );
};
var removeBotsfromContributorList = function () {
// Query to get bots from the contributor list
return window.API.get( {
action: "query",
list: "users",
ususers: Object.keys( window.mtd.config.pagedata.contributors ).join( "|" ),
usprop: ["groups"]
} )
.then( function(result) {
for ( let userResult of result.query.users ) {
if ( userResult.groups.indexOf( "bot" ) > -1 ) {
window.mtd.config.pagedata.contributors[ userResult.name ] = 0;
}
}
return Promise.resolve();
} );
};
var sortContributorsByEdits = function () {
// temporary array to ease sorting
let sortable = [];
for ( let contributor in window.mtd.config.pagedata.contributors ) {
if ( contributor !== window.mtd.config.mw.wgUserName ) {
sortable.push({"c": contributor,
"e": window.mtd.config.pagedata.contributors[contributor] });
}
}
sortable.sort( function( a, b ) {
// make sure that the page creator is on the top
if( a.c === window.mtd.config.pagedata.author ) {
return -100;
}
if( b.c === window.mtd.config.pagedata.author ) {
return 100;
}
// sorting by number of edits
return b.e - a.e;
});
window.mtd.config.pagedata.contributors = {};
for ( let i in sortable ) {
if ( i >= 5 || sortable[ i ].e < 1 ) {
// only show the top 5 contributors with more than 0 edits
break;
}
window.mtd.config.pagedata.contributors [ sortable[ i ].c ] = sortable[ i ].e;
}
return Promise.resolve();
};
var processAuthorQuery = function (result) {
// Check if page is currently a redirect
if ( result.query.pages[window.mtd.config.mw.wgArticleId].redirect ) {
window.API.abort();
alert("Error: " + window.mtd.config.mw.wgPageName + " is a redirect");
return;
}
// query to look for first non-redirect revision
return window.API.get( {
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
prop: "revisions",
rvprop: ["user", "content", "timestamp", "comment", "flags", "tags"],
rvlimit: "max",
rvdir: "newer"
} )
.then( processMaxRvAuthorQuery )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
window.API.abort();
const retry = confirm("Could not retrieve page author:\n"+makeErrorMsg(c, r)+"\n\nTry again?");
if ( retry ) {
screen0();
} else {
$("#M2D-modal").remove();
}
} );
};
//Get contributor Data
var getContributorData = function() {
setTaskStatus( 0, "started" );
return window.API.get( {
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
prop: ["revisions", "info"],
rvprop: "content",
rvlimit: 1,
rvdir: "newer"
} )
.then( processAuthorQuery )
.then( function() {
setTaskStatus( 0, "done" );
return Promise.resolve();
})
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
window.API.abort();
const retry = confirm("Could not retrieve page author:\n"+makeErrorMsg(c, r)+"\n\nTry again?");
if ( retry ) {
screen0();
} else {
$("#M2D-modal").remove();
}
return Promise.reject( "getContributorData failed!" );
} );
};
/* ---------- Current wikitext ----------------------------------------- */
var getCurrentWikiText = function() {
setTaskStatus( 1, "started" );
window.API.get( {
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
prop: "revisions",
rvprop: "content"
} )
.then( function(result) {
window.mtd.config.pagedata.oldwikitext = result.query.pages[window.mtd.config.mw.wgArticleId].revisions[0]["*"];
setTaskStatus( 1, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
window.API.abort();
const retry = confirm("Could not retrieve page wikitext:\n" +
makeErrorMsg(c, r) + "\n\nTry again?"
);
if ( retry ) {
screen0();
} else {
$("#M2D-modal").remove();
}
return Promise.reject( "getCurrentWikiText failed!" );
} );
};
//TODO(?): also get proposed Draft: page (to check if it is empty or not)
/* ---------- Redirects ------------------------------------------------ */
var redirectTitles = [];
var processRedirectsQuery = function(result) {
if ( !result.query || !result.query.pages ) {
// No results
window.mtd.config.pagedata.redirects = false;
return Promise.resolve();
}
// Gather redirect titles into array
$.each(result.query.pages, function(_id, info) {
redirectTitles.push(info.title);
});
// Continue query if needed
if ( result.continue ) {
doRedirectsQuery( result.continue );
return Promise.resolve();
}
// Check if redirects were found
if ( redirectTitles.length === 0 ) {
window.mtd.config.pagedata.redirects = false;
return Promise.resolve();
}
// Set redirects
window.mtd.config.pagedata.redirects = redirectTitles;
return Promise.resolve();
};
var doRedirectsQuery = function(extraQueryParam = null) {
setTaskStatus( 2, "started" );
const redirectsQuery = {
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
generator: "redirects",
grdlimit: 500
};
return window.API.get( $.extend( redirectsQuery, extraQueryParam ) )
.then( processRedirectsQuery )
.then( function() {
setTaskStatus( 2, "done" );
return Promise.resolve();
})
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
window.API.abort();
const retry = confirm("Could not retrieve redirects:\n" + makeErrorMsg(c, r) +
"\n\nTry again? (or Cancel to skip)");
if ( retry ) {
screen0();
} else {
window.mtd.config.pagedata.redirects = false;
setTaskStatus( 2, "skipped" );
return Promise.resolve();
}
} );
};
/* ---------- Review (Page Triage) status ------------------------------ */
var getPageTriageStatus = function() {
setTaskStatus( 3, "started" );
return window.API.get( {
action: "pagetriagelist",
page_id: window.mtd.config.mw.wgArticleId
} )
.then( function(result) {
if ( !result.pagetriagelist.pages.length ) {
var keepGoing = confirm( "WARNING: This page has already been reviewed by " +
"a New Page Patroller OR was created by an autopatrolled user. Are you " +
"sure you want to draftify this page?" );
if ( !keepGoing ) {
window.API.abort();
$("#M2D-modal").remove();
return Promise.resolve;
}
}
setTaskStatus( 3, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
window.API.abort();
const retry = confirm("Could not retrieve page triage status:\n"+ makeErrorMsg(c, r)+"\n\nTry again?");
if ( retry ) {
screen0();
} else {
$("#M2D-modal").remove();
}
return Promise.reject( "getPageTriageStatus failed!" );
} );
};
const promises = [];
promises.push( getContributorData() );
promises.push( getCurrentWikiText() );
promises.push( doRedirectsQuery() );
promises.push( getPageTriageStatus() );
Promise.allSettled( promises )
.then( showContributorScreen )
.catch( console.log.bind( console ) );
};
//Sets the status of tasks in the progress screen
var setTaskStatus = function( taskNumber, status, extraText ) {
var timeTaken;
if( taskNumber < 0 || taskNumber > 6 ) {
//Invalid tasknumber
return;
}
switch ( status ) {
case "started":
$( "#M2D-task" + taskNumber ).css( { "color": "#00F", "font-weight": "bold" } );
$( "#M2D-status" + taskNumber ).text( "In process" );
//Set the start time
window.mtd.config.processTimer[ taskNumber ] = new Date();
break;
case "done":
$( "#M2D-task" + taskNumber ).css( { "color": "#000", "font-weight": "" } );
$( "#M2D-status" + taskNumber ).html( "✓" );
$( "#M2D-status" + taskNumber ).css( "color", "green" );
timeTaken = (new Date()) - window.mtd.config.processTimer[ taskNumber ];
break;
case "skipped":
$( "#M2D-task" + taskNumber ).css( { "color": "#F00", "font-weight": "" } );
$( "#M2D-status" + taskNumber ).text( "Skipped!" );
break;
}
if ( extraText !== undefined && extraText !== "" ) {
$( "#M2D-status" + taskNumber ).append( " (" + extraText + ")" );
}
if ( timeTaken !== undefined ) {
$( "#M2D-status" + taskNumber ).append(
$('<span>').append( " (" + timeTaken + "ms)" )
.css('color', 'rgba(0, 0, 0, 0.4)')
);
}
};
//Check if redirectsuppression is allowed
var isRedirectSuppressionAllowed = function() {
//Only local sysops and extendedmovers are allowed to suppress redirects
return ( window.mtd.config.mw.wgUserGroups.includes( "sysop" ) ||
window.mtd.config.mw.wgUserGroups.includes( "extendedmover" ) );
};
//Move page
var movePage = function() {
setTaskStatus( 0, "started" );
// First check the page hasn't been draftified in the meantime
return window.API.get({
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
format: "json",
formatversion: "2"
}).then(function(response) {
var page = response && response.query && response.query.pages &&
response.query.pages[0];
if (!page) {
return $.Deferred().reject();
} else if (page.missing) {
return $.Deferred().reject("moveToDraft-pagemissing");
} else if (page.ns === 118 /* Draft NS */) {
return $.Deferred().reject("moveToDraft-alreadydraft");
} else if (page.ns !== window.mtd.config.mw.wgNamespaceNumber) {
return $.Deferred().reject("moveToDraft-movednamespace");
}
return window.API.postWithToken( "csrf", {
action: "move",
fromid: window.mtd.config.mw.wgArticleId,
to: window.mtd.config.inputdata.newTitle,
movetalk: 1,
noredirect: isRedirectSuppressionAllowed(),
reason: window.mtd.config.inputdata.rationale + " " +
window.mtd.config.draftReasonsShort,
tags: window.mtd.config.changeTags
} );
})
.then( function() {
setTaskStatus( 0, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r && r.textStatus === "abort" ) {
return;
} else if (c === "moveToDraft-pagemissing") {
alert( "The page no longer appears to exists. It may have been deleted." );
$( "#M2D-modal" ).remove();
window.location.reload();
return;
} else if (c === "moveToDraft-alreadydraft") {
alert( "Aborted: The page has already been moved to draftspace." );
$( "#M2D-modal" ).remove();
window.location.reload();
return;
}
const retry = confirm( "Could not move page:\n"
+ makeErrorMsg( c, r )
+ "\n\nTry again ?" );
if ( retry ) {
return movePage();
} else {
showOptionsScreen(true);
return Promise.reject( "Move failed!" );
}
} );
};
var tagRedirect = function() {
setTaskStatus( 6, "started" );
if ( isRedirectSuppressionAllowed () ) {
setTaskStatus( 6, "skipped" );
return Promise.resolve();
}
return window.API.postWithToken( "csrf", {
action: "edit",
title: window.mtd.config.mw.wgPageName,
prependtext: '{{Db-r2}}\n',
summary: '[[WP:R2|R2]] speedy deletion request (article moved to draftspace)',
tags: window.mtd.config.changeTags
} )
.then( function() {
// This null edit is needed until https://phabricator.wikimedia.org/T321192 is fixed
return window.API.postWithToken( 'csrf', {
action: 'edit',
title: window.mtd.config.mw.wgPageName,
prependtext: '',
summary: 'Null edit',
tags: window.mtd.config.changeTags
} );
} )
.then( function() {
setTaskStatus( 6, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) { return; }
const retry = confirm( "Could not tag redirect for speedy deletion:\n"+
makeErrorMsg(c, r) + "\n\nTry again ?" );
if ( retry ) {
return tagRedirect();
} else {
setTaskStatus( 6, "skipped" );
return Promise.resolve();
}
} );
};
//Find which images are non-free
var getImageInfo = function() {
setTaskStatus( 1, "started" );
var processImageInfo = function( result ) {
var nonFreeFiles = [];
if ( result && result.query ) {
$.each(result.query.pages, function( id, page ) {
if ( id > 0 && page.categories ) {
nonFreeFiles.push( page.title );
}
});
}
setTaskStatus( 1, "done", nonFreeFiles.length + " found" );
return Promise.resolve( nonFreeFiles );
};
return window.API.get( {
action: "query",
pageids: window.mtd.config.mw.wgArticleId,
generator: "images",
gimlimit: "max",
prop: "categories",
cllimit: "max",
clcategories: "Category:All non-free media",
} )
.then( function(result){
return processImageInfo( result );
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve( [] );
}
const retry = confirm("Could not find if there are non-free files:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
return getImageInfo();
} else {
setTaskStatus( 1, "skipped" );
return Promise.resolve( [] );
}
} );
};
//Comment out non-free files, turn categories into links, add draft template, list any redirects
var editWikitext = function( nonFreeFiles ) {
setTaskStatus( 2, "started" );
var redirectsList = ( !window.mtd.config.pagedata.redirects ) ? "" : "\n"+
"<!-- Note: The following pages were redirects to [[" + window.mtd.config.mw.wgPageName +
"]] before draftification:\n" +
"*[[" + window.mtd.config.pagedata.redirects.join("]]\n*[[") + "]]\n-->\n";
var wikitext = "{{Draft article}}\n" +
redirectsList +
window.mtd.config.pagedata.oldwikitext
.replace(/(\s*\{\{Drafts moved from mainspace(\|[^\}]+)?\}\})+/gm, "")
.replace(/((\[\[:?\s*[Cc]ategory\s*:[^\]]*\]\]\s*)+)/gm,
"{{Draft categories|\n$1\n}}\n") +
"\n{{subst:Drafts moved from mainspace}}";
// non-free files
// (derived from [[WP:XFDC]] - https://en.wikipedia.org/wiki/User:Evad37/XFDcloser.js )
if ( nonFreeFiles.length > 0 ) {
// Start building regex strings
var normalRegexStr = "(";
var galleryRegexStr = "(";
var freeRegexStr = "(";
for (let i=0; i<nonFreeFiles.length; i++ ) {
// Take off namespace prefix
const filename = nonFreeFiles[i].replace(/^.*?:/, "");
// For regex matching: first character can be either upper or lower case, special
// characters need to be escaped, spaces can be either spaces or underscores
const filenameRegexStr = "[" + mw.util.escapeRegExp(filename.slice(0, 1).toUpperCase()) +
mw.util.escapeRegExp(filename.slice(0, 1).toLowerCase()) + "]" +
mw.util.escapeRegExp(filename.slice(1)).replace(/ /g, "[ _]");
// Add to regex strings
normalRegexStr += "\\[\\[\\s*(?:[Ii]mage|[Ff]ile)\\s*:\\s*" + filenameRegexStr +
"\\s*\\|?.*?(?:(?:\\[\\[.*?\\]\\]).*?)*\\]\\]";
galleryRegexStr += "^\\s*(?:[Ii]mage|[Ff]ile):\\s*" + filenameRegexStr + ".*?$";
freeRegexStr += "\\|\\s*(?:[\\w\\s]+\\=)?\\s*(?:(?:[Ii]mage|[Ff]ile):\\s*)?" +
filenameRegexStr;
if ( i+1 === nonFreeFiles.length ) {
normalRegexStr += ")(?![^<]*?-->)";
galleryRegexStr += ")(?![^<]*?-->)";
freeRegexStr += ")(?![^<]*?-->)";
} else {
normalRegexStr += "|";
galleryRegexStr += "|";
freeRegexStr += "|";
}
}
// Check for normal file usage, i.e. [[File:Foobar.png|...]]
var normalRegex = new RegExp( normalRegexStr, "g");
wikitext = wikitext.replace(normalRegex, "<!-- Commented out: $1 -->");
// Check for gallery usage, i.e. instances that must start on a new line, eventually
// preceded with some space, and must include File: or Image: prefix
var galleryRegex = new RegExp( galleryRegexStr, "mg" );
wikitext = wikitext.replace(galleryRegex, "<!-- Commented out: $1 -->");
// Check for free usages, for example as template argument, might have the File: or Image:
// prefix excluded, but must be preceeded by an |
var freeRegex = new RegExp( freeRegexStr, "mg" );
wikitext = wikitext.replace(freeRegex, "<!-- Commented out: $1 -->");
}
return window.API.postWithToken( "csrf", {
action: "edit",
pageid: window.mtd.config.mw.wgArticleId,
text: wikitext,
summary: window.mtd.config.wikitext.editsummary,
tags: window.mtd.config.changeTags
} )
.then( function(){
setTaskStatus( 2, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve();
}
const retry = confirm("Could not edit draft artice:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
editWikitext(nonFreeFiles);
} else {
setTaskStatus( 2, "skipped" );
return Promise.resolve();
}
} );
};
var notifyContributors = function() {
if(window.mtd.config.pagedata.notifyList.length === 0 ||
!window.mtd.config.inputdata.notifyEnable ) {
setTaskStatus( 3, "skipped" );
return Promise.resolve();
}
setTaskStatus( 3, "started" );
const promises = [];
for ( let i in window.mtd.config.pagedata.notifyList ) {
promises.push( notifyContributor( window.mtd.config.pagedata.notifyList[i] ) );
}
return Promise.allSettled( promises )
.then( function () {
setTaskStatus( 3, "done" );
return Promise.resolve();
} );
};
var notifyContributor = function( contributor ) {
return window.API.postWithToken( "csrf", {
action: "discussiontoolsedit",
page: "User talk:" + contributor,
paction: "addtopic",
sectiontitle: window.mtd.config.inputdata.notifyMsgHead,
wikitext: window.mtd.config.inputdata.notifyMsg,
tags: window.mtd.config.changeTags
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve();
}
const retry = confirm("Could not edit author talk page:\n"+ makeErrorMsg(c, r)+"\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
return notifyContributor( contributor );
}
return Promise.resolve();
} );
};
var updateTalk = function() {
setTaskStatus( 4, "started" );
//if page exists, do a regex search/repace for class/importances parameters
var processTalkWikitext = function(result) {
var talkPageId = result.query.pageids[0];
if ( talkPageId < 0 ) {
setTaskStatus( 4, "done", "talk page does not exist" );
return Promise.resolve();
}
var oldTalkWikitext = result.query.pages[talkPageId].revisions[0]["*"];
var newTalkWikitext = oldTalkWikitext.replace(/(\|\s*(?:class|importance)\s*=\s*)[^|}]*(?=[^}]*\}\})/g, "$1");
if ( newTalkWikitext === oldTalkWikitext ) {
setTaskStatus( 4, "done", "no changes needed" );
return Promise.resolve();
}
return window.API.postWithToken( "csrf", {
action: "edit",
pageid: talkPageId,
section: "0",
text: newTalkWikitext,
summary: 'Remove class/importance from project banners',
tags: window.mtd.config.changeTags
} )
.then( function(){
setTaskStatus( 4, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve();
}
const retry = confirm("Could not edit draft's talk page:\n"
+ makeErrorMsg(c, r)
+ "\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
return updateTalk();
} else {
setTaskStatus( 4, "skipped" );
return Promise.resolve();
}
} );
};
//get talk page wikitext (section 0)
return window.API.get( {
action: "query",
titles: window.mtd.config.inputdata.newTitle.replace("Draft:", "Draft talk:"),
prop: "revisions",
rvprop: "content",
rvsection: "0",
indexpageids: 1
} )
.then( processTalkWikitext )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve();
}
const retry = confirm("Could not find draft's talk page:\n"
+ makeErrorMsg(c, r)
+ "\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
return updateTalk();
} else {
setTaskStatus( 4, "skipped" );
return Promise.resolve();
}
} );
};
var logDraftification = function() {
if (window.mtd.config.doNotLog) {
setTaskStatus( 5, "skipped" );
return Promise.resolve();
}
setTaskStatus( 5, "started" );
var logpage = "User:" + window.mtd.config.mw.wgUserName + "/Draftify_log";
var monthNames = window.mtd.config.mw.wgMonthNames.slice(1);
var now = new Date();
var heading = monthNames[now.getUTCMonth()] + " " +
now.getUTCFullYear();
// Get a list of sections of a page
var getSections = function( pageTitle ) {
return window.API.get( {
action: "parse",
page: pageTitle,
redirects: 1,
prop: "sections"
});
};
// Check if page exists
var doesPageExist = function( pageTitle ) {
return window.API.get( {
action: "query",
titles: pageTitle,
prop: "revisions",
redirects: 1,
rvlimit: 1,
indexpageids: 1
} )
.then ( function( result ) {
var id = result.query.pageids[0];
return Promise.resolve( id >= 0 );
} );
};
// Search within the result for the current month's heading
var searchSectionsForText = function( result, heading ) {
var sections = result.parse.sections;
if ( sections.length > 18 ) {
console.log( "Your draftify log file is large. " +
"Consider archiving old logs by year." );
}
for ( let i = sections.length - 1; i >= 0; i-- ) {
if ( sections[i].line === heading ) {
// Found the current month's section
return sections[i].index;
}
}
return -1;
};
var queryParams;
return doesPageExist( logpage )
.then ( function( result ) {
var editSummary = 'Logging [[' + window.mtd.config.inputdata.newTitle + ']] (' +
window.mtd.config.draftReasonsShort + ')';
if ( result ) {
return getSections( logpage )
.then ( function( result ) {
const sectionNumber = searchSectionsForText( result, heading );
if ( sectionNumber === -1 ) {
// Section for current month needs to be created
queryParams = {
text: window.mtd.config.inputdata.logMsg,
section: "new",
sectiontitle: heading,
summary: editSummary + ' (new month)',
};
} else {
// Append log line to current month's section
queryParams = {
appendtext: "\n" + window.mtd.config.inputdata.logMsg,
section: sectionNumber,
summary: editSummary,
};
}
} );
} else {
// Draftify_log page does not exist
var createlog = confirm("Log draftification (at " + logpage + ") ?");
if ( !createlog ) {
setTaskStatus( 5, "skipped" );
return Promise.resolve();
}
const logpageWikitext = "This is a log of pages moved to draftspace using the [[User:MPGuy2824/MoveToDraft|MoveToDraft]] script."
+ "\n\n==" + heading + "==\n" + window.mtd.config.inputdata.logMsg;
queryParams = {
prependtext: logpageWikitext,
summary: editSummary + ' (first draftification)',
nocreate: false
};
return Promise.resolve();
}
} )
.then( () => {
queryParams = $.extend({
action: "edit",
redirect: 1,
title: logpage,
tags: window.mtd.config.changeTags
},
queryParams );
return window.API.postWithToken( "csrf", queryParams );
} )
.then( function(){
setTaskStatus( 5, "done" );
return Promise.resolve();
} )
.catch( function( c, r ) {
if ( r.textStatus === "abort" ) {
return Promise.resolve();
}
const retry = confirm("Could not edit log page:\n" + makeErrorMsg(c, r) + "\n\n[Okay] to try again, or [Cancel] to skip");
if ( retry ) {
return logDraftification();
} else {
setTaskStatus( 5, "skipped" );
return Promise.resolve();
}
} );
};
var setupHeader = function(subText) {
$("#M2D-interface-header").append(
$("<button>").text("X")
.attr('title', 'Close')
.css('float', 'right')
.click(function(){
$("#M2D-modal").remove();
}),
$('<div>')
.append(
makeLink(window.mtd.config.script.location, 'Move To Draft'),
' <small>(v' + window.mtd.config.script.version + ')</small>',
subText
)
.css({"font-weight": "bold", "font-size": "large",
"margin": "0.25em", "text-align": "center"})
);
};
var notifyChange = function() {
$('#M2D-option-message-head').prop('disabled', !this.checked);
for (let key in window.mtd.config.draftReasons) {
$("#M2D-option-reasons-checkbox-"+key).prop("disabled", !this.checked);
}
$("#M2D-option-reasons-checkbox-other").prop("disabled", !this.checked);
$("#M2D-reason-other").prop("disabled", !this.checked);
if (this.checked) {
$('#M2D-option-message-preview-outer').show();
} else {
$('#M2D-option-message-preview-outer').hide();
}
};
// --- Interface screens ---
//0) Initial screen
var screen0 = function() {
$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty();
setupHeader("...");
$("#M2D-interface-content").text("Gathering data...");
$("#M2D-interface-content").append(
getProgressTasks(
[ 'Get contributor data',
'Get current wikiText',
'Get redirects',
'Get PageTriage status'
] )
);
$("#M2D-interface-footer").append(
$('<button>').attr('id', 'M2D-abort').text('Abort uncompleted tasks'),
$('<span>').attr('id', 'M2D-finished').hide().append(
'Finished!',
$('<button>').attr('id', 'M2D-close').text('Close')
.css('margin-left', '0.5em')
)
);
$("#M2D-close").click( function(){
$("#M2D-modal").remove();
window.location.reload();
} );
grabPageData();
};
//1) Contributors
var showContributorScreen = function() {
var numContributors = Object.keys(window.mtd.config.pagedata.contributors).length;
if( numContributors === 0) {
console.log("No notifiable contributors");
//show next screen
showOptionsScreen();
return;
} else if( numContributors === 1) {
console.log("Only one contributor. Moving to next screen");
const singleContributor = Object.keys(window.mtd.config.pagedata.contributors)[0];
window.mtd.config.pagedata.notifyList = [ singleContributor ];
//show next screen
showOptionsScreen();
return;
}
$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty();
setupHeader(": contributors");
$("#M2D-interface-content").append(
$('<div>')
.css('padding-bottom', '1em')
.append("View ",
$('<a>').attr({
'href': '?action=history',
'target':'_blank'
}).text("Page History")
),
$('<div>')
.css('padding-bottom', '0.5em')
.text("Choose contributors to send notifications to: ")
)
.css('margin', '1em');
for ( const [key, edits] of Object.entries( window.mtd.config.pagedata.contributors ) ) {
var contributor = `${key}`;
var contributorForDivId = contributor.replace(/\W/g, "_");
$('#M2D-interface-content').append(
$('<div>').append(
$('<input>').attr({'id':'M2D-option-authors-checkbox-'+contributorForDivId,
'name':'contributors', 'type':'checkbox', 'value':contributor}),
$('<label>').attr({'id':'M2D-option-authors-label-'+contributorForDivId,
'for':'M2D-option-authors-checkbox-'+contributorForDivId})
.text(`${key}`+" - "+`${edits}`+" edit" + ((`${edits}`!==`1`)?"s":"") )
.css({'margin-left':'0.5em'}),
$('<br/>')
)
);
if( contributor === window.mtd.config.pagedata.author ) {
$('#M2D-option-authors-label-'+contributorForDivId).append(' (Page Creator)');
}
if( window.mtd.config.pagedata.notifyList.includes( contributor ) ) {
$('#M2D-option-authors-checkbox-'+contributorForDivId).prop('checked', true);
}
}
$("#M2D-interface-footer").append(
$('<button>').attr('id', 'M2D-next').text('Next'),
$('<button>').attr('id', 'M2D-cancel').css('margin-left','3em').text('Cancel')
);
$("#M2D-cancel").click(function(){
$("#M2D-modal").remove();
});
$("#M2D-next").click(function(){
var markedCheckboxes = document.querySelectorAll('input[name="contributors"]:checked');
window.mtd.config.pagedata.notifyList = [];
for (let checkbox of markedCheckboxes ) {
window.mtd.config.pagedata.notifyList.push( checkbox.value );
}
//show next screen
showOptionsScreen();
});
};
//2) User inputs
/**
*
* @param {boolean} restoreValues Restore previously set values
*/
var showOptionsScreen = function(restoreValues) {
$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty();
setupHeader(": options");
$("#M2D-interface-content").append(
$('<div>').css('margin-bottom','0.5em').append(
$('<div>')
.attr({'id': 'M2D-warning'})
.css({
display: 'block',
color: 'darkblue',
'font-weight': 'bold',
}).append(
'Please ensure draftifying is appropriate per ',
makeLink("WP:DRAFTIFY")
),
$('<div>')
.css({ display: 'block', color: 'blue', 'font-style': 'italic', margin: '0.5em auto'})
.append(
'Add maintenance tags first if you wish, then check any or all boxes that match the issues with this article, then submit the message.'
),
$('<label>').attr('for','M2D-option-newtitle').append(
'Move to ',
$('<b>').text('Draft:')
),
$('<input>').attr({'type':'text', 'name':'M2D-option-newtitle', 'id':'M2D-option-newtitle'})
.css({'margin-left': '0.25em',
'width': '25em'})
),
$('<div>').css('margin-bottom','0.5em').append(
$('<label>').attr({'for':'M2D-option-movelog', 'id':'M2D-option-movelog-label'})
.html('<b>Reason for move (log summary):</b>'),
$('<input>').attr({'type':'text', 'name':'M2D-option-movelog', 'id':'M2D-option-movelog'})
.css({'width':'44em', 'margin-left':'0.25em',
'background-color': '#e8e8e8',
'border': '1px solid #808080'})
),
$('<div>').attr({'id':'M2D-option-author'}).append(
$('<input>').attr({'type':'checkbox', 'id':'M2D-option-message-enable'})
.css('margin-right', '0.25em')
.prop('checked', true),
$('<label>').attr({'for':'M2D-option-message-enable'}).append(
$('<b>').text( 'Notify contributor/s: '))
).css('margin-bottom', '0.5em'),
$('<div>').css('margin-bottom','0.5em').append(
$('<label>').attr({'for':'M2D-option-message-head', 'id':'M2D-option-message-head-label'})
.css({'margin-top':'0.5em'}).append(
$('<b>').text('Notification heading:')
),
$('<input>').attr({'id':'M2D-option-message-head', 'type':'text'})
.css({'width':'50em', 'margin-left':'0.25em',
'background-color': '#e8e8e8',
'border': '1px solid #808080'})
),
$('<div>').css('margin-bottom','0.5em').append(
$('<label>').attr({'id':'M2D-option-reasons-label'})
.css({'margin-top':'0.5em'}).append(
$('<b>').text('Reason/s:')
),
$('<div>').attr({'id':'M2D-option-reasons'})
.css({'width':'99%', 'columns': '2'}),
$('<div>').attr({'id':'M2D-option-reasons-other'})
.css({'width':'99%', 'margin-bottom':'0.5em'})
),
$('<div>').attr({'id':'M2D-option-message-preview-outer'}).append(
$('<label>').attr({'for':'M2D-option-message-preview', 'id':'M2D-option-message-label'})
.css({'display':'block', 'font-weight':'bold'})
.text('Notification preview:'),
$('<div>').attr({'id':'M2D-option-message-preview'})
.css({'width': '98%', 'background': '#fff',
'border': '1px solid #0002', 'padding': '0 0.5em'})
)
);
for (let key in window.mtd.config.draftReasons) {
$("#M2D-option-reasons").append(
$('<div>').append(
$('<input>').attr({'id':'M2D-option-reasons-checkbox-'+key, 'type':'checkbox', 'value':key})
.change( reasonChange ),
$('<label>').attr({'id':'M2D-option-reasons-label-'+key, 'for':'M2D-option-reasons-checkbox-'+key})
.text(window.mtd.config.draftReasons[key].long.charAt(0).toUpperCase() + window.mtd.config.draftReasons[key].long.slice(1))
.css({'margin-left':'0.5em'}),
$('<br/>')
)
);
}
//text box and label
$("#M2D-option-reasons-other").append(
$('<input>').attr({'id':'M2D-option-reasons-checkbox-other', 'type':'checkbox', 'value':'other'})
.css({'float':'left'}),
$('<label>').attr({'for':'M2D-reason-other', 'id':'M2D-reason-other-label'})
.css({'float':'left', 'margin':'auto 0.5em'}).text('Other/additional reasons:'),
$('<textarea>').attr({'id':'M2D-reason-other', 'rows':'1'})
.css({'width':'75%'})
.on("input keyup paste", reasonChange )
);
//Adding a warning, if needed
setDraftifyWarning("#M2D-warning");
//Setting one of the checkboxes as checked by default
$('#M2D-option-reasons-checkbox-0').prop('checked', true);
$('#M2D-option-movelog').val(window.mtd.config.wikitext.rationale);
$('#M2D-option-newtitle').val(getPageText(window.mtd.config.mw.wgPageName)).change(function() {
$('#M2D-option-message-head').val(
$('#M2D-option-message-head').val().trim()
.replace(/\[\[Draft:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val().trim() + "|" )
);
window.mtd.config.notificationMessage =
window.mtd.config.notificationMessage.trim()
.replace(/\[\[Draft:.*?\|/, "[[Draft:" + $('#M2D-option-newtitle').val().trim() + "|" );
});
$('#M2D-option-message-enable').change(notifyChange);
if (window.mtd.config.pagedata.notifyList.length === 0) {
$("#M2D-option-author").append( " None ");
$("#M2D-option-message-enable").prop("checked", false);
$("#M2D-option-message-enable").prop("disabled", true);
notifyChange();
} else {
$("#M2D-option-author").append(
" " + window.mtd.config.pagedata.notifyList.toString().replace(/,/g, ", ") + " "
);
}
if( Object.keys(window.mtd.config.pagedata.contributors).length > 1) {
$("#M2D-option-author").append(
$('<a>').text("Change")
.click(showContributorScreen)
);
}
$('#M2D-option-message-head').val(
window.mtd.config.wikitext.notificationHeading.replace(/\$1/g, getPageText(window.mtd.config.mw.wgPageName))
);
reasonChange();
$("#M2D-interface-footer").append(
$('<button>').attr('id', 'M2D-next').text('Continue'),
$('<button>').attr('id', 'M2D-cancel').css('margin-left','3em').text('Cancel')
);
$("#M2D-cancel").click(function(){
$("#M2D-modal").remove();
});
if (restoreValues) {
$( '#M2D-option-movelog' ).val( window.mtd.config.inputdata.rationale );
$( '#M2D-option-newtitle' ).val( window.mtd.config.inputdata.newTitle.replace( "Draft:", "" ) );
$( '#M2D-option-author' ).val( window.mtd.config.inputdata.authorName );
$( '#M2D-option-message-enable' ).prop( 'checked', window.mtd.config.inputdata.notifyEnable );
$( '#M2D-option-message-head' ).val( window.mtd.config.inputdata.notifyMsgHead );
window.mtd.config.notificationMessage = window.mtd.config.inputdata.notifyMsg;
}
$("#M2D-next").click(function(){
//Gather inputs
window.mtd.config.inputdata = {
rationale: $('#M2D-option-movelog').val().trim(),
newTitle: "Draft:" + $('#M2D-option-newtitle').val().trim(),
authorName: $('#M2D-option-author').val().trim(),
notifyEnable: $('#M2D-option-message-enable').prop('checked'),
notifyMsgHead: $('#M2D-option-message-head').val().trim(),
notifyMsg: window.mtd.config.notificationMessage.trim()
};
window.mtd.config.inputdata.logMsg = window.mtd.config.wikitext.logMsg
.replace(/\$1/g, getPageText(window.mtd.config.mw.wgPageName))
.replace(/\$2/g, window.mtd.config.inputdata.newTitle)
+ ' (' + window.mtd.config.draftReasonsShort + ')';
//Verify inputs
var errors=[];
if ( window.mtd.config.inputdata.newTitle.length === 0 ) {
errors.push("Invalid draft title");
}
if ( window.mtd.config.inputdata.rationale.length === 0 ) {
errors.push("Move log reason is empty");
}
if ( window.mtd.config.inputdata.notifyEnable ) {
if ( window.mtd.config.inputdata.notifyMsgHead.length === 0 ) {
errors.push("Notification heading is empty");
}
if ( window.mtd.config.inputdata.notifyMsg.length === 0 ) {
errors.push("Notification message is empty");
}
}
if ( errors.length >= 1 ) {
alert("Error:\n\n" + errors.join(";\n"));
return;
}
//start process off
showProgressScreen();
startTheProcess();
});
};
var startTheProcess = function() {
movePage()
.then( getImageInfo )
.then( function ( nonFreeFiles ) {
const promises = [];
promises.push( editWikitext( nonFreeFiles ) );
promises.push( notifyContributors() );
promises.push( updateTalk() );
promises.push( logDraftification() );
promises.push( tagRedirect() );
return Promise.allSettled( promises );
} )
.then( function() {
$("#M2D-finished, #M2D-abort").toggle();
setTimeout( function () {
window.location.reload();
}, 2000 );
}
)
.catch( console.log.bind( console ) );
};
//Checks if draftification is appropriate and warns the user accordingly
var setDraftifyWarning = function( divId ) {
var now = new Date();
var articleCreatedOn = new Date( window.mtd.config.pagedata.creationTimestamp );
var articleAgeInDays = Math.round( ( now - articleCreatedOn ) / ( 1000 * 60 * 60 * 24 ) );
var articleEditedOn = new Date( window.mtd.config.pagedata.lastEditTimestamp );
var lastEditAgeInMinutes = Math.round( ( now - articleEditedOn ) / ( 1000 * 60 ) );
var extraText = '';
if ( window.mtd.config.pagedata.previousDraftification ) {
extraText = ", since this article has been previously draftified.";
} else if ( articleAgeInDays > window.mtd.config.maximumAgeInDays ) {
extraText = ", since this article is more than " +
window.mtd.config.maximumAgeInDays +
" days old.";
} else if ( lastEditAgeInMinutes < window.mtd.config.minimumAgeInMinutes ) {
extraText = ", since this article was edited less than " +
window.mtd.config.minimumAgeInMinutes +
" minutes ago.";
} else {
return;
}
$(divId).text( '' )
.css( { "color": "red", "font-size": "large" } );
$(divId).append(
"Draftifying isn't appropriate per ",
makeLink( "WP:DRAFTIFY" ),
extraText
);
};
//Called when the state of any of the reason checkboxes changes
var reasonChange = function() {
var reasons = [];
var reasonsShort = [];
for (let key in window.mtd.config.draftReasons) {
if ( $("#M2D-option-reasons-checkbox-"+key).prop("checked") === true ) {
reasons.push(key);
reasonsShort.push(window.mtd.config.draftReasons[key].short);
}
}
//Cloning the array
var draftifyReasons = JSON.parse(JSON.stringify(window.mtd.config.draftReasons));
//Other reasons text box
var otherText = $("#M2D-reason-other").val();
if ( otherText !== '' ) {
draftifyReasons.other = { 'long': otherText };
reasons.push('other');
reasonsShort.push('custom reason');
$('#M2D-option-reasons-checkbox-other').prop("checked", true);
} else {
$('#M2D-option-reasons-checkbox-other').prop("checked", false);
}
var reasonText = "";
if (reasons.length === 0) {
$('#M2D-next').prop('disabled', true);
} else {
$('#M2D-next').prop('disabled', false);
reasonText = " because '''" + draftifyReasons[reasons[0]].long + "'''";
if (reasons.length > 1) {
for (let i = 1; i < (reasons.length - 1); i++) {
reasonText += ", '''" + draftifyReasons[reasons[i]].long + "'''";
}
reasonText += " and '''" + draftifyReasons[reasons[reasons.length-1]].long + "'''";
}
}
if (reasonsShort.length === 0) {
reasonsShort.push("unspecified");
}
window.mtd.config.draftReasonsShort = "Reason/s: " + reasonsShort.join(', ');
window.mtd.config.notificationMessage = window.mtd.config.wikitext.notificationTemplate
.replace(/\$1/g, getPageText($('#M2D-option-newtitle').val()))
.replace(/\$3/g, reasonText);
//Converting the message text into a preview
window.API.get( {
action: 'parse',
text: "==" + $('#M2D-option-message-head').val() + "==\n" + window.mtd.config.notificationMessage,
disableeditsection: true,
contentmodel: 'wikitext'
} )
.then( function(result) {
$('#M2D-option-message-preview').html( result.parse.text['*'] );
} );
};
// Progress tasks
var getProgressTasks = function( tasks ) {
var output = $('<ul>').attr('id', 'M2D-tasks').css("color", "#888");
//Resetting to empty array
window.mtd.config.processTimer = [];
for ( const [ index, taskText ] of Object.entries( tasks ) ) {
output.append(
$('<li>').attr('id', 'M2D-task' + index ).append(
taskText,
$('<span>').attr('id','M2D-status' + index )
.css( "margin", "0.75em" )
)
);
}
return output;
};
//3) Progress indicators
var showProgressScreen = function() {
$("#M2D-interface-header, #M2D-interface-content, #M2D-interface-footer").empty();
setupHeader(": In progress...");
$("#M2D-interface-content").append(
getProgressTasks(
[ 'Moving page',
'Searching for non-free images',
'Editing page wikitext',
'Notifying contributor/s',
'Updating talk page banners',
'Logging',
'Marking redirect for deletion'
] )
);
$("#M2D-interface-footer").append(
$('<button>').attr('id', 'M2D-abort').text('Abort uncompleted tasks'),
$('<span>').attr('id', 'M2D-finished').hide().append(
'Finished!',
$('<button>').attr('id', 'M2D-close').text('Close')
.css('margin-left', '0.5em')
)
);
$("#M2D-close").click( function(){
$("#M2D-modal").remove();
window.location.reload();
} );
$("M2D-abort").click( function(){
window.API.abort();
$("#M2D-modal").remove();
window.location.reload();
} );
};
// --- Add link to 'More' menu (or user-specified portlet) which starts everything ---
mw.util.addPortletLink( ( window.m2d_portlet||'p-cactions' ), '#', 'Move to draft', 'ca-m2d', null, null, "#ca-move");
$( '#ca-m2d' ).on( 'click', function( e ) {
e.preventDefault();
// Add interface shell
$('body').prepend('<div id="M2D-modal">'+
'<div id="M2D-interface">'+
'<div id="M2D-interface-header"></div>'+
'<hr>'+
'<div id="M2D-interface-content"></div>'+
'<hr>'+
'<div id="M2D-interface-footer"></div>'+
'</div>'+
'</div>');
// Interface styling
$("#M2D-modal").css({
"position": "fixed",
"z-index": "1001",
"left": "0",
"top": "0",
"width": "100%",
"height": "100%",
"overflow": "auto",
"background-color": "rgba(0,0,0,0.4)"
});
$("#M2D-interface").css({
"background-color": "#f0f0f0",
"margin": "7% auto",
"padding": "2px 20px",
"border": "1px solid #888",
"width": "80%",
"max-width": "60em",
"font-size": "90%"
});
$("#M2D-interface-content").css("min-height", "7em");
$("#M2D-interface-footer").css("min-height", "2em");
// Initial interface content
screen0();
});
// </nowiki>