MediaWiki:Gadget-MyWiki.js

From Support Wiki
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/** const */ var
	EVENTS_TO_PREVENT_UNCONFIGURED = [ 'click', 'contextmenu' ],
	STORAGE_KEY = 'mywikiinfo',
	Text = {
		POPUP_HEADER: 'Select your wiki',
		POPUP_CLOSE: 'Cancel',
		POPUP_NOT_FREEFORM: 'My wiki has left onboarding (no longer needs a password to view).',
		POPUP_WIKI_LABEL: 'Subdomain of the wiki to link to (e.g. terraria, temtem):',
		POPUP_LANG_LABEL: 'In this language:',
		POPUP_INPUT_ERROR: 'This must be filled out and may only contain letters, digits and dashes.',
		CONFIG_BUTTON_TIP: 'Configure wiki for direct links',
		NOPREF_ALERT: 'The link you\'ve clicked will lead you to your own wiki, but first you need to select one in the box that has just been opened for you.\n\nIn future, if you wish to change your settings, click the cog icon next to the link.',
	};
/** let */ var configPopupElement = null, configWrapperElement = null, configContentsElement = null;
/** let */ var configPopupFirstLoad = true;


function getSavedWikiInfo() {
	return JSON.parse( localStorage.getItem( STORAGE_KEY ) || '[null, null]' );
}


function setSavedWikiInfo( wikiId, language ) {
	localStorage.setItem( STORAGE_KEY, JSON.stringify( [
		wikiId.trim(),
		language.trim()
	] ) );
	document.querySelectorAll( '.mywiki-link > a' ).forEach( function ( element ) {
		EVENTS_TO_PREVENT_UNCONFIGURED.forEach( function ( event ) {
			element.removeEventListener( event, handleUnsetPreferenceClick );
		} );
	} );
	updateLinks();
}


function closeConfigPopup() {
	configPopupElement.style.display = 'none';
}


function buildConfigPopupContent( isFreeform ) {
	if ( configContentsElement ) {
		configContentsElement.remove();
		configContentsElement = null;
	}

	var langSelectElement, saveButtonElement, wikiSelectElement, loaderElement;
	var wasRestored = false;
	var fieldCounter = 0;

	function _createField( labelText, inputElement ) {
		var result = document.createElement( 'div' ),
			labelElement = document.createElement( 'label' );
		result.className = 'mywiki__popup-field';
		labelElement.textContent = labelText;
		inputElement.id = labelElement.htmlFor = 'mywiki__config__' + fieldCounter++;
		if ( inputElement.type === 'checkbox' ) {
			result.classList.add( 'mywiki__popup-field--checkbox' );
			result.appendChild( inputElement );
			result.appendChild( labelElement );
		} else {
			result.appendChild( labelElement );
			result.appendChild( inputElement );
		}
		return result;
	}

	function _buildSelects() {
		function _onWikiSet() {
			langSelectElement.innerHTML = '';
			langSelectElement.disabled = true;
			saveButtonElement.disabled = true;
			
			var wikiId = wikiSelectElement.value;
			if ( !wikiId || wikiId === 'null' ) {
				return;
			}

			var opt = document.createElement( 'option' );
			opt.value = 'en';
			opt.text = 'English';
			langSelectElement.add( opt );

			fetch( 'https://' + wikiId + '.wiki.gg/api.php?action=query&meta=siteinfo&origin=*&siprop=interwikimap&format=json' )
				.then( function ( r ) { return r.json() } )
				.then( function ( r ) {
					r.query.interwikimap.forEach( function ( iwEntry ) {
						if (
							iwEntry.language
							&& iwEntry.url.startsWith( 'https://'+wikiId+'.wiki.gg/' )
							&& !iwEntry.url.startsWith( 'https://'+wikiId+'.wiki.gg/wiki/' )
						) {
							var opt = document.createElement( 'option' );
							opt.value = iwEntry.prefix;
							opt.text = iwEntry.language;
							langSelectElement.add( opt );
						}
					} );
				} )
				.then( function () {
					var savedLang = getSavedWikiInfo()[ 1 ];
					if ( savedLang && wasRestored ) {
						langSelectElement.value = savedLang;
						wasRestored = false;
					}
					langSelectElement.disabled = false;
					saveButtonElement.disabled = false;
				} );
		}

		function _onSave() {
			setSavedWikiInfo( wikiSelectElement.value, langSelectElement.value );
			closeConfigPopup();
		}

		langSelectElement = document.createElement( 'select' );
		langSelectElement.disabled = true;

		var optPlaceholder = document.createElement( 'option' );
		optPlaceholder.value = null;
		optPlaceholder.text = '';
		wikiSelectElement = document.createElement( 'select' );
		wikiSelectElement.disabled = true;
		wikiSelectElement.add( optPlaceholder );

		configContentsElement.appendChild( _createField( Text.POPUP_WIKI_LABEL, wikiSelectElement ) );
		configContentsElement.appendChild( _createField( Text.POPUP_LANG_LABEL, langSelectElement ) );

		wikiSelectElement.addEventListener( 'change', _onWikiSet );
		saveButtonElement.addEventListener( 'click', _onSave );

		fetch( 'https://app.wiki.gg/wikis' )
			.then( function ( r ) { return r.json() } )
			.then( function ( r ) {
				r.forEach( function ( wiki ) {
					var opt = document.createElement( 'option' );
					opt.value = wiki.wiki_id;
					opt.text = wiki.wiki_name;
					wikiSelectElement.add( opt );
				} );
			} )
			.then( function () {
				var savedWikiId = getSavedWikiInfo()[ 0 ];
				wikiSelectElement.value = savedWikiId;
				wikiSelectElement.disabled = false;

				if ( savedWikiId && !wikiSelectElement.value ) {
					setTimeout( function () {
						buildConfigPopupContent( true );
					} );
					return;
				}
				
				if ( wikiSelectElement.value ) {
					wasRestored = true;
					_onWikiSet();
				}

				loaderElement.remove();
			} );
	}

	function _buildFreeform() {
		function _checkAllFilled() {
			saveButtonElement.disabled = !wikiSelectElement.value || wikiSelectElement.value === 'null'
				|| !langSelectElement.value || langSelectElement.value === 'null';
		}

		function _onSave() {
			var isOk = true;
			[ wikiSelectElement, langSelectElement ].forEach( function ( element ) {
				element.setCustomValidity( element.validity.valid ? '' : Text.POPUP_INPUT_ERROR );
				isOk = isOk && element.reportValidity();
			} );

			if ( isOk ) {
				setSavedWikiInfo( wikiSelectElement.value, langSelectElement.value );
				closeConfigPopup();
			}
		}

		wikiSelectElement = document.createElement( 'input' );
		wikiSelectElement.type = 'text';
		wikiSelectElement.value = getSavedWikiInfo()[ 0 ];

		langSelectElement = document.createElement( 'input' );
		langSelectElement.type = 'text';
		langSelectElement.value = getSavedWikiInfo()[ 1 ] || 'en';
		langSelectElement.disabled = false;

		[ wikiSelectElement, langSelectElement ].forEach( function ( element ) {
			element.setAttribute( 'required', true );
			element.setAttribute( 'pattern', '(?:\\s+)?[a-z0-9\\-]+(?:\\s+)?' );
		} );

		if ( wikiSelectElement.value ) {
			wasRestored = true;
		}

		_checkAllFilled();

		configContentsElement.appendChild( _createField( Text.POPUP_WIKI_LABEL, wikiSelectElement ) );
		configContentsElement.appendChild( _createField( Text.POPUP_LANG_LABEL, langSelectElement ) );

		wikiSelectElement.addEventListener( 'change', _checkAllFilled );
		langSelectElement.addEventListener( 'change', _checkAllFilled );
		saveButtonElement.addEventListener( 'click', _onSave );

		setTimeout( function () {
			loaderElement.remove();
		}, 0 );
	}

	var headerElement = document.createElement( 'h3' );
	headerElement.textContent = Text.POPUP_HEADER;

	var freeformToggle = document.createElement( 'input' );
	freeformToggle.type = 'checkbox';
	freeformToggle.checked = !isFreeform;
	freeformToggle.addEventListener( 'click', function () {
		buildConfigPopupContent( !freeformToggle.checked );
	} );

	var closeButtonElement = document.createElement( 'button' );
	closeButtonElement.textContent = Text.POPUP_CLOSE;
	closeButtonElement.addEventListener( 'click', closeConfigPopup );

	saveButtonElement = document.createElement( 'button' );
	saveButtonElement.className = 'mywiki__button--primary';
	saveButtonElement.textContent = 'Save';
	saveButtonElement.disabled = true;

	var buttonGroupElement = document.createElement( 'div' );
	buttonGroupElement.className = 'mywiki__popup__buttons';
	buttonGroupElement.appendChild( closeButtonElement );
	buttonGroupElement.appendChild( saveButtonElement );

	loaderElement = document.createElement( 'div' );
	loaderElement.className = 'mywiki__popup__loading';

	configContentsElement = document.createElement( 'div' );
	configContentsElement.className = 'mywiki__popup';
	configContentsElement.addEventListener( 'click', function ( event ) {
		event.stopPropagation();
	} );
	configContentsElement.appendChild( headerElement );
	configContentsElement.appendChild( _createField( Text.POPUP_NOT_FREEFORM, freeformToggle ) );
	if ( isFreeform ) {
		_buildFreeform();
	} else {
		_buildSelects();
	}
	configContentsElement.appendChild( buttonGroupElement );
	configContentsElement.appendChild( loaderElement );

	configWrapperElement.appendChild( configContentsElement );
}


function buildConfigPopupRoot() {
	configWrapperElement = document.createElement( 'div' );
	configWrapperElement.className = 'mywiki__popup-wrapper';

	configPopupElement = document.createElement( 'div' );
	configPopupElement.className = 'mywiki__popup-root';
	configPopupElement.addEventListener( 'click', closeConfigPopup );
	configPopupElement.appendChild( configWrapperElement );

	document.body.appendChild( configPopupElement );
}


function openConfigPopup( params ) {
	if ( configPopupElement === null ) {
		buildConfigPopupRoot();
	}

	if ( configContentsElement === null ) {
		buildConfigPopupContent( false );
	}

	configPopupElement.style.display = 'block';
}
	
	
function updateLinks() {
	var wi = getSavedWikiInfo();
	var userWikiId = wi[ 0 ], userWikiLang = wi[ 1 ];
		
	document.querySelectorAll( '.mywiki-link' ).forEach( function ( element ) {
		var linkElement = element.children[ 0 ];
		linkElement.href = element.dataset.generic.replace( '%SERVER%', userWikiId )
		.replace( '/%LANG%', userWikiLang === 'en' ? '' : '/' + userWikiLang );
	} );
}
	
	
function makeConfigButton() {
	var result = document.createElement( 'button' );
	result.className = 'mywiki__config-button';
	result.title = Text.CONFIG_BUTTON_TIP;
	result.addEventListener( 'click', openConfigPopup );
	return result;
}
	
	
function handleUnsetPreferenceClick( event ) {
	event.preventDefault();
	alert( Text.NOPREF_ALERT );
	openConfigPopup( event );
}
	
	
function initialiseLink( element ) {
	var linkElement = element.children[ 0 ],
	wgServer = mw.config.get( 'wgServerName' );
	element.appendChild( makeConfigButton() );
	// Prepare link template data attributes. This will make it much easier to update everything on a preference change.
	element.dataset.generic = linkElement.href.replace( 'https://' + wgServer, 'https://%SERVER%.wiki.gg/%LANG%' );
}
	
	
mw.hook( 'wikipage.content' ).add( function ( $content ) {
	var rootNode = $content[ 0 ];
		
	rootNode.querySelectorAll( '.mywiki-link' ).forEach( function ( element ) {
		initialiseLink( element );
	} );
		
	if ( localStorage.getItem( STORAGE_KEY ) ) {
		updateLinks();
	} else {
		rootNode.querySelectorAll( '.mywiki-link > a' ).forEach( function ( element ) {
			EVENTS_TO_PREVENT_UNCONFIGURED.forEach( function ( event ) {
				element.addEventListener( event, handleUnsetPreferenceClick );
			} );
		} );
	}
} );