User:Alexis Jazz/WASP.js

// 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 = ''+mw.config.get('wgUserName')+''; 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 element, second argument should be a 	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(' — Alexis Jazz (talk or ping me) ');			}			if ( AJWASP.sig ) {				AJWASP.pushToWorkboard(' 18:52, 25 November 2023 (UTC) ');			}			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 + '— Alexis Jazz (talk or ping me) 18:52, 25 November 2023 (UTC)'; }	text = text.replace(/([^~])— Alexis Jazz (talk or ping me) 18:52, 25 November 2023 (UTC)($|[^~])/g,'$1— 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(/— 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(''+text+' '); return ''; } }; AJWASP.basicMarkup = function(text) { return text.replace(/(([^']|'[^'])*)/g,'$1').replace(/(([^']|'[^'])*)/g,'$1').replace(/(([^']|'[^'])*)/g,$1); }; AJWASP.lists = function(text,oldtext) { text = text.replace(/((\n\*(.*)){2,500})/g,'\n$1\n'); text = text.replace(/((\n\#(.*)){2,500})/g,'\n$1\n'); oldtext = ''; while ( text != oldtext ) { oldtext = text+''; text = text.replace(/(([^<]|<(?!\/ul>))*)\n\*(.*)(([^<]|<(?!\/ul>))*)<\/ul>/,'$1\n$3$4'); text = text.replace(/(([^<]|<(?!\/ol>))*)\n\#(.*)(([^<]|<(?!\/ol>))*)<\/ol>/,'$1\n$3$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 te2st test te3st'; AJWASP.attach($('#wasptest1')[0],$('#wasptest2')[0]);