User:Alexis Jazz/AnonLoader.js

/* <nowiki>
AnonLoader, allows anons to load scripts specified in window.AnonLoaderScripts (which can be defined in MediaWiki:common.js) through portlet links
AnonLoader is public domain, irrevocably released as WTFPL Version 2[www.wtfpl.net/about/] by its author, Alexis Jazz.
Installation instructions:
* Create MediaWiki:Gadget-AnonLoader.js with the contents of this file.
* Add this line to MediaWiki:Gadgets-definition: "AnonLoader[ResourceLoader|dependencies=mediawiki.util,mediawiki.storage|targets=desktop,mobile|default|hidden]|AnonLoader.js"
* Put (for example) window.AnonLoaderScripts = ['CD','Factotum:gadget','dark-mode-toggle,dark-mode-toggle-pagestyles:gadget:Dark mode','dark-mode:gadget:Instant dark mode:urlLoad']; in MediaWiki:Common.js AND MediaWiki:Mobile.js (MobileFrontend doesn't load Common.js)
* Optional: add window.AnonLoaderShowAll=1 in common.js+mobile.js to show all gadgets without a collapsible menu.
AnonLoaderScripts format description:
Gadgets:
['GADGET1:gadget[:displayname][:urlLoad]','GADGET2[,GADGET3][,GADGET4]:gadget[:displayname]','GADGET5:gadget[:displayname]']
Only one gadget can have urlLoad. This will intercept links to add &withgadget=gadgetname. Dark mode requires this to prevent white flashes.
To load multiple gadgets for one entry, enter them separated by commas.
Scripts (that can't be loaded as gadgets):
['SCRIPTNAME1[:0][:displayname]','SCRIPTNAME2[:0][:displayname]','SCRIPTNAME3[:0][:displayname]']
If :gadget is added (HIGHLY RECOMMENDED) it will load ext.gadget.SCRIPTNAME, e.g. https://commons.wikimedia.beta.wmflabs.org/w/load.php?lang=en&modules=ext.gadget.AnonLoader
Otherwise it will load MediaWiki:Gadget-SCRIPTNAME.js. Insecure page titles are not supported.
If you include displayname that will be used for the link name.
Technically anons could modify their localStorage to load scripts/gadgets that aren't specified in AnonLoaderScripts, but they would still have to comply with the requirements of being a gadget or titled MediaWiki:Gadget-SCRIPTNAME.
There's no UI for that, but very meta, one could write a gadget for that and load it with AnonLoader.
*/
/*globals mw:false,$:false*/
window.AnonL = {};
var AnonL = window.AnonL;
AnonL.L = function(name,u1,u2,u3,u4,p,p2,p3,e,ls,lsk,s,s2,i,i2,i3,a,el,eln,dir,g,n,le,c,mobile,pID,aID,dN,cg,sa,gpv) {
'use strict';
gpv = function(k){
	return mw.util.getParamValue(k);
};
cg = function(k){
	return mw.config.get(k);	
};
if (cg('wgUserName')){
	return;//only anons need this as they don't have Special:Preferences
}
if (Array.from(document.getElementsByTagName('body')[0].classList).indexOf('rtl') != -1){
	dir='right';
} else {
	dir='left';
}
a = (window.AnonLoaderScripts||[]);
sa = gpv('AnonLoaderShowAll') || window.AnonLoaderShowAll; //ShowAll, no menu that toggles the gadget list, just show all
mobile = cg('skin') == 'minerva';
le = '.AnonLMin,.AnonLPlus';
c = 'AnonLNoD';
p = gpv('ALSet');
p2 = gpv('ALRemove');
p3 = Number(mw.storage.get('AnonLCheck')||0);
dN = new Date().getTime();
ls = JSON.parse((mw.storage.get('AnonLoader')||'{}'));
if ( p3+30000 > dN ) { //link was clicked <30s ago
	if ( p ) {
		ls[p.split(/:/)[0]] = p;
	}
	if ( p2 ) {
		delete ls[p2.split(':')[0]];
	}
}
mw.storage.remove('AnonLCheck');
lsk = Object.keys(ls);
for(i3=0;i3<lsk.length;i3++){
	if ( a.indexOf(ls[lsk[i3]]) == -1 ) {
		delete ls[lsk[i3]]; //remove entries from localStorage that don't match window.AnonLoaderScripts
		lsk = Object.keys(ls);
	}
}
mw.storage.set('AnonLoader',JSON.stringify(ls));
g=[];
AnonL.fixUrl = function(event,targ,uri){
	targ = event.delegateTarget;
	try{
		uri = new mw.Uri(targ.href);
		if ( uri.host == cg('wgServerName') ) {
			uri.extend({withgadget:AnonL.gdgt});
			targ.href = uri.toString();
			event.delegateTarget.href = uri.toString();
		}
	} catch (ev) {}
};
AnonL.fixU = function(el,skip) {
	$(el).on('click mousedown focus',AnonL.fixUrl);
	if ( !skip ) {
		$(el).addClass('ALwgdgt');
	}
};
/* test scenarios (in all skins+mobile from the search box and Special:Search):
* Enter search term, click ajax result
* Enter search term that matches a title, press enter (goes to article)
* Enter search term that doesn't, press enter (goes to results)
* Skins: cologneblue, modern, timeless, minerva, monobook, vector, vector-2022
* dark-mode does not support cologneblue
*/
AnonL.rS = function(s,i2) { //restore search api.php action
	for (i2=0;i2<$(s).length;i2++) {
		$(s)[i2].attributes.action.value = cg('wgScript');
	}
};
AnonL.fixSearch = function(els,i,i2,u,uri,t,nl,s,sv,me) {
	els = $('.suggestions-results a,.skin-minerva .results-list-container a');
	s = 'form#searchform,.skin-minerva form.search-box';
	if ( !els.length ) {
		AnonL.rS(s);
	} else {
		sv = $('input#searchInput')[0].value;
		me = '.skin-minerva .search-overlay .search-box input.search';
		if ( mobile && sv == '' && $(me)[0] ) {
			sv = $(me)[0].value;
		}
	}
	for (i=0;i<els.length;i++) {
		u = els[i];
		try{
			uri = new mw.Uri(u.href);
			t = uri.query.search;
			if ( t ) {
				nl = cg('wgArticlePath').replace('$1',t);
				if ( i == 0 && sv.toLowerCase() == t.toLowerCase() ) { //entered search term matches first result, you'd get redirected there anyway, I'll save you the trip
					for (i2=0;i2<$(s).length;i2++) {
						$(s)[i2].attributes.action.value = nl;
					}
				} else if ( i == 0 ) {
					AnonL.rS(s);
				}
				u.href = cg('wgServer')+nl;
			} else if ( cg('wgArticlePath').replace('$1',sv).toLowerCase() == uri.path.toLowerCase() ) {
				//adjust search form target
				for (i2=0;i2<$(s).length;i2++) {
					$(s)[i2].attributes.action.value = uri.path;
					if ( !$(s)[i2].classList.contains('ALwgdgt') ) { //add hidden input to minerva overlay search
						$(s)[i2].append(AnonL.hEl[0]);
						$(s)[i2].classList.add('ALwgdgt');
					}
				}
			}
			$(u).on('click mousedown focus',AnonL.fixUrl);
		} catch (ev) {}
	}
};
AnonL.uLoad = function(fl,fEl,hEl,i,sEls,sElUrls,s){
	AnonL.observe = function(rec,newEl,i,i2,nn) {
		AnonL.fixSearch();
		//AnonL.fixU('.suggestions-results a:not(.ALwgdgt)');
/*
		newEl = rec[0].addedNodes;
		for (i=0;i<newEl.length;i++){
			try{
				nn = newEl[i].nodeName;
				if ( nn == 'A' ) {
					AnonL.fixU(newEl[i]);
				} else if ( nn != '#text' ) {
					AnonL.fixU(newEl[i].querySelectorAll('a:not(.ALwgdgt)'));
				}
			} catch (ev) {}
		}
*/
	};
	AnonL.observer = new MutationObserver(AnonL.observe);
	AnonL.observer.observe(document.body,{childList: true,subtree: true,attributes: false,characterData: false}); //MediaWiki magically creates elements in some cases (like basic search turning into something else on click)
/*
	sEls = 'input:not(.ALwgdgt),.results-list-container:not(.ALwgdgt)';
	sElUrls = '.results-list-container a:not(.ALwgdgt)';
	AnonL.hrefFix = function() {
		$(sEls).on('click touchend keydown',AnonL.hrefFix); //spread
		$(sEls).addClass('ALwgdgt');
		AnonL.fixU(sElUrls);
	};
	$(sEls).on('click touchend keydown',AnonL.hrefFix);
*/
	AnonL.fixU('a',1);
	fEl = 'form:not(.ALwgdgt)'; //adding hidden input to forms so withgadget will persist when they are submitted
	hEl = {};
	$('body,input').on('click touchend',function(event,targ,uri){
		for(i=0;i<$(fEl).length;i++){
			if ( $(fEl)[i].attributes.action.value.match(/^\/[^\/]/) ) {
				hEl[i] = document.createElement('input');
				hEl[i].type = 'hidden';
				hEl[i].name = 'withgadget';
				hEl[i].value = AnonL.gdgt;
				AnonL.hEl = hEl;
				$(fEl)[i].append(hEl[i]);
				$(fEl)[i].classList.add('ALwgdgt');
			}
		}
	});
	s = 'input#searchInput:not(.ALwgdgt),.skin-minerva .search-box input.search:not(.ALwgdgt)';
	$(s).on('click touchend change',AnonL.fixSearch);
	$(s).addClass('ALwgdgt');
};
for (i=0;i<lsk.length;i++){
	s = ls[lsk[i]].split(':');
	if (s[1]=='gadget') {
		s2 = s[0].split(',');
		for (i2=0;i2<s2.length;i2++){
			g.push('ext.gadget.'+s2[i2]);	//collect gadget
		}
		if ( s[3] == 'urlLoad' ) { //there can be only one gadget loaded this way it seems
			AnonL.gdgt = s2[0];
			AnonL.uLoad();
		}
	} else {
		mw.loader.load(cg('wgScriptPath')+'/index.php?title=MediaWiki:gadget-'+s[0]+'.js&action=raw&ctype=text/javascript');
	}
}
mw.loader.load(g);//load gadgets
el={};
eln={};
if ( mobile ) {
	pID = 'p-navigation';
	aID = '';
} else {
	pID = 'p-tb';
	aID = '#t-specialpages';
}
if ( !sa ) {
	el.menu = mw.util.addPortletLink(
		pID,
		'',
		AnonL.lang['prefs-gadgets'],
		'AnonLoaderMenu',
		AnonL.lang['prefs-gadgets'],
		undefined,
		aID
	);
	if ( mobile ) {
		$('.mw-ui-icon-portletlink-AnonLoaderMenu').addClass('mw-ui-icon-minerva-die');
	}
}
$('#AnonLoaderMenu,#AnonLoaderMenu a').on('click',function(event){
	event.stopPropagation();event.preventDefault();
	if ( $(le).hasClass(c) ) {
		$(le).removeClass(c);
	} else {
		$(le).addClass(c);
	}
});
for (i=0;i<a.length;i++){
	if ( ls[a[i].split(':')[0]] ) {
		u1='Remove';
		u3='AnonLMin';
	} else {
		u1='Set';
		u3='AnonLPlus';
	}
	n = a[i].split(':');
	u4 = '';
	if ( n[3] == 'urlLoad' && u1 == 'Set' ) {
		u4 = '&withgadget='+n[0];
	}
	eln[i] = (n[2] || n[0]);
	if ( sa && u1 == 'Set' ) {
		eln[i] = AnonL.lang['AnonLoader-enable'].replace('$1',eln[i]);
	} else if ( sa && u1 == 'Remove' ) {
		eln[i] = AnonL.lang['AnonLoader-disable'].replace('$1',eln[i]);
	}
	el[i] = mw.util.addPortletLink(
	pID,
	'?AL'+u1+'='+a[i]+u4,
	eln[i],
	'AnonLoader'+i,
	eln[i],
	undefined,
	aID
	);
	$('body.skin-minerva #'+'AnonLoader'+i+' .mw-ui-icon').addClass(c);
	if ( mobile ) {
		document.getElementById('AnonLoader'+i.toString()).querySelectorAll('a')[0].classList.add(u3);
	} else {
		document.getElementById('AnonLoader'+i.toString()).classList.add(u3);
	}
}
$(le).addClass(c);
$(le).on('click',function(){
	mw.storage.set('AnonLCheck',new Date().getTime());
});
if ( !sa ) {
	mw.util.addCSS('.AnonLNoD{display:none !important}.AnonLMin:before,.AnonLPlus:before{margin-'+dir+':0.5em;display:inline-block;text-align:center;width:1em;font-size:larger;font-weight:bold}.AnonLMin:before{content:"✔";color:#060}.AnonLPlus:before{content:"✘";color:#600}');
}
};
AnonL.loadLang = function(l,ls,d,e) {
	l = mw.config.get('wgUserLanguage');
	ls = JSON.parse((mw.storage.get('AnonLoaderLang2')||'{}'));
	e = 'Enable $1';
	d = 'Disable $1';
	ls.en = {'prefs-gadgets':'Gadgets','AnonLoader-enable':e,'AnonLoader-disable':d};
	if ( ls[l] ) {
		AnonL.wait(ls[l]);
	} else {
		mw.loader.using(['mediawiki.api'], function(){
			var api = new mw.Api();
			api.get( {action:'query',meta:'allmessages',ammessages:['prefs-gadgets','AnonLoader-enable','AnonLoader-disable'],amlang:l}).done( function ( data,dq ) {
				dq = data.query.allmessages;
				ls[l] = {'prefs-gadgets':dq[0]['*'],'AnonLoader-enable':(dq[1]['*']||e),'AnonLoader-disable':(dq[2]['*']||d)};
				mw.storage.set('AnonLoaderLang2',JSON.stringify(ls));
				AnonL.wait(ls[l]);
			});
		});
	}
};
AnonL.wait = function(g,i) {
	AnonL.lang = g;
	i=0;
	var AnonW = setInterval(function(){ //wait for the presence of window.AnonLoaderScripts
		i++;
		if (window.AnonLoaderScripts || i > 50) { //test 10 times a second for 5 seconds
			clearInterval(AnonW);
			mw.loader.using(['mediawiki.Uri','mediawiki.util'], function(){
				AnonL.L();
			});
		}
	}, 100);
};
AnonL.loadLang();
//</nowiki>

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.