User:Stussll/Common.js

mw.loader.load( '/w/index.php?title=User:GhostInTheMachine/TalkHelper2.js&action=raw&ctype=text/javascript' ); // Backlink: User:GhostInTheMachine/TalkHelper2.js;

mw.loader.load( 'https://meta.wikimedia.org/w/index.php?title=User:ESanders_(WMF)/commentlinks.js&action=raw&ctype=text/javascript' );

// /* EditNoticesOnMobile is public domain (for the embedded lz-string library licensing see below), irrevocably released as WTFPL Version 2 by its author, Alexis Jazz. The community requested edit notices on the mobile site for years (see community wishlist 2021 and 2022 and T201595), but the WMF hasn't implemented them yet nor given a clear roadmap for their implementation. This workaround was created to get them anyway until a native implementation becomes available.

lz-string by Pieroxy, originally licensed WTFPL, Version 2. Functions that were unneeded for EditNoticesOnMobile were stripped. The license currently offered by Pieroxy is MIT:

MIT License Copyright (c) 2013 pieroxy Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

The MIT license is included here to be safe, the lz-string 1.4.4 archive that lz-string was copied from for EditNoticesOnMobile contains a copy of the WTFPL Version 2 license.

Known issues: -should preferably use event listeners instead of any kind of polling if possible (not a massive deal, more of a coders' purist issue) /*globals $:false,OO:false,ve:false,mw:false*/ window.enom = {}; var enom = window.enom; //start lz-string by Pieroxy enom.LZString=function{/*jshint bitwise:false,asi:true,boss:true,expr:true*/function o(o,r){if(!t[o]){t[o]={};for(var n=0;n<o.length;n++)t[o][o.charAt(n)]=n}return t[o][r]}var r=String.fromCharCode,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$",t={},i={compressToUTF16:function(o){return null==o?"":i._compress(o,15,function(o){return r(o+32)})+" "},decompressFromUTF16:function(o){return null==o?"":""==o?null:i._decompress(o.length,16384,function(r){return o.charCodeAt(r)-32})},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return"";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u)}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}else{for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a]}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++)}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for{if(m<<=1,v==r-1){d.push(n(m));break}v++}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var t,i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(t=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return"";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else{if(l!==d)return null;v=s+s.charAt(0)}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++)}}};return i}; //end lz-string by Pieroxy //console.log('enom: EditNoticesOnMobile loaded'); enom.time = new Date.getTime; enom.testValidJSON = function (string) { if(string==null){return false;} try{enom.parsedJSON = JSON.parse(string);}catch(e){return false;} return enom.parsedJSON; }; enom.storeNotice = function(notice){ if ( window.localStorage ) { enom.cachedNotices = enom.testValidJSON(enom.LZString.decompressFromUTF16(window.localStorage.ENOM)); }	if ( window.localStorage && ! enom.cachedNotices ) { window.localStorage.setItem('ENOM',enom.LZString.compressToUTF16('{}')); }	if ( window.localStorage && enom.cachedNotices ) { //not sure how all browsers behave if localStorage is unavailable, so to be safe, test validity enom.cachedNotices[mw.config.get('wgPageName')] = {text:notice,date:enom.time}; enom.cachedNoticesNew = JSON.stringify(enom.cachedNotices); if ( enom.cachedNoticesNew.length > 1000000 ) { //it's quite unlikely a user would accumulate >1MB of edit notices, but if it somehow happens we purge all and start with a clean slate enom.cachedNoticesNew = '{}'; }		window.localStorage.setItem('ENOM',enom.LZString.compressToUTF16(enom.cachedNoticesNew)); } }; enom.getNotice = function(trigger) { enom.cachedNotices = enom.testValidJSON(enom.LZString.decompressFromUTF16(window.localStorage.ENOM)); if ( trigger && trigger.target && trigger.target.href ) { //console.log('enom: extract page title from URL'); enom.pageTitle = mw.util.getParamValue("title", trigger.target.href); } else { //console.log('enom: use title of current page'); enom.pageTitle = mw.config.get('wgPageName'); }	enom.newNotice = false; if ( window.localStorage && enom.cachedNotices ) { enom.update = false; if ( enom.cachedNotices[enom.pageTitle] ) { enom.oldEditnotice = enom.cachedNotices[enom.pageTitle].text; }		for (enom.cachedInt=0;enom.cachedInt48 hours (172800000 ms) old or the current page and >2 hours (7200000 ms) old //console.log('enom: removed '+Object.keys(enom.cachedNotices)[enom.cachedInt]+' from locally cached notices'); delete enom.cachedNotices[Object.keys(enom.cachedNotices)[enom.cachedInt]]; enom.update = true; }			if ( enom.update ) { window.localStorage.setItem('ENOM',enom.LZString.compressToUTF16(JSON.stringify(enom.cachedNotices))); }		}		if ( enom.cachedNotices[mw.config.get('wgPageName')] ){ //notice was cached less than 2 hours ago enom.noticeText = enom.cachedNotices[mw.config.get('wgPageName')].text; //console.log('enom: found cached notice'); enom.popupNotice(enom.noticeText,false); return; }	}	/* provides edit notices in a structured form but also a lot of unneeded big stuff enom.editNoticeParams = { format:'json', action:'visualeditor', paction:'metadata', page:enom.pageTitle, formatversion:'2', };	*/	enom.editNoticeParams = { format:'json', action:'parse', disableimages:true, disablelimitreport:true, title:enom.pageTitle, pst:'1', prop:'text', formatversion:'2', text:' ', };	mw.loader.using(['mediawiki.api'], function{		//console.log('enom: download notice');		enom.newNotice = true;		var api = new mw.Api;		api.post( enom.editNoticeParams ).done( function ( data ) { /* structured editnotices from VE API. see commented out params above enom.parsednotices = ' '; for(enom.noticeint=0;enom.noticeint<Object.keys(data.visualeditor.notices).length;enom.noticeint++){ if ( Object.keys(data.visualeditor.notices)[enom.noticeint].match(/editnotice/) ) { //there's also semiprotectedwarning, presumably some other protection warnings. is there an overview of all possible messages? enom.parsednotices = enom.parsednotices + data.visualeditor.notices[Object.keys(data.visualeditor.notices)[enom.noticeint]]; }			}			enom.parsednotices = enom.parsednotices+' '; */			enom.parsednotices = data.parse.text.replace(/id="mf-section-0"/g,''); //just causes display:none through skins.minerva.talk.styles. I'd fix it in the DOM but being an ID it just has to bloody go ASAP. Why is it even here? enom.testIfEmpty = document.createElement('div'); enom.testIfEmpty.classList = 'enomTempDiv enomTempDivNew'; //need to attach this to the DOM to work with it.. maybe it's possible another way but this'll work enom.testIfEmpty.innerHTML = enom.parsednotices; enom.oldEditnoticeDiv = document.createElement('div'); enom.oldEditnoticeDiv.classList = 'enomTempDiv enomTempDivOld'; enom.oldEditnoticeDiv.innerHTML = enom.oldEditnotice; $('body:eq(0)').append(enom.testIfEmpty).append(enom.oldEditnoticeDiv); /*			We are going to test if the notice contains any text content after removing elements that don't contain vital information about editing some particular page. * Nomobile elements wouldn't be displayed anyway. Edit notice creators should use "nomobile" as they see fit. Please consider that edit notices on mobile are presented as a popup, not a passive box above the text area. * The editnotice-link just links to the page containing the notice. It should be displayed on mobile (so nomobile shouldn't be added to it) but we ignore it when checking if the notice has any text content. * ..			*/			$('.enomTempDiv .nomobile,.enomTempDiv .editnotice-area .editnotice-link').remove; enom.testIfEmptyInnerText = $('.enomTempDivNew')[0].innerText.trim; enom.oldEditnoticeDivInnerText = $('.enomTempDivOld')[0].innerText.trim; if ( enom.testIfEmptyInnerText == '' ) { //console.log('enom: notice is (practically) empty, blanking'); enom.parsednotices = ''; }			$('.enomTempDiv').remove; if ( enom.testIfEmptyInnerText == enom.oldEditnoticeDivInnerText ) { //console.log('enom: notice is identitcal to what we had cached, adding button but no popup.'); enom.popupNotice(enom.parsednotices,false); } else { //console.log('enom: notice is different from what (if anything) was cached, adding button and creating popup'); enom.popupNotice(enom.parsednotices,true); }			enom.storeNotice(enom.parsednotices); });	}); }; enom.cleanupStyling = function{ $('#EditNoticeOnMobile .mf-section-0').addClass('stopHidingMe'); $('#EditNoticeOnMobile *').removeClass(['fmbox','tmbox','tmbox-content']); for (enom.noticeElementsInt=0;enom.noticeElementsInt<$('#EditNoticeOnMobile *').length;enom.noticeElementsInt++){ $('#EditNoticeOnMobile *')[enom.noticeElementsInt].style.background = ''; //remove background colors that many edit notices have. Not appropriate in a popup }	if ( $('#EditNoticeOnMobile .mw-collapsible')[0] ) { mw.loader.using('jquery.makeCollapsible').then( function { //WP:HD includes a collapsible "Help desk templates" block			mw.util.addCSS('#EditNoticeOnMobile .mw-parser-output .mw-collapsible-toggle a{color:#3366cc}#EditNoticeOnMobile .mw-parser-output .mw-collapsible-toggle{font-weight:normal;}');			$($('#EditNoticeOnMobile .mw-collapsible')).makeCollapsible; //T111565 FTFY		}); } }; enom.showPopup = function(noticetext){ mw.loader.using(['oojs-ui-core','oojs-ui-windows']).then(function{		OO.ui.alert(new OO.ui.HtmlSnippet(noticetext),{size:'larger'});		var DelayClassFix = setInterval(function{ //popup doesn't immediately exist.. clearInterval(DelayClassFix); if ( $('#EditNoticeOnMobile')[0] ){ enom.cleanupStyling; } else { //no popup yet, wait some more var DelayClassFix = setInterval(function{					clearInterval(DelayClassFix);					enom.cleanupStyling;				}, 500); }		}, 250);	}); }; enom.popupNotice = function(noticetext,popup){ if ( noticetext == '' || noticetext.match(/]*EditNoticeOnMobile[^>]*><\/div>/) ) { //empty notice, don't show anything //console.log('enom: notice is empty (no notice for this page)'); return; }	mw.util.addCSS('.stopHidingMe{display:unset !important}#EditNoticeOnMobile .mbox-image,.enomTempDiv{display:none}#EditNoticeOnMobile .tmbox{background:unset;border:unset;margin:unset}@media screen and (max-width:767px){#enomButtonvisual{display:none !important}}'); if ( popup ) { //shove popup into user's face only if freshly downloaded enom.showPopup(noticetext); }	enom.attachButton = function(saveButtonSelector,type) { if ( $(saveButtonSelector)[0] && ! $('#enomButton'+type)[0] ) { enom['showNoticeButton'+type] = $('.overlay-header:not(.hidden) .header-action button:eq(0)').clone; enom['showNoticeButton'+type][0].classList.remove('mw-ui-icon-mf-next-invert','continue'); enom['showNoticeButton'+type][0].classList.add('mw-ui-icon-mf-alert'); enom['showNoticeButton'+type][0].disabled = false; enom['showNoticeButton'+type][0].style = ''; enom['showNoticeButton'+type][0].title = 'Editnotice'; enom['showNoticeButton'+type][0].id = 'enomButton'+type; if ( type == 'source' ) { enom['showNoticeButton'+type][0].title = 'Editnotice (source)'; } else { enom['showNoticeButton'+type][0].title = 'Editnotice (visual)'; }			enom['showNoticeButton'+type].on('click',function{enom.showPopup(noticetext);}); $(saveButtonSelector)[0].parentElement.insertBefore(enom['showNoticeButton'+type][0],$(saveButtonSelector)[0]); $('.oo-ui-tool-name-editModeVisual,.oo-ui-tool-name-editModeSource').on('click', function {				enom.waitingForVE(enom.sourceClass,'source',100,1000);				enom.waitingForVE(enom.VEClass,'visual',100,1000);			}); }	};	enom.int=0; enom.waitingForVE = function(saveButtonSelector,type,delay,sourceDelay) { var DelayWaitForVE = setInterval(function{ //wait for ve.init.target to come into existence. there's probably another event that could be used for this part, but this is an improvement over the previous lengthy DOM polling			clearInterval(DelayWaitForVE);			if ( typeof ve != 'undefined' && typeof ve.init != 'undefined' && typeof ve.init.target != 'undefined' && ve.init.target != null && ve.init.target.loading != null && type == 'visual' ) {				//console.log('enom: found VE in loading state, attach button on surfaceReady');				ve.init.target.on( 'surfaceReady', function{ //console.log('enom: surfaceReady, attaching button'); enom.attachButton(saveButtonSelector,type); });			} else if ( enom.int < 30 && type == 'visual' ) {				//console.log('enom: looking for VE in loading state but not (yet?) found, try again in 100ms');				enom.int++;				enom.waitingForVE(saveButtonSelector,type,100,1000);			} else { // source mode or VE finished loading before enom.waitingForVE was called				//console.log('enom: attach button ('+type+')');				var DelayWaitForSource = setInterval(function{ clearInterval(DelayWaitForSource); enom.attachButton(saveButtonSelector,type); },sourceDelay);			}		},delay); };	enom.sourceClass = '.overlay-header:not(.hidden) .header-action button:eq(0)'; enom.VEClass = '.overlay-header .toolbar .oo-ui-toolbar-tools .ve-ui-toolbar-group-save'; enom.waitingForVE(enom.sourceClass,'source',100,500); // source toolbar (500ms delay), might fail on slow devices/big pages?? enom.waitingForVE(enom.sourceClass,'source',100,750); // middle ground enom.waitingForVE(enom.sourceClass,'source',100,1500); // source toolbar (1500ms delay, unlikely to fail) enom.waitingForVE(enom.VEClass,'visual',100,1000); // visual toolbar }; if ( $('#ca-edit')[0] ) { $('#ca-edit,.mw-editsection .edit-page').on('click', function(event) { enom.getNotice(event); } ); } if ( window.location.href.match(/\#\/editor\//) ) { enom.getNotice; } //