Jump to content

User:Alexis Jazz/Inline-editor.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.
/*<nowiki>
Extremely minimalist editor for small edits like fixing typos. Triple click the paragraph and (if nothing goes wrong) its elements become editable and buttons appear to publish/cancel.
Todo:
	Edit summary field/minor edit checkbox
	Translations (import from MediaWiki)
	Maybe check for duplicate paragraphs?
	Show diff function?
Known/suspected limitations:
	Cannot introduce new elements, only intended for plain text editing
	Cannot alter link targets, references, markup, etc
	Can only alter text that exists within a <p>
	If you enter wikitext it'll probably save the wikitext but you'd continue to see the unparsed wikitext until you reload the page
	Possible bugs if you introduce new elements anyway
	Won't work if templates or tags are used that transclude text that doesn't exist in the wikitext. For example, <ref> generates supertext "[1]" which breaks this script. As <ref> is very common, the script has been made to ignore references.
	If the whole textContent of an element isn't unique in wikitext it might replace the wrong instance (Buffalo buffalo Buffalo buffalo ''buffalo'' [[buffalo]] Buffalo buffalo)
*/
//This script is public domain, irrevocably released as WTFPL Version 2[www.wtfpl.net/about/] by its author, Alexis Jazz.
/*globals $:false,mw:false*/
window.AJQE={};
var AJQE=window.AJQE;
AJQE.msg = {
fail: 'failed to load inline editor',
save: 'Publish changes',
cancel: 'Cancel',
savefailregexp: 'failed to save changes (not found in wikitext)',
savefailapi: 'failed to save changes (API error: $ARG)', //do not translate "$ARG"
summary: '[[[User:Alexis Jazz/Inline-editor|inline-editor]]]',
success: 'Your edit was saved.'
};
AJQE.findTarget=function(target,wikitext,int){
	if ( target.detail != 3 ) {
		return;
	}
	if ( $('.AJIE')[0] ) { //only allow one editor instance at a time
		return;
	}
	if ( ! wikitext ) {
		AJQE.api.get( {action: 'query', prop: 'revisions', format: 'json', titles: mw.config.get('wgPageName'), rvlimit: 1, rvprop: 'timestamp|content|ids', rvslots: '*',
		} ).then( function ( data ) {
			AJQE.pageData=data;
			if ( data.query && data.query.pages && data.query.pages[ Object.keys(data.query.pages)[0] ] && data.query.pages[ Object.keys(data.query.pages)[0] ].revisions && data.query.pages[ Object.keys(data.query.pages)[0] ].revisions[0] ) {
				AJQE.pageRevisionCurrentText = data.query.pages[ Object.keys(data.query.pages)[0] ].revisions[0].slots.main['*'];
				AJQE.revid=data.query.pages[ Object.keys(data.query.pages)[0] ].revisions[0].revid;
				AJQE.findTarget(target,AJQE.pageRevisionCurrentText);
			}
		});
		return;
	}
	AJQE.target=target.target;
	AJQE.innerHTML=AJQE.target.innerHTML; //used to restore content upon cancel
	AJQE.regexArr='(.*)';
	for(int=0;int<4;int++){
		if ( AJQE.target.tagName != 'P' ) {
			AJQE.target = AJQE.target.parentElement;
		}
	}
	if ( AJQE.target.tagName != 'P' ) { //we want to work on a paragraph as a single element may not have enough unique content to locate in wikitext
		return;
	} else {
		AJQE.target.classList.add('AJIE');
	}
	for(int=0;int<AJQE.target.childNodes.length;int++){	//we can't make textnodes editable, so we make them into spans
		if (AJQE.target.childNodes[int].nodeName == '#text' ) {
			AJQE.newSpan=document.createElement('span');
			AJQE.newSpan.innerText=AJQE.target.childNodes[int].textContent;
			$('.AJIE')[0].childNodes[int].replaceWith(AJQE.newSpan);
		}
	}
	AJQE.int2=2;
	for(int=0;int<AJQE.target.childNodes.length;int++){
		if ( ! AJQE.target.childNodes[int].classList.contains('reference') && getComputedStyle(AJQE.target.childNodes[int]).display == 'inline' ) {
			AJQE.target.childNodes[int].contentEditable='true'; //make every element of the paragraph editable. Do NOT make the <p> editable as that would (more often) allow introduction of new elements (by pressing <enter>) which will trip things up
			AJQE.regexArr+=mw.util.escapeRegExp(AJQE.target.childNodes[int].textContent)+'(.*)';
			AJQE.int2++;
		}
	}
	AJQE.checkRegEx=new RegExp(AJQE.regexArr);
	AJQE.checkWikitextMatch=AJQE.pageRevisionCurrentText.match(AJQE.checkRegEx);
	if ( AJQE.checkWikitextMatch ) {
	
		AJQE.saveBtn=document.createElement('input');
		AJQE.saveBtn.type='submit';
		AJQE.saveBtn.id='AJQEsaveBtn';
		AJQE.saveBtn.classList='cdx-button cdx-button--action-progressive cdx-button--weight-primary';
		AJQE.saveBtn.style.display='inline-block'; //prevents the button from being inserted in wikitext
		AJQE.saveBtn.value=AJQE.msg.save;
		AJQE.target.append(AJQE.saveBtn);
		$('#AJQEsaveBtn').on('click',AJQE.saveEdit);
		AJQE.cancelBtn=document.createElement('input');
		AJQE.cancelBtn.type='submit';
		AJQE.cancelBtn.id='AJQEcancelBtn';
		AJQE.cancelBtn.classList='cdx-button cdx-button--action-destructive';
		AJQE.cancelBtn.style.display='inline-block'; //prevents the button from being inserted in wikitext
		AJQE.cancelBtn.style.margin='0 1em 0 1em';
		AJQE.cancelBtn.value=AJQE.msg.cancel;
		AJQE.target.append(AJQE.cancelBtn);
		$('#AJQEcancelBtn').on('click',AJQE.cancelEdit);
		AJQE.target.childNodes[0].focus();
	} else {
		mw.notify(AJQE.msg.savefailregexp,{type:'error'});
	}
};
AJQE.saveEdit=function(int){
	AJQE.regexReplacement='$1';
	AJQE.int2=2;
	for(int=0;int<AJQE.target.childNodes.length;int++){
		if ( ! AJQE.target.childNodes[int].classList.contains('reference') && getComputedStyle(AJQE.target.childNodes[int]).display == 'inline' ) {
			AJQE.regexReplacement+=AJQE.target.childNodes[int].textContent+'$'+AJQE.int2;
			AJQE.int2++;
		}
	}
	AJQE.newWikitext=AJQE.pageRevisionCurrentText.replace(AJQE.checkRegEx,AJQE.regexReplacement);
	if ( AJQE.newWikitext == AJQE.pageRevisionCurrentText ) {
		mw.notify(AJQE.msg.savefailregexp,{type:'error'});
		return;
	}
	AJQE.params = {format: 'json', assert:'user', action: 'edit', title: mw.config.get('wgPageName'), baserevid: AJQE.revid, watchlist: 'nochange', text: AJQE.newWikitext, summary: AJQE.msg.summary};
	AJQE.api.postWithEditToken( AJQE.params ).then( function ( data ) {
		mw.notify(AJQE.msg.success);
		AJQE.removeEditor();
	}, function ( code, data ) {
		AJQE.removeEditor();
		mw.notify(AJQE.msg.savefailapi.replace('$ARG',code),{type:'error'});
	});
};
AJQE.removeEditor = function(int) {
	for(int=0;int<AJQE.target.childNodes.length;int++){
		if ( ! AJQE.target.childNodes[int].classList.contains('reference') && getComputedStyle(AJQE.target.childNodes[int]).display == 'inline' ) {
			AJQE.target.childNodes[int].contentEditable='false';
		}
	}
	$('#AJQEsaveBtn,#AJQEcancelBtn').remove();
	AJQE.target.classList.remove('AJIE');
};
AJQE.cancelEdit = function() {
	$('.AJIE')[0].innerHTML = AJQE.innerHTML;
	$('.AJIE').removeClass('AJIE');
};
if ( mw.config.get('wgIsProbablyEditable') ) {
	mw.loader.using(['mediawiki.api','mediawiki.util'], function(){
		AJQE.api = new mw.Api();
		$('#mw-content-text').on('click',AJQE.findTarget);
	});
}
//</nowiki>