User:Arashiryuu0/global.js

/** * Testing out UCP global JS. * @namespace UCPGlobal */ /*	jshint undef: true, noarg: true, devel: true, jquery: true, strict: global, eqeqeq: true, freeze: true, newcap: true, esnext: true, browser: true, latedef: true, shadow: outer, varstmt: true, laxbreak: true, quotmark: single, singleGroups: true, futurehostile: true /*	globals mw, Symbol, Promise, requestIdleCallback 'use strict'; window.CodeblockLinesConfig = { ready: false }; __main__: { if (window.UCP && window.UCP.globalJS) break __main__; const useRef = function (v) { return { current: v }; };	const raf = requestAnimationFrame; const has = Object.prototype.hasOwnProperty; const slice = Array.prototype.slice; const toString = Object.prototype.toString; const pages = ['js', 'ts', 'cpp', 'css', 'json', 'javascript']; const levels = ['log', 'info', 'warn', 'debug', 'error']; const extension = window.location.pathname.split('.').pop; const _Object = function (value) { const obj = Object.create(null); Object.defineProperty(obj, Symbol.toStringTag, { value: value }); return obj; };	const Logger = _Object('Logger'); const Utils = _Object('Tools'); const applyBinds = function (n) { const counter = useRef(0); const fns = Object.getOwnPropertyNames(n).filter(function (method) {			return typeof n[method] === 'function';		}); while (counter.current < fns.length) { const f = fns[counter.current++]; n[f] = n[f].bind(n); }	};	const debounce = function (callback, delay, immediate) { const t = useRef(0); return function { const context = this; const args = arguments; const later = function { t.current = 0; if (!immediate) callback.apply(context, args); };			const now = immediate && t.current === 0; clearTimeout(t.current); t.current = setTimeout(later, delay); if (now) callback.apply(context, args); };	};	const throttle = function (callback, delay) { const last = useRef(0); return function { const now = Date.now; if (now - last.current >= delay) { last.current = now; callback.apply(this, arguments); }		};	};	__logger__: { const counter = useRef(0); const getParts = function (name) { return [ '%c[' + name + ']%c \u2014 %s', 'color: #C3E88D;', '',				new Date.toUTCString ];		};		const getLevel = function (level) { return levels.includes(level) ? level : 'log'; };		const _log = function (props) { if (toString.call(props) !== '[object Object]') return null; const name = 'Logger|' + getLevel(props.type); return function { console.groupCollapsed.apply(null, getParts(name)); console[props.type].apply(null, arguments); console.groupEnd; };		};		while (counter.current < levels.length) { const level = levels[counter.current++]; Logger[level] = _log({ type: level }); }		applyBinds(Logger); Object.freeze(Logger); }	const preloads = useRef(2); const isNil = function (n) { return n === undefined || n === null; };	const create = function (type, props) { if (typeof type !== 'string') type = 'div'; const e = document.createElement(type); const children = slice.call(arguments, 2); if (isNil(props)) { if (children.length) e.append.apply(e, children); return e;		} if (!has.call(props, 'children') && children.length) { e.append.apply(e, children); }		const isEvent = function (key) { return key.slice(0, 2) === 'on' && key[2] === key[2].toUpperCase; };		const isDataAttr = function (key) { return key.startsWith('data') && key.toLowerCase !== key; };		const normalize = function (name) { return name === 'doubleclick' ? 'dblclick' : name; };		const normalizeDataAttr = function (name) { return name.replace(/([A-Z]){1}/g, '-$1').toLowerCase; };		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 'style': { if (typeof props[key] === 'string') { e.setAttribute(key, props[key]); break; }					try { Object.assign(e[key], props[key]); break; } catch (fail) { Logger.error(fail); }					break; }				case 'htmlFor': { e.setAttribute('for', props[key]); break; }				case 'className': props[key] = props[key].split(' '); // jshint ignore: line case 'classList': case 'classes': { if (!Array.isArray(props[key])) props[key] = [props[key]]; e.classList.add.apply(e.classList, props[key]); break; }				case 'children': { if (!Array.isArray(props[key])) props[key] = [props[key]]; e.append.apply(e, props[key]); break; }				default: { if (isEvent(key)) { const event = normalize(key.slice(2).toLowerCase); e.addEventListener(event, props[key]); break; }					if (isDataAttr(key)) { const attr = normalizeDataAttr(key); e.setAttribute(attr, props[key]); break; }					e.setAttribute(key, props[key]); break; }			}		}		e.$$props = props; return e;	}; const queryTree = function (tree, query, options) { if (isNil(options)) options = { walkable: null, ignore: [] }; if (typeof query === 'string') { if (has.call(tree, query)) return tree[query]; } else if (query(tree)) { return tree; }		if (typeof tree !== 'object' || tree === null) return null; const length = useRef(0); const counter = useRef(0); const ret = useRef(null); if (Array.isArray(tree)) { for (counter.current = 0, length.current = tree.length; counter.current < length.current; counter.current++) { const value = tree[counter.current]; ret.current = queryTree(value, query, options); if (!isNil(ret.current)) return ret.current; }		} else { const walkable = options.walkable === null ? Object.keys(tree) : options.walkable; for (counter.current = 0, length.current = walkable.length; counter.current < length.current; counter.current++) { const key = walkable[counter.current]; if (!has.call(tree, key) || options.ignore.includes(key)) continue; ret.current = queryTree(tree[key], query, options); if (!isNil(ret.current)) return ret.current; }		}		return ret.current; };	const getFromHook = function (hookName) { if (typeof hookName !== 'string') return Promise.reject('Invalid hook name, hook names must be strings.'); return new Promise(function (resolve) {			mw.hook(hookName).add(resolve);		}); };	const getHookValues = function { const args = slice.call(arguments); if (!args.length) return []; return args.map(getFromHook); };	const run = function { const myHook = mw.hook('window.ready'); __hljs__: { if (window.hljs) break __hljs__; [				'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/atom-one-dark.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/highlight.min.js' ].forEach(function (url, index) {				const isCss = index === 0;				const type = isCss					? 'text'					: 'script';				$.get(url, void 0, $.noop, type).then(function { if (isCss) { const link = create('link', {							href: url,							rel: 'stylesheet'						}); document.head.appendChild(link); } else { mw.hook('ext.hljs').fire(window.hljs); }					--preloads.current; }, Logger.error);			}); }		/**		 * Change source editor theme. */		__ace__: { const isAcePage = ['edit', 'submit'].includes(mw.config.get('wgAction')); if (!isAcePage || !pages.includes(extension)) break __ace__; if (!window.ace) { const modules = [ 'ext.codeEditor.ace', 'ext.codeEditor.ace.modes' ];				mw.loader.using(modules, function (req) {					req(modules[0]);					req(modules[1]);				}, Logger.error) .then(function {					window.ace.config.set('basePath', '/extensions-ucp/mw139/CodeEditor/modules/ace/');				}, Logger.error); }			mw.hook('codeEditor.configure').add(function (session) {				const editor = window.ace.edit(document.querySelector('.ace_editor'));				raf(function { editor.setOptions({						theme: 'ace/theme/tomorrow_night',						tabSize: 4,						fontSize: 13,						showGutter: true,						fontFamily: 'Cascadia Code PL SemiLight',						useSoftTabs: false,						printMargin: false,						placeholder: '你已经死了',						showInvisibles: false,						showLineNumbers: true,						showFoldWidgets: true,						showPrintMargin: false,						indentedSoftWrap: true,						fixedWidthGutter: true,						navigateWithinSoftTabs: true,						enableLiveAutocompletion: true,						autoScrollEditorIntoView: true					}); if (window.ace.config.$modes['ace/mode/css']) { const rules = [ 'ids', 'important', 'empty-rules', 'regex-selectors', 'known-properties', 'adjoining-classes', 'universal-selector', 'order-alphabetical', 'bulletproof-font-face', 'overqualified-elements' ];						session.$worker.call('setDisabledRules', [							rules.join('|')						]); }				});			});		}		/**		 * Codeblock theme buttons */		__themes__: { if (!pages.includes(extension)) break __themes__; const base = 'https://dev.fandom.com/wiki/MediaWiki:Highlight-js/styles/'; const themes = [ 'atom-one-dark', 'tomorrow-night', 'solarized-dark', 'monokai-sublime', 'dracula', 'vs2015', 'nord' ];			const buttons = []; const container = create('div'); container.setAttribute('id', 'code-button-container'); container.setAttribute('style', 'display: block; padding: 5px 0 10px 0; text-align: center;'); const handleLinks = function (links, activeTheme) { const found = links.find(function (link) {					return link.getAttribute('href').includes(activeTheme);				}); links.forEach(function (link) {					if (link.getAttribute('disabled') !== null) return;					link.setAttribute('disabled', '');				}); if (!found) { const fresh = create('link', {						href: base + activeTheme + '.css?action=raw&ctype=text/css',						rel: 'stylesheet'					}); document.head.appendChild(fresh); return; }				found.removeAttribute('disabled'); };			const setTheme = function (theme) { const links = slice.call(document.querySelectorAll('link[href*="' + base + '"]')); raf(function {					handleLinks(links, theme);				}); };			const buttonClick = function (button) { const theme = button.getAttribute('data-theme'); return function { if (!theme) return; setTheme(theme); };			};			const addButtons = function { if (buttons.length || document.contains(container)) return; const target = document.getElementById('mw-clearyourcache'); if (!target) return; themes.forEach(function (theme, idx) {					if (idx === 4) {						const n = create('hr', { style: { visibility: 'hidden', margin: '0' }						});						buttons.push(n);					}					const btn = create('button', { classes: ['wds-button'] }, theme);					btn.setAttribute('data-theme', theme);					btn.setAttribute('style', 'margin-right: 2px;');					btn.addEventListener('click', buttonClick(btn));					buttons.push(btn);				}); container.append.apply(container, buttons); target.appendChild(container); };			mw.hook('wikipage.content').add(addButtons); }		const ready = function (modules) { if (preloads.current > 0) return setTimeout(ready, 1000, modules); const toasts = modules[1]; const lines = modules[2]; Object.assign(Utils, {				'pull': window.importArticles,				'lines': lines,				'logger': Logger,				'toasts': toasts,				'debounce': debounce,				'throttle': throttle,				'queryTree': queryTree,			}); applyBinds(Utils); Object.assign(Utils.toasts, toasts); Object.freeze(Utils); myHook.fire(Utils); };		myHook.add(function (utils) {			const uri = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/atom-one-dark.min.css';			const highlightTheme = document.head.querySelector('link[href="' + uri + '"]');			const href = highlightTheme && highlightTheme.getAttribute('href');			// if (utils.lines) utils.lines.process;			if (utils.logger) utils.logger.log('Tools:', utils);			if (href) {				raf(function { const hrefN = href.replace(/atom-one-(light|dark)/, 'vs2015'); highlightTheme.setAttribute('href', hrefN); });			}			window.UCP.tools = utils;		}); const useHljs = function (deps) { const hljs = deps[0]; const lines = deps[2]; if (!hljs || !lines) return; const addIdentifier = function (element) { return function { const lang = useRef('javascript'); if (pages.includes(extension)) { lang.current = hljs.getLanguage(extension).name.toLowerCase; } else { const mwLang = slice.call(element.parentElement.classList).find(function (string) {							return string.startsWith('mw-highlight-language-');						}); if (mwLang) lang.current = mwLang.split('-').pop; }					element.classList.add(lang.current); element.setAttribute('data-lang', lang.current); element.style.setProperty('--attr', 'data-lang'); raf(lines.process); };			};			const _paint = function (element) { return function { hljs.highlightBlock(element); raf(addIdentifier(element)); };			};			const paint = function (element) { raf(_paint(element)); };			const pres = document.querySelectorAll('.mw-highlight pre'); const counter = useRef(0); while (counter.current < pres.length) { const pre = pres[counter.current++]; paint(pre); }		};		Promise .all(getHookValues('ext.hljs', 'dev.toasts', 'dev.CodeblockLineNumbers')) .then(function (deps) {				ready(deps);				useHljs(deps);			}); };	mw.hook('dev.preact').add(function (Preact) {		const h = Preact.h;		const render = Preact.render;		const unmount = Preact._preact.options.unmount; // jshint ignore: line		const useMemo = Preact.useMemo; // jshint ignore: line		const useState = Preact.useState;		const useEffect = Preact.useEffect;		const useReducer = Preact.useReducer; // jshint ignore: line		const useCallback = Preact.useCallback;		const useLayoutEffect = Preact.useLayoutEffect;		const useAnimationFrame = function (callback) {			const fn = useRef(callback);			const frame = useRef;			const animate = useCallback(function (now) { fn.current(now); frame.current = raf(animate); }, []);			useLayoutEffect(function { frame.current = raf(animate); return function { if (frame.current) cancelAnimationFrame(frame.current); };			}, []);		};		const useInterval = function (callback, delay) { // jshint ignore: line			const ref = useRef(callback);			const tick = useCallback(function { ref.current; }, []);			useLayoutEffect(function { ref.current = callback; }, [callback]);			useEffect(function { const id = setInterval(tick, delay); return function { clearInterval(id); };			}, [delay]);		};		const getClockState = function {			const date = new Date;			const lang = mw.user.options.get('language') || 'en-gb';			return {				day: date.toLocaleDateString(lang, { weekday: 'long' }),				time: date.toLocaleTimeString(lang, { hour12: false })			};		};		const Clock = function  {			const _d = useState(getClockState);			const hovered = useState(false);			const update = useCallback(throttle(function  {				_d.set(getClockState);			}, 1000), []);			useAnimationFrame(update);			return h('span', { key: 'ClockMain', className: 'clock', children: [ h('span', {						key: 'ClockDay',						className: 'clock-day',						children: _d.value.day					}), h('span', {						key: 'ClockSeparator',						className: 'clock-separator',						children: String.fromCodePoint(160)					}), h('span', {						key: 'ClockTime',						className: 'clock-time',						children: _d.value.time					}) ],				onMouseEnter: function (e) { if (hovered.value) return; e.target.classList.add('hovered'); hovered.set(true); },				onMouseLeave: function (e) { if (!hovered.value) return; e.target.classList.remove('hovered'); hovered.set(false); },				onClick: function { location.href = location.href; }			});		};		const addClock = function {			if (document.getElementById('DisplayClock')) return;			const timeRoot = create('div', { id: 'DisplayClock' });			const target = document.querySelector('.fandom-community-header__top-container + .fandom-community-header__local-navigation');			render(h(Clock), timeRoot);			target.appendChild(timeRoot);		};		addClock;	}); window.UCP = window.UCP || {}; window.UCP.globalJS = Object.assign(_Object('GlobalJS'), { version: '1.0.0' }); window.importArticles.apply(null, [		{			type: 'script',			articles: [				'u:dev:MediaWiki:Toasts.js',				'u:dev:MediaWiki:Preact.js',				'u:dev:MediaWiki:View_Source/code.js',				'u:dev:MediaWiki:CodeblockLineNumbers/code.js'			]		},		{			type: 'style',			articles: [				'u:dev:MediaWiki:CodeblockLineNumbers.css'			]		}	]); requestIdleCallback(run); } /*@end@*/