User:Arashiryuu0/common.js

/** * @namespace Personal_JS */ /*	jshint undef: true, noarg: true, devel: true, jquery: true, strict: true, eqeqeq: true, freeze: true, newcap: true, esnext: true, browser: true, latedef: true, shadow: outer, varstmt: true, quotmark: single, singleGroups: true, futurehostile: true

/** * @typedef Ref * @property {*} current */ /** * Callback for when Fandom is ready. * @callback cb * @returns {void} */ /** * Method of the Logger object. * @callback logMethod * @returns {void} */ /* global mw, requestIdleCallback */ 'use strict';

jsScope: { if (window.UCP && window.UCP.localJS) break jsScope; const has = Object.prototype.hasOwnProperty; const slice = Array.prototype.slice; const toString = Object.prototype.toString; /**    * Creates clean objects with a Symbol.toStringTag description of the object. * @param {!string} value * @returns {!object} */   const _Object = function (value) { if (arguments.length < 1) value = 'NullObject'; if (typeof value !== 'string') throw new TypeError('Description must be of a `string` value.'); const obj = Object.create(null); Object.defineProperty(obj, Symbol.toStringTag, { value: value }); // RL prevents using modern syntax, so can't simply do this: // Object.create(null, { [Symbol.toStringTag]: { value } }) return obj; };   /**     * Cache of fired hooks. */   const fired = _Object('HookCache'); /**    * Ref helper. * @param {*} initialValue * @returns {!Ref} */   const useRef = function (initialValue) { // initialValue = arguments.length > 0 ? arguments[0] : null; // createRef was a hardcoded null, useRef uses the value passed in		return { current: initialValue }; };	/**	 * `at` polyfill */	const at = function (o, idx) { if (o === undefined || o === null) return undefined; if ([-Infinity, +Infinity].includes(idx)) return undefined; const i = useRef(Math.trunc(idx) || 0); i.current = i.current < 0 ? o.length + i.current : i.current; if (i.current < 0 || i.current >= o.length) return undefined; return o[i.current]; };   /**     * Determines if something is undefined or null. * Accounting for `document.all` and other potential weirdness version. */   const isNil = function (anything) { return anything === undefined || anything === null; };   /**     * Simple polyfill for the nullish coalescing operator (??) * @alias ifNull * @description if `expected` is null or undefined, return the `fallback` value. * @param {*} expected * @param {*} fallback * @returns {*} */   const ifNull = function (expected, fallback) { return isNil(expected) ? fallback : expected; };   /**     * Safely traverses an object. * @param {!object} obj * @param {!string} path * @returns {*} */   const getProp = function (obj, path) { return path.split(/\s?\.\s?/).reduce(function (o, prop) {			return o && o[prop];		}, obj); };   /**	 * Gets a more accurate description of an object than `typeof`. * @name typeOf * @param {*} item * @returns {!string} */	const typeOf = function (item) { return toString .call(item) .replace(/\[object (\w+)\]/i, '$1') .toLowerCase; };   /**     * Checks whether something is a Node instance. * @name isNode * @param {*} tester * @returns {!boolean} */   const isNode = function (tester) { return tester instanceof Node; };   /**     * Self-binds to all methods on an object. * @param {?object} obj */   const applyBinds = function (obj) { if (isNil(obj)) return; const methods = Object.getOwnPropertyNames(obj).filter(function (name) {			return typeof obj[name] === 'function';		}); if (!methods.length) return; const counter = useRef(0); while (counter.current < methods.length) { const method = methods[counter.current++]; obj[method] = obj[method].bind(obj); }   };    /**     * Create Logger object. */   const Logger = _Object('Logger'); loggerScope: { const counter = useRef(0); /**		 * @type {!string[]} */		const levels = ['log', 'dir', 'info', 'warn', 'debug', 'error']; /**		 * Retrieves label data for logging methods. * @param {!string} name * @returns {!string[]} */		const getParts = function (name) { return [ '%c[' + name + ']%c \u2014 %s', 'color: #82AAFF;', 'color: #F78C6A;', new Date.toUTCString ];		};		/**        * Creates log methods. * @name makeLog * @param {!string} level * @param {!string} name * @returns {!logMethod} */       const makeLog = function (level, name) { return function { console.groupCollapsed.apply(null, getParts(name)); console[level].apply(null, arguments); console.groupEnd; };       };        while (counter.current < levels.length) { const level = levels[counter.current++]; Logger[level] = makeLog(level, 'User:Arashiryuu0/common.js'); }       applyBinds(Logger); Object.freeze(Logger); }   /**     * Create Utils object. */   const Utils = _Object('Utils'); utilsScope: { Object.assign(Utils, {			at: at,			type: typeOf,			isNil: isNil,			ifNull: ifNull,			isNode: isNode,			useRef: useRef,			getProp: getProp		}); applyBinds(Utils); Object.freeze(Utils); }   /**     * DOM element helper utilities. */   const DOM = _Object('DOM'); Object.assign(DOM, {		/**		 * A `document.createElement` helper function.		 * @param {!string} type		 * @param {?object} props		 * @returns {!HTMLElement}		 */		create: function (type, props) {			if (typeof type !== 'string') type = 'div';			const e = document.createElement(type);			const children = slice.call(arguments, 2);			if (typeOf(props) !== 'object' || !Object.keys(props).length) {				if (children.length) e.append.apply(e, children);				return e;			}			if (!has.call(props, 'children') && children.length) {				e.append.apply(e, children);			}			/**			 * @param {!string} key			 * @returns {!boolean}			 */			const isEvent = function (key) {				return key.slice(0, 2) === 'on' && key[2] === key[2].toUpperCase;			};			/**			 * @param {!string} name			 * @returns {!string}			 */			const normalize = function (name) {				return name === 'doubleclick'					? 'dblclick'					: name;			}; const counter = useRef(0); const keys = Object.keys(props); while (counter.current < keys.length) { const key = keys[counter.current++]; switch (key) { case 'text': { e.textContent = props[key]; break; }					case 'className': { e[key] = props[key]; break; }					case 'classes': { if (!Array.isArray(props[key])) props[key] = [props[key]]; e.classList.add.apply(e.classList, props[key]); break; }					case 'style': { Object.assign(e[key], props[key]); break; }					case 'children': { if (!Array.isArray(props[key])) props[key] = [props[key]]; e.append.apply(e, props[key]); break; }					case 'htmlFor': { e.setAttribute('for', props[key]); break; }					default: { if (isEvent(key)) { const event = normalize(key.slice(2).toLowerCase); e.addEventListener(event, props[key]); break; }						e.setAttribute(key, props[key]); break; }				}			}			e.$$props = props; return e;		}, /**		 * A `DocumentFragment` helper function. * @param {!(string | Node)[]} [children] * @returns {!DocumentFragment} */		fragment: function (children) { const frag = new DocumentFragment; if (isNil(children)) return frag; if (!Array.isArray(children)) children = [children]; frag.append.apply(frag, children); return frag; }   });    applyBinds(DOM);    /**     * @type {cb}     */    const loaded = function  {        if (fired.loaded) return;        /**         * Loading scripts through the ResourceLoader via mw.loader         */        scriptsScope: {            const sUri = 'https://reds-test.fandom.com/wiki/User:Arashiryuu0/sandbox';            const sQuery = '?ctype=text/javascript&action=raw';            const queries = [				sUri + sQuery,				sUri + '.javascript' + sQuery			];            const load = function (url) {                if (typeof url !== 'string') return;                mw.loader.load(url);            };            queries.forEach(load);        }        bannersScope: {			const bannerItems = [				document.querySelector('.wds-global-navigation__logo')			].concat(slice.call(document.querySelectorAll('.wds-global-navigation__link')));			bannerItems.forEach(function (item) { if (!item) return; item.setAttribute('tabindex', '-1'); });		}		buttonsScope: {			if (fired.buttons) break buttonsScope;			const wdsButtons = document.querySelectorAll('.wiki-tools.wds-button-group');			if (!wdsButtons.length) break buttonsScope;			const clone = function (button) {				return button.cloneNode(true);			};			const firstName = at(wdsButtons, 0).firstElementChild.className;			const buttons = [				DOM.create('a', { className: firstName.replace('wiki-tools__search', 'wiki-tools__user-js'), text: 'JS', href: '/wiki/User:Arashiryuu0/common.js' }),				DOM.create('a', { className: firstName.replace('wiki-tools__search', 'wiki-tools__user-css'), text: 'CSS', href: '/wiki/User:Arashiryuu0/common.css' }),				DOM.create('a', { className: firstName.replace('wiki-tools__search', 'wiki-tools__random'), text: '?', href: '/wiki/Special:Random' })			];			const frag = DOM.fragment;			const counter = useRef(0);			while (counter.current < wdsButtons.length) {				const wds = wdsButtons[counter.current++];				const cloned = buttons.map(clone);				frag.append.apply(frag, cloned);				wds.appendChild(frag);				/**				 * fix button positioning				 * layout should be:				 * search, discussions, recent changes, theme, my buttons, dropdown				 */				wds.appendChild(wds.querySelector('.wds-dropdown'));			}			fired.buttons = true;		}		mw.hook('dev.highlight').add(function (hljs) { const ref = useRef; const disableOuterTheme = function { const n = document.querySelector('link[href$=".min.css"]'); if (!n) { ref.current = requestAnimationFrame(disableOuterTheme); return; }				n.setAttribute('disabled', ''); ref.current = null; };			const useTheme = function { hljs.useTheme('atom-one-dark'); };			const _loaded = function { requestAnimationFrame(useTheme); };			ref.current = requestAnimationFrame(disableOuterTheme); hljs.loadAllLanguages.then(_loaded, Logger.error); window.UCP.__rafRef = ref; });       fired.loaded = true;    };	window.UCP = window.UCP || {};	Object.assign(window.UCP, { localJS: fired, Logger: Logger, Utils: Utils, DOM: DOM });	window.importArticles.apply(null, [ {			type: 'script', articles: [ 'u:dev:MediaWiki:Preact.js', 'u:dev:MediaWiki:Highlight-js.js' ]		}	]);	requestIdleCallback(loaded); } /*@end@*/