User:Jackmcbarn/editProtectedHelper.js/CILI sandbox.js

From Wikipedia, the free encyclopedia
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.
//The following was copied from [[User:Jackmcbarn/editProtectedHelper.js]] for sandbox purposes
// <nowiki>
var browserHasDataCorruptionBug = $('<div style="color:#ffd"></div>')[0].outerHTML != '<div style="color:#ffd"></div>';
if(browserHasDataCorruptionBug && ('undefined' === typeof ephAllowDataCorruptionBug || !ephAllowDataCorruptionBug)) {
	$(document).ready(function() {
		$('.editrequest .mbox-text').append('<small>The editProtectedHelper script is currently disabled because your browser has a bug which will cause corruption of pages that it edits with Parsoid. See <a href="https://www.mediawiki.org/wiki/User:Catrope/IE_bugs#style_attributes_are_aggressively_normalized.2C_causing_data_loss">here</a> for details. To override this, add "ephAllowDataCorruptionBug = true;" immediately above the line where you load this script. You are responsible for any damage to pages that this causes.</small>');
	});
// only enable this on the latest revision of the page
} else if(mw.config.get('wgRevisionId') == mw.config.get('wgCurRevisionId')) {
	$(document).ready(function() {
		mw.loader.using( ['mediawiki.api'], function() {
			'use strict';
			var templateResponses = [
				[ '', '(No template response)' ],
				[ 'd', 'Done' ],
				[ 'pd', 'Partly done:' ],
				[ 'nd', 'Not done:' ],
				[ 'nfn', 'Not done for now:' ],
				[ 'c', 'Not done: please establish a consensus for this alteration before using the {{edit protected}} template.'] , // TODO make dynamic
				[ 'rs', 'Not done: please provide reliable sources that support the change you want to be made.' ],
				[ 'xy', 'Not done: it\'s not clear what changes you want made.' ],[ 'xy', 'Please mention the specific changes in a "change X to Y" format.' ],
				[ 'mis', 'Not done: this is the talk page for discussing improvements to the template {{EP}}'],[ 'mis', 'Please make your request at the talk page for the article concerned.'], // TODO make dynamic
				[ 'sb', 'Not done: please make your requested changes to the template\'s sandbox first; see WP:TESTCASES.' ],
				[ 'tp', 'Not done: this is the talk page for discussing improvements to the template {{EP}}.' ],[ 'tp', 'If possible, please make your request at the talk page for the article concerned.' ],[ 'tp', 'If you cannot edit the article\'s talk page, you can instead make your request at' ],[ 'tp', 'Wikipedia:Requests for page protection#Current requests for edits to a protected page.' ],
				[ 'a', 'Already done' ],
				[ 'hr', 'Not done: According to the page\'s protection level and your user rights, you should be able to edit the page yourself.' ], [ 'hr', 'If you seem to be unable to, please reopen the request with further details.'],
				[ 'nlp', 'Not done: The page\'s protection level has changed since this request was placed.' ],[ 'nlp', 'You should now be able to edit the page yourself. If you still seem to be unable to, please reopen the request with further details.'],
				[ 'doc', 'Not done: {{edit protected}} is usually not required for edits to the documentation, categories,' ],[ 'doc', 'or interlanguage links of templates using a documentation subpage.' ],[ 'doc', 'Use the \'edit\' link at the top of the green "Template documentation" box to edit the documentation subpage.' ],
				[ 'drv', 'Not done: requests for recreating deleted pages protected against creation should be made at Wikipedia:Deletion review.' ],
				[ 'r', 'Not done: requests for increases to the page protection level should be made at Wikipedia:Requests for page protection.' ],
				[ 'ru', 'Not done: requests for decreases to the page protection level should be directed to the protecting admin' ],[ 'ru', 'or to Wikipedia:Requests for page protection if the protecting admin is not active or has declined the request.' ],
				[ 'p', 'Not done: this is not the right page to request additional user rights.' ],[ 'p', 'You may reopen this request with the specific changes to be made and someone will add them for you.'], // TODO make dynamic
				[ 'm', 'Not done: page move requests should be made at Wikipedia:Requested moves.' ],
				[ 'q', 'Question:' ],
				[ 'note', 'Note:' ],
				[ 'udp', 'Undone: This request (or the completed portion of it) has been undone.' ],
				[ 'ud', 'Undone: This request has been undone.' ]
			];
			var selector = $('.editrequest .mbox-text');

			// Global variable. In onParsoidDomReceived, this is set
			// to the ETag header so that we can pass it back later 
			// in convertModifiedDom.
			var gEtag;

			// Global variable. In onParsoidDomReceived, this is
			// set to the string we get back from the Parsoid API.
			// Used in convertModifiedDom.
			var parsoidDom;

			var selectedLevel = { semi: [' selected="selected"', '', '', '', ''], extended: ['', ' selected="selected"', '', '', ''], template: ['', '', ' selected="selected"', '', ''], full: ['', '', '', ' selected="selected"', ''], interface: ['', '', '', '', ' selected="selected"'] };
			var templateLevel = { semi: 'semi-', extended: 'extended-', template: 'template-', full: 'fully-', interface: 'interface-' };
			var responseLevel = { semi: '{' + '{subst:ESp|', extended: '{' + '{subst:EEp|', template: '{' + '{subst:ETp|', full: '{' + '{subst:EP|', interface: '{' + '{subst:EIp|' };
			var warnOnRespond = false, warnOnQuickRespond = true, warnOnRemove = true, autoFixLevel = true;
			var quickResponses = [
				[ 'd', '', 'Done'],
				[ 'rs', '', 'Needs reliable sources'],
				[ 'xy', '', 'Unclear/X to Y'],
				[ 'hr', '', 'Could always edit'],
				[ 'nlp', '', 'Can edit now'],
				[ 'mis', '', 'Misplaced']
			];
			if('undefined' !== typeof ephWarnOnRespond) {
				warnOnRespond = ephWarnOnRespond;
			}
			if('undefined' !== typeof ephWarnOnQuickRespond) {
				warnOnQuickRespond = ephWarnOnQuickRespond;
			}
			if('undefined' !== typeof ephWarnOnRemove) {
				warnOnRemove = ephWarnOnRemove;
			}
			if('undefined' !== typeof ephAutoFixLevel) {
				autoFixLevel = ephAutoFixLevel;
			}
			if('undefined' !== typeof ephQuickResponses) {
				quickResponses = ephQuickResponses;
			}
			function yesno(val, def) {
				if(typeof val === 'string') {
					val = val.toLowerCase();
				}
				if(typeof val === 'undefined' || val === '') {
					return undefined;
				} else if(val === 'yes' || val === 'y' || val === 'true' || val === 1) {
					return true;
				} else if(val === 'no' || val === 'n' || val === 'false' || val === 0) {
					return false;
				}
				return def;
			}
			function getBanner(level, pagename, answered, force, demo) {
				return '{{edit ' + templateLevel[level] + 'protected' + (pagename !== '' ? '|' + pagename : '') + '|answered=' + (answered ? 'yes' : 'no') + (force ? '|force=yes' : '') + (demo ? '|demo=yes}}' : '}}');
			}
			function getResponse(level, template, free) {
				return ':' + (template === '' ? '' : responseLevel[level] + template + '}} ') + (free === '' ? '' : free + ' ') + '~~' + "~~\n";
			}
			function makeUniqueString(index) {
				// this looks like a strip marker on purpose
				return "\x7fUNIQ" + Math.random().toString(16).substr(2) + '-editProtectedHelper-' + index + "-QINU\x7f";
			}
			function saveWikitextFromParsoid(parsoidObj, data) {
				var newWikitext, tmp, removerequest = false;
				if(parsoidObj.removerequest) {
					tmp = data.split(parsoidObj.templateMarker);
					removerequest = true;
					newWikitext = tmp[0];
					if(parsoidObj.respondedInPage) {
						tmp = tmp[1].split(parsoidObj.responseMarker);
						newWikitext = newWikitext.replace(/\n+$/, '') + "\n\n" + tmp[1].replace(/^\n+/, '');
					}
				} else {
					var response = getResponse(parsoidObj.form.level.value, parsoidObj.form.responsetemplate.value, parsoidObj.form.responsefree.value);
					var banner = getBanner(parsoidObj.form.level.value, parsoidObj.form.pagetoedit.value, parsoidObj.form.answered.checked, parsoidObj.form.force.checked, parsoidObj.demo);
					// prevent empty response
					if(response == ':~~' + "~~\n") {
						response = '';
					}
					tmp = data.split(parsoidObj.templateMarker);
					newWikitext = tmp[0].replace(/\n*$/, tmp[0].trim().length ? "\n\n" : '') + banner + tmp[1].replace(/^\n*/, "\n");
					if(parsoidObj.respondedInPage) {
						tmp = newWikitext.split(parsoidObj.responseMarker);
						newWikitext = tmp[0].replace(/\n*$/, "\n") + response + tmp[1].replace(/^\n*/, "\n");
					} else {
						newWikitext = newWikitext.replace(/\n*$/, "\n") + response;
					}
				}
				var resultObj = parsoidObj.resultObj, sectionName = parsoidObj.section && parsoidObj.section.text().trim();
				if(sectionName && (sectionName.indexOf(parsoidObj.templateMarker) !== -1 || sectionName.indexOf(parsoidObj.responseMarker) !== -1)) {
					// someone put an edit request template inside the section header, or something like that.
					// don't even try to include a working section link in that case
					sectionName = null;
				}
				//console.log(newWikitext);
				resultObj.text('Saving...');
				debugger; // triggers only if debugger is active
				new mw.Api().get( { action: 'query', prop: 'revisions', rvprop: 'timestamp', revids: mw.config.get('wgRevisionId') }).done(function(data) {
					new mw.Api().postWithEditToken( { action: 'edit', pageid: mw.config.get('wgArticleId'), text: newWikitext, summary: (sectionName ? '/' + '* ' + sectionName + ' *' + '/ ' : '') + (removerequest ? 'Removed' : 'Responded to') + ' edit request ([[WP:EPH|EPH]])', notminor: true, nocreate: true, basetimestamp: data.query.pages[mw.config.get('wgArticleId')].revisions[0].timestamp } ).done(function(result) {
						if(typeof(result.edit.newrevid) === 'undefined' || typeof(result.edit.oldrevid) === 'undefined') {
							resultObj.css('color', 'red').text("Error: The API response omitted required information. Please check the page's history manually to see whether your edit was saved properly. The console may contain details.");
						} else {
							resultObj.css('color', 'green').text('Success: Loading diff...');
							if( window.editProtectedHelperReloadAfter ) {
								window.location.reload( /* forcedReload */ true );
							} else {
								location.href = mw.config.get('wgScript') + '?diff=' + result.edit.newrevid + '&oldid=' + result.edit.oldrevid;
							}
						}
					}).fail(function(err) {
						resultObj.css('color', 'red').text('Error: ' + err + '. The console may contain details.');
					}).always(console.log);
				});
			}
			function convertModifiedDom(e) {
				var parsoidObj, nextRequestBeforeHeader = false;
				if(warnOnRespond && !confirm('Are you sure you want to respond to this edit request?')) {
					return false;
				}
				$('.editrequest button').prop('disabled', true);
				parsoidObj = e.data;
				parsoidObj.resultObj.text('Preparing new wikitext...');
				$(parsoidObj).before(parsoidObj.templateMarker);
				$('h1,h2,h3,h4,h5,h6,.editrequest', parsoidDom).each(function() {
					var obj = $(this);
					if(!obj.hasClass('editrequest')) {
						// a heading
						if(obj.closest('[typeof="mw:Transclusion"]').length) {
							// it's from a template transclusion. ignore
							return true;
						}
						if(obj.add(parsoidObj)[0] === this) {
							// before our banner. set as our section header (tentatively) and otherwise ignore
							parsoidObj.section = obj;
							return true;
						}
					} else if (obj.add(parsoidObj)[0] === this) {
						// not after our banner. ignore
						return true;
					} else {
						nextRequestBeforeHeader = true;
					}
					obj.before(parsoidObj.responseMarker);
					parsoidObj.respondedInPage = true;
					return false;
				});
				if(parsoidObj.removerequest && !nextRequestBeforeHeader && $(parsoidObj).prev().is(parsoidObj.section)) {
					// if the section header is immediately before a request being removed, remove it too
					parsoidObj.section.remove();
				}
				$('[about="' + $(parsoidObj).attr('about').replace('\\', '\\\\').replace('"', '\\"') + '"]', parsoidDom).remove();
				$.ajax({
					type: 'POST',
					url: '/api/rest_v1/transform/html/to/wikitext/' + encodeURIComponent(mw.config.get('wgPageName')) + '/' + mw.config.get('wgRevisionId'),
					headers: {
						Accept: 'text/plain; charset=utf-8; profile="mediawiki.org/specs/wikitext/1.0.0"',
						'Api-User-Agent': 'editProtectedHelper (https://en.wikipedia.org/wiki/User:Jackmcbarn/editProtectedHelper)',
						'If-Match': gEtag
					},
					data: { html: parsoidDom.documentElement.outerHTML },
					success: function(data) { return saveWikitextFromParsoid(parsoidObj, data); }
				});
			}
			function appendForm(obj, level, pagename, answered, force, parsoidObj) {
				if(browserHasDataCorruptionBug) {
					$(obj).append('<div style="color:red;font-weight:bold">WARNING: Your browser has a bug which will cause corruption of pages that it edits with Parsoid. See <a href="https://www.mediawiki.org/wiki/User:Catrope/IE_bugs#style_attributes_are_aggressively_normalized.2C_causing_data_loss">here</a> for details. You are responsible for any damage to pages that this causes.</div>');
				}
				var form = $('<form class="editProtectedHelper" style="display: none" />');
				form.append('<style scoped>.mw-ui-input { background-color: white; }</style>');
				form.append('<label>Level: <select name="level" class="mw-ui-input mw-ui-input-inline"><option value="semi"' + selectedLevel[level][0] + '>Semi-protected</option><option value="extended"' + selectedLevel[level][1] + '>Extended-confirmed-protected</option><option value="template"' + selectedLevel[level][2] + '>Template-protected</option><option value="full"' + selectedLevel[level][3] + '>Fully protected</option><option value="interface"' + selectedLevel[level][4] + '>Interface-protected</option></select></label> ');
				if(force) {
					form.append('<label>Disable protection level autodetection (use only if necessary): <input type="checkbox" name="force" checked="checked" /></label> ' );
				} else {
					form.append('<input type="checkbox" name="force" style="display: none" />' ); // if this is off and you want to turn it on, do it with firebug or something. otherwise people will use this when they shouldn't
				}
				var label = $('<label>Page to be edited: </label>');
				label.append($('<input type="text" name="pagetoedit" class="mw-ui-input mw-ui-input-inline" />').attr('value', pagename !== '<!-- Page to be edited -->' ? pagename : ''));
				form.append(label);
				form.append(' <label>Answered: <input type="checkbox" name="answered"' + (answered ? ' checked="checked"' : '') + ' /></label><br />Response: ');
				var select = $('<select name="responsetemplate" class="mw-ui-input" />');
				templateResponses.forEach(function(r) {
					select.append($('<option />').attr('value', r[0]).text(r[1]));
				});
				form.append(select);
				form.append('<textarea name="responsefree" class="mw-ui-input" placeholder="Enter any custom response text here. A signature will automatically be appended."></textarea> ~~' + '~~ ');
				var resultObj = $('<span></span>');
				var button = $('<button type="button" class="mw-ui-button mw-ui-constructive">Submit</button>').click(parsoidObj, convertModifiedDom);
				form.append(button);
				form.append(' ');
				$(obj).append(form);
				var buttons = $('<div />');
				buttons.append($('<button type="button" class="mw-ui-button mw-ui-progressive">Respond</button>').click(function(){
					buttons.hide();
					$(obj).closest('.editrequest').removeClass('mbox-small');
					form.show();
				}))
				.append(' ')
				.append($('<button type="button" class="mw-ui-button mw-ui-destructive">Remove request</button>').click(function(){
					if(!warnOnRemove || confirm('Are you sure you want to completely remove this edit request from the page? In general, this should not be done to good-faith requests, even if they are blank, unless they are duplicates of other requests by the same user, etc.')) {
						warnOnRespond = false;
						parsoidObj.removerequest = true;
						button.click();
					}
				}));
				if(!answered) {
					quickResponses.forEach(function(r){
						buttons.append(' ').append($('<button type="button" class="mw-ui-button mw-ui-constructive" />').text(r[2]).click(function(){
							if(!warnOnQuickRespond || confirm('Are you sure you want to respond to this edit request?')) {
								warnOnRespond = false;
								parsoidObj.form.answered.checked = true;
								parsoidObj.form.responsetemplate.value = r[0];
								parsoidObj.form.responsefree.value = r[1];
								button.click();
							}
						}));
					});
				}
				$(obj).append(buttons);
				$(obj).append(resultObj);
				parsoidObj.form = form[0];
				parsoidObj.resultObj = resultObj;
			}
			function parsoidSetupFieldsForTemplate(index) {
				var obj = $(this);
				var data_mw = obj.attr('data-mw');
				if(data_mw === undefined) {
					console.log("No data-mw attribute was found on edit request banner " + index + ". This is probably because some template above it on the page opened an HTML tag but didn't close it.");
					return;
				}
				data_mw = JSON.parse(data_mw);
				var templateName = data_mw.parts[0].template.target.wt;
				var level = obj.attr('data-origlevel');
				if(autoFixLevel) {
					switch(this.id) {
						case 'editsemiprotected':
							level = 'semi';
							break;
						case 'editextendedprotected':
							level = 'extended';
							break;
						case 'edittemplateprotected':
							level = 'template';
							break;
						case 'editprotected':
							level = 'full';
							break;
						case 'editinterfaceprotected':
							level = 'interface';
					}
				}
				var params = [];
				for(var key in data_mw.parts[0].template.params) {
					params[key] = data_mw.parts[0].template.params[key].wt;
				}
				
				// this only runs on numerical parameters
				params.forEach(function(value, key) {
					if(/=/.test(value)) {
						params[key] = key + '=' + value;
					}
				});
				
				var pagename = params.join('|').replace(/^\|+|\|+$/g, '').replace(/\|+/g, '|');
				var answered = yesno(params.ans || params.answered, true);
				this.demo = yesno(params.demo);
				var force = yesno(params.force);
				this.params = params;
				this.templateMarker = makeUniqueString(2 * index);
				this.responseMarker = makeUniqueString(2 * index + 1);
				appendForm(selector[index], level, pagename, answered, force, this);
			}
			function onParsoidDomReceived(data, textStatus, jqXHR) {

				// Grab the ETag header and store it in the global
				// gEtag for later use
				var headers = jqXHR.getAllResponseHeaders().split( "\r\n" );
				for( var i = 0, n = headers.length; i < n; i++ ) {
					if( headers[i].substring( 0, headers[i].indexOf( ":" ) ) === "etag" ) {
						gEtag = headers[i].substring( headers[i].indexOf( ":" ) + 2 );
						break;
					}
				}

				parsoidDom = new DOMParser().parseFromString(data, 'text/html');
				if(!parsoidDom) {
					// blech. Safari.
					parsoidDom = document.implementation.createHTMLDocument('');
					parsoidDom.documentElement.innerHTML = data;
				}
				var parsoidSelector = $('.editrequest', parsoidDom);
				if(selector.length != parsoidSelector.length) {
					console.log('Sanity check failed: ' + selector.length + ' edit requests exist in the page but only ' + parsoidSelector.length + ' were found in Parsoid\'s output.');
					return;
				}
				parsoidSelector.each(parsoidSetupFieldsForTemplate);
			}
			if(selector.length) {
				mw.loader.load('mediawiki.api.edit');
				mw.loader.load('mediawiki.ui.button');
				mw.loader.load('mediawiki.ui.input');
				$.ajax({
					url: '/api/rest_v1/page/html/' + encodeURIComponent(mw.config.get('wgPageName')) + '/' + mw.config.get('wgRevisionId'),
					headers: {
						Accept: 'text/html; charset=utf-8; profile="https://www.mediawiki.org/wiki/Specs/HTML/2.0.0"',
						'Api-User-Agent': 'editProtectedHelper (https://en.wikipedia.org/wiki/User:Jackmcbarn/editProtectedHelper)'
					},
					success: onParsoidDomReceived
				});
			}
		});
	});
}
// </nowiki>]