User:Fred Gandt/userResourceManager.js

/********************************************************************************************************************************* * Currently still in development, this is designed to provide control over user JavaScripts and StyleSheets. * If you encounter any problems using this script, please tell User:Fred_Gandt on either my talk page or this script's talk page. * *********************************************************************************************************************************/( function( DOM_d ) {"use strict";var BASE = "fg-js-and-css-manager",EXT = BASE + "-",DROPEE_BOTTOM = EXT + "dropee-bottom",JAVASCRIPTS = EXT + "javascripts",STYLESHEETS = EXT + "stylesheets",DROPEE_TOP = EXT + "dropee-top",MANAGABLE = EXT + "managable",DROPZONE = EXT + "dropzone",CHANGED = EXT + "changed",SAVING = EXT + "saving",DRAGEE = EXT + "dragee",FALSE = EXT + "false",TRUE = EXT + "true",THIS = EXT + "this",FILE = EXT + "file",BIN = EXT + "bin",FILE_IMG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAARTAAAEUwECr+6lAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcG" +"Uub3Jnm+48GgAAAkZQTFRFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcHAAAAGBgYAAAAAAAAAAAAAAAACQkJCQkJDQ0NDAwMDAwMHh4eHR0dHR0dHBwcIC" +"AgIiIiJCQkJiYmIyMjIyMjLCwsLy8vIiIiIiIiJCQkJSUlJycnKCgoKioqLCwsJCQkJSUlJycnKCgoKioqKysrLS0tLi4uKioqLS0tKCgoKysrKCgoKSkpKysrMDAwMDAwOjo6PDw8Pz8/PT09QEBAQUFBQ0NDHx" +"8fICAgIyMjJCQkJiYmJycnKSkpKioqKysrLCwsLS0tLi4uLy8vMDAwMTExMjIyMzMzNDQ0NTU1NjY2Nzc3ODg4OTk5Ojo6Ozs7PDw8PT09Pj4+Pz8/QEBAQUFBQkJCQ0NDRERERUVFRkZGR0dHSEhISUlJSkpKS0" +"tLTExMTU1NTk5OT09PUFBQUVFRUlJSU1NTVFRUVVVVVlZWV1dXWFhYWVlZWlpaW1tbXFxcXV1dXl5eX19fYGBgYWFhYmJiY2NjZGRkZWVlZmZmZ2dnaGhoampqa2trbGxsbW1tbm5ub29vcHBwcnJyc3NzdHR0dX" +"V1eHh4eXl5enp6e3t7gICAgYGBgoKChISEhYWFh4eHiIiIkZGRk5OTlpaWmZmZmpqanJycnZ2dnp6en5+foKCgpKSkq6ursLCwtLS0tra2t7e3uLi4u7u7vLy8vb29vr6+w8PDxsbGyMjIy8vLzs7O0tLS1dXVB+" +"nTgQAAAEp0Uk5TAAECAwQGBwkKCw0REhUWFxgfJikqLi8wMjY3PD5AVFdZWmlqhZWgoqKio6SkpKSkpKSlpaWlpaWlpbu7xcXGxsbT4Ovx8vP09fd9otd6AAACsElEQVQ4y21Ve0/TUBT/nduuXTfKHOAggko0xp" +"CgURL+8wv4af0QJibGF0LiI0QQiYrbGFvLHr299x7/2PqmyWmT29Pfq6e9BAAgIkLhYGYuLNjzc6shCstkwrHkSqNw2/tAYd30jvuz/IoFAOT5m47t05I2KXVteSx1hRpNtOOWozBKQfiufXgZZZiJNHLudd2TAI" +"krEsHzJysulajddif4p7oA0bwAI9c2dSBNiZrQ0U7oiJWQhmuWks54Ngz9PT68SrwvEFc6FlyLApIzo651HEXRVNnOHTWMTQERZLfquh93yWACBkws5chqPOSDMOYC4mxzJmNub4d6odKASI2X/MEcM0V0f04Ajv" +"oAAUwED2AQ1lcjzXkzft27BIGTEgIMgCEs4nyOUy38OoGI0ozmZVtFM47b4l5kGTCBCYUq5Di6M/W9HdPfvOhyTgGISvG4ZxPgDzBgUAUur3E1okRhUWXxFQIBI286gyxppImAmd+zoPOPlBFv3+bRqq71bVFjeL" +"3zssrUDF3/EheGDMAAmQyy3HgFajaUcDQUKOJRqrI8jxpkGSWacinyZ3FdmljfnCMYAXinV6dGoG7Zde5OTT7HtHFOFXsNfb1s4/xxb1369b9XHJVdE3jLkSdg8AXs+J0w9NtWFWqiJQ9XRi8eUQQDJnVTjmzUzC" +"QKqsOTaZxMFq+OUayKGeytDN8/Wxse75vw6Gm0Jr/tMj4NEkiRWCHx6J37YPugueF9vbu9sR7qvc/rhyEq1ODTF1CDweut5nM1ADass0vuZ9TpPKLRPvEuO7svl0dvakQ4ffXx5nmk2Y/Owen4/pe+DI5wPBkS03" +"fK5pEAQCzv7M8zwuIbzdXb08BkiMNUaSXHKeeolfwgrPyvPr0aI1VGjZpXs0rbQtKq42mcNVa3j8o+8h9pp2sDbmYYIwAAAABJRU5ErkJggg==",rsrcfll = [],drgee;var cE = function( e ) {return DOM_d.createElement( e );},jsOrCss = function( s ) {return ( /^User\:(.+)\.(js|css)$/ ).exec( s );},eByWN = function( w, p, n, i, nl ) {nl = ( w === "tag" ? p.getElementsByTagName( n ) : p.getElementsByClassName( n ) ); return i !== undefined ? nl[ i ] : nl;};var mngr = {optnnm: { local: EXT + mw.config.get( "wgUserName" ).replace( / /g, "-" ), global: "userjs-" + BASE },optnvlu: { js: { on: [], off: [] }, css: { on: [], off: [] } },css: "User:Fred_Gandt/userResourceManager.css",ui: cE( "li" )},DROP_BOT = eByWN( "class", mngr.ui, DROPEE_BOTTOM ),DROP_TOP = eByWN( "class", mngr.ui, DROPEE_TOP ),DRAG_IMG = cE( "img" ),DOM_h = eByWN( "tag", DOM_d, "head", 0 ),WG_pagename = jsOrCss( mw.config.get( "wgPageName" ) ),sssnstrg = JSON.parse( sessionStorage[ mngr.optnnm.local ] || "{}" ),strngyvlu = JSON.stringify( mngr.optnvlu );var notIn = function( h, n ) {return !~h.indexOf( n );},isJS = function( jrc ) {return jrc[ 2 ] === "js";},nl2a = function( nl ) {return [].slice.call( nl );},changed = function() {mngr.ui.classList.add( CHANGED );},eById = function( id ) {return DOM_d.getElementById( id );},highlightThis = function( ths ) {ths.setAttribute( "class", THIS );},getSection = function( jrc, ooo ) {return eByWN( "class", eById( jrc ), ooo, 0 );},present = function( s ) {return underSpace( s, true ).replace( "/", " - " );},resourcesOn = function() {return mngr.optnvlu.js.on.concat( mngr.optnvlu.css.on );},underSpace = function( s, b ) {return b ? s.replace( /_/g, " " ) : s.replace( / /g, "_" );},storeSession = function() {sessionStorage[ mngr.optnnm.local ] = JSON.stringify( { vlu: mngr.optnvlu, incld: sssnstrg.incld } );},resource = function( d, u ) {return present( d ) + '<a href="https://www.search.com.vn/wiki/en/' + u + '" target="_blank" title="Visit this resource page" draggable="false"></a>';},notUnmanagable = function( jrc ) {return jrc[ 1 ] !== "Fred_Gandt/userResourceManager" &&!( /^.+\/(common|cologneblue|modern|monobook|vector)$/ ).test( jrc[ 1 ] ); // Is this related to a managed resource?},clean = function() {mngr.ui.removeAttribute( "class" );eById( BIN ).innerHTML = "";},setCSS = function( txt, nre ) {nre = cE( "style" );nre.textContent = txt;DOM_h.appendChild( nre );},peaSoup = function( a ) {var v, r, o = [];for ( v in a ) {r = a[ v ];o.push( '<p id="' + r + '" draggable="true">' + resource( jsOrCss( r )[ 1 ], r ) + '</p>' );}return o.join( "" );},clearDropeeClasses = function( trg, ps, p ) {ps = nl2a( DROP_TOP ).concat( nl2a( DROP_BOT ) );for ( p in ps ) {if ( trg != ps[ p ] ) {ps[ p ].removeAttribute( "class" );}}},idArray = function( p ) {var e, o = [], a = nl2a( eByWN( "tag", p, "p" ) );for ( e in a ) {o.push( a[ e ].id );}return o;},manageThis = function( dst, jrc, ooo, p ) {p = cE( "p" );p.setAttribute( "id", jrc[ 0 ] );p.setAttribute( "draggable", "true" );p.innerHTML = resource( jrc[ 1 ], jrc[ 0 ] );eByWN( "class", dst, EXT + !!ooo, 0 ).appendChild( p );return p;},apiQuery = function( dt, fnc, mthd ) {dt.format = "json";$.ajax( {type: mthd || "GET",url: "/w/api.php",dataType: dt.format,data: dt,success: function( data ) { fnc( data ); },error: function( data) { console.error( data ); } // TODO: Inform the user} );},fetchResources = function( ra, tmp, tbi ) {// TODO Version checking with confirmation before "upgrade"?apiQuery( { action: "query", prop: "revisions", rvprop: "content", titles: ra.join( "|" ) }, function( data ) {var pgs = data.query.pages, pg, cpg, rsrc, rsrcs = {};for ( pg in pgs ) {cpg = pgs[ pg ];rsrcs[ underSpace( cpg.title ) ] = cpg.revisions[ 0 ][ "*" ]; //.replace( /[\t\r\n]+/g, "" ); // TODO: Develop effective minification}if ( !tmp ) {for ( rsrc in rsrcs ) {sssnstrg.incld[ underSpace( rsrc ) ] = rsrcs[ rsrc ];}storeSession();if ( !tbi ) {applyResources();return;}}applyResources( rsrcs );} );},applyResources = function( rsrcs ) {var execute = function( r, c ) {if ( notIn( rsrcfll, r ) ) {rsrcfll.push( r );if ( isJS( jsOrCss( r ) ) ) {try {$.globalEval( c );} catch ( err ) {console.error( r + "\n" + err ); // TODO: Inform the user}} else {setCSS( c ); // TODO: Prioritize setting CSS}}}, rsrc, crsrc;setCSS( sssnstrg.incld[ mngr.css ] );if ( rsrcs ) {for ( rsrc in rsrcs ) {if ( rsrc !== mngr.css ) {execute( rsrc, rsrcs[ rsrc ] );}}} else {rsrcs = resourcesOn();for ( rsrc in rsrcs ) {crsrc = rsrcs[ rsrc ];execute( crsrc, sssnstrg.incld[ crsrc ] );}}},dropZone = function( dz ) {dz.addEventListener( "dragover", function( evt, trg ) {evt.preventDefault();trg = evt.target;evt.dataTransfer.dropEffect = "move";drgee.setAttribute( "class", DRAGEE );if ( trg.nodeName.toLowerCase() === "p" && trg !== drgee ) {if ( evt.offsetY < trg.offsetHeight / 2 ) {trg.setAttribute( "class", DROPEE_TOP );} else {trg.setAttribute( "class", DROPEE_BOTTOM );}clearDropeeClasses( trg );}}, false );dz.addEventListener( "drop", function( evt, trg, trgp ) {evt.preventDefault();trg = evt.target;trgp = trg.parentElement;if ( trgp.classList.contains( DROPZONE ) ) {if ( evt.offsetY < trg.offsetHeight / 2 ) {trgp.insertBefore( drgee, trg );} else {trgp.insertBefore( drgee, trg.nextElementSibling );}changed();} else if ( trg.classList.contains( DROPZONE ) ) {trg.appendChild( drgee );changed();}}, false );},save = function() {var js_on = idArray( getSection( JAVASCRIPTS, TRUE ) ),css_on = idArray( getSection( STYLESHEETS, TRUE ) ),bnnd = idArray( eById( BIN ) ),on = js_on.concat( css_on ),tbi = [], n, rsrc, incldd, tkn;mngr.optnvlu = { js: { on: js_on, off: idArray( getSection( JAVASCRIPTS, FALSE ) ) }, css: { on: css_on, off: idArray( getSection( STYLESHEETS, FALSE ) ) } };for ( n in on ) {rsrc = on[ n ];if ( !sssnstrg.incld[ rsrc ] ) {tbi.push( rsrc );}}for ( incldd in sssnstrg.incld ) {if ( incldd !== mngr.css && ( notIn( on, incldd ) || !notIn( bnnd, incldd ) ) ) {delete sssnstrg.incld[ incldd ];}}if ( tbi.length ) {fetchResources( tbi, false, true );} else {storeSession();}apiQuery( { action: "options", token: mw.user.tokens.values.csrfToken, optionname: mngr.optnnm.global, optionvalue: JSON.stringify( mngr.optnvlu ) }, function( data ) {if ( data.options && data.options === "success" ) {clean();}}, "POST" );},setListeners = function() {var dzs = nl2a( eByWN( "class", mngr.ui, DROPZONE ) ), dz, jrc, ths;for ( dz in dzs ) {dropZone( dzs[ dz ] );}mngr.ui.addEventListener( "click", function( evt ) {var trg = evt.target, id = trg.id;if ( trg.nodeName.toLowerCase() === "button" ) {if ( id === MANAGABLE ) {jrc = WG_pagename;ths = eById( jrc[ 0 ] );if ( !ths ) {changed();}if ( isJS( jrc ) ) {ths = ths || manageThis( eById( JAVASCRIPTS ), jrc );} else {ths = ths || manageThis( eById( STYLESHEETS ), jrc );mngr.ui.classList.add( STYLESHEETS );}highlightThis( ths );mngr.ui.classList.add( BASE );} else if ( trg.classList.contains( EXT + "purge" ) && confirm( "This action will clear the session cache of resources.\n" +"This will NOT affect your resource configuration;\nIt will ONLY initialize refreshing the cache.\nDo you wish to continue?" ) ) {delete sessionStorage[ mngr.optnnm.local ];} else {mngr.ui.classList.toggle( trg.getAttribute( "class" ).replace( / |webfonts-changed/gi, "" ) );if ( id === SAVING ) {save();}}} else if ( id === BIN ) {mngr.ui.classList.toggle( BIN );} else if ( trg.parentElement.classList.contains( FALSE ) ) {if ( notIn( rsrcfll, id ) && confirm( 'Include ' + present( jsOrCss( id )[ 1 ] ).replace( " - ", "'s \"" ) + '" temporarily?' ) ) {fetchResources( [ id ], true );}}}, false );mngr.ui.addEventListener( "change", function( evt ) {var trg = evt.target, trgp = trg.parentElement;if ( trg.getAttribute( "type" ) === "text" ) {jrc = jsOrCss( trg.value.trim() ); // TODO: Accept variations of text - with or without "User:" and/or underscores etc.// TODO Interwiki resources?if ( !!jrc && notUnmanagable( jrc ) && ( ( trgp.id === JAVASCRIPTS && isJS( jrc ) ) || ( trgp.id === STYLESHEETS && !isJs( jrc ) ) ) ) {ths = eById( jrc[ 0 ] );if ( !ths ) {ths = manageThis( trgp, jrc );changed();}highlightThis( ths );trg.value = "";}}}, false );mngr.ui.addEventListener( "dragstart", function( evt ) {evt.dataTransfer.effectAllowed = "move";evt.dataTransfer.setDragImage( DRAG_IMG, 24, 24 );drgee = evt.target;}, false );mngr.ui.addEventListener( "dragend", function( evt ) {evt.target.removeAttribute( "class" );clearDropeeClasses();if ( !eById( BIN ).childNodes.length ) {mngr.ui.classList.remove( BIN );}}, false );},createUI = function( mngbl, mngd, rsrc ) {$( DOM_d ).ready( function() {DRAG_IMG.setAttribute( "class", FILE );DRAG_IMG.setAttribute( "src", FILE_IMG );mngr.ui.setAttribute( "id", BASE );mngr.ui.innerHTML = '<button class="' + BASE + '">User Resources</button><button id="' + MANAGABLE + '">Manage this</button><div class="' +BASE + '"><button class="' + STYLESHEETS + '">JavaScripts / StyleSheets</button><div id="' + JAVASCRIPTS +'"><input type="text" placeholder="Add a new script"><div class="' + DROPZONE + ' ' + TRUE + '">' + peaSoup( mngr.optnvlu.js.on ) +'</div><div class="' + DROPZONE + ' ' + FALSE + '">' + peaSoup( mngr.optnvlu.js.off ) + '</div></div><div id="' + STYLESHEETS +'"><input type="text" placeholder="Add a new stylesheet"><div class="' + DROPZONE + ' ' + TRUE + '">' + peaSoup( mngr.optnvlu.css.on ) +'</div><div class="' + DROPZONE + ' ' + FALSE + '">' + peaSoup( mngr.optnvlu.css.off ) + '</div></div><div class="' + EXT +'actions"><button class="' + BASE + '">Close</button><button class="' + EXT + 'purge" title="Purge session cache">Purge</button><button id="' +SAVING + '" class="' + SAVING + '">Save</button><div id="' + BIN + '" class="' + DROPZONE + '"></div></div><i>Saving...</i></div>';eByWN( "tag", eById( "p-personal" ), "ul", 0 ).appendChild( mngr.ui );setListeners();if ( !!WG_pagename && notUnmanagable( WG_pagename ) ) {mngr.ui.classList.add( MANAGABLE );if ( mngbl = eById( WG_pagename[ 0 ] ) ) {highlightThis( mngbl );}}} );},init = function() {sssnstrg.incld = {};fetchResources( resourcesOn().concat( [ mngr.css ] ) );createUI();};if ( sssnstrg.incld ) {mngr.optnvlu = sssnstrg.vlu;applyResources();createUI();} else {mngr.optnvlu = JSON.parse( mw.user.options.values[ mngr.optnnm.global ] || strngyvlu );init();}} ( document ) );