User:Fred Gandt/quickLinks.js

/********************************************************************************************************************************* * Currently still in development, this is designed to provide a custom list of Quick Links to Wikipedia pages. * If you encounter any problems using this script, please tell User:Fred_Gandt on either my talk page or this script's talk page. * *********************************************************************************************************************************/ /* TODO: Handle #sections */ /* TODO: Reduce API calls */

( function {	"use strict";	var eByTn = function( p, n, i, nl ) { nl = p.getElementsByTagName( n ); return i !== undefined ? nl[ i ] : nl; },		eById = function( id ) { return document.getElementById( id ); },		cE = function( e ) { return document.createElement( e ); },		nl2a = function( nl ) { return [].slice.call( nl ); },		WG_pagename = mw.config.get( "wgPageName" ),		BASE = "fg-quick-links",		EXT = BASE + "-",		SWITCH = EXT + "switch",		VIEW = EXT + "view",		EMPTY = EXT + "empty", 		OPEN = EXT + "open",		TITLE = EXT + "title",		STORAGE = EXT + "storage",		QL = EXT + "ql",		QLE,		NPT,		namespace = l => /^(?:([^\:]+)\:)?(.+)$/.exec( l ),		toggleBase = e => ( e || ql.ui ).classList.toggle( BASE ),		underspace = ( s, b ) => b ? s.replace( /_/g, " " ) : s.replace( / /g, "_" ),		gotIt = v => ql.ui.querySelector( 'a[title="' + underspace( v || WG_pagename, true ).replace( /\"/g, "\\\"" ) + '"]' ), ql = { optnnm: { local: EXT + mw.config.get( "wgUserName" ).replace( / /g, "-" ), global: "userjs-" + BASE }, optnvlu: [ { "Mainspace": [] }, { "Mainspace talk": [] } ], alss: { undefined: "Mainspace", "talk": "Mainspace talk" }, ui: cE( "li" ) },		initOptionValue = function { var fns = mw.config.get( "wgFormattedNamespaces" ), nsi = mw.config.get( "wgNamespaceIds" ), ns, cns, cnsi, tmp; for ( ns in nsi ) { cnsi = nsi[ ns ]; cns = fns[ cnsi ]; if ( underspace( cns ).toLowerCase === ns && cnsi !== 0 && cnsi !== 1 ) { tmp = {}; tmp[ cns ] = []; ql.optnvlu.push( tmp ); } else { ql.alss[ ns ] = cns; }			}			return ql.optnvlu; },		linkify = function( v, d ) { v = v.replace( /^Mainspace(?:[ _]{1}talk)?\:/i, "" ); var u = underspace( v, true ); if ( d ) { u = u.replace( /[\.\%]{1}(2[1-9a-c]{1}|[357][b-e]{1}|[23]f|[46]0|c2[\.\%]{1}a([01]{1}))/gi, function( m, g1, g2 ) {					if ( g2 ) {						return { "0": " ", "1": "&iexcl;" }[ g2.toLowerCase ];					}					return { "21": "!", "22": "&quot;", "23": "#", "24": "$", "25": "%", "26": "&amp;", "27": "&apos;", "28": "(", "29": ")", "2a": "*", "2b": "+", "2c": ",", "2f": "&sol;",						"3b": ";", "3c": "&lt;", "3d": "=", "3e": "&gt;", "3f": "?", "5b": "[", "5c": "&bsol;", "5d": "]", "5e": "^", "7b": "{", "7c": "|", "7d": "}", "7e": "~",						"40": "@", "60": "&grave;" }[ g1.toLowerCase ];				} ); }			return '' + namespace( u )[ 2 ] + '';		},		quickLinks = function {			var vlus = ql.optnvlu, o = [], vlu, qls, on, oa, ok,				iterate = function( a ) {					var i = [], v;					for ( v in a ) {						i.push( linkify( a[ v ] ) );					}					return i.join( '' );				},				filler = function( a ) {					if ( a.length ) {						return '' + iterate( a ) + '';					}					return "";				},				brynner = function( t, c, f ) {					var u = cE( "ul" );					u.id = underspace( EXT + t );					if ( c ) {						u.setAttribute( "class", c );					}					u.innerHTML = '' + t + '' + f;					return u.outerHTML;				};			for ( vlu in vlus ) {				qls = vlus[ vlu ];				ok = Object.keys( qls );				on = ok[ 0 ];				oa = qls[ on ];				o.push( brynner( on, !oa.length ? EMPTY : ( ok[ 1 ] ? OPEN : false ), filler( oa ) ) );			}			return o.join( "" );		},		switchSwitch = function( t ) {			var s = eById( SWITCH );			if ( t ) {				toggleBase( s );			} else {				s.classList.toggle( BASE, gotIt );			}		},		save = function( ss ) {			var uls = nl2a( eByTn( ql.ui, "ul" ) ), tmpoptnvlu = [],				ul, la, tmp, cul,				titleArray = function( a ) {					var l, ta = [];					for ( l in a ) {						ta.push( underspace( eByTn( a[ l ], "a", 0 ).title ) );					}					return ta.sort;				},				showError = function( e ) {					alert( "Something went wrong:\n\n" + e );				};			for ( ul in uls ) {				tmp = {};				cul = uls[ ul ];				la = nl2a( eByTn( cul, "li" ) );				tmp[ la[ 0 ].textContent ] = titleArray( la.slice( 1 ) );				if ( cul.classList.contains( OPEN ) ) {					tmp.open = true;				}				tmpoptnvlu.push( tmp );			}			$.ajax( { type: "POST", url: "/w/api.php", dataType: "json", data: { format: "json", action: "options", token: mw.user.tokens.values.csrfToken, optionname: ql.optnnm.global, optionvalue: JSON.stringify( tmpoptnvlu ) },				success: function( data ) { if ( !data.error ) { localStorage[ STORAGE ] = JSON.stringify( QLE.innerHTML ); ql.optnvlu = tmpoptnvlu; switchSwitch( ss ); } else { QLE.innerHTML = quickLinks; showError( data.error.info ); }				},				error: function( something, went, wrong ) { QLE.innerHTML = quickLinks; console.error( something ); showError( went + ":\n\n" + wrong ); }			} );		},		addThis = function( v, d ) {			var alias = function( q ) {					return ql.alss[ q ? q.toLowerCase : q ] || q;				},				li, ul = eById( EXT + underspace( alias( namespace( v )[ 1 ] ) ) );			if ( ul ) {				li = cE( "li" );				li.innerHTML = linkify( v, d );				ul.appendChild( li );				ul.classList.remove( EMPTY );				return li;			}			return false;		},		removeThis = function( t ) {			var tp = t.parentElement, tpp = tp.parentElement;			tpp.removeChild( tp );			tpp.classList.toggle( EMPTY, nl2a( eByTn( tpp, "li" ) ).length < 2 );		},		setListeners = function {			var prepText = function( txt ) {					return ( /(?:^.*w(?:iki)?\/(?:.+title\=)?)?([^&]+)/ ).exec( txt.trim )[ 1 ];				},				processText = function( vlu, d ) {					if ( vlu && !gotIt( vlu ) ) {						if ( addThis( underspace( vlu ), d ) ) {							save;							NPT.value = "";						} else if ( !confirm( "Something about that value isn't correct.\nModify it and try again?" ) ) {							NPT.value = ""; }					}				};			ql.ui.addEventListener( "click", evt => {				var trg = evt.target, nn = trg.nodeName.toLowerCase, ths = gotIt;				if ( nn === "button" ) {					toggleBase;				} else if ( nn === "a" ) {					if ( trg.id === SWITCH ) {						evt.preventDefault;						if ( !ths ) {							addThis( WG_pagename );						} else {							removeThis( ths );						}						save( true );					} else if ( trg.id === VIEW ) {						evt.preventDefault;						toggleBase;					}				} else if ( nn === "li" ) {					if ( !trg.classList.contains( TITLE ) ) {						removeThis( eByTn( trg, "a", 0 ) );					} else {						trg.parentElement.classList.toggle( OPEN );					}					save;				}			}, false ); ql.ui.addEventListener( "dragover", evt => evt.preventDefault ); ql.ui.addEventListener( "drop", evt => {				evt.preventDefault;				processText( prepText( evt.dataTransfer.getData( "text" ) ), true );			} ); NPT.addEventListener( "paste", evt => {				evt.preventDefault;				processText( prepText( evt.clipboardData.getData( "text" ) ), true );			}, false ); NPT.addEventListener( "change", evt => processText( NPT.value ) ); window.addEventListener( "storage", evt => {				var k = evt.key, nv = evt.newValue;				if ( k && k === STORAGE && nv ) {					QLE.innerHTML = JSON.parse( nv );					switchSwitch;					delete localStorage[ STORAGE ];				}			}, false ); };	ql.optnvlu = JSON.parse( mw.user.options.values[ ql.optnnm.global ] || JSON.stringify( initOptionValue ) ); $( document ).ready( => {		ql.ui.id = BASE;		ql.ui.innerHTML = `   ${quickLinks} Close   `;		const STYLE_SHEET = new CSSStyleSheet;		document.adoptedStyleSheets = [ ...document.adoptedStyleSheets, STYLE_SHEET ];		STYLE_SHEET.replaceSync( `#fg-quick-links-switch { text-decoration: none; padding: .5em .2em; font-size: 1.7em; background: none; height: 1.46em; color: #ffbc41; width: 1em; }	content: "☆"; }	content: "★"; }	text-decoration: none; padding: .8em .3em; background: none; font-size: 1.1em; height: 2.3em; color: unset; opacity: .5; width: 2em; }	content: "🔍"; }	display: inline; }	display: none; position: absolute; min-width: 300px; background: #fff; z-index: 2000; margin-top: 2.2em; padding: 1em; border: 1px solid #a7d7f9; border-radius: 3px; box-shadow: 2px 2px 15px -2px rgba(0, 0, 0, 0.5); }	max-height: calc( 80vh - 13em ); padding-right: 2em; overflow: auto; overflow-x: hidden; overscroll-behavior: contain; }	float: none !important; background: none; }	display: none; }	float: none !important; height: auto; background: none; }	font-weight: bold; color: #666; cursor: pointer; }	display: none; margin-left: 1.3em; }	padding: 0; float: none; height: auto; display: block; margin-left: 1.3em; background-image: none; }	content: "► "; float: left; color: #aaa; }	content: "▼ "; }	content: "x"; float: left; color: #fff; background: rgba( 255, 0, 0, 0.5 ); border-radius: 100%; padding: 1px 3px; font-size: 10px; line-height: 10px; margin-top: 2px; cursor: pointer; }	margin-bottom: 1em; width: calc( 100% - 2em - 2px ); padding: 0.5em 1em 0.6em; border: 1px solid #aaa; border-radius: 3px; }	display: none; margin-top: 1em; }	display: block; }` );		eByTn( eById( "p-views" ), "ul", 0 ).append( ql.ui );		NPT = eByTn( ql.ui, "input", 0 );		QLE = eById( QL );		switchSwitch;		setListeners;	}, { once: true } ); } );
 * 1) fg-quick-links-switch::before {
 * 1) fg-quick-links-switch.fg-quick-links::before {
 * 1) fg-quick-links-view {
 * 1) fg-quick-links-view::before {
 * 1) fg-quick-links span > span {
 * 1) fg-quick-links span > div {
 * 1) fg-quick-links-ql {
 * 1) fg-quick-links-ql ul {
 * 1) fg-quick-links-ql ul.fg-quick-links-empty {
 * 1) fg-quick-links-ql li {
 * 1) fg-quick-links-ql li.fg-quick-links-title {
 * 1) fg-quick-links-ql li:not( .fg-quick-links-title ) {
 * 1) fg-quick-links-ql li a {
 * 1) fg-quick-links-ql ul li.fg-quick-links-title::before {
 * 1) fg-quick-links-ql ul.fg-quick-links-open li.fg-quick-links-title::before {
 * 1) fg-quick-links-ql li:not( [class=fg-quick-links-title] )::before {
 * 1) fg-quick-links input {
 * 1) fg-quick-links button {
 * 1) p-views,
 * 2) fg-quick-links span:hover > div,
 * 3) fg-quick-links.fg-quick-links button,
 * 4) fg-quick-links.fg-quick-links span > div,
 * 5) fg-quick-links-ql ul.fg-quick-links-open li {