Jump to content

MediaWiki:Gadget-EditNoticesOnMobile.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Xaosflux (talk | contribs) at 13:18, 14 July 2022 (sync from User:Alexis Jazz/EditNoticesOnMobile.js). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
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>
/*
EditNoticesOnMobile is public domain (for the embedded lz-string library licensing see below), irrevocably released as WTFPL Version 2[http://www.wtfpl.net/about/] 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.
If an edit notice contains an element with the "nopopupnotice" class the automatic popup will be suppressed.
At @media screen and (max-width:767px) the button to (re)visit a notice will be hidden in the visual editor. (that toolbar is already crowded)
----
lz-string by Pieroxy[https://pieroxy.net/blog/pages/lz-string/index.html], 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/concerns:
-Some people voiced concerns over showing the edit notice over the anoneditwarning, before the user has confirmed they are okay with anon editing. I think it's good actually: before they bother to log in or sign up (captcha!), tell the user that the earth is REALLY not flat and they shouldn't bother us with conspiracy theories and old wives' tales. But if this would end up being a sticking point, ctrl+f for "uncomment this to have EditNoticesOnMobile not show the popup" and uncomment that stuff.
-On really really narrow screens the popup - OO.ui.alert(new OO.ui.HtmlSnippet(noticetext),{size:'larger'}); - covers the whole screen. If this is a problem, I'd say it's a problem with OO.ui.alert. I see no configuration option to force the popup to be a window on https://doc.wikimedia.org/oojs-ui/master/js/#!/api/OO.ui.MessageDialog-method-getSetupProcess
-General concern that users will be annoyed by too many popups. (please note that the Android app already has edit notices as popups) The same notice for the same page doesn't repeat, pages without a notice have no popup, the gadget can be turned off in preferences. The community should consider adding an element with the "nopopupnotice" class to notices that are informative in nature and not warnings. Also apply the "nomobile" class where appropriate.
*/
/*globals $:false,OO:false,ve:false,mw:false*/
//only load EditNoticesOnMobile on Minerva skin on the mobile site and if it's not already loaded
if ( mw.config.get('skin') == 'minerva' && window.location.host.match(/(^|\.)m\./) && ( ! window.enom || ! window.enom.time ) ) {
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;i<o.length;i+=1)if(u=o.charAt(i),Object.prototype.hasOwnProperty.call(s,u)||(s[u]=f++,p[u]=!0),c=a+u,Object.prototype.hasOwnProperty.call(s,c))a=c;else{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++),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.evilClasses = ['fmbox','tmbox','tmbox-content','messagebox'];
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('{}'));
		enom.cachedNotices = enom.testValidJSON(enom.LZString.decompressFromUTF16(window.localStorage.ENOM));
	}
	if ( window.localStorage && enom.cachedNotices ) { //not sure how all browsers behave if localStorage is unavailable, so to be safe, test validity
		enom.cachedNotices[enom.pageTitle] = {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.pageTitle = enom.pageTitle.replace(/_/g,' ');
	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.cachedInt<Object.keys(enom.cachedNotices).length;enom.cachedInt++){
			enom.checkEntry = enom.cachedNotices[Object.keys(enom.cachedNotices)[enom.cachedInt]];
			if ( typeof window.enomCacheExpiry == 'number' ) { //give the user some control over the expiry period by specifying enomCacheExpiry in their common.js.
				enom.cacheExpiry = window.enomCacheExpiry;
			} else {
				enom.cacheExpiry = 172800000; //48 hours
			}
			if ( enom.checkEntry.date < enom.time -enom.cacheExpiry || ( enom.checkEntry.date < enom.time -7200000 && Object.keys(enom.cachedNotices)[enom.cachedInt] == enom.pageTitle) ) {//entry >48 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[enom.pageTitle] ){ //notice was cached less than 2 hours ago
			enom.noticeText = enom.cachedNotices[enom.pageTitle].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.namespaces = mw.util.escapeRegExp(Object.values(mw.config.get('wgFormattedNamespaces')).toString()).replace(/^,/,'').replace(/,/g,'|'); // "Talk|User|User talk|" etc
	enom.pageTitleNoNS = enom.pageTitle.replace(new RegExp('^('+enom.namespaces+'):'),'').replace(/\//g,'-');
	enom.editNoticeParams = {
		format:'json',
		action:'parse',
		disablelimitreport:true,
		title:enom.pageTitle,
		pst:'1',
		prop:'text',
		formatversion:'2',
		text:'<div id="EditNoticeOnMobile">{{#ifexist:MediaWiki:Editnotice-{{NAMESPACENUMBER}}-'+enom.pageTitleNoNS+'|{{MediaWiki:Editnotice-{{NAMESPACENUMBER}}-'+enom.pageTitleNoNS+'}}|{{#ifexist:MediaWiki:Editnotice-{{NAMESPACENUMBER}}|{{MediaWiki:Editnotice-{{NAMESPACENUMBER}}}}}}}}</div><div id="enom_end"></div>',
	};
	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 = '<div id="EditNoticeOnMobile">';
			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+'</div>';
			*/
			enom.parsednotices = data.parse.text.replace(/\n/g,'').replace(/.*(<div id="EditNoticeOnMobile">.*<\/div>)<div id="enom_end"><\/div>.*/,'$1');
			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);
			enom.suppressPopup = typeof $('.enomTempDivNew .nopopupnotice')[0];
			/*
			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();
			$('.enomTempDiv *').removeClass(enom.evilClasses);
			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 ) || enom.suppressPopup == 'object' ) {
				//console.log('enom: notice is identitcal to what we had cached or contains "nopopupnotice" class, 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 *').removeClass(enom.evilClasses);
	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-collapsible-toggle a{color:#3366cc}#EditNoticeOnMobile .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 DelayClassFix2 = setInterval(function(){
					clearInterval(DelayClassFix2);
					enom.cleanupStyling();
				}, 500);
			}
		}, 250);
	});
};
enom.popupNotice = function(noticetext,popup){
	if ( noticetext == '' || noticetext.match(/<div[^>]EditNoticeOnMobile[^>]*><\/div>/) ) { //empty notice, don't show anything
		//console.log('enom: notice is empty (no notice for this page)');
		return;
	}
	mw.util.addCSS('#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 && window.EnomNeverPopup != true ) { //shove popup into user's face only if freshly downloaded and user didn't define EnomNeverPopup as true in their common.js
		//uncomment this to have EditNoticesOnMobile not show the popup on the "Edit without logging in"/log in/sign up screen
		/*
		if ( mw.config.get('wgUserName') == null ) {
			//console.log('enom: anon, put event listener on "Edit without logging in" button instead of showing it now');
			enom.observer = new MutationObserver(function(mutations){
				if ( mutations[0].target.querySelectorAll('.anonwarning a.anonymous')[0] ) {
					//console.log('enom: adding event listener to anonymous button');
					$('.anonwarning a.anonymous').on('click',function(){enom.showPopup(noticetext)});
					enom.observer.disconnect();
				}
			});
			enom.observer.observe(document.body, {childList: true,subtree: true,attributes: false,characterData: false});
			if ( $('.anonwarning a.anonymous')[0] ) { //oh it's already here
				$('.anonwarning a.anonymous').on('click',function(){
					enom.showPopup(noticetext);
				});
			}
		} else {
		*/
		//uncomment this to have EditNoticesOnMobile not show the popup on the "Edit without logging in"/log in/sign up screen
			enom.showPopup(noticetext);
		//}//uncomment this to have EditNoticesOnMobile not show the popup on the "Edit without logging in"/log in/sign up screen
	}
	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.observerInt = 0;
	enom.observer = new MutationObserver(function(mutations){ //looks for new HTML elements in the DOM. like the source editor that we expect to be loading
		enom.observerInt++;
		if ( $(enom.sourceClass)[0] ) {
			//console.log('enom: source editor loaded, adding button');
			enom.waitingForVE(enom.sourceClass,'source',100,0);
			enom.observer.disconnect();
		} else if ( enom.observerInt > 20 ) {
			enom.observer.disconnect(); //no idea why this would happen but the observer shouldn't keep running until the end of time
		}
	});
	enom.observer.observe(document.body, {childList: true,subtree: true,attributes: false,characterData: false});
	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\//) ) {
	//console.log('enom: found #editor in location.href, loading');
	enom.getNotice();
}
} else {
	//console.log('enom: don\'t load EditNoticesOnMobile on anything other than the mobile site with Minerva and don\'t try to load it twice.');
}
//</nowiki>