User:Saftzie/global.js

ShoutWiki — express yourself and be heard!
Jump to navigation Jump to search

Note: After saving, 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: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
(function ($) {
	'use strict';

	var
		global;

	// short-circuit running this file, Wikia-style, usually for debugging
	if (/(?:\?|&)useuserjs=0(?:&|$)/.test(location.search)) {
		throw 'Exiting on useuserjs=0';
	}

	// define load states for global/common/skin js
	(window.user = window.user || {}).state = user.state || {};
	// global.js
	global = user.state.global = user.state.global || {
		last: null,
		defer: $.Deferred()
	};
	if (global.last === 'global') {
		throw 'Exiting on repeat load'; // prevent repeat loading
	}
	global.last = 'global';
}(jQuery));

//----------------------------------------------------------------------
// define user.util module
(window.user = window.user || {}).util = user.util || (function () {
	'use strict';

	var
		self = {
			// drop-in for mw.util.getParamValue
			getParamValue: function (key) {
				if (typeof key === 'string') {
					return (query[key] !== undefined) ? query[key] : null;
				} else {
					throw new TypeError('key is not a string');
				}
			}
		},
		query, s, t, i;

	// init getParamValue
	query = {};
	s = location.search.substr(1).split('&');
	for ( i = 0 ; i < s.length ; ++i ) {
		t = s[i].split('=');
		if (t[1]) {
			query[t[0]] = decodeURIComponent(t[1].replace(/\+/g, ' '));
		}
	}
	return self;
}());
//----------------------------------------------------------------------
/**
 * Description:
 * Load scripts/stylesheets from wiki articles
 *
 * Version 1.0: 15 May 2015
 *   Original version for Wikipedia use
 * Version 2.0: 6 Jul 2016
 *   Rewrite with jQuery.Deferred; Implement using
 * Version 2.1: 5 Sep 2016
 *   ArticlePath/ScriptPath switch; Additional logging
 */
(window.user = window.user || {}).loader = user.loader || (function (mw, $) {
	'use strict';

	var
		self = {
			message: new Date().toISOString() + ' Initializing',
			version: '2.1: 5 Sep 2016',
			useCanPath: false, // canonical vs "pretty" article path
			load: load,
			using: using
		},
		// use "pretty" path so .css names show in style inspector
		urlBase1 = mw.config.get('wgArticlePath') +
			'?action=raw&ctype=$2',
		// use canonical path when, e.g., the web server blocks pretty paths
		// like WikiMedia blocks action=raw for any title with "." in it
		urlBase2 = mw.config.get('wgScriptPath') +
				'/index.php?title=$1&action=raw&ctype=$2',
		wikiDomain = mw.config.get('wgServerName').split('.'),
		wikiSubdomain = [],
		registry = {},
		useCanPath; // instance version of self.useCanPath

	// get a single title, either JavaScript or CSS
	// return a promise, possibly used by $.when
	function getTitle(title, wiki) {
		var
			type, url, t;

		if (/css$/.test(title)) {
			type = 'text/css';
		} else if (/js$/.test(title)) {
			type = 'text/javascript';
		} else {
			self.message += '\n' + new Date().toISOString() +
				' Invalid type: "' + title + '" must be css or js';
			return $.Deferred().reject().promise();
		}
		if (useCanPath) {
			// "wiki" encode the article title, skips encoding some characters
			url = urlBase2
				.replace('$2', type)
				.replace('$1', title.replace(/\s/g, '_')
					.replace(/[^$,/:;@]/g, function (c) {
						return encodeURIComponent(c);
					})
				);
		} else {
			url = urlBase1
				.replace('$2', type)
				.replace('$1', encodeURI(title.replace(/\s/g, '_'))
					.replace(/\?/g, '%3F') // title "?" must be encoded
				);
		}
		if (wiki !== wikiSubdomain) {
			url = '//' + wiki + wikiDomain + url;
		}
		switch (type) {
		case 'text/css':
			t = document.createElement('link');
			t.rel = 'stylesheet';
			t.href = url;
			$('head').append(t);
			t = $.Deferred().resolve().promise();
			self.message += '\n' + new Date().toISOString() +
				' getTitle :: Added link element: ' + title;
			break;
		case 'text/javascript':
			t = $.ajax(url, {
				cache: true,
				crossDomain: true,
				dataType: 'script'
			}).promise();
			self.message += '\n' + new Date().toISOString() +
				' getTitle :: loading script: ' + title;
			t.done(function () {
				self.message += '\n' + new Date().toISOString() +
					' jQuery :: load succeeded: ' + title;
			}).fail(function () {
				self.message += '\n' + new Date().toISOString() +
					' jQuery :: load failed: ' + title;
			});
			break;
		}
		return t;
	}

	// load wiki articles as scripts or styles
	// articles = string or array of strings
	//   which are titles of articles to load
	// JavaScript articles terminate with js
	// stylesheet articles terminate with css
	// titles can be prepended with the form "u:<host>:"
	//   to designate a different host in the same domain
	// path (optional, default: self.useCanPath) = Boolean
	//   to use canonical or "pretty" article path
	// return an array of promises
	function load(articles, path) {
		var
			d = [], // the array of promises, one element per title
			wiki, title, t, i;

		// enforce Boolean condition on self.useCanPath
		if ((self.useCanPath !== true) &&
			(self.useCanPath !== false)) {
			self.useCanPath = false;
		}
		// enforce Boolean condition on path argument
		if ((path === true) || (path === false)) {
			useCanPath = path;
		} else {
			useCanPath = self.useCanPath;
		}
		if (typeof articles === 'string') {
			articles = [articles];
		} else if (!$.isArray(articles)) {
			self.message += '\n' + new Date().toISOString() +
				' load :: articles is not an array: ' + typeof articles;
			return [$.Deferred().reject().promise()];
		}
		for ( i = 0; i < articles.length; ++i ) {
			title = articles[i];
			if (typeof title === 'string' ) {
				t = title.split(':');
				if ((t.length > 2) && (t[0] === 'u')) {
					wiki = t[1];
					title = t.splice(2).join(':');
				} else {
					wiki = wikiSubdomain;
				}
				t = wiki + ':' + title;
				if (registry[t]) {
					self.message += '\n' + new Date().toISOString() +
						' load :: already registered: ' + title;
				} else {
					registry[t] = getTitle(title, wiki);
				}
				d[i] = registry[t];
			} else {
				self.message += '\n' + new Date().toISOString() +
					' load :: title ' + i + ' is not a string: ' + typeof title;
				d[i] = $.Deferred().reject().promise();
			}
		}
		return d;
	}

	// load wiki articles as scripts or styles
	//   on the condition that the prerequisites are loaded
	// prereqs = string or array of strings
	//   which are titles to load before articles
	// articles = string or array of strings
	//   which are titles of articles to load
	// path (optional, default: self.useCanPath) = Boolean
	//   to use canonical or "pretty" article path
	// return a promise
	function using(prereqs, articles, path) {
		if (typeof prereqs === 'string') {
			prereqs = [prereqs];
		} else if (!$.isArray(prereqs)) {
			self.message += '\n' + new Date().toISOString() +
				' using :: prereqs is not an array: ' + typeof prereqs;
			return $.Deferred().reject().promise();
		}
		if (typeof articles === 'string') {
			articles = [articles];
		} else if (!$.isArray(articles)) {
			self.message += '\n' + new Date().toISOString() +
				' using :: articles is not an array: ' + typeof articles;
			return $.Deferred().reject().promise();
		}
		// load the articles when the prereqs complete
		// when() wants a list of individual arguments, not an array, so apply it
		return $.when.apply(null, load(prereqs, path)).done(function () {
			// when() resolves when the prereqs are loaded, not when they've run
			//   so enqueue load(articles) to give prereqs a chance to run
			setTimeout(load, 0, articles, path);
		}).promise();
	}

	// break the current qualified name
	//   into subdomain and domain names
	while (wikiDomain.length > 2) {
		wikiSubdomain.push(wikiDomain.shift());
	}
	wikiSubdomain = wikiSubdomain.join('.');
	wikiDomain = '.' + wikiDomain.join('.');
	self.message += '\n' + new Date().toISOString() +
		' subdomain = ' + wikiSubdomain +
		', domain = ' + wikiDomain;
	return self;
}(mediaWiki, jQuery));
//----------------------------------------------------------------------
// main procedure
(function (mw, $) {
	'use strict';

	var
		wgCanonicalSpecialPageName = mw.config.get('wgCanonicalSpecialPageName'),
		wgNamespaceNumber = mw.config.get('wgNamespaceNumber'),
		blankSpecial = user.util.getParamValue('blankspecial'),
		idDiv = user.util.getParamValue('divid'),
		js = user.util.getParamValue('js'),
		articles = [];

	// add a div to Special:BlankPage to make other stuff easier
	if ((wgCanonicalSpecialPageName === 'Blankpage') &&
		(blankSpecial === 'userjs')) {
		if (idDiv !== null) {
			$('#mw-content-text').empty().append('<div id="' + idDiv + '"></div>');
		}
		if (js !== null) {
			user.loader.load([js]);
			// only MediaWiki-supplied skins supported
			$('#firstHeading').text(js);
		}
	}

	articles.push('u:www:User:Saftzie/ui.wikimarks.css');  // [[User:Saftzie/ui.wikimarks.css]]
	articles.push('u:www:User:Saftzie/ui.wikimarks.js');   // [[User:Saftzie/ui.wikimarks.js]]
	articles.push('u:www:User:Saftzie/ui.clock.css');      // [[User:Saftzie/ui.clock.css]]
	articles.push('u:www:User:Saftzie/ui.clock.js');       // [[User:Saftzie/ui.clock.js]]
	if ((wgNamespaceNumber === 2) || (wgNamespaceNumber === 3) ||
		(wgCanonicalSpecialPageName === 'Contributions')) {
		articles.push('u:www:User:Saftzie/UserInfo.js'); // [[User:Saftzie/UserInfo.js]]
	}
	if ($('#wpTextbox1').length) {
		articles.push('u:www:User:Saftzie/ui.tabinsert.js'); // [[User:Saftzie/ui.tabinsert.js]]
	}
	if (articles.length) {
		user.loader.load(articles);
	}

	// enforce global -> common -> skin js serialization
	user.state.global.defer.resolve(); // common.js is next
}(mediaWiki, jQuery));