Jump to content

User:Evad37/SPS.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Evad37 (talk | contribs) at 02:42, 14 June 2017 (fixes). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/**
 Signpost Publishing Script (SPS)
 by Evad37
 ------------
 Note 1: This script will only run for users specified in the publishers array.
 ------------ 
 Note 2: This script assumes users have the following permissions - please request them if you do
 not already have them.
 
 * Page mover (or administrator) on English Wikipedia
   - This ensures redirects are not left behind when moving pages during publication.

 * Mass message sender (or administrator) on English Wikipedia
   - This allows posting the Signpost on the talkpages of English Wikipedia subscribers.

 * Mass message sender (or administrator) on Meta
   - This allows posting the Signpost on the talkpages of subscribers on other projects.
 
**/
// <nowiki>
mw.loader.using( ['mediawiki.util', 'mediawiki.api'], function () {
$( function($) {
//Only work for the following approved users:
var publishers = ['Evad37', 'Peteforsyth'];
if ( !mw.config.get('wgPageName').includes('Wikipedia:Wikipedia_Signpost/Newsroom') || -1 === $.inArray(mw.config.get('wgUserName'), publishers) ) {
	return;
}

var script_ad = " ([[User:Evad37/SPS.js|via script]])";
var path = "Wikipedia:Wikipedia Signpost";
var dryrun = false;

//Info about each article is stored as an object, with key:values pairs for pageid, title, blurb, etc.
//These objects are stored in the following array, in the order they are to be published (for index page, SPV, etc)
var articles = [];
//publishing number is 1-indexed, unless there should be special formatting for the top item (in which case it is 0-indexed)
var firstitem = 1;
//object to hold key-value pairs of "section name": "previous issue date for this section" 
var previssuedate = {};

//date/volume/issue paramters
var today = new Date();
var today_iso = today.toISOString().slice(0, 10);
var monthnames = ["January", "February", "March", "April", "May", "June",
	"July", "August", "September", "October", "November", "December"];
var today_dmy = today.getUTCDate().toString() + " " + monthnames[today.getUTCMonth()] +
	" " + today.getUTCFullYear().toString();
//  vars to be filled in by editIssueSubpage function, but available to other functions 
var previous_iso = "";
var vol = "";
var iss = "";

//make a html link from a Wikipedia pagetitle
var makeLink = function(linktarget, linktext) {
	if ( linktext == null ) {
		linktext = linktarget;
	}
	return '<a href="https://en.wikipedia.org/wiki/' +
		mw.util.wikiUrlencode(linktarget) + '" target="_blank">' + linktext + '</a>';
};

//Make error message from returned values when api fails
var makeErrorMsg = function(code, result) {
	var details = "";
	if ( code === "http" ) {
		details = "HTTP error: " + result.textStatus; // result contains the jqXHR object
	} else if ( code === "ok-but-empty" ) {
		details = "Error: Got an empty response from the server";
	} else {
		details = "API error: " + code;
	}
	return details;
};


// Find page titles of next edition's articles.
var getArticles = function() {
	var apiFailedCallback_getArticles = function(c, r) {
		alert(makeErrorMsg(c, r));
		screen0();
	};
	var apiCallback_getArticles = function(result) {
		//Pass array of titles through to screen 1
		var page_ids = result.query.pageids;
		var t = new Array(page_ids.length);
		for (var i = 0; i < page_ids.length; i++) {
			t[i] = result.query.pages[page_ids[i]].title;
		}
		screen1(t);
	};
	new mw.Api().get( {
		action: 'query',
		generator: 'allpages',
		gapprefix: 'Wikipedia_Signpost/Next_issue/',
		gapnamespace: 4,
		gapfilterredir: 'nonredirects',
		gaplimit: 'max',
		indexpageids: 1
	} ).done( apiCallback_getArticles )
	.fail( apiFailedCallback_getArticles );
};

//Get article info (snippets etc)
var getInfo = function(pagetitles) {
	var apiFailedCallback_getInfo = function(c, r) {
		alert(makeErrorMsg(c, r));
		getArticles();
	};
	var apiCallback_getInfo = function(result) {
		//Store each object in the articles array.
		var page_ids = result.query.pageids;
		for (var i = 0; i < page_ids.length; i++) {
			var wikitext = result.query.pages[page_ids[i]].revisions[ 0 ][ '*' ];
			var pagetitle = result.query.pages[page_ids[i]].title;
			var snips = wikitext.match(/\{\{[Ss]ignpost draft[.\n]*?\|\s*title\s*= *(.*)\n*?\|\s*blurb\s*= *(.*)/);
			var rss_ = wikitext.match(/\{\{[Ww]ikipedia:[Ww]ikipedia Signpost\/Templates\/RSS description\s*\|\s*(1\s*=\s*)?(.*?)\s*\}\}/);
		    var rss = "";
			if (rss_ && rss_[2]) {
				rss = rss_[2];
			} else {
				rss = snips[1].trim() + ": " + snips[2].trim();
			}
			articles[i] = {
				'pageid': page_ids[i],
				'section': pagetitle.slice(40), //slicing removes "Wikipedia:Wikipedia Signpost/Next issue/"  
				'title': snips[1].trim(),
				'blurb': snips[2].trim(),
				'rss': rss,
				'wikitext': wikitext
			};
			if (previssuedate[pagetitle.slice(40)]) {
				articles[i].prev = previssuedate[pagetitle.slice(40)];
			} else {
				articles[i].prev = "";
			}
			if (i === page_ids.length-1) screen2();
		}
	};		
	var do_getInfo = function() {
		new mw.Api().get( {
			action: 'query',
			titles: pagetitles,
			prop: 'revisions',
			rvprop: 'content',
			indexpageids: 1
		} ).done( apiCallback_getInfo )
		.fail( apiFailedCallback_getInfo );
	};

	//Find the previous issue in which each section ran
	var apiFailedCallback_getPrevTitles = function(c, r) {
		alert(makeErrorMsg(c, r));
		getArticles();
	};
	var apiCallback_getPrevTitles = function(result) {
		var prevpages = result.query.allpages;
		for ( var i=0; i<prevpages.length; i++ ) {
			var parts = prevpages[i].title.split("/");
			if ( parts[1] && parts[2] && !(previssuedate[parts[2]]) ) {
				previssuedate[parts[2]] = parts[1];
			}
		}
		do_getInfo();
	};	
	new mw.Api().get( {
			action: 'query',
			list: 'allpages',
			apfrom: 'Wikipedia Signpost/' + (today.getFullYear()+1),
			apnamespace: 4,
			apfilterredir: 'nonredirects',
			apminsize: '1500',
			aplimit: '500',
			apdir: 'descending'
		} ).done( apiCallback_getPrevTitles )
		.fail( apiFailedCallback_getPrevTitles );	
	
};


// --- Publishing tasks ---
// vars to store current and next function, so retry and skip buttons can be provided in case of api errors
var fn_retry, fn_skip;

//a) create issue page
var makeIssuePage = function() {
	
	fn_retry = makeIssuePage;
	fn_skip = prepareArticles;
	$("#SPS-task01").css("color", "#00F").children().text("...");
	
	var issuepage = path + "/" + today_iso;
	var issuetext = new Array(articles.length);
	var issuesum = "New edition" + script_ad;
	for ( var i=0; i<articles.length; i++ ) {
		issuetext[i] = "{{Wikipedia:Signpost/Template:Cover-item|{{{1}}}|" + (i+firstitem) +
		"|" + today_iso + "|" + articles[i].section + "|" + articles[i].title + "}}\n";
	}
	
	if ( dryrun ) {
		console.log("[SPS] ----makeIssuePage----\n"+
			"issuepage = " + issuepage + "\n"+
			"issuetext = " + issuetext.join('') + "\n"+
			"issuesum = " + issuesum + "\n"+
			"---- ----");
		$("#SPS-task01").css("color", "#0A0").children().text("Done!");
		prepareArticles();
	} else {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: issuepage,
			text: issuetext.join(''),
			summary: issuesum
		} ).done( function() {
			$("#SPS-task01").css("color", "#0A0").children().text("Done!");
			prepareArticles();
		} ).fail( function(c, r) {
			$("#SPS-task01").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task01").children().show();
		} );
	}
};

//b) edit articles
var prepareArticles = function() {

	fn_skip = moveArticles;
	$("#SPS-task02").css("color", "#00F").children().text("...");

	var editsum = "Preparing for publication" + script_ad;
	
	var edits_made = 0;
	var edit_fails = 0;
	
	var checkIfDone = function() {
		if ( edits_made === articles.length ) {
			// Done, without errors
			$("#SPS-task02").css("color", "#0A0").children().text("Done!");
			moveArticles();
		} else if ( edits_made+edit_fails === articles.length ) {
			// Done, with errors
			$("#SPS-task02").css("color", "#A00").children().text("Failed (edited " + edits_made + "/" + articles.length + " articles)" );
			$("#SPS-errorbox").appendTo("#SPS-task02").children().not("#SPS-error-retry").show();
		}
	};
	
	var edit_done = function() {
		edits_made++;
		checkIfDone();
	};
	var edit_failed = function() {
		edit_fails++;
		checkIfDone();
	};
	
	for (var i=0; i<articles.length; i++) {
		//clean wikitext
		// replace top noincude section with noincluded rss description and header templates
		var new_top = "<noinclude>{{Wikipedia:Wikipedia Signpost/Templates/RSS description|1=" +
			articles[i].rss + "}}{{Wikipedia:Signpost/Template:Signpost-header|||}}</noinclude>";
		var wikitext = articles[i].wikitext.replace(/<noinclude>(?:.|\n)*?<\/noinclude>/, new_top).replace("{{{1|(Your article's descriptive subtitle here)}}}", "{{{1|"+articles[i].title+"}}}");
		// if footer template doesn't exist, add it; otherwise add previous issue date if missing
		if ( wikitext.lastIndexOf("{{Wikipedia:Signpost/Template:Signpost-article-comments-end") === -1 ) {
			wikitext = wikitext.trim() + "\n<noinclude>{{Wikipedia:Signpost/Template:Signpost-article-comments-end||" + articles[i].prev + "|}}</noinclude>";
		} else {
			wikitext = wikitext.replace("{{Wikipedia:Signpost/Template:Signpost-article-comments-end|||}}", "{{Wikipedia:Signpost/Template:Signpost-article-comments-end||" + articles[i].prev + "|}}");
		}
		
		
		//make the edit and move the page
		if ( dryrun ) {
				console.log("[SPS] ----prepareArticles (edit)----" + "\n" +
					"pageid = " + articles[i].pageid + "\n" +
					"text = " + wikitext + "\n" +
					"summary: " + editsum + "\n" +
					"---- ----");
				edit_done();
		} else {
			//Edit:
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				pageid: articles[i].pageid,
				text: wikitext,
				summary: editsum
			} ).done( edit_done ).fail( edit_failed );
		}
	}
	
};

//c) move articles
var moveArticles = function() {

	fn_skip = editIssueSubpage;
	$("#SPS-task02b").css("color", "#00F").children().text("...");

	var mvreason = "Publishing" + script_ad;
	
	var moves_made = 0;
	var move_fails = 0;
	
	var checkIfDone = function() {
		if ( moves_made === articles.length ) {
			// Done, without errors
			$("#SPS-task02b").css("color", "#0A0").children().text("Done!");
			editIssueSubpage();
		} else if ( moves_made+move_fails === articles.length ) {
			// Done, with errors
			$("#SPS-task02b").css("color", "#A00").children().text("Failed (moved " + moves_made + "/" + articles.length + " articles)" );
			$("#SPS-errorbox").appendTo("#SPS-task02b").children().not("#SPS-error-retry").show();
		}
	};
	
	var move_done = function() {
		moves_made++;
		checkIfDone();
	};	
	var move_failed = function() {
		move_fails++;
		checkIfDone();
	};
	
	for (var i=0; i<articles.length; i++) {		
		//move each page
		if ( dryrun ) {
				console.log("[SPS] ----moveArticles----" + "\n" +
					"fromid = " + articles[i].pageid + "\n" +
					"to = " + path + "/" + today_iso + "/" + articles[i].section + "\n" +
					"reason: " + mvreason + "\n" +
					"---- ----");
				move_done();
		} else {
			new mw.Api().postWithToken( 'csrf', {
				action: 'move',
				fromid: articles[i].pageid,
				to: path + "/" + today_iso + "/" + articles[i].section,
				noredirect: 1,
				reason: mvreason
			} ).done( move_done ).fail( move_failed );
		}
	}
	
};

//d) edit issue subpage
var editIssueSubpage = function() {
	
	fn_retry = editIssueSubpage;
	fn_skip = editMain;
	$("#SPS-task03").css("color", "#00F").children().text("...");
	
	var apiCallback_getIssSub = function(result) {
		var pid = result.query.pageids[0];
		var wikitext = result.query.pages[pid].revisions[ 0 ]['*'];
		//Update the YYYY-MM-DD
		wikitext = wikitext.replace(/((\d{4})-\d{2}-\d{2})/, today_iso);
		//Store previous edition date for later use
		previous_iso = RegExp.$1;
		//Get the current volume and issue numbers
		var edition_patt = /Volume (\d+), Issue (\d+)/;
		//If the previous edition was last year, increment volume and reset issue number to 1
		if ( parseInt(RegExp.$2) < today.getFullYear() ) {
			edition = edition_patt.exec(wikitext);
			vol = (parseInt(edition[1])+1).toString();
			iss = "1";
		} else { //increment issue number
			edition = edition_patt.exec(wikitext);
			vol = edition[1];
			iss = (parseInt(edition[2])+1).toString();
		}
		//update volume and issue numbers
		wikitext = wikitext.replace(/Volume (\d+), Issue (\d+)/, "Volume " + vol +", Issue " + iss );
		
		//TODO: make the edit
		if ( dryrun ) {
			console.log("[SPS] ----editIssueSubpage----" + "\n" +
				"title = " + path + "/" + "Issue" + "\n" +
				"text = " + wikitext + "\n" +
				"---- ----");
			$("#SPS-task03").css("color", "#0A0").children().text("Done!");
			editMain();
		} else {
			//Edit:
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				title: path + "/" + "Issue",
				text: wikitext,
				summary: "Publishing new edition" + script_ad
			} ).done( function() {
				$("#SPS-task03").css("color", "#0A0").children().text("Done!");
				editMain();
			} ).fail( function(c, r) {
				$("#SPS-task03").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
				$("#SPS-errorbox").appendTo("#SPS-task03").children().show();
			} );
		}
	};
	
	//grab current wikitext
	new mw.Api().get( {
		action: 'query',
		titles: 'Wikipedia:Wikipedia Signpost/Issue',
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1
	} ).done( apiCallback_getIssSub )
	.fail( function(c, r) {
		$("#SPS-task03").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task03").children().show();
	} );
	
	
};

//e) Edit main Signpost page
var editMain = function() {
	
	fn_retry = editMain;
	fn_skip = makeSingle;
	$("#SPS-task04").css("color", "#00F").children().text("...");
	
	var topwikitext = "{{Wikipedia:Signpost/Template:Signpost-header|{{Str left|{{Wikipedia:Wikipedia Signpost/Issue|2}}|9}}|{{date|{{Wikipedia:Wikipedia Signpost/Issue|1}}|dmy}}|{{Str right|{{Wikipedia:Wikipedia Signpost/Issue|2}}|10}}}}\n"+
		"<!-- Main area -->\n"+
		"{{Signpost-main-page-body-begin}}\n\n";
		
	var bottomwikitext = "{{Signpost-main-page-body-end-footer-begin}}\n"+
		"'''[[Wikipedia:Wikipedia Signpost/Single/{{Wikipedia:Wikipedia Signpost/Issue|1}}|Single-page edition]]{{#ifexist: Book:Wikipedia Signpost/{{Wikipedia:Wikipedia Signpost/Issue|1}} | &nbsp;{{·}} [[Book:Wikipedia Signpost/{{Wikipedia:Wikipedia Signpost/Issue|1}}|Book edition]] | &nbsp;}}'''</div>\n"+
		"{{Wikipedia:Signpost/Template:Signpost-footer|{{Wikipedia:Wikipedia Signpost/Issue|3}}|}}\n\n"+
		"<noinclude>{{italic title|string=Signpost}}"+
		"[[Category:Wikipedia Signpost]]</noinclude>";
	
	var midwikitext = new Array(articles.length);
	for ( var i=0; i<articles.length; i++ ) {
		midwikitext[i] = "{{Wikipedia:Signpost/Template:Signpost-snippet|{{Wikipedia:Wikipedia Signpost/Issue|1}}|" + articles[i].section + "|" + articles[i].title + "|" + articles[i].blurb + "}}\n\n";
	}

	if ( dryrun ) {
		console.log("[SPS] ----editMain----" + "\n" +
			"title = " + path + "\n" +
			"text = " + topwikitext + midwikitext.join('') + bottomwikitext + "\n" +
			"---- ----");
		$("#SPS-task04").css("color", "#0A0").children().text("Done!");
		makeSingle();
	} else {
		//Edit:
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: path,
			text: topwikitext + midwikitext.join('') + bottomwikitext,
			summary: "Publishing new edition" + script_ad
		} ).done( function() {
			$("#SPS-task04").css("color", "#0A0").children().text("Done!");
			makeSingle();
		} ).fail( function(c, r) {
			$("#SPS-task04").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task04").children().show();
		} );
	}

};

//f) create single page edition
var makeSingle = function() {
	
	fn_retry = makeSingle;
	fn_skip = makeArchive;
	$("#SPS-task05").css("color", "#00F").children().text("...");
	
	if ( dryrun ) {
		console.log("[SPS] ----makeSingle----" + "\n" +
			"title = " + path + "/Single/" + today_iso + "\n",
			"text = " + "{{Wikipedia:Wikipedia Signpost/Single|issuedate=" + today_iso + "}}" + "\n" +
			"---- ----");			
		$("#SPS-task05").css("color", "#0A0").children().text("Done!");
		makeArchive();
	} else {
		//Edit:
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: path + "/Single/" + today_iso,
			text: "{{Wikipedia:Wikipedia Signpost/Single|issuedate=" + today_iso + "}}",
			summary: "Publishing new single page edition" + script_ad
		} ).done( function() {
			$("#SPS-task05").css("color", "#0A0").children().text("Done!");
			makeArchive();
		} ).fail( function(c, r) {
			$("#SPS-task05").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task05").children().show();
		} );
	}	
};

//g) create archive page
var makeArchive = function() {
	
	fn_retry = makeArchive;
	fn_skip = updatePrevArchive;
	$("#SPS-task06").css("color", "#00F").children().text("...");
	
	//Make new archive
	if ( dryrun ) {
		console.log("[SPS] ----makeArchive (current)----" + "\n" +
			"title = " + path + "/Archives/" + today_iso + "\n",
			"text = " + "{{Signpost archive|" + previous_iso + "|" + today_iso + "|}}" + "\n" +
			"---- ----");	
		$("#SPS-task06").css("color", "#0A0").children().text("Done!");
		updatePrevArchive();		
	} else {
		//Edit:
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: path + "/Archives/" + today_iso,
			text: "{{Signpost archive|" + previous_iso + "|" + today_iso + "|}}",
			summary: "Publishing new single page edition" + script_ad
		} ).done( function() {
			$("#SPS-task06").css("color", "#0A0").children().text("Done!");
			updatePrevArchive();
		} ).fail( function(c, r) {
			$("#SPS-task06").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task06").children().not("#SPS-error-retry").show();
		} );
	}

};

var updatePrevArchive = function() {	
	
	fn_retry = updatePrevArchive;
	fn_skip = purgePages;
	$("#SPS-task07").css("color", "#00F").children().text("...");
	
	//Previous archive
	apiCallback_getPrevArch = function(result) {
		var pid = result.query.pageids[0];
		var wikitext = result.query.pages[pid].revisions[ 0 ]['*'];
		wikitext = wikitext.replace(/\|?\s*}}/, "|" + today_iso + "}}");
		if ( dryrun ) {
			console.log("[SPS] ----updatePrevArchive----" + "\n" +
				"title = " + path + "/Archives/" + previous_iso + "\n",
				"text = " + wikitext + "\n" +
				"---- ----");
			$("#SPS-task07").css("color", "#0A0").children().text("Done!");
			purgePages();			
		} else {
			//Edit:
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				title: path + "/Archives/" + previous_iso,
				text: wikitext,
				summary: "Add next edition date" + script_ad
			} ).done( function() {
				$("#SPS-task07").css("color", "#0A0").children().text("Done!");
				purgePages();
			} ).fail( function(c, r) {
				$("#SPS-task07").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
				$("#SPS-errorbox").appendTo("#SPS-task07").children().show();
			} );
		}
	};
	
	//grab previous archive wikitext
	new mw.Api().get( {
		action: 'query',
		titles: path + "/Archives/" + previous_iso,
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1
	} ).done( apiCallback_getPrevArch )
	.fail( function(c, r) {
		$("#SPS-task07").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
		$("#SPS-errorbox").appendTo("#SPS-task07").children().show();
	} );

};

//h) purge pages
var purgePages = function() {
	
	fn_retry = purgePages;
	fn_skip = massmsg;
	$("#SPS-task08").css("color", "#00F").children().text("...");

	var purgetitles = new Array(articles.length+1);
	purgetitles[0] = "Wikipedia:Wikipedia Signpost/Issue|Wikipedia:Wikipedia Signpost|Wikipedia:Wikipedia Signpost/Single|" + path + "/Archives/" + today_iso;
	for ( var i=0; i<articles.length; i++) {
		purgetitles[i+1] = path + "/" + today_iso + "/" + articles[i].section;
	}
	
	if ( dryrun ) {
		console.log("[SPS] ----purge pages----" + "\n" +
			"titles = " + purgetitles.join('|') + "\n" +
			"---- ----");
		$("#SPS-task08").css("color", "#0A0").children().text("Done!");
		massmsg();			
	} else {	
		new mw.Api().postWithToken( 'csrf', {
			action: 'purge',
			titles: purgetitles.join('|')
		} ).done( function() {
			$("#SPS-task08").css("color", "#0A0").children().text("Done!");
			massmsg();
		} ).fail( function(c, r) {
			$("#SPS-task08").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task08").children().show();
		} );
	}
};

//i) Mass-message enwiki subscribers
var massmsg = function() {
	
	fn_retry = massmsg;
	fn_skip = globalmassmsg;
	$("#SPS-task09").css("color", "#00F").children().text("...");
	
	var msg_spamlist = path + "/Tools/Spamlist";
	var msg_content = '<div lang="en" dir="ltr" class="mw-content-ltr"><div style="-moz-column-count:2; -webkit-column-count:2; column-count:2;"> '+
	'{{Wikipedia:Wikipedia Signpost/' + today_iso + '}} </div><!--Volume ' + vol + ', Issue ' + iss + '--> '+
	'<div class="hlist" style="margin-top:10px; font-size:90%; padding-left:5px; font-family:Georgia, Palatino, Palatino Linotype, Times, Times New Roman, serif;"> '+
	'* \'\'\'[[Wikipedia:Wikipedia Signpost|Read this Signpost in full]]\'\'\' * [[Wikipedia:Wikipedia Signpost/Single/' + today_iso + '|Single-page]] * '+
	'[[Wikipedia:Wikipedia Signpost/Subscribe|Unsubscribe]] * [[User:MediaWiki message delivery|MediaWiki message delivery]] ([[User talk:MediaWiki message delivery|talk]]) ~~~~~ </div></div>';
	var msg_subject = "''The Signpost'': " + today_dmy;
	
	if ( dryrun ) {
		console.log("[SPS] ----massmsg (enwiki)----" + "\n" +
			"spamlist = " + msg_spamlist + "\n" +
			"subject = " + msg_subject + "\n" +
			"message = " + msg_content + "\n" +
			"---- ----");			
		$("#SPS-task09").css("color", "#0A0").children().text("Done!");
		globalmassmsg();
	} else {
		new mw.Api().postWithToken( 'csrf', {
			action: 'massmessage',
			spamlist: msg_spamlist,
			subject: msg_subject,
			message: msg_content
		} ).done( function() {
			$("#SPS-task09").css("color", "#0A0").children().text("Done!");
			globalmassmsg();
		} ).fail( function(c, r) {
			$("#SPS-task09").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task09").children().show();
		} );
	}
};

//j) Mass-message global subscribers
var globalmassmsg = function() {
	
	fn_retry = globalmassmsg;
	fn_skip = updateYearArchive;
	$("#SPS-task10").css("color", "#00F").children().text("...");
	
	var msg_spamlist = "Global message delivery/Targets/Signpost";
	var msg_subject = "''The Signpost'': " + today_dmy;
	var msg_top = '<div lang="en" dir="ltr" class="mw-content-ltr">'+
	'<div style="margin-top:10px; font-size:90%; padding-left:5px; font-family:Georgia, Palatino, Palatino Linotype, Times, Times New Roman, serif;">'+
	'[[File:WikipediaSignpostIcon.svg|40px|right]] \'\'News, reports and features from the English Wikipedia\'s weekly journal about Wikipedia and Wikimedia\'\'</div>\n'+
	'<div style="-moz-column-count:2; -webkit-column-count:2; column-count:2;">\n';
	var msg_mid = new Array(articles.length);
	for ( var i=0; i<articles.length; i++ ) {
		msg_mid[i] = "* " + articles[i].section + ": [[w:en:Wikipedia:Wikipedia Signpost/" + today_iso + "/" + articles[i].section + "|" + articles[i].title + "]]\n\n";
	}
	var msg_bottom = '</div>\n'+
	'<div style="margin-top:10px; font-size:90%; padding-left:5px; font-family:Georgia, Palatino, Palatino Linotype, Times, Times New Roman, serif;">'+
	'\'\'\'[[w:en:Wikipedia:Wikipedia Signpost|Read this Signpost in full]]\'\'\' · [[w:en:Wikipedia:Signpost/Single|Single-page]] · '+
	'[[m:Global message delivery/Targets/Signpost|Unsubscribe]] · [[m:Global message delivery|Global message delivery]] ~~~~~\n'+
	'</div></div>';

	if ( dryrun ) {
		console.log("[SPS] ----massmsg (enwiglobalki)----" + "\n" +
			"spamlist = " + msg_spamlist + "\n" +
			"subject = " + msg_subject + "\n" +
			"message = " + msg_top + msg_mid.join('') + msg_bottom + "\n" +
			"---- ----");	
		$("#SPS-task10").css("color", "#0A0").children().text("Done!");
		updateYearArchive();	
	} else {	
		new mw.ForeignApi( 'https://meta.wikimedia.org/w/api.php' ).postWithToken( 'csrf', {
			action: 'massmessage',
			assert: 'user',
			spamlist: msg_spamlist,
			subject: msg_subject,
			message: msg_top + msg_mid.join('') + msg_bottom
		} ).done( function() {
			$("#SPS-task10").css("color", "#0A0").children().text("Done!");
			updateYearArchive();
		} ).fail( function(c, r) {
			$("#SPS-task10").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task10").children().show();
		} );
	}
};


//k) Update current year's archive overview page
var updateYearArchive = function() {
	
	fn_retry = updateYearArchive;
	fn_skip = createArchiveCats;
	$("#SPS-task11").css("color", "#00F").children().text("...");
	
	apiCallback_getYearArchive = function(result) {
		var padded_iss = iss;
		if  ( padded_iss.length === 1) {
			padded_iss = "0" + padded_iss;
		}
		
		var newtext = "===[[Wikipedia:Wikipedia Signpost/Archives/" + today_iso + "|Volume " + vol + ", Issue " + padded_iss + "]], " + today_dmy + "===\n{{Wikipedia:Wikipedia Signpost/" + today_iso + "}}\n\n";
		var pid = result.query.pageids[0];
		var wikitext = "";		
		if ( pid < 0 ) {
			//page doesn't yet exist
			wikitext = "{{Wikipedia:Wikipedia Signpost/Archives/Years|year=" + today.getUTCFullYear + "}}\n\n<br />\n{{Template:TOCMonths}}\n\n"+
			"== " + monthnames[today.getUTCMonth()] + " ==\n" + newtext + "{{Wikipedia:Signpost/Template:Signpost-footer}}\n"+
			"[[Category:Wikipedia Signpost archives|" + today.getUTCFullYear + "]]\n[[Category:Wikipedia Signpost archives " + today.getUTCFullYear + "| ]]";
		} else {
			wikitext = result.query.pages[pid].revisions[ 0 ]['*'];
			var insertAbove = "";
			if ( wikitext.indexOf("{{Wikipedia:Signpost/Template:Signpost-footer}}") !== -1 ) {
				insertAbove = "{{Wikipedia:Signpost/Template:Signpost-footer}}";
			} else if ( wikitext.indexOf() !== -1 ) {
				// footer not found, insert new wikitext above categories
				insertAbove = "[[Category:";
			}
			// insert new wikitext
			if ( insertAbove === "" ) {
				wikitext = wikitext.trim() + newtext.trim();
			} else if ( wikitext.indexOf(monthnames[today.getUTCMonth()]) === -1) {
				wikitext = wikitext.replace(insertAbove, "== " + monthnames[today.getUTCMonth()] + " ==\n" + newtext + insertAbove);
			} else {
				wikitext = wikitext.replace(insertAbove, newtext + insertAbove);
			}
		}
		
		//make the edit
		if ( dryrun ) {
			console.log("[SPS] ----updateYearArchive----" + "\n" +
				"title = " + path + "/Archives/" + today.getUTCFullYear() + "\n",
				"text = " + wikitext + "\n" +
				"---- ----");
			$("#SPS-task11").css("color", "#0A0").children().text("Done!");
			createArchiveCats();			
		} else {
			//Edit:
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				title: path + "/Archives/" + today.getUTCFullYear(),
				text: wikitext,
				summary: "Add next edition" + script_ad
			} ).done( function() {
				$("#SPS-task11").css("color", "#0A0").children().text("Done!");
				createArchiveCats();
			} ).fail( function(c, r) {
				$("#SPS-task11").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
				$("#SPS-errorbox").appendTo("#SPS-task11").children().show();
			} );
		}		
	};

	//grab page wikitext (if it exists)
	new mw.Api().get( {
		action: 'query',
		titles: path + "/Archives/" + today.getUTCFullYear(),
		prop: 'revisions',
		rvprop: 'content',
		indexpageids: 1
	} ).done( apiCallback_getYearArchive )
	.fail( function(c, r) {
		$("#SPS-task11").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
		$("#SPS-errorbox").appendTo("#SPS-task11").children().show();
	} );
	
};

//k-2) Create archive categories for current month and year, if they don't already exist
var createArchiveCats = function() {
	
	fn_retry = createArchiveCats;
	fn_skip = updateOldNextLinks;
	$("#SPS-task11b").css("color", "#00F").children().text("...");

	month_cat_title = "Category:Wikipedia Signpost archives " + today_iso.slice(0, 7);
	month_cat_text = "[[Category:Wikipedia Signpost archives " + today_iso.slice(0, 4) +
	"|" + today_iso.slice(5, 7) + "]]";
	
	year_cat_title = "Category:Wikipedia Signpost archives " + today_iso.slice(0, 4);
	year_cat_text = "{{Wikipedia:Wikipedia Signpost/Archives/Years}}\n\n" +
	"[[Category:Wikipedia Signpost archives|" + today_iso.slice(0, 4) + "]]";


	var makeyearcat = function() {
		if ( dryrun ) {
			console.log("[SPS] ----createArchiveCats (year)----" + "\n" +
				"title = " + year_cat_title + "\n" +
				"text = " + year_cat_text + "\n" +
				"summary = " + "Create" + script_ad + "\n" +
				"---- ----");
				$("#SPS-task11b").css("color", "#0A0").children().text("Done!");
				updateOldNextLinks();
		} else {
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				title: year_cat_title,
				text: year_cat_text,
				summary: "Create" + script_ad,
				createonly: 1
			} ).done( function() {
				$("#SPS-task11b").css("color", "#0A0").children().text("Done!");
				updateOldNextLinks();		
			} ).fail( function(c, r) {
				$("#SPS-task11b").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
				$("#SPS-errorbox").appendTo("#SPS-task11b").children().show();
			} );
		}
	};

	
	if ( dryrun ) {
		console.log("[SPS] ----createArchiveCats (month)----" + "\n" +
			"title = " + month_cat_title + "\n" +
			"text = " + month_cat_text + "\n" +
			"summary = " + "Create" + script_ad + "\n" +
			"---- ----");
		makeyearcat();
	} else {
		new mw.Api().postWithToken( 'csrf', {
			action: 'edit',
			title: month_cat_title,
			text: month_cat_text,
			summary: "Create" + script_ad,
			createonly: 1
		} ).done( makeyearcat )
		.fail( function(c, r) {
			$("#SPS-task11b").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task11b").children().show();
		} );
	}
};

//l) Update previous issue's "next" links
var updateOldNextLinks = function() {

	fn_retry = updateOldNextLinks;
	fn_skip = function() {
		$("#SPS-fin").show();
	};
	$("#SPS-task12").css("color", "#00F").children().text("...");
	
	var count = 0;
	var edited = 0;
	var failed = 0;
	
	var checkIfDone = function() {
		if ( edited === count ) {
			//all done, no errors
			$("#SPS-task12").css("color", "#0A0").children().text("Done!");
			$("#SPS-fin").show();
		} else if ( edited + failed === count ) {
			//all done, with errors
			$("#SPS-task12").css("color", "#A00").children().text("Failed to edit " + failed + " articles (edited" + edited + "/" + count + ")");
			$("#SPS-fin").show();
		}
	};
	
	var editprevpage = function(pagetitle, pagetext) {
		if ( dryrun ) {
			console.log("[SPS] ----updateOldNextLinks----" + "\n" +
				"title = " + pagetitle + "\n",
				"text = " + pagetext + "\n" +
				"---- ----");
			edited++;
			checkIfDone();
		} else {
			new mw.Api().postWithToken( 'csrf', {
				action: 'edit',
				title: pagetitle,
				text: pagetext,
				summary: "Add next edition" + script_ad
			} ).done( function() {
				edited++;
				checkIfDone();		
			} ).fail( function() {
				failed++;
				checkIfDone();		
			} );
		}
	};
	
	apiCallback_getPrev = function(result) {
		var page_ids = result.query.pageids;
		for (var ii = 0; ii < page_ids.length; ii++) {
			var pagetitle = result.query.pages[page_ids[ii]].title;
			var wikitext = result.query.pages[page_ids[ii]].revisions[ 0 ][ '*' ];
			wikitext = wikitext.replace(/({{Wikipedia:Signpost\/Template:Signpost-article-comments-end\|\|\d{4}-\d{2}-\d{2}\|)}}/, "$1"+today_iso+"}}");
			editprevpage(pagetitle, wikitext);
		}
	};

	//grab wikitext of sections in previous issue
	var prevtitles = [];
	for ( var i=0; i<articles.length; i++ ) {
		if ( previssuedate[articles[i].section] ) {
			prevtitles.push(path + "/" + previssuedate[articles[i].section] + "/" + articles[i].section);
		}
	}
	count = prevtitles.length;
	if ( count > 0 ) {
		new mw.Api().get( {
			action: 'query',
			titles: prevtitles.join("|"),
			prop: 'revisions',
			rvprop: 'content',
			indexpageids: 1
		} ).done( apiCallback_getPrev )
		.fail( function(c, r) {
			$("#SPS-task12").css("color", "#A00").children().text("Failed (" + makeErrorMsg(c, r) + ")");
			$("#SPS-errorbox").appendTo("#SPS-task12").children().show();
		} );
	} else {
		$("#SPS-task12").css("color", "#A00").children().text("None found");
		$("#SPS-errorbox").appendTo("#SPS-task12").children().show();		
	}
};

// --- Interface screens ---

// Initial 'welcome' screen
screen0 = function() {
	$("#SPS-interface-header").html('Welcome to the Signpost Publishing Script');
	$("#SPS-interface-content").html('<p style="text-align:center;">Make sure each article has a fully completed {{Signpost draft}} template,<br/>and is a subpage of Wikipedia:Wikipedia_Signpost/Next_issue/<br /><br/><button id="SPS-start">Get Started</button></p>');
	$("#SPS-interface-footer").html('<p style="text-align:center;"><button id="SPS-dry">Dry run only</button><br />(simulates the publising process without making any actual changes &ndash; actions are logged to the browser\'s javascript console instead)<br /><br /><button id="SPS-button-cancel">Cancel</button></p>');
	$("#SPS-start").click(function(){
		dryrun = false;
		$("#SPS-interface-header").html('Loading...');
		$("#SPS-interface-content").html('...');
		$("#SPS-interface-footer").html('...');
		getArticles();
	});
	$("#SPS-dry").click(function(){
		dryrun = true;
		$("#SPS-interface-header").html('Loading...');
		$("#SPS-interface-content").html('...');
		$("#SPS-interface-footer").html('...');
		getArticles();
	});
	$("#SPS-button-cancel").click(function(){
		$("#SPS-modal").remove();
	});
};

// Screen to select which articles to publish
screen1 = function(titles) {
	if ( dryrun ) {
		$("#SPS-interface-header").html('1) Select articles [dry run]');		
	} else {
		$("#SPS-interface-header").html('1) Select articles');
	}
	var checklist = new Array(titles.length);
	for (var i = 0; i < titles.length; i++) {
		checklist[i] = '<input type="checkbox" name="title" value="'+titles[i]+'" id="title'+i+'" checked /><label for="title'+i+'">'+titles[i]+' '+makeLink(titles[i],'→')+'</label><br/>';
	}
	$("#SPS-interface-content").html('<div id="SPS-checklist">'+checklist.join('')+'</div>');
	$("#SPS-checklist").find("a").attr("title", "open page");
	$("#SPS-interface-footer").html('<button id="SPS-next">Continue</button><button id="SPS-button-cancel" style="margin-left:3em;">Cancel</button>');
	$("#SPS-next").click(function(){
		//get selected page titles, seperated by pipes
		var selected_titles = $("input[name=title]:checked").map(function () {
				return this.value;
			}).get().join("|");
		//reset screen
		$("#SPS-interface-header").html('Loading...');
		$("#SPS-interface-content").html('...');
		$("#SPS-interface-footer").html('...');
		//get article info (snippets etc) for each selected page title
		getInfo(selected_titles);
	});
	$("#SPS-button-cancel").click(function(){
		$("#SPS-modal").remove();
	});
};

// Screen to allow order, titles, blurbs to be adjusted 
screen2 = function() {
	var next_button_text = "Publish";
	if ( dryrun ) {
		$("#SPS-interface-header").html('2) Check article order, titles, and blurbs [dry run]');
		next_button_text = "Simulate publishing";		
	} else {
		$("#SPS-interface-header").html('2) Check article order, titles, and blurbs');
	}	
	var article_list = new Array(articles.length);
	for (var i = 0; i < articles.length; i++) {
		article_list[i] = '<div class="SPS-info-row" id="SPS-row-'+i+'"><a class="SPS-moveUp" title="move up">-↑-</a> '+
		'<a class="SPS-moveDown" title="move down">-↓-</a> <span class="SPS-section">'+articles[i].section+' '+
		makeLink(path+'/Next_issue/'+articles[i].section,'→')+'</span> <span class="SPS-title">&nbsp;'+articles[i].title+
		'&nbsp;</span> &ndash; <span class="SPS-blurb">&nbsp;'+articles[i].blurb+'&nbsp;</span></div>';
	}
	$("#SPS-interface-content").html('<div id="SPS-articlelist" style="margin-bottom:1em;">'+article_list.join('')+'</div>'+
	'<input type="checkbox" name="startatzero" id="startatzero" /><label for="startatzero">Use announcement ("from the editors") formatting for first item');
	$(".SPS-info-row").css({"padding": "2px", "border-bottom": "1px dashed #bbb"});
	$(".SPS-section").css({
		"font-variant": "small-caps",
		"color": "#666"
	}).children('a').attr("title", "open page");
	$(".SPS-title").css({
		"font-size": "90%",
		"background": "#ffe"
	});
	$(".SPS-blurb").css({
		"font-size": "80%",
		"background": "#fff"
	});

	//Prevent moving beyond first/last positions
	$(".SPS-moveUp").first().remove();
	$(".SPS-moveDown").last().remove();
	$(".SPS-moveDown").first().before('<span style="visibility:hidden;">-↑- </span>');
	$(".SPS-moveUp").last().after('<span style="visibility:hidden;"> -↓-</span>');
	//Move positions when clicked
	$(".SPS-moveUp").click(function(){
		//get current position
		var cur_pos = parseInt($(event.target).parent().attr("id").slice(8));
		//duplicate item at one spot higher;
		articles.splice(cur_pos-1, 0, articles[cur_pos]);
		//then remove original item
		articles.splice(cur_pos+1, 1);
		//refresh this screen's display to reflect change in order
		screen2();
	});
	$(".SPS-moveDown").click(function(){
		//get current position
		var cur_pos = parseInt($(event.target).parent().attr("id").slice(8));
		//duplicate item at one spot lower;
		articles.splice(cur_pos+2, 0, articles[cur_pos]);
		// then remove original item
		articles.splice(cur_pos, 1);
		//refresh this screen's display to reflect change in order
		screen2();
	});
	//Edit snippets when clicked
	$(".SPS-title").attr("title", "Click to edit title").css("cursor", "pointer").click(function(){
		//get current position
		var cur_pos = parseInt($(event.target).parent().attr("id").slice(8));
		//prompt for new title
		var edited_title = prompt("Enter new title for "+articles[cur_pos].section , articles[cur_pos].title);
		if ( edited_title != null && edited_title.trim() !== "" ) {
			//first update rss, if needed
			if ( articles[cur_pos].rss === articles[cur_pos].title + ": " + articles[cur_pos].blurb ) {
				articles[cur_pos].rss = edited_title.trim() + ": " + articles[cur_pos].blurb;
			}
			articles[cur_pos].title = edited_title.trim();
		}
		//refresh this screen's display to reflect change
		screen2();
	});
	$(".SPS-blurb").attr("title", "Click to edit blurb").css("cursor", "pointer").click(function(){
		//get current position
		var cur_pos = parseInt($(event.target).parent().attr("id").slice(8));
		//prompt for new title
		var edited_blurb = prompt("Enter new blurb for "+articles[cur_pos].section , articles[cur_pos].blurb);
		if ( edited_blurb != null && edited_blurb.trim()!== "" ) {
			//first update rss, if needed
			if ( articles[cur_pos].rss === articles[cur_pos].title + ": " + articles[cur_pos].blurb ) {
				articles[cur_pos].rss = articles[cur_pos].title + ": " + edited_blurb.trim();
			}
			articles[cur_pos].blurb = edited_blurb.trim();
		}
		//refresh this screen's display to reflect change
		screen2();
	});
	$("#SPS-interface-footer").html('<button id="SPS-next">'+next_button_text+'</button><button id="SPS-back" style="margin-left:3em;">Back</button><button id="SPS-button-cancel" style="margin-left:3em;">Cancel</button>');
	$("#SPS-next").click(function(){
		//if checkbox ticked, firstitem is 0, otherwise firstitem is 1 (default)
		if($("#startatzero").is(':checked')) {
			firstitem = 0;
		}
		//show next screen
		screen3();
		//start the publishing process
		makeIssuePage();	
	});
	$("#SPS-back").click(function() {
		//reset articles array
		articles = [];
		$("#SPS-interface-header").html('Loading...');
		$("#SPS-interface-content").html('...');
		$("#SPS-interface-footer").html('...');
		getArticles();
	});
	$("#SPS-button-cancel").click(function(){
		$("#SPS-modal").remove();
	});
};

// Screen to report progress, and request user input if errors are encountered
screen3 = function() {
	if ( dryrun ) {
		$("#SPS-interface-header").html('Publishing [dry run]');	
	} else {
		$("#SPS-interface-header").html('Publishing');
	}

	$("#SPS-interface-content").html('<ul>'+
	'<li id="SPS-task01" class="SPS-task">Creating issue page... <span>waiting</span></li>'+
	'<li id="SPS-task02" class="SPS-task">Preparing articles... <span>waiting</span></li>'+
	'<li id="SPS-task02b" class="SPS-task">Moving articles... <span>waiting</span></li>'+
	'<li id="SPS-task03" class="SPS-task">Editing issue page... <span>waiting</span></li>'+
	'<li id="SPS-task04" class="SPS-task">Editing main Signpost page... <span>waiting</span></li>'+
	'<li id="SPS-task05" class="SPS-task">Creating single page edition... <span>waiting</span></li>'+
	'<li id="SPS-task06" class="SPS-task">Creating archive page... <span>waiting</span></li>'+
	'<li id="SPS-task07" class="SPS-task">Updating previous edition\'s archive page... <span>waiting</span></li>'+
	'<li id="SPS-task08" class="SPS-task">Purging pages... <span>waiting</span></li>'+
	'<li id="SPS-task09" class="SPS-task">Mass-messaging enwiki subscribers ... <span>waiting</span></li>'+
	'<li id="SPS-task10" class="SPS-task">Mass-messaging global subscribers... <span>waiting</span></li>'+
	'<li id="SPS-task11" class="SPS-task">Updating current year\'s archive overview page... <span>waiting</span></li>'+
	'<li id="SPS-task11" class="SPS-task">Creating archive categories (if needed)... <span>waiting</span></li>'+
	'<li id="SPS-task12" class="SPS-task">Updating "Next" links in previous issues ... <span>waiting</span></li>'+
	'</ul>'+
	'<div id="SPS-errorbox"><button id="SPS-error-retry">Retry task</button> <button id="SPS-error-donext">Continue with next task</button> <button id="SPS-error-close">Close without finishing</button></div>');
	$(".SPS-task").css("color", "#777");
	$("#SPS-interface-footer").html('<span id="SPS-fin">Finished! Remember to announce the new issue on the mailing list, Twitter, and Facebook. <button id="SPS-button-close">Close</button>');
	$("#SPS-errorbox").children().hide();
	$("#SPS-fin").hide();
	$("#SPS-button-close, #SPS-error-close").click(function(){
		$("#SPS-modal").remove();
	});
	$("#SPS-error-retry").click(function(){
		$("#SPS-errorbox").appendTo("#SPS-interface-content").children().hide();
		fn_retry();
	});
	$("#SPS-error-donext").click(function(){
		$("#SPS-errorbox").appendTo("#SPS-interface-content").children().hide();
		fn_skip();
	});
};

// Add link to 'More' menu which starts everything 
mw.util.addPortletLink( 'p-cactions', '#', 'Publish next edition', 'ca-pubnext');
$('#ca-pubnext').on('click', function(e) {
	e.preventDefault();
	// Add interface shell
	$('body').prepend('<div id="SPS-modal">'+
		'<div id="SPS-interface">'+
			'<h4 id="SPS-interface-header"></h4>'+
			'<hr>'+
			'<div id="SPS-interface-content"></div>'+
			'<hr>'+
			'<div id="SPS-interface-footer"></div>'+
		'</div>'+
	'</div>');
	
	// Interface styling
	$("#SPS-modal").css({
		"position": "fixed",
		"z-index": "1",
		"left": "0",
		"top": "0",
		"width": "100%",
		"height": "100%",
		"overflow": "auto",
		"background-color": "rgba(0,0,0,0.4)"
	});
	$("#SPS-interface").css({
		"background-color": "#f0f0f0",
		"margin": "15% auto",
		"padding": "2px 20px",
		"border": "1px solid #888",
		"width": "80%",
		"max-width": "60em",
		"font-size": "90%"
	});
	$("#SPS-interface-content").css("min-height", "7em");
	$("#SPS-interface-footor").css("min-height", "3em");
		
	// Initial interface content
	screen0();
});


// End of full file closure wrappers
});
});
// </nowiki>