Benutzer:Schnark/js/fliegelflagel.js/load.js

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.

  • Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
  • Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
  • Internet Explorer/Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
  • Opera: Strg+F5
//<nowiki>
/*global mediaWiki*/
(function ($, mw) {
"use strict";

var hasOwn = Object.prototype.hasOwnProperty, l10n = {
//jscs:disable maximumLineLength
	en: {
		'schnark-fliegelflagel': 'Fliegelflagel',
		'tooltip-u-schnark-fliegelflagel': 'Manage user scripts',
		'schnark-fliegelflagel-version-update-console': 'Fliegelflagel: You should update the code to load Fliegelflagel, see https://de.wikipedia.org/wiki/Benutzer:Schnark/js/fliegelflagel for the current version.'
	},
	de: {
		'tooltip-u-schnark-fliegelflagel': 'Benutzerskripte verwalten',
		'schnark-fliegelflagel-version-update-console': 'Fliegelflagel: Du solltest den Code aktualisieren, mit dem du Fliegelflagel lädst, siehe https://de.wikipedia.org/wiki/Benutzer:Schnark/js/fliegelflagel für die aktuelle Version.'
	}
//jscs:enable maximumLineLength
}, didRunVePlugins, $errorIndicator,
loadUrl = document.currentScript && document.currentScript.src;

function getUserOption (id, def) {
	return String(mw.user.options.get('userjs-' + id, def));
}

function initL10N (l10n) {
	var i, chain = mw.language.getFallbackLanguageChain();
	for (i = chain.length - 1; i >= 0; i--) {
		if (chain[i] in l10n) {
			mw.messages.set(l10n[chain[i]]);
		}
	}
}

function showErrors (errors) {
	if (!$errorIndicator) {
		$errorIndicator = $('<li>')
			.text('!').css({color: '#d33', fontWeight: 'bold'})
			.appendTo('#p-personal ul');
	}
	$errorIndicator.attr('title', errors.join('\n'));
}

function initErrorHandler () {
	var mwLogWarn = mw.log.warn, errors = [];
	mw.log.warn = function (msg) {
		mwLogWarn(msg);
		errors.push(msg);
		showErrors(errors);
	};
	$(window).on('error', function (e) {
		try {
			mw.log.warn('Error: ' + e.originalEvent.message);
		} catch (e) {
			window.alert('Fliegelflagel: Error while reporting an error!');
		}
	});
}

//Loader
function Loader (debugProtocol, debug) {
	this.cache = {};
	this.debug = debug || {};
	this.debugProtocol = debugProtocol;
	this.initDebug();
}

Loader.interwiki = function (base, title) {
	var m, lang, project, code,
		mono = {
			c: 'commons.wikimedia',
			commons: 'commons.wikimedia',
			d: 'www.wikidata',
			m: 'meta.wikimedia',
			meta: 'meta.wikimedia',
			mw: 'www.mediawiki'
		}, iwmap = {
			b: 'wikibooks',
			n: 'wikinews',
			q: 'wikiquote',
			s: 'wikisource',
			v: 'wikiversity',
			voy: 'wikivoyage',
			w: 'wikipedia',
			wikt: 'wiktionary'
		};
	m = /^https:\/\/([a-z\-]+\.[a-z]+)\.org\/w\/index\.php$/.exec(base);
	if (!m) {
		return [base, title];
	}
	base = m[1];
	m = base.split('.');
	lang = m[0];
	project = m[1];
	for (m in mono) {
		if (mono[m] === base) {
			lang = 'en';
			project = m;
			break;
		}
	}
	m = title.split(':');
	if (m[0] === '') {
		m.shift();
	}
	while (true) {
		if (m.length === 1 || !(/^[a-z\-]+$/.test(m[0]))) {
			break;
		}
		code = m.shift();
		if (code in mono) {
			lang = 'en';
			project = code;
		} else if (code in iwmap) {
			if (iwmap[code] === project) {
				lang = 'en';
			} else {
				project = iwmap[code];
			}
		} else {
			lang = code;
			if (project in mono) {
				project = 'wikipedia';
			}
		}
	}
	title = m.join(':');
	if (project in mono) {
		base = mono[project];
	} else {
		base = lang + '.' + project;
	}
	return ['https://' + base + '.org/w/index.php', title];
};

Loader.prototype.initDebug = function () {
	var debug = this.debug, proto = this.debugProtocol, script;
	for (script in debug) {
		if (hasOwn.call(debug, script)) {
			mw.log.warn('Debug: ' + script);
		}
	}
	$.ajaxPrefilter(function (o, opts) {
		if (opts.url && opts.url.indexOf(proto + ':') === 0) {
			return proto;
		}
	});
	$.ajaxTransport(proto, function (o) {
		var script = o.url.slice(proto.length + 1);
		o.dataTypes = o.dataTypes.filter(function (dt) {
			return dt !== proto;
		});
		if (script !== '') {
			script = debug[script] || '';
		}
		return {
			send: function (h, callback) {
				callback(200, 'success', {text: script});
			},
			abort: $.noop
		};
	});
};

Loader.prototype.getUrl = function (script, base) {
	var ret;
	if (mw.config.get('wgAction') === 'submit' && script.replace(/[\s_]+/g, '_') === mw.config.get('wgPageName')) {
		//TODO Benutzerin: <-> Benutzer: etc.
		return this.debugProtocol + ':';
	}
	if (script in this.debug) {
		return this.debugProtocol + ':' + script;
	}
	if (!(/^(?:https?:)?\/\//).test(script)) {
		ret = Loader.interwiki(base, script);
		script = ret[0] + '?title=' +
			encodeURIComponent(ret[1].replace(/[\s_]+/g, '_'))
				//siehe mw.util.wikiUrlencode
				.replace(/'/g, '%27')
				.replace(/%3B/g, ';')
				.replace(/%40/g, '@')
				.replace(/%24/g, '$')
				.replace(/%2C/g, ',')
				.replace(/%2F/g, '/')
				.replace(/%3A/g, ':') +
			'&action=raw&ctype=text/javascript';
	}
	return script;
};

Loader.prototype.loadUrl = function (url, reload) {
	if (reload || !(url in this.cache)) {
		this.cache[url] = $.ajax({
			url: url,
			dataType: 'script',
			crossDomain: true, //<script> statt eval
			cache: true, //jQuery hat für script als Standardwert hier false
			async: true
		});
	}
	return this.cache[url];
};

Loader.prototype.loadUrls = function (urls, reload) {
	var d = this.loadUrl(urls[0], reload), i;

	function makeNext (i, that) {
		return function () {
			return that.loadUrl(urls[i], reload);
		};
	}

	for (i = 1; i < urls.length; i++) {
		d = d.then(makeNext(i, this));
	}

	return d;
};

//Script
function Script (id, data, collection) {
	this.id = id;
	this.data = data;
	this.collection = collection;
}

Script.getUserOption = getUserOption;

Script.prototype.extend = function (data) {
	$.extend(this.data, data);
};

Script.prototype.addConfig = function (callback) {
	this.configCallback = callback;
};

Script.prototype.getScripts = function () {
	var scripts = this.data.scripts;
	if (!Array.isArray(scripts)) {
		scripts = [scripts];
	}
	return scripts.map(function (script) {
		return script.replace(/^\[+|\]+$/g, '');
	});
};

Script.prototype.getUrls = function () {
	return this.getScripts().map(function (script) {
		return this.collection.loader.getUrl(script, this.data.base);
	}, this);
};

Script.prototype.runBefore = function () {
	var before = this.data.before;
	if (typeof before === 'function') {
		before = before();
	}
	if (before) {
		return this.collection.loader.loadUrl(before);
	} else {
		return $.Deferred().resolve().promise();
	}
};

Script.prototype.runAfter = function (data) {
	if (this.data.after) {
		this.data.after(data);
	}
	if (this.configCallback) {
		try {
			this.configCallback(data);
		} catch (e) {
			mw.log.warn(e);
		}
	}
};

Script.prototype.load = function (reload) {
	var urls = this.getUrls(), didRunAfter = false, d = $.Deferred().resolve();
	mw.hook('userjs.load-script.' + this.id).add(function (data) {
		didRunAfter = true;
		this.runAfter(data);
	}.bind(this));
	if (this.data.readyWait) {
		d = $.Deferred();
		mw.hook('userjs.script-ready.' + this.id).add(d.resolve);
	}
	return this.runBefore().then(function () {
		return this.collection.loader.loadUrls(urls, reload).then(function () {
			if (!didRunAfter) {
				this.runAfter();
			}
			return d;
		}.bind(this));
	}.bind(this));
};

Script.prototype.loadAsVePlugin = function (reload) {
	this.collection.addVePlugin(function () {
		return this.load(reload);
	}.bind(this));
};

Script.prototype.checkExists = function (global) {
	var wiki = mw.config.get('wgDBname');
	return !this.data.wiki ||
			wiki === 'my_wiki' ||
			(!global && this.data.wiki.indexOf(wiki) > -1) ||
			(global && this.data.wiki.length > 1);
};

Script.prototype.getEnabledKey = function (profile) {
	var key = 'schnark-fliegelflagel-' + this.id + '-enabled';
	if (profile) {
		key += '-' + profile;
	}
	return key;
};

Script.prototype.getUserEnabled = function (profile, def) {
	return Script.getUserOption(this.getEnabledKey(profile), def);
};

Script.prototype.checkDefaultEnabled = function (profile) {
	/*jshint bitwise: false*/
	if (this.data.defaultEnabled === true) {
		return true;
	}
	if (profile) {
		return this.checkEnabled();
	}
	return !!((this.data.defaultEnabled || 0) & Number(Script.getUserOption('schnark-fliegelflagel-defaultEnabled', '3')));
};

Script.prototype.checkEnabled = function (profile) {
	return this.getUserEnabled(profile, this.checkDefaultEnabled(profile) ? '1' : '0') === '1';
};

Script.prototype.checkNeeded = function (data) {
	return !this.data.only || this.data.only(data.ns, data.action);
};

Script.prototype.run = function (data) {
	var needed, reRun = true, done;
	if (!this.checkExists()) {
		return false;
	}
	if (!this.checkEnabled(data.profile)) {
		return false;
	}
	needed = data.force || this.checkNeeded(data);
	if (needed && this.data.type !== 've') {
		done = this.load();
		reRun = false;
	}
	if (this.data.type === 'hybrid' || (needed && this.data.type === 've')) {
		this.loadAsVePlugin(this.data.type === 'hybrid');
		reRun = false;
	}
	return reRun || done;
};

//Collection
function Collection (base, debug) {
	this.list = [];
	this.scripts = {};
	this.checkRun = {};
	this.vePlugins = [];
	this.base = base || (mw.config.get('wgServer') + mw.config.get('wgScript'));
	this.loader = new Loader('fliegelflagel-debug', debug);
	this.order = 0;
}

Collection.prototype.getOrder = function () {
	this.order++;
	return this.order;
};

Collection.prototype.getById = function (id) {
	if (this.list.indexOf(id) === -1) {
		mw.log.warn('Fliegelflagel: Unknown ID "' + id + '"!');
		return;
	}
	return this.scripts[id];
};

Collection.prototype.bulkWork = function (data, callback) {
	var id;
	for (id in data) {
		if (hasOwn.call(data, id)) {
			callback.call(this, id, data[id]);
		}
	}
};

Collection.prototype.forAll = function (callback, order) {
	var i, id, scripts = this.scripts, list = this.list.slice();
	if (order) {
		list.sort(function (a, b) {
			return (scripts[a].data.order || Infinity) - (scripts[b].data.order || Infinity);
		});
	}
	for (i = 0; i < list.length; i++) {
		id = list[i];
		callback.call(this, scripts[id], id);
	}
};

Collection.prototype.addScript = function (id, data) {
	if (this.list.indexOf(id) > -1) {
		mw.log.warn('Fliegelflagel: Duplicate ID "' + id + '"!');
		return;
	}
	if (!(/^[a-zA-Z0-9_\-]{1,200}$/.test(id))) {
		mw.log.warn('Fliegelflagel: Invalid ID "' + id + '"!');
		return;
	}
	if (typeof data === 'string') {
		data = {
			scripts: data,
			defaultEnabled: true
		};
	}
	this.list.push(id);
	this.checkRun[id] = true;
	this.scripts[id] = new Script(id, $.extend({base: this.base}, data), this);
};

Collection.prototype.extendScript = function (id, data) {
	id = this.getById(id);
	if (id) {
		id.extend($.extend({order: this.getOrder()}, data));
	}
};

Collection.prototype.configureScript = function (id, config) {
	id = this.getById(id);
	if (id) {
		id.addConfig(config);
	}
};

Collection.prototype.bulkAdd = function (data) {
	this.bulkWork(data, this.addScript);
};

Collection.prototype.bulkExtend = function (data) {
	this.bulkWork(data, this.extendScript);
};

Collection.prototype.bulkConfigure = function (data) {
	this.bulkWork(data, this.configureScript);
};

Collection.prototype.run = function (data) {
	var done = [], result;
	this.forAll(function (script, id) {
		if (this.checkRun[id]) {
			result = script.run(data);
			if (typeof result !== 'boolean') {
				done.push(result);
				result = false;
			}
			this.checkRun[id] = result;
		}
	});
	return $.when.apply($, done);
};

Collection.prototype.addVePlugin = function (load) {
	this.vePlugins.push(load);
};

Collection.prototype.loadVePlugins = function () {
	var plugins = this.vePlugins.slice();
	this.vePlugins = [];
	return $.when.apply($, plugins.map(function (load) {
		return load();
	}));
};

//init
function addNavLink () {
	$.when(mw.loader.using(['mediawiki.util', 'mediawiki.language']), $.ready).then(function () {
		mw.util.addPortletLink(
			'p-personal',
			mw.util.getUrl('Special:Fliegelflagel'),
			mw.msg('schnark-fliegelflagel'),
			'pt-fliegelflagel',
			mw.msg('tooltip-u-schnark-fliegelflagel'),
			null, //access key
			'#pt-watchlist'
		);
	});
}

function getProfile (profile) {
	var possibleProfiles, i, key, temp, ua;
	possibleProfiles = {
		mobile: /mobile|tablet|ip(ad|hone|od)|android/i,
		old: /Mozilla\/[0-4]|Trident|Presto|(?:Chrome|Firefox)\/[12]?\d\.|AppleWebKit\/[1-5]?\d?\d\./
	};
	if (typeof profile === 'function') {
		profile = profile();
	}
	if (profile === undefined) {
		profile = ['mobile'];
	}
	if (Array.isArray(profile)) {
		temp = {};
		for (i = 0; i < profile.length; i++) {
			key = profile[i];
			if (hasOwn.call(possibleProfiles, key)) {
				temp[key] = possibleProfiles[key];
			}
		}
		profile = temp;
	}
	if ($.isPlainObject(profile)) {
		ua = navigator.userAgent;
		for (key in profile) {
			if (hasOwn.call(profile, key) && profile[key].test(ua)) {
				profile = key;
				break;
			}
		}
		profile = '';
	}
	if (typeof profile !== 'string') {
		mw.log.warn('Fliegelflagel: Wrong type for profile!');
		return '';
	}
	if (!(/^[a-zA-Z0-9_\-]{0,20}$/.test(profile))) {
		mw.log.warn('Fliegelflagel: Wrong format for profile!');
		return '';
	}
	return profile;
}

function getAction () {
	var m;
	switch (mw.config.get('wgAction')) {
	case 'submit': return 'edit';
	case 'historysubmit': return 'diff';
	case 'view':
		if (location.search.search(/[?&]diff=/) > -1) {
			return 'diff';
		}
		/* falls through */
	case 'nosuchaction':
		m = (/^[^#]*[&?]action=([^&#]*)/).exec(document.location.href);
		if (m && m.length > 1) {
			return decodeURIComponent(m[1].replace(/\+/g, '%20'));
		}
		/* falls through */
	default: return mw.config.get('wgAction') || 'view';
	}
}

function checkClearCache () {
	if (mw.config.get('wgNamespaceNumber') !== -1 || mw.config.get('wgTitle') !== 'FliegelflagelPurge') {
		return false;
	}
	if (window.name === 'cache-cleared') {
		window.name = '';
		return false;
	}
	window.name = 'cache-cleared';
	mw.hook('userjs.script-ready.fliegelflagel').add(function () {
		window.location.reload(true);
	});
	return true;
}

function init (internal, user, debug) {
	var data, collection = new Collection(internal.base, debug);
	data = {
		ns: mw.config.get('wgNamespaceNumber'),
		action: getAction(),
		profile: getProfile(user.profile),
		force: checkClearCache()
	};
	mw.loader.using(['mediawiki.language']).then(function () {
		initL10N(l10n);
		if (user.warnVersionUpdate) {
			mw.log.warn(mw.msg('schnark-fliegelflagel-version-update-console'));
		}
	});
	if (getUserOption('schnark-fliegelflagel-manage-nolink', '0') === '0') {
		addNavLink();
	}
	collection.removableOptions = internal.removableOptions;
	collection.bulkAdd(internal.modules);
	collection.bulkAdd(user.additional || {});
	collection.bulkConfigure(user.config || {});
	mw.hook('userjs.schnark-fliegelflagel.l10n').add(function (data) {
		mw.loader.using('mediawiki.language').then(function () {
			initL10N(data);
		});
	});
	mw.hook('userjs.schnark-fliegelflagel.configdefine').add(function (data) {
		collection.bulkExtend(data.scripts || data);
		//TODO bisherige Daten .scripts verschieben,
		//.removableOptions und .suggestedOptions dort speichern und hier auslesen
	});
	mw.hook('userjs.schnark-fliegelflagel.configextend').add(function (extend) {
		var modules = mw.loader.using([
			'mediawiki.language', 'mediawiki.util', 'mediawiki.jqueryMsg',
			'oojs-ui-core', 'oojs-ui-widgets', 'oojs-ui-windows', 'oojs-ui.styles.icons-alerts'
		]), data;
		if (user.global) {
			data = mw.loader.using('mediawiki.api').then(function () {
				var api = new mw.Api();
				return api.get({
					action: 'query',
					meta: 'globalpreferences'
				});
			}).then(function (data) {
				return data.query.globalpreferences.preferences;
			});
		} else {
			data = $.Deferred().resolve().promise();
		}
		$.when(data, modules).then(function (globalPrefs) {
			extend(Script, Collection);
			$(function () {
				collection.initConfigure(data.profile, globalPrefs, user.warnVersionUpdate);
			});
		});
	});
	collection.run(data).then(function () {
		mw.hook('userjs.script-ready.fliegelflagel').fire();
	});
	mw.hook('userjs.schnark-fliegelflagel.ve').add(function (d) {
		didRunVePlugins = true;
		collection.loadVePlugins().then(d.resolve);
		setTimeout(d.resolve, 10000); //nach 10 Sekunden aufgeben und VE auch ohne Plugins laden
	});
	mw.hook('ve.activationComplete').add(function () {
		data.action = 've';
		collection.run(data);
	});
	mw.hook('ve.deactivationComplete').add(function () {
		data.action = 'view';
		collection.run(data);
	});
}

function getPromise (name) {
	var d = $.Deferred();
	mw.hook('userjs.schnark-fliegelflagel.' + name).add(d.resolve);
	return d.promise();
}

function checkDisabled (key) {
	try {
		if (localStorage[key]) {
			mw.log.warn(
				'Fliegelflagel is disabled. ' +
				'To enable again, execute `delete localStorage[\'' + key + '\']`.'
			);
			return true;
		}
	} catch (e) {
	}
	try {
		if (sessionStorage[key]) {
			mw.log.warn(
				'Fliegelflagel is disabled in this tab. ' +
				'To enable again, execute `delete sessionStorage[\'' + key + '\']`.'
			);
			return true;
		}
	} catch (e) {
	}
}

function validateUserConfig (user, defineUrl, debug) {
	var versionUpdater = {
			1.0: function () {
				user.version = 1.1;
				if ($('html.ve-available').length) { //only warn when VE is available, there are no benefits otherwise
					user.warnVersionUpdate = true;
				}
			},
			1.1: true
		}, urls = {
			//jscs:disable maximumLineLength
			define: 'https://de.wikipedia.org/w/index.php?title=Benutzer:Schnark/js/fliegelflagel.js/define.js&action=raw&ctype=text/javascript',
			load: 'https://de.wikipedia.org/w/index.php?title=Benutzer:Schnark/js/fliegelflagel.js/load.js&action=raw&ctype=text/javascript'
			//jscs:enable maximumLineLength
		};
	mw.hook('ve.activationComplete').add(function () {
		if (!didRunVePlugins) {
			mw.log.warn('Fliegelflagel: VE activated without plugins!');
		}
	});
	while (user.version in versionUpdater && versionUpdater[user.version] !== true) {
		versionUpdater[user.version]();
	}
	if (!versionUpdater[user.version]) {
		mw.log.warn('Fliegelflagel: Wrong version!');
		return;
	}
	if (user.global && !debug) { //TODO
		mw.log.warn('user.global isn\'t ready yet');
		return;
	}
	if (loadUrl) {
		if (loadUrl !== urls.load || defineUrl !== urls.define) {
			user.warnVersionUpdate = true;
		}
	}
	return user;
}

$.when(getPromise('define'), getPromise('userdefine'), mw.loader.using('user.options')).then(function (internal, user) {
	var debug;
	if (checkDisabled('fliegelflagel-disable')) {
		return;
	}
	if (getUserOption('schnark-fliegelflagel-manage-debug', '0') === '1') {
		initErrorHandler();
		try {
			debug = JSON.parse(sessionStorage['fliegelflagel-debug']);
		} catch (e) {
		}
	}
	user = validateUserConfig(user || {}, internal.defineUrl, debug);
	if (user) {
		init(internal, user, debug);
	}
});

})(jQuery, mediaWiki);
//</nowiki>