User:Andrybak/Scripts/Vectron.js

// (function {	'use strict';

const USERSCRIPT_NAME = 'Vectron'; const config = { wikipage: '${USERSCRIPT_NAME}', version: '6' };	const LOG_PREFIX = `[${USERSCRIPT_NAME} v${config.version}]:`; const mw = window.mw; // a hack to trick my JS editor into believing that `mw` exists

// TODO: Not sure if still need to support the toggle button. Keep it for now. const TOGGLE_BUTTON_SELECTOR = '#p-dock-bottom button.vector-limited-width-toggle'; const STANDARD_WIDTH_RADIO_BUTTON_SELECTOR = '#skin-client-pref-vector-feature-limited-width-value-1'; const WIDE_WIDTH_RADIO_BUTTON_SELECTOR = '#skin-client-pref-vector-feature-limited-width-value-0';

const ANY_CONTROL_SELECTOR = `${TOGGLE_BUTTON_SELECTOR}, ${STANDARD_WIDTH_RADIO_BUTTON_SELECTOR}, ${WIDE_WIDTH_RADIO_BUTTON_SELECTOR}`;

/*	 * MediaWiki has automatic scrolling to (and highlighting of) messages * with mentions. This hash disappears quickly from document.location, * so we have to remember it separately from function maybeShowHashTarget. */	let mentionHash = null;

function error(...toLog) { console.error(LOG_PREFIX, ...toLog); }

function warn(...toLog) { console.warn(LOG_PREFIX, ...toLog); }

function info(...toLog) { console.info(LOG_PREFIX, ...toLog); }

function debug(...toLog) { console.debug(LOG_PREFIX, ...toLog); }

function getLimitedWidthToggleButton { return document.querySelector(TOGGLE_BUTTON_SELECTOR); }

function isWideVector { return document.querySelector('html').classList.contains('vector-feature-limited-width-clientpref-0'); }

function isNarrowVector { return !isWideVector; }

/*	 * Because we are messing with the layout of the page * on the fly, we need to ensure that the user sees * the linked section or linked mention on a discussion page. */	function maybeShowHashTarget { /*		 * These aren't regular #Section_heading anchors, but rather special * tags used by MediaWiki to highlight mentions. */		if (mentionHash !== null) { /*			 * MediaWiki adds an empty at the _start_ of a message with the * corresponding `id`. * This id also corresponds to the attribute `data-mw-thread-id` stored * in an empty at the _end_ of the same message after the "Reply" * button (mw:Extension:DiscussionTools), right after the signature. */			const maybeMentionMessage = document.getElementById(mentionHash.slice(1)); /*			 * `null` will be returned in following cases: *  - when the message was removed (e.g. into an archive) *  - when the timestamp in the signature was re-generated (e.g. en:Special:Diff/1222421941) */			if (maybeMentionMessage !== null) { maybeMentionMessage.scrollIntoView; return; }		}		/*		 * If a mention wasn't highlighted, try to scroll to a regular * anchor of a section. */		if (document.location.hash === "") { return; }		const targetId = document.location.hash.slice(1).replaceAll(' ', '_'); document.getElementById(targetId)?.scrollIntoView; }

function ensureNeededWidth(checkFn, adjective, verb, controlClass) { if (checkFn) { debug(`Already ${adjective}.`); return; }		info(verb); const control = document.querySelector(controlClass); control.click; maybeShowHashTarget; }

function ensureWide { ensureNeededWidth(			isWideVector,			'wide',			'Widening.',			`${WIDE_WIDTH_RADIO_BUTTON_SELECTOR}, ${TOGGLE_BUTTON_SELECTOR}`		); }

function ensureNarrow { ensureNeededWidth(			isNarrowVector,			'narrow',			'Narrowing.',			`${STANDARD_WIDTH_RADIO_BUTTON_SELECTOR}, ${TOGGLE_BUTTON_SELECTOR}`		); }

/*	 * The main function of the script. */	function runScript { /*		 * Reference documentation about keys and values in mw.config: * https://www.mediawiki.org/wiki/Manual:Interface/JavaScript#mw.config */		if (!mw.config.get('wgIsArticle')) { // This variable is badly named -- it is not related to a page being a main namespace "article". info('Not a wiki page.'); ensureWide; return; }		if (mw.config.get('wgDiffNewId') != null || mw.config.get('wgDiffOldId') != null) { info('Diff view.'); ensureWide; return; }		const namespaceNumber = mw.config.get('wgNamespaceNumber'); if (namespaceNumber === -1) { info('This is a "Special:" page.'); ensureWide; return; }		const contentModel = mw.config.get("wgPageContentModel"); // ['javascript', 'css', 'sanitized-css', 'Scribunto'].includes(contentModel) if (contentModel !== 'wikitext') { info('Content model of the page is for source code (Lua, JS, CSS, etc).'); ensureWide; return; }		info('Assuming wiki page.'); ensureNarrow; }

function wait(message) { info(message); setTimeout(lazyLoadVectron, 200); }

function rememberMentionHash { const params = new URLSearchParams(document.location.search); if (params.get('markasread') !== null) { mentionHash = document.location.hash; info('Remembered comment hash:', mentionHash); }	}

/*	 * Infrastructure to ensure the script can run. */	function lazyLoadVectron { debug('Loading...'); const skinId = mw.config.get("skin"); if (skinId === null) { wait('Skin is not loaded yet. Waiting...'); return; }		if (skinId !== 'vector-2022') { warn(`Skin ${skinId} is not supported by the script. Aborting.`); return; }		const anyControl = document.querySelector(ANY_CONTROL_SELECTOR); if (anyControl === null) { wait('The UI controls are not loaded yet. Waiting...'); return; }		const button = getLimitedWidthToggleButton; if (button !== null) { const theAttribute = button.getAttribute('data-event-name'); if (theAttribute === null) { wait('Attribute "data-event-name" of the toggle button is not loaded yet. Waiting...'); return; }		}		runScript; }

rememberMentionHash; if (document.readyState !== 'loading') { lazyLoadVectron; } else { warn('Cannot load yet. Setting up a listener...'); document.addEventListener('DOMContentLoaded', lazyLoadVectron); } }); //