User:Alexis Jazz/WASP.js

Source: 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.
// AJ's Wikitext Advanced Speed Preview
// WIP
// Create a textarea and a div, run AJWASP.attach(yourtextarea,yourdiv) and watch the magic
// Aims to be fast (for limited amounts of text) with minimal need for API requests. Does NOT aim to be 100% accurate. (though depending on input it could be)
// Goal is to preview parser that is *good enough* for most comments in discussions while using minimal bandwidth and causing minimal server load.
// #hugeparadigmshift(tm) Trademark belongs to Remsense.
// This script is irrevocably released as WTFPL Version 2[www.wtfpl.net/about/] by its author, Alexis Jazz.
// Need a signature to be added?
// window.AJWASPSIG = 1;window.AJWASPSIGPREPEND = '— '
// todo: nowiki, templates, events
window.AJWASPSIG = 1;window.AJWASPSIGPREPEND = '— ';
$('#wasptest1,#wasptest2,#AJWASP_workboard').remove(); //TESTING
var AJWASP = {};
AJWASP.wikilink = function(val) {
	path = mw.config.get('wgArticlePath').replace('\$1',encodeURIComponent(val));
	return path;
};
if ( window.AJWASPSIG ) {
	AJWASP.sig = 1;
	AJWASP.sigPrepend = (window.AJWASPSIGPREPEND || '');
	AJWASP.sigText = '<a href="'+AJWASP.wikilink('User:'+mw.config.get('wgUserName'))+'">'+mw.config.get('wgUserName')+'</a>';
	AJWASP.sigTextTime = new Date().toString();
}
mw.util.addCSS('.AJWASP_preview{padding:0.5em}');
String.prototype.WASPwikilink = function(val,path){
	path = mw.config.get('wgArticlePath').replace('\$1',encodeURIComponent(this));
	return path;
};
AJWASP.attach = function(textEl,previewEl) { // first argument should be a <textarea> element, second argument should be a <div>
	AJWASP.caseSensitive = ( mw.config.get('wgCaseSensitiveNamespaces').includes(mw.config.get('wgNamespaceNumber')));
	if ( ! $('#AJWASP_workboard')[0] ) {
		AJWASP.workboard = document.createElement('div'); //we'll dump parsed stuff the API returns in here. It'll be invisible, it'll just be searched for link classes etc. also embeds images so they shouldn't fall out of browser cache
		AJWASP.workboard.id = 'AJWASP_workboard';
		AJWASP.workboard.style = 'display:none';
		AJWASP.workboard.style['background-color'] = 'lightblue';
		$('#content').append(AJWASP.workboard);
	}
	if ( typeof AJWASP.redLinkTitle == 'undefined' && $('#content a.new:eq(0)')[0] ) {
		AJWASP.redLinkTitle = $('#content a.new:eq(0)')[0].title.replace(new mw.Uri($('#content a.new:eq(0)')[0].href).query.title.replace(/_/g,' '),'$1');
	} else if ( ! $('#content a.new:eq(0)')[0] ) {
		AJWASP.redLinkTitle = 0;
	}
	AJWASP.wikitextForWorkboard = [];
	AJWASP.lastAPI = 0;
	AJWASP.workboardAPI = setInterval(function(){
		//make parse request if there is something to parse OR if a signature should be rendered and the last parse request was >60s ago (this ensures the timestamp gets updated)
		if ( AJWASP.wikitextForWorkboard.length > 0 || ( AJWASP.sig && AJWASP.lastAPI != 0 && (AJWASP.lastAPI+60000) < new Date().getTime() ) ) {
			if ( AJWASP.sig && ! AJWASP.gotSig ) {
				AJWASP.pushToWorkboard('<span class="AJWASPsig">— <span style="color:#e08020">Alexis Jazz</span> ([[User talk:Alexis Jazz|talk]] or ping me)</span>');
			}
			if ( AJWASP.sig ) {
				AJWASP.pushToWorkboard('<span class="AJWASPsigTime">18:52, 25 November 2023 (UTC)</span>');
			}
			AJWASP.addToWorkboard(AJWASP.wikitextForWorkboard);
			AJWASP.wikitextForWorkboard = [];
		}
	},2000);
	textEl.onchange = function(){AJWASP.update(textEl,previewEl)};
	textEl.onkeyup = function(){AJWASP.update(textEl,previewEl)};
	textEl.classList.add('AJWASP_textarea');
	previewEl.classList.add('AJWASP_preview');
};
AJWASP.pushToWorkboard = function(text) {
	if ( AJWASP.wikitextForWorkboard.indexOf(text) == -1 ) {
		AJWASP.wikitextForWorkboard.push(text);
	}
};
AJWASP.addToWorkboard = function(wikitext,int) {
	wikitext = wikitext.toString();
	mw.loader.using(['mediawiki.api']).then( function() {
		if ( ! AJWASP.api ) {
			AJWASP.api = new mw.Api();
		}
		AJWASP.params = {action:'parse',text:wikitext,format:'json',disablelimitreport:true,prop:['text'],pst:1};
		AJWASP.api.get(AJWASP.params).then(function(data){
			AJWASP.lastAPI = new Date().getTime();
			$('#AJWASP_workboard').append(data.parse.text['*']);
			if ( AJWASP.sig && ! AJWASP.gotSig ) {
				AJWASP.sigText = $('.AJWASPsig:eq(0)')[0].innerHTML;
				AJWASP.gotSig = 1;
				sigPrependRegExp = new RegExp('^'+AJWASP.sigPrepend);
				if ( AJWASP.sigText.match(sigPrependRegExp) ) {
					AJWASP.sigPrepend = '';
				}
			}
			if ( AJWASP.sig ) {
				AJWASP.sigTextTime = $('.AJWASPsigTime')[$('.AJWASPsigTime').length-1].innerHTML;
			}
			for (int=0;int<$('.AJWASP_textarea').length;int++){ //trigger update. If there are multiple instances, um, just update all of 'em, even though multiple instances aren't expected to work properly (also: WHY would you want that?)
				$('.AJWASP_textarea')[int].onchange();
			}
		});
	});
};
AJWASP.update = function(textEl,previewEl,text) {
	//console.log('AJWASP: updating preview');
	text = textEl.value;
	if ( AJWASP.sig ) {
		text = text + AJWASP.sigPrepend + '— <span style="color:#e08020">Alexis Jazz</span> ([[User talk:Alexis Jazz|talk]] or ping me) 18:52, 25 November 2023 (UTC)';
	}
	text = text.replace(/([^~])— <span style="color:#e08020">Alexis Jazz</span> ([[User talk:Alexis Jazz|talk]] or ping me) 18:52, 25 November 2023 (UTC)($|[^~])/g,'$1— <span style="color:#e08020">Alexis Jazz</span> ([[User talk:Alexis Jazz|talk]] or ping me) 18:52, 25 November 2023 (UTC)$2');
	text = AJWASP.addSig(text);
	text = AJWASP.parseImages(text); //images MUST be parsed before links
	text = AJWASP.parseLinks(text);
	text = AJWASP.basicMarkup(text);
	text = AJWASP.lists(text);
	previewEl.innerHTML = text;
};
AJWASP.addSig = function(text) {
	text = text.replace(/18:52, 25 November 2023 (UTC)/g, AJWASP.sigTextTime);
	text = text.replace(/— <span style="color:#e08020">Alexis Jazz</span> ([[User talk:Alexis Jazz|talk]] or ping me)/g, AJWASP.sigText);
	return text;
};
AJWASP.imgParsed = {};
AJWASP.parseImages = function(text,int,imgmatches,imgcode) {
	for(int=0;int<$('.AJWASPimage').length;int++){
		AJWASP.imgParsed[$('.AJWASPimage')[0].dataset.wikitext] = $('.AJWASPimage')[0].innerHTML;
		$('.AJWASPimage')[0].remove(); // got it in AJWASP.imgParsed now, no longer needed. free up memory
	}
	AJWASP.imgRegExp = new RegExp('\\[\\[(File|Image|'+mw.config.get('wgFormattedNamespaces')[6]+'):(([^\\]]|\\](?!\\]))*)\\]\\]','g');
	imgmatches = text.match(AJWASP.imgRegExp);
	if ( imgmatches ) {
		for(int=0;int<imgmatches.length;int++){
			imgcode = AJWASP.parseImage(imgmatches[int]);
			text = text.replace(imgmatches[int],imgcode);
		}
	}
	return text;
	//https://commons.wikimedia.org/w/thumb.php?f=ESLShavianAre.png&w=135
};
AJWASP.parseImage = function(text,int) {
	if ( AJWASP.imgParsed[text] ) {
		return AJWASP.imgParsed[text];
//	} else if ( $('div[data-wikitext="'+text+'"]')[0] ) { //should not be needed anymore
//		AJWASP.imgParsed[text] = $('div[data-wikitext="'+text+'"]')[0].innerHTML;
//		$('div[data-wikitext="'+text+'"]')[0].remove(); // got it in AJWASP.imgParsed now, no longer needed. free up memory
//		return AJWASP.imgParsed[text];
	} else {
		AJWASP.pushToWorkboard('<div class="AJWASPimage" data-wikitext="<nowiki>'+text+'</nowiki>'+'">'+text+'</div>');
		return '';
	}
};
AJWASP.basicMarkup = function(text) {
	return text.replace(/'''''(([^']|'[^'])*)'''''/g,'<b><i>$1</i></b>').replace(/'''(([^']|'[^'])*)'''/g,'<b>$1</b>').replace(/''(([^']|'[^'])*)''/g,'<i>$1</i>');
};
AJWASP.lists = function(text,oldtext) {
	text = text.replace(/((\n\*(.*)){2,500})/g,'\n<ul>$1\n</ul>');
	text = text.replace(/((\n\#(.*)){2,500})/g,'\n<ol>$1\n</ol>');
	oldtext = '';
	while ( text != oldtext ) {
		oldtext = text+'';
		text = text.replace(/<ul>(([^<]|<(?!\/ul>))*)\n\*(.*)(([^<]|<(?!\/ul>))*)<\/ul>/,'<ul>$1\n<li>$3</li>$4</ul>');
		text = text.replace(/<ol>(([^<]|<(?!\/ol>))*)\n\#(.*)(([^<]|<(?!\/ol>))*)<\/ol>/,'<ol>$1\n<li>$3</li>$4</ol>');
	}
	return text;
};
AJWASP.ucFirst = function(text){
	return text[0].toUpperCase()+text.slice(1);
};
AJWASP.intLinkClasses = {};
AJWASP.getIntLinkClass = function(title) {
	if ( ! AJWASP.caseSensitive ) {
		title = AJWASP.ucFirst(title);
	}
	//console.log('AJWASP: get link class for page '+title);
	if ( AJWASP.intLinkClasses[title] ) {
		return AJWASP.intLinkClasses[title];
	}
	if ( AJWASP.redLinkTitle && $('a.new[title="'+title.replace(/(.*)/,AJWASP.redLinkTitle)+'"]')[0] ) {
		AJWASP.intLinkClasses[title] = 'new';
		return 'new';
	}
	if ( $('a.mw-redirect[title="'+title+'"]')[0] ) {
		AJWASP.intLinkClasses[title] = 'mw-redirect';
		return 'mw-redirect';
	}
	if ( $('a.mw-disambig[title="'+title+'"]')[0] ) {
		AJWASP.intLinkClasses[title] = 'mw-disambig';
		return 'mw-disambig';
	}
	if ( $('a:not(.unknown)[title="'+title+'"]')[0] ) {
		AJWASP.intLinkClasses[title] = 'bluelink';
		return '';
	}
	AJWASP.pushToWorkboard('[['+title+']]');
	return 'unknown';
};
AJWASP.parseLinks = function(text,regex,regexg,int,newlink,newLinkComponent,allLinks,regexstr,intlinks,intLinkClass,title,titleAttr) {
	regexstr = '\\[\\[(([^\]\|]){0,200})\|(([^\]]){0,200})\\]\\]';
	regex = new RegExp(regexstr);
	regex = new RegExp(regexstr);
	regexg = new RegExp(regexstr,'g');
	//piped wikilinks
	if ( text.match(/\[\[([^\|\]]*)\|([^\]]*)\]\]/) ) {
		text = text.replace(/\[\[([^\|\]]*)\|([^\]]*)\]\]/g,'<a class="AJintlink" href="AJINTLINKHREFSTART$1AJINTLINKHREFEND" data-title="$1">$2</a>');
	}
	//unpiped wikilinks
	if ( text.match(/\[\[([^\|\]]*)\]\]/) ) {
		text = text.replace(/\[\[([^\|\]]*)\]\]/g,'<a class="AJintlink" href="AJINTLINKHREFSTART$1AJINTLINKHREFEND" data-title="$1">$1</a>');
	}
	intlinks = text.match(/AJINTLINKHREFSTART([^"]*)AJINTLINKHREFEND/g);
	if ( intlinks ) {
		for(int=0;int<intlinks.length;int++){
			title = intlinks[int].replace(/AJINTLINKHREFSTART([^"]*)AJINTLINKHREFEND/,'$1');
			if ( ! AJWASP.caseSensitive ) {
				title = AJWASP.ucFirst(title);
			}
			title = AJWASP.wikilink(title);
			text = text.replace(intlinks[int],title);
		}
	}
	intlinks = text.match(/"AJintlink" href="([^"]*)" data-title="([^"]*)"/g);
	if ( intlinks ) {
		for(int=0;int<intlinks.length;int++){
		AJWASP.intlinks1 = intlinks;
		AJWASP.text = text;
			title = decodeURIComponent(intlinks[int].match(/data\-title="(.*)"/)[1].replace(/_/,' '));
			intLinkClass = AJWASP.getIntLinkClass(title);
			if ( intLinkClass == 'new' ) {
				titleAttr = AJWASP.redLinkTitle.replace('$1',title);
			} else {
				titleAttr = title;
			}
			titleAttr = AJWASP.ucFirst(titleAttr);
			text = text.replace(intlinks[int],intlinks[int].replace(/("AJintlink" href=").*"/,'$1'+intlinks[int].replace(/"AJintlink" href="(.*)"/,'$1'))+'"').replace('AJintlink',intLinkClass+'" title="'+titleAttr);
		}
	}
	return text;
};
//TESTING
AJWASP.el1 = document.createElement('textarea');
AJWASP.el1.rows=5;
AJWASP.el1.id = 'wasptest1';
AJWASP.el2 = document.createElement('div');
AJWASP.el2.id = 'wasptest2';
AJWASP.el2.style = 'border:1px solid black;min-height:1em;margin-top:1em';
$('#content').append(AJWASP.el1);
$('#content').append(AJWASP.el2);
$('#wasptest1')[0].value = 'test [[test@test|te2st]] test [[test2@test2|te3st]]';
AJWASP.attach($('#wasptest1')[0],$('#wasptest2')[0]);