User:Web SourceContent/simpleSVGcheck.js

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
/**
* @Description:
Adds several SVG-valid-check-links on file-pages. Get the real byte-size for SVG-images. Put the template Igen prefilled in the file-description page.
* 14.07.2016 added tool-name recognition from SVG-code
For usage show [[c:Template:Image generation]]
* @Revision: 13:10, 1 July 2018 (UTC)
* @Author: [[User:Perhelion]]
* @License: CC BY-SA 3.0
* @Required modules: jquery.badge, mediawiki.api, mediawiki.util, jquery.spinner
* @TODO:
**/
// [[Category:User scripts|simpleSVGcheck]] <nowiki>
/* global jQuery, mediaWiki*/
/* eslint space-in-parens:0, indent:0, vars-on-top:0, computed-property-spacing:0, array-bracket-spacing:0, quote-props:0, one-var:0, curly:0 */
/* jshint curly:false, scripturl:true, forin:false, jquery:true*/
(function ($, mw) {
'use strict';
var gbs = [],
	ac = mw.config.get('wgAction'),
	pn = mw.config.get('wgPageName'),
	ti = mw.config.get('wgTitle'),
	isEdit = /^(edit|submit)$/.test(ac),
	err,
	// size, // For simplSVG
	userName = mw.config.get('wgUserName'), // For simplSVG
	toolName,
	$textarea,
	expeUser = /^Sarang|Perhelion$/.test(userName),
	summary = '([[User:Perhelion/simpleSVGcheck.js|Script]]) + SVG-[[Template:Image generation|Igen]]';

var cSVG = {
	version: '0.2.01',
	name: 'simpleSVGcheck',
	failStr: 'FAIL automatic insert Igen, please insert manually',
	curSize: 0,
	badSVG: 0,
	PGF: 0,
	switchTrans: 0,
	init: function () {
		// W3C-Validator check-link for every SVG (by [[User: Perhelion]] fixed also now for admins)
		var $g = $('<a>', {
			title: 'Get exact byte-size'
		}).badge('B?', 'inline', 1);
		var $igen = $('<a>', {
			title: 'Insert Template:Igen with W3-validity',
			href: '#',
			text: '→Igen?',
			onclick: 'mw.libs.simpleSVGcheck.getW3Data(event)'
		});// .click(mw.libs.simpleSVGcheck.getW3Data);
		var $SVGc = $('<a>', {
			title: 'Commons-libRSVG-Validator',
			href: '/wiki/Commons:Commons_SVG_Checker?withJS=MediaWiki:CommonsSvgChecker.js&checkSVG=' + encodeURIComponent(pn),
			text: '→SVG Checker',
			target: '_blank'
		});
		var $rows = $('#mw-imagepage-section-filehistory').find('tr').slice(1);
		var $t1 = $rows.first();
		$t1.children('td').slice(-3, -2).append(['<br>', $igen]);
		$t1.find('td[style]').append(['<br>', $SVGc]);
		$rows.each(function (i) {
			var $t = $(this);
			$t.find('td[style]>a:first').after(function () {
				var linkTxt = '→Valid SVG';
				var file = $(this).attr('href');
				// var href = "http://validator.w3.org/check?uri=" + $(this).attr("href") + "&group=1"; // there is an discrepance in automatic check

				var href = 'https://validator.nu/?doc=' + file + '&group=1&schema=http%3A%2F%2Fs.validator.nu%2Fsvg-xhtml5-rdf-mathml.rnc+http%3A%2F%2Fs.validator.nu%2Fhtml5%2Fassertions.sch+http%3A%2F%2Fc.validator.nu%2Fall%2F&parser=xml';

				var a = $('<a>', {
					title: 'W3C-Nu-Validator',
					href: href,
					target: '_blank'
				});
				return ['<br>', a.text(linkTxt),
					a.clone().attr('href', href + '&showsource=1#result').text(' (+src)?')
				// ,"<br>",a.clone().attr("href", "http://validator.w3.org/check?uri=" + file + "&group=1&ss=1").text( "(→old W3-check)" )
				];
			});
			$t.children('td').slice(-3, -2).children('span').after(function () {
				if (!/Byte/.test(this.textContent)) {
					var $gb = $g.clone();
					gbs.push($gb.attr('name', i).click(cSVG.getImgData));
					return $gb;
				}
				gbs.push('');
			});
		});
	},

	getImgData: function () {
		mw.loader.using(['mediawiki.api'], function () {
			new mw.Api().get({
				prop: 'imageinfo',
				iiprop: 'user|size', // |archivename
				iilimit: 9, // max requested images
				titles: pn
			}).done(function (json) {
				if (!json || !json.query || !json.query.pages)
					return;
				var data = json.query.pages;
				for (var p in data)
					p = data[p];
				if (!p)
					return;
				data = [];
				for (var i = 0; i < p.imageinfo.length; i++)
					data.push(p.imageinfo[i].size);
				userName = p.imageinfo[0].user; // global var
				cSVG.setImgSize(data);
			});
		});
	},

	warnMsg: function (w) {
		mw.loader.using([], function () {
			if (typeof w === 'number') {
				return mw.notify('SVG file is with ' + w + ' error' + ((w === 1) ? '' : 's') + ' not valid! ', { title: 'W3-Validity-check', type: 'warn' });
			}
			if (w instanceof Object)
				return mw.notify(cSVG.failStr + '!', w);
			mw.notify(w + ' Please check manually again with the web-service.',
				{ title: 'W3-Validity-check warning!', type: 'error', autoHideSeconds: 7 });
		}
		);
	},

	getW3Data: function (e) {
		// FIXME: maybe use also Rillke Bot
		if (isEdit) {
			$('#editform').prepend($.createSpinner({ id: 'w3nu', size: 'large', type: 'block' }));
			if (this.err)
				return cSVG.addToFileDesc($textarea, this.err, '', this.toolName);
		}
		var file = '';
		if (!(isEdit && e instanceof Object)) {
			e.preventDefault();
			file = $(e.target).parent().prev().find('a');
			if (file.length)
				file = file.get(0).href;
		}
		if (!file)
			file = 'https:' + mw.config.get('wgServer') + '/wiki/Special:Filepath/' + ti.replace(' ', '_');
		// console.log(typeof e, e.nodeName , file );

		/* https://validator.w3.org/docs/api.html */
		$.getJSON('https://validator.w3.org/nu/', {
			'out': 'json',
			'showsource': 1, // for check tool-name
			// "charset" : "UTF-8",
			'doctype': 'SVG 1.1 + URL + XHTML + MathML 3.0', // SVG+1.1+%2B+URL+%2B+XHTML+%2B+MathML+3.0
			'parser': 'xml',
			'user-agent': 'W3C_Validator/1.3 http://validator.w3.org/services', // W3C_Validator%2F1.3+http%3A%2F%2Fvalidator.w3.org%2Fservices
			'schema': 'http://s.validator.nu/svg-xhtml5-rdf-mathml.rnc http://s.validator.nu/html5/assertions.sch http://c.validator.nu/all/',
			'doc': file
		}
			/* https://validator.w3.org/docs/users.html#Options
$.getJSON("https://validator.w3.org/check?uri=" + file + "&ss=1&output=json"*/ // CORS-Kopfzeile 'Access-Control-Allow-Origin' fehlt
		).done(
			function (json) {
				var data = json.messages;
				if (!data || !json.source || !json.source.code)
					return cSVG.warnMsg('Fail to read file ' + json);
				var code = json.source.code;
				var toolName = cSVG.toolName = cSVG.getToolName(code);
				var doctype = /<!DOCTYPE svg/.test(code);
				var err = [];
				var warn = [];
				var warnRDF = 'This validator does not validate RDF.'; // FIXME only discrepance if <!DOCTYPE present
				var para = {}; // Parameter 5
				para.curSize = code.length;
				para.badSVG = /[<:]image[ >]/.test(code);
				para.PGF = /<i:pgf|adobe_illustrator_pgf/.test(code);
				if (!(para.badSVG && para.PGF)) {
					para.switchTrans = /[<:]switch[ >]/.test(code);
					if (!para.switchTrans && expeUser)
						para.textTrans = /[^>]\s?(<\/tspan>)?\s?<\/text>/.test(code);
				}

				for (var i in data) {
					// console.log(i, data[i], doctype); // data.messages is an array
					json = data[i].type;
					if (!json) continue;
					if (json === 'error')
						err.push(data[i].message);
					else if (json === 'info' && data[i].subType === 'warning')
						warn.push(data[i].message);
				}
				err = err.length;
				var w = warn.length;

				if (w && doctype) {
					while (w--) {
						if (!warn[w].indexOf(warnRDF)) {
							warn = warnRDF;
							warnRDF = '';
							break;
						}
					}
				}
				// console.log("Successfully checked for SVG errors ", err, w, warn.length, warnRDF); // error messages
				if (isEdit) {
					if (!(err || warnRDF))
						cSVG.warnMsg(warn);
					$.extend( cSVG, para );
					return cSVG.addToFileDesc($textarea, err, warn, toolName);
				} else if (!(err || warnRDF)) {
					cSVG.warnMsg(warn);
					setTimeout(function () {
						cSVG.addIgenURL(err, '', toolName, para);
					}, 4000);
				} else { cSVG.addIgenURL(err, '', toolName, para); }
			}).fail(function (e) {
			console.log('Ajax error to ', e);
			if (isEdit)
				return cSVG.addToFileDesc($textarea, -1);
		});
	},

	getToolName: function (code) {
		// TODO can be extended
		var toolList = ['sodipodi', 'Sketch', 'openoffice', 'libreoffice', 'inkscape', 'QGIS', 'MetaPost', 'MATLAB', 'Illustrator', 'Gnuplot', 'fig2svg', 'CorelDRAW', 'Chemtool', 'Batik', 'ArcMap', 'Adobe'];
		var toolName = '';
		var i = toolList.length - 1;
		while (i--) {
			if (code.indexOf(toolList[i]) + 1) {
				toolName = toolList[i];
				break;
			}
		}
		return toolName;
	},

	setImgSize: function (data) { // get & set file size
		$.each(gbs, function (i) {
			$(this).prev().remove();
			// no link for first
			if (this && this[0].name !== '0') {
				return $(this).text('→' + data[i])
					.attr('title', 'Insert Template:Igen with actual simple size: ' + data[0])
					.off('click')
					.click(function (/* e*/) {
						cSVG.addIgenURL('0', [data[i], data[0]]);
					});
			}
			// first badge link only for display
			return $(this).replaceWith(data[0] + ' Byte');
		});
	},

	/* registerModules: function() { // Register custom modules
if ( !mw.loader.getState('mediawiki.commons.MwJSBot') ) mw.loader.implement('mediawiki.commons.MwJSBot', ["//commons.wikimedia.org/w/index.php?action=raw&ctype=text/javascript&title=User:Rillke/MwJSBot.js"], {
}, {
});
},
run: function() { // Create GUI
cSVG.registerModules();
mw.loader.using(['mediawiki.commons.MwJSBot'], function() {
});
}, */

	setIgen: function (pre, post) {
		$textarea.textSelection('encapsulateSelection', {
			pre: pre,
			// peri: '',
			post: post
		}
		);
	},

	replaceTag: function (txt, err, toolname) {
		if (!txt) return;
		var size = this.size,
			txT = txt, // for test
			imRE = /^\s*\| *[Oo]ther[_ ]fields *=\s*\{\{[Ii]mgen\|(\w+)\}\} *$/m,
			coa = /\{\{COAInformation\s*\|\s*/.test(txT), // coat of arms template boolean
			imgenRE = /\|\s*[Ii]m?gen *=\s*[^\s]+ *\n/,
			imgen = ((coa && imgenRE.test(txT)) || imRE.test(txT)),
			toolName = '', // default nothing, most possible is Inkscape
			g = '', // specify the Graphic lab
			T = '', // additional trailing text for templates (extension)
			p5 = '', // Parameter 5
			r = '', // retouched
			s = '', // suffix for subcat
			s1 = '', // leading whitespaces as indent?
			s2 = '', // whitespace on parameter?
			ls = '', // last substring index
			top = '', // template igen on top position
			replaced = false,
			ofRE = /( )*\|\s*[Oo]ther[_ ]fields( )*=\s*([^\n]*\n)/g,
			inRE = [
				/\{\{(COA)?[Ii]nformation\s*\|\s*/,
				/( )*\|\s*[Pp]ermission( )*=\s*[^\n]*\n/,
				/( )*\|\s*[Oo]ther[_ ]versions( )*=(\n?[^\n]*\n)/
			],
			template = '{{Igen|',
			tempPre = '}}\n',
			user = '', // for u=
			// Normalized toolnames different to code
			toolNames = {
				'Fig2svg': 'Fig2SVG',
				'Illustrator': 'Adobe',
				'Libreoffice': 'LibreOffice',
				'Openoffice': 'OpenOffice.org'
			},
			// Toolname abbreviations
			toolAbbr = {
				'Adobe': 'A',
				'Inkscape': 'I',
				'ChemDraw': 'C',
				'CorelDraw': 'D',
				'CorelDRAW': 'D',
				'Fig2SVG': 'F',
				'Gnuplot': 'G',
				'LibreOffice': 'L',
				'OpenOffice.org': 'OOo'
		};

		function _ucfirst(s) { // Uppercase
			return s.slice(0, 1).toUpperCase() + s.slice(1);
		}
		function _findPosition(t) { // t instanceof RegExp
			// console.log(t,t.lastIndex, s1, s2, RegExp.$1, RegExp.$2);
			var p; // end of info template
			t = t.lastIndex || txt.search(t); // sometimes index = 0
			s1 = RegExp.$1 || s1;
			s2 = RegExp.$2 || s2;
			// try to go at template end (fallback risky if another template ends in same way)
			p = txt.substr(t).search(/^\}\}$\n/m); // \s is trimmed before
			// console.log("p ", p, t, txt.substr(t))
			t = (p >= 0) ? t + p : 0;
			return t;
		}
		function generalCleanup(text) {
		// TODO: namespace User could be extended with any language
		// lowercase parameter name default, trim leading whitespace
			if (inRE[0].test(text))	{
				text = text.replace(/^( *)\|\s*([A-Z][^=\n{}|?!.]+ *=)/gm,
					function (m, p1, p2) { return p1 + '|' + p2.toLowerCase(); }
				);
			}
			return text
				.replace(inRE[2], '$1|other versions$2=$3') // standardize
				.replace(/[[{]{2}[Ww]?[:|]?(\w\w)?[:|]?(?:[Uu]ser|Benutzer):([^\]|[]+)\|? ?\2 ?[\]}]{2}/g,
					function (m, p1, p2) {
						p1 = p1 ? '||' + p1 : '';
						// if ( p3 && ( p3 === p2 || /=/.test( p3 ) ) ) p3 = "";
						return '{{U|' + p2 + p1 + '}}';
					})
				.replace(/\(?\[\[[Uu]ser[ _]talk: ?([^\]|[]+)\|?[^\][]*\]\]\)?/g, '')
				.replace(/\[\[Category:Location not applicable ?(\|[^\n\]]+)?\]\]\n*/, '')
				.replace(/\[\[Category:SVG files ?(\|[^\n\]]+)?\]\]\n*/, '') // MetaCat
				.replace(/ ?(?:[Vv]ector(?:ized|ization)|r?e?drawn|[Vv]ektorisiert) ?(?:by|von)? ?(\{\{[Uu]\|(?:1=)?[^\]|[]+(?:[^}]*)\s?}}|\[{2}?[Uu]ser:[^}\n]*\]{2}?)/g, ' {{author|Vectorization|$1}}')
				.replace(/\[\[:((?:[Ff]ile|[Ii]mage):) ?([^\]|[]+)(?:\|\1? ?\2 ?)\]\]/g, '{{F|$2|+}}') // Could be TODO: there are many parameters
				.replace(/\[\[:[Cc]ategory: ?([^\]|[\n]+)(?:\| ?\1 ?)\]\]/g, '{{C|$1}}')
				.replace(/=\s*\{?\{?(own)?\}?\}? ?(work|made|after|nach)?,? (?:based|basierend)? (after|nach|on):?/gi, '={{own based}}')
				.replace(/=\s*\{\{own based\}\};?\s*(?:\[\[:(?:[Ff]ile|[Ii]mage):|\{\{[Ff]\|) ?([^\]|[\n]+)(?:\]\]\.?|\}\})/g, '={{own based|$1}}')
				.replace(/=\s*(?:(?:[Nn]achgezeichnet )?nach|[Bb]ased on|(?:[Rr]edrawn )?after)? (?:Vorlage)?:?\s*\*?\[?(?:\{\{[Nn]gw2\|(?:1|url ?=)?)? ?(?:(?:heraldrywiki\/index\.php\?title=)([\w_ ()]*)|(?:(?:http:\/\/)?www\.ngw\.nl\/?(?:heraldrywiki\/index\.php\?title=([\w_ ()]*)|int\/dld\/\w+\/(?:images\/)?([\w_ ()]*)\.\w{3})?))\}?\}?(?: [\w .]+)?\]?/g, function (m, p1, p2, p3) {
					m = ''; var l = [p1, p2, p3], p = 3, r = /\d$/, w = '<!--PLEASE CHECK-->';
					while (p--) {
						m += (l[p] || '');
						if (r.test(m)) { m = m.replace(r, w); w = ''; }
					}
					if (w && (m.length === 8 || ti.toLowerCase().indexOf(m) === -1)) m += w;
					return '={{Ngw3|' + _ucfirst(m) + '}}';
				}) // never use ngw2 on SVG
				.replace(/\[\[[Ww]?:(\w\w)?:? ?([^\]|[]+)(?:\| ?\2 ?)\]\]/g, function (m, p1, p2) {
					p1 = (p1 && p1 !== 'en') ? '||' + p1 : ''; return '{{W|' + p2 + p1 + '}}';
				})
				.replace(/([^{])[Dd]erivative versions? ?(?!of):?([^s}])/, '$1{{Derivative versions}}$2') // TODO: parameter as filenames
				.replace(/([^{])[Dd]erivative work ?(?!of):?([^s}]+)/, '$1{{Derivative}}$2') // TEST
				.replace(/[Ss]ource( +)?=( +)?\{\{\w\w\|(?:1=)?([^|\]]+)([| ])(?:Vorlage|Template)(\]\]?)?\s*\}\}/, 'source$1=$2$3$4{{Template-ns}}$5')
				.replace(/=( )?\s*<gallery>\s*(?:[Ff]ile|[Ii]mage)?:?([^\n|]+)(\|[^\n]+)?\s*<\/gallery>\s*/,
					/* "=$1{{Other|$2$3}}\n" */ '=$1{{F|$2$3|G}}\n') // (?: showfilename[="1]*)?
			/* General very specific cleanup (old usernames) */
			// .replace( /\[\[[Uu]ser:[Jj]uergenk59(\|[^\]|[]+)?\]\]/g, "{{U|Jürgen Krause}}" )
			// .replace( /\[\[[Uu]ser:[Mm]axxl2(\|[^\]|[]+)?\]\]/g, "{{U|MaxxL}}" )
			// AutVec
				.replace(/Original:? ([^\n|<]+)(?:<br ?\/?>)?\s*(?:SVG:? )(\[\[[\w:]*User:[^\n|[\]]+(?:\|[^\n|[\]]+)?\]\])/i, '{{AutVec|o=$1|v=$2}}');

		}

		if (/=\s*\{\{[Ii](m?gen|mage[ _]generation)/.test(txT) || imgen)
		// COAInformation has special parameter ¿imgen? so warn if present
			cSVG.warnMsg({ title: 'Already present!', type: 'warn' });

		/* SVG Template and cosmetic replace */
		txt = txt /* Other SVG related templates (for T), currently only working for sure if they are on a single line */
			.replace(/(\{\{(\w+werkstatt|[Gg]raphic Lab[^\n}]+|[Aa]telier graphiqu[^\n}]+)}})\s?/,
				function (m, p) { T += p; return ''; })
			.replace(/(\{\{(TracedSVG|Autotraced|Bad[ _]trace|PoorSVG)\}\})\s?/i,
				function (m, p) { T += p; return ''; }) // PoorSVG
			.replace(/(\{\{(Bad[ _]?SVG)\}\})\s?/i,
				function (/* m, p*/) { p5 = '|!'; return ''; }) // BadSVG
			.replace(/(\{\{([Tt]ranslat(?:e|able|ion possible)|[Ee]asy translation)[^\\n}]*}})\s?/,
				function (/* m, p*/) { /* T += p;*/ p5 += '|%'; return ''; })
			.replace(/(?:\{\{(?:[Rr]etouched ?(?:[Pp]icture|image)|[Rr]estored|[Mm]odified)([^\n]+\}\}))/,
				function (m, p) { T += '{{Retouched' + p; return ''; })
			.replace(/\{\{\s?(in)?valid[ _]?(SVG)?[^}]*\}\}\s?/ig, '') // remove SVG validity templates
			//	.replace( /( )*\|\s*[Pp]ermission( )*=\s*[^\n]*\n/, "" )     // remove empty permission parm
			.replace(/^\s*\}\}\s*$/gm, '}}\n'); // trim on every separate temp end

		/* Search SVG templates for tool-name and remove them */
		if (size) {
			txt = txt.replace(/\{\{\s?[Ss]implSVG\|([^{}|]+)\|[^}]+\}\}\s*/ig,
				function (m, p) { toolName = p; return '\n'; }
			);
		}
		txt = txt.replace(/\{\{(Inkscape(?:-hand)?|Adobe(?:[ _]Illustrator|-hand)?|Gnuplot|CorelDraw)\|?[^{}]*\}\}/i,
			function (m, p) { toolName = p; replaced = 1; return ''; } // replace success
		);
		if (!replaced) { // search Created with ... templates
			txt = txt.replace(/\{\{[Cc]reated with ([^{}|]+)\|?[^{}]*\}\}\s*/,
				function (m, p) { toolName = p; replaced = 1; return '\n'; } // replace success
			);
			if (!replaced) { // TODO: search redirects to Created with ... templates
				txt = txt.replace(/\{\{(QGIS|[Ll]ibreOffice|[Oo]penOffice(?:\.org)?|MATLAB|[Gg]numeric)\|?[^{}]*\}\}\s*/,
					function (m, p) { toolName = p || toolName; replaced = 1; return '\n'; }
				);
				if (!replaced) { // replace previous igen
					txt = txt.replace(/(\s)?\{\{[Ii](?:gen|mage[ _]generation)\|([\w ]*)?\|?[^{}]*\}\}\1?/,
						function (m, s, p) { toolName = p || toolName; return '\n'; } // replace success
					);
				}
			}
		}
		toolName = toolname || toolName; // "toolname" from SVG code  has priority over "toolName" from file desc

		/* Exclude nowiki text with split */
		var nowiki = txt.split('<nowiki>'); // TODO: Works only on non nested (may be sufficient)
		var wikiTxt = [nowiki.shift()];
		if (nowiki.length) {
			for (var n = 0; n < nowiki.length; n++) {
				var nClose = nowiki[n].split('<\/nowiki>');
				if (nClose.length > 1) {
					nowiki[n] = '<nowiki>' + nClose.shift() + '<\/nowiki>';
					wikiTxt.push(nClose.join());
				} else {
					wikiTxt[n - 1] += nowiki.splice(n, 1);
				}
			// console.log(n, nowiki[n])
			}
		}
		txt = ''; // clear

		for (var wt in wikiTxt)
			txt += generalCleanup(wikiTxt[wt]) + (nowiki[wt] || '');

		txT += pn;
		/* Search for sub suffix parameter */
		if (/[Mm]aps/.test(txT))
			s = 'm'; // :Maps
		if (/text ?logo/i.test(txT)) {
			this.textTrans = 0; // Logos don't get translated
			s = 'tl'; // :textlogo
		} else if (/Logos? of |:Logos?/i.test(txT))
			s = 'l'; // :Logos
		else if (/[: ]icon/i.test(txT))
			s = 'i'; // :Icons
		else if (/Diagram/i.test(txT))
			s = 'd'; // ":Diagrams"
		else if (coa)
			s = '-COAInformation'; // :CoA
		else if (/PD-Flag-|flag/.test(txT))
			s = 'f'; // :Flags
		else if (/PD-Coa|coats? of arms|insignia|:CoA/i.test(txT))
			s = 'c'; // :CoA

		// default: "Unknown tool" (but can also be Text Editor)
		toolName = _ucfirst(toolName) || ((this.curSize && this.curSize > 50 && this.curSize < 2000) ? 'T' : 'U');
		toolName = toolNames[toolName] ? toolNames[toolName] : toolName;
		if (expeUser) toolName = toolAbbr[toolName] ? toolAbbr[toolName] : toolName;

		if (T) {
			// Graphic Lab replace
			T = T.replace(/\{\{Grafikwerkstatt\}\}/, function (/* m*/) { g = 'de'; return ''; });
			g = g ? '|g=' + g : '';
			if (/^\{\{[Rr]etouched\|([^\n]+)\}\}/.test(T)) {
				r = '|r=' + RegExp.$1;
				T = T.replace(RegExp.lastMatch, ''); // FIXME: non standard
			}
			T = (T.length > 4) ? '|T=' + T : '';
		}

		/** Get lastIndex, from now, NO text manipulation anymore, or var top must be fixed */
		if (!ofRE.test(txt)) {
			var i = inRE.length;
			while (i--) { // iter over possible positions
				if (inRE[i].test(txt)) {
					top = _findPosition(inRE[i]);
					break;
				}
			}
		} else {
			top = ofRE.lastIndex;
			txt = txt.replace(ofRE, '');
			top -= RegExp.lastMatch.length;
			ls = RegExp.$3;
		}
		s1 = s1 || RegExp.$1;
		s2 = s2 || RegExp.$2;

		if (!coa && /[Ii]mgen\|/.test(ls)) { // CoA but without COAInformation
			template = '|other fields' + s2 + '=' + s2;
			tempPre = '';
		} else if (!top && /\}\}/g.test(txt)) {
			top = txt.lastIndexOf(tempPre) + 3; // put on last postion of a template
		} else { template = '|other fields' + s2 + '=' + s2 + template; }

		// console.log("ofRE s1 '%s', s2 '%s', ls '%s'", s1, s2, ls, top);

		if (typeof err === 'number') {
			if (!top) {
				this.setIgen(template += (toolName || '×?×') + (err ? '|' + err.toString() : '') + '|+|s=' + s, tempPre);
				return '';
			}
		}

		// Parameter 5
		if (this.badSVG && !/^\|!/.test(p5)) p5 += '|!<!--PLEASE CHECK, not generally for all graphics-->'; // Maybe
		if (this.curSize > 4194304) p5 += '|>'; // Large SVG 4 MB?
		if (this.PGF) p5 += '|~';
		else if (this.switchTrans) p5 += '|%s';
		else if (this.textTrans && !/\|%/.test(p5)) p5 += '|%';

		T = p5 + r + T;

		if (top) {
			err = Number(err);
			if (size) {
				template += toolName + '|' + size + '|9=+|10=S|user=' + userName + ((s === '-COAInformation') ? '|sub=' : '|4=') + s + T;
			} else {
				if (s === '-COAInformation' && $.inArray(toolName, ['I', 'Inkscape', '']) !== -1) {
					var coaUsers;
					// ls = ls.replace( /\{\{[Ii]mgen\|([\w\d ]+)\}\} *\n/, function(m, p){ user = p; return "";} );
					if (!user) {
						coaUsers = [ // [[Template:COAInformation/Imgen]]
							['{{Projet Blasons}}', 'PB'], // must be first
							['Spax89', 'S0'],
							['SteveK', 'SK'],
							['Gliwi', 'Gl'],
							['Madboy74', 'Mb'],
							['MaxxL', 'ML'],
							['Chris die Seele', 'CS'],
							['Jimmy44', 'JN'],
							['Jürgen Krause', 'JK'],
							['Kaboldy', 'Ka']
						];
						var l = coaUsers.length;
						while (l--) {
							if (txt.indexOf(coaUsers[l][0]) !== -1) {
								user = coaUsers[l][1];
								coaUsers = coaUsers[l][0];
								break;
							}
						}
					}
					if (user) {
						if (err) {
							template += 'I|' + err + '|s=:CoA by ' + coaUsers + T;
						} else {
							template = '|imgen' + s2 + '=' + s2 + user;
							tempPre = '\n';
						}
						if (user !== 'PB') {
							// Remove redundant cats
							coaUsers = {
								'JK': 'COA by User:Jürgen Krause',
								'Gl': 'Files by User:Gliwi',
								'ML': 'COAs by User:MaxxL',
								'Mb': 'Pictures by Madboy74 - 0',
								'Ka': 'Files by User:Kaboldy',
								'S0': '{{Projet Blasons}}' // "Blason spax89" don't want
								// "SK": "COA by SteveK"  don't want
							}[user];

							if (coaUsers && $.inArray(user, ['S0', 'SK']) === -1) { // do not want
								coaUsers = '[[Category:' + coaUsers + ']]\n';
							}
						}
						if (txt.lastIndexOf(coaUsers) < top)
							top -= coaUsers.length;
						txt = txt.replace(coaUsers, '');
						if (imgen) { // replace only
						// top -= txt.length;
							txt = txt.replace(imgenRE, template + '\n');
							// top += txt.length;
							template = '';
							tempPre = '';
							s1 = '';
						}
					// ls = ""; // delete other fields?
					} else if (!imgen) { // Default
						template = '|imgen' + s2 + '=' + s2 + err;
						tempPre = '\n';
					}
				} else { template += toolName + (err ? '|' + err : '') + '|+' + g + T + '|s=' + s; }
			}

			txt = txt.substr(0, top) + s1 + template + tempPre + ls + txt.substr(top);
			return txt;
		}
		console.log('Igen not inserted ');
		return { type: 'warn' };
	},

	addToFileDesc: function ($textarea, e, warn, toolName) {
		var txt = $textarea.val();
		if (typeof e === 'string') {
			e = Number(e);
			if (e)
				cSVG.warnMsg(e);
			if (warn)
				cSVG.warnMsg(warn);
		}
		$.removeSpinner('w3nu');
		// if (e instanceof Object) then e from button
		txt = cSVG.replaceTag(txt, e, toolName);
		if (txt) {
			if (txt instanceof Object)
				return cSVG.warnMsg(txt);
			else $textarea.val(txt);
		// mw.notify("Igen inserted ", { title: "Done!" });
		}
		$('#wpSummary').val(function (i, v) {
			return $.trim(v.replace(summary, '') + ' ' + summary);
		});
		$('#wpMinoredit').prop('checked', 1);
		mw.hook('gadget.magog.cleanup.run').fire(); // cleanup same time?
		// if (typeof e === 'string') $('#wpSave').click(); // I'm feeling lucky!
		return false;
	},

	addIgenURL: function (err, size, toolName, para) {
		var param = '';
		if (typeof err === 'number') console.log('Successfully checked for SVG errors: ', err);
		// else err = 0;
		// e.preventDefault();
		size = ($.isArray(size)) ? '&SVGsize=' + size[0] + '|' + size[1] + '&username=' + encodeURIComponent(userName) : '';
		toolName = toolName ? '&toolname=' + encodeURI(toolName) : '';
		if (para instanceof Object) {
			$.each(para, function (i, o) {
				param += '&' + i + '=' + Number(o);
			});
		}
		location.href = mw.config.get('wgScript') + '?title=' + encodeURIComponent(pn) + '&action=edit&' + cSVG.name + '=' + err + size + toolName + param;
	},

	addButton: function ($textarea) {
		$textarea.wikiEditor('addToToolbar', {
			section: 'main',
			group: 'insert',
			tools: {
				Igen: {
					label: 'SVG Igen',
					type: 'button',
					icon: 'https://upload.wikimedia.org/wikipedia/commons/thumb/6/6c/SVG_Simple_Icon.svg/24px-SVG_Simple_Icon.svg.png',
					action: {
						type: 'callback',
						execute: function (/* context*/) {
							mw.libs.simpleSVGcheck.getW3Data($textarea);
						}
					}
				}
			}
		});
	}
};

mw.libs.simpleSVGcheck = cSVG; // Expose globally

if (mw.config.get('wgNamespaceNumber') === 6 && /SVG/i.test(ti.slice(-3))) {
	if (isEdit) {
		$.when(mw.loader.using(['mediawiki.util', 'mediawiki.user', 'jquery.spinner', 'user.options']), $.ready).done(function () {
			$textarea = $('#wpTextbox1');
			if (mw.user.options.get('usebetatoolbar') && mw.config.get('wgPageContentModel') === 'wikitext' && /^(edit|submit)$/.test(ac)) {
				if ( $.fn.wikiEditor ) cSVG.addButton($textarea);
				else $textarea.on( 'wikiEditor-toolbar-doneInitialSections', function () {
					cSVG.addButton($textarea);
				} );
			}

			if (!window.addCleanupFunction) // cleanup?
				mw.loader.load('//commons.wikimedia.org/w/index.php?title=User:Perhelion/cleanup.js&action=raw&ctype=text/javascript');
			// Get parameter
			var URL = window.location.search;
			var param = {};
			var nameReg = new RegExp(cSVG.name + '=(\\d+)', 'g');
			err = nameReg.test(URL);
			if (ac === 'edit' && err) {
				cSVG.err = err = mw.util.getParamValue(cSVG.name); // TODO only one err needed
				cSVG.size = mw.util.getParamValue('SVGsize');
				userName = mw.util.getParamValue('username');
				toolName = mw.util.getParamValue('toolname');
				URL = URL.substr(nameReg.lastIndex + 1);
				for (var aItKey, nKeyId = 0, aCouples = URL.split('&'); nKeyId < aCouples.length; nKeyId++) {
					aItKey = aCouples[nKeyId].split('=');
					param[aItKey[0]] = aItKey.length > 1 ? Number(aItKey[1]) : 0;
				}
				$.extend( cSVG, param );
				cSVG.addToFileDesc($textarea, err, '', toolName);
			}
		});
	} else { mw.loader.using(['ext.gadget.jquery.badge'], cSVG.init); }
}
}(jQuery, mediaWiki));
// EOF </nowiki>