User:Perhelion/fixconverttosvg.js
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.
Documentation for this user script can be added at User:Perhelion/fixconverttosvg. |
/**
* SORT CONVERT-TO-SVG TAGS on all sub-cats of [[:Category:Images that should use vector graphics]]
* @created 2006-02-21
* @source [[c:Template:Convert to SVG]]
* @revision 21:34, 15 October 2019 (UTC)
* @author [[User:Ilmari Karonen]], 2006, 2010
* @author [[c:User:Perhelion]], 2010, 2017-2019
* Commons: [[Category:User scripts|fixconverttosvg]]
* @license GPL v.3
* @ToDo:
** Redirect solving https://commons.wikimedia.org/w/index.php?title=File:Jawa_Cakra.png&action=history
** type simple support as 2nd parameter
*/
// <nowiki>
/* eslint no-var:"error", one-var:"error"*/
/* eslint-env es6*/
/* global mw, importScript, convertToSVGTypes, HotCat */
(function () {
'use strict';
const st = '{Convert to SVG|', // template name
vt = 'Vector version available',
ns = mw.config.get('wgNamespaceNumber'),
isEdit = !mw.config.get('wgIsArticle'),
ti = mw.config.get('wgTitle'),
sts = ' set tag ',
SLink = '([[User:Perhelion/fixconverttosvg.js|Script]])',
catRegx = /([\S ]*) images that should use vector graphics/;
let cleanupContents = [], // list of file objects
cleanupReady = -1,
token;
if (window.HotCat)
HotCat.blacklist = HotCat.blacklist ? new RegExp(HotCat.blacklist + '|' + catRegx) : catRegx;
function toSVGname(s) { // try to made valid SVG filename extension
s = s || ti;
s = s.replace(/^[Ff]ile:/, '');
if (!/\.SVG$/.test(s))
s = s.replace(/\.\D{3}\D?$/, '.svg');
return s;
}
function simplifyVVA(s) {
s = (s.replace(/\.SVG$/i, '') !== ti.replace(/\.\D{3}\D?$/, '')) ? '|' + s : '';
if (!s) mw.notify('VVA name is equal and can be omitted.', { title: 'Simplified!' });
return s;
}
/**
* Replace old existing redundant tags
*
* @param {string} txt The text
* @param {string} type The type
* @param {Object} textarea The DOM textarea
* @return {Array} [tuple] (text, type)
*/
function replaceTag(txt, type, textarea) {
const reSVG = /\{\{\s?(?:Convert[_ ]?to|to|Should[_ ]?Be)?[_ ]?(SVG|Vectorize|In SVG konvertieren)[^}]*\}\}\s*/i,
reVVA = /\{\{\s?(((Superseded|Converted to )SVG)|VVA|(((Vector|SVG)[_ ]?(version)?[_ ]?)available))[^}]*\}\}\s*/i;
let para = '', // parameter
laMa = '',
laPo = -1,
vva = '',
iO;
while (reSVG.test(txt)) { // remove SVG, save parameter
laMa = RegExp.lastMatch;
laPo = txt.indexOf(laMa); // TEST
laPo = (laPo === -1) ? reSVG.lastIndex - laMa.length : laPo;
txt = txt.replace(laMa, '\n');
// console.log("'%s'", laMa, laPo, txt.slice(0, laPo))
if (/\|+([^|}]+)\s*\}\}\s*$/g.test(laMa)) // lastMatch
para = RegExp.$1;
}
while (reVVA.test(txt)) { // remove VVA, save parameter
laMa = RegExp.lastMatch;
txt = txt.replace(laMa, '\n');
if (/\|\s?([^|}]+\.svg)/i.test(laMa)) // lastMatch
vva = RegExp.$1;
}
// Remove Cat from template
txt = txt.replace(new RegExp('\\[\\[[Cc]ategory:' + catRegx.source + '(\\|[^\n]]+)?\\]\\] *\\n*', 'g'), '');
if (vva || (para && /\.SVG$/i.test(para) && !/vectordata=/.test(para))) { // check wrong using of VVA template and correct it
para = vva || para;
para = toSVGname(para);
para = simplifyVVA(para);
type = '{{' + vt + para + '}}\n';
return [type + txt, type];
}
if (type) { // Put SVG template
if (type === '-') {
type = st + ' removed-';
} else {
iO = /vectordata=/g.test(para);
if (type === 'vectordata=')
type = iO ? /\|([^|}]+\s*\|[^|}]+)/.exec(laMa)[1] : para + '|' + type;
else if (iO)
type += '|' + para;
para = '{' + st + type + '}}\n';
if (laPo < 0) {
if (textarea)
laPo = $(textarea).textSelection('getCaretPosition');
// console.log("End '%s'", laMa, laPo)
laPo = (laPo < 0) ? 0 : laPo;
}
laMa = txt.slice(laPo);
// If in template remove extra line
if (/^\n}}/.test(laMa)) laMa = laMa.slice(1);
txt = txt.slice(0, laPo) + para + laMa;
type = para;
}
}
// cleanup
txt = txt.replace(/\{\{ ?Bad ?JPE?G\}\}\s*/gi, '');
return [txt, type];
}
function callbackTimer(cb) {
setTimeout(cb, 500); // I'm feeling lucky!
}
/**
* Create VVA template string and link SVG file name
*
* @param {string} s Existing VVA param
* @return {Array} [tuple] string, string for summary
*/
function makeVVA(s) {
s = toSVGname(s);
s = simplifyVVA(s);
const VVA = '{{' + vt + s + '}}\n';
s = s ? VVA.replace(s.replace(/^\|/, ''), '[[File:$&]]') : VVA;
return [VVA, s];
}
/**
* Add VVA template with param (SVG file name)
*
* @param {Array} txt [tuple] string, type
* @param {string} s Existing VVA param
* @return {Array} [tuple] string, string
*/
function doVVA(txt, s) {
const p = txt[1] || ''; // type (old VVA para)
let VVA = makeVVA(s);
txt = txt[0];
s = VVA[1];
VVA = VVA[0];
txt = p ? txt.replace(p, VVA) : VVA + txt;
return [txt, s];
}
function apiFail(code, r) {
let warn;
if (!code.indexOf('http'))
warn = 'HTTP error: ' + r.textStatus;
else if (code === 'ok-but-empty')
warn = 'Got an empty response from the server';
else
warn = 'API error: ' + code;
mw.log.warn(warn);
mw.notify('There was an error processing your request.\n\t\t\t:' +
warn + '\n\n\t\t\t\tPlease try again.', {
title: 'Error!',
autoHide: 0,
type: 'error'
});
}
function savePage(content, file, summary) {
new mw.Api().post({
action: 'edit',
summary: summary,
watchlist: 'nochange',
title: file,
token: token,
text: content,
minor: 1
}).done(function () {
mw.notify('SVG parameter successfully inserted on ' + file, { title: 'Done!' });
}).fail(apiFail);
}
function doCleanupHook(cContent) {
if (typeof cContent === 'object') {
const file = cContent.file;
// console.log(cContent.text, file);
cleanupReady--;
if (cleanupReady > 0 && cleanupContents.length) {
callbackTimer(function () {
mw.hook('gadget.cleanup.run').fire(cleanupContents.shift());
});
}
savePage(cContent.text, file, cContent.sum);
// If direct on filepage
if (mw.config.get('wgPageName') === file.replace(/ /g, '_')) {
callbackTimer(function () {
location.reload();
});
}
}
}
function loadCleanupScript(cb) {
// Cleanup script loaded?
if (cleanupReady === -1) {
mw.hook('gadget.cleanup.done').add(doCleanupHook);
cleanupReady = 0;
}
if (!mw.libs.fastCleanup) {
mw.hook('gadget.cleanup.loaded').add(cb);
importScript('User:Perhelion/cleanup.js');
} else {
cb();
}
}
function doPageContent(txt, file, type, SVG) {
txt = replaceTag(txt, type);
if (!type && SVG)
txt = doVVA(txt, SVG);
type = txt[1];
txt = txt[0];
txt = {
file: file, text: txt, sum: SLink + sts + type
};
cleanupContents.push(txt);
loadCleanupScript(function () {
if (!cleanupReady) {
mw.hook('gadget.cleanup.run').fire(cleanupContents.shift());
cleanupReady++;
}
});
}
/**
* Ajax Api to open file
*
* @param {string} file The file
* @param {string} t The type
* @param {string} SVG The SVG file param
* @return {(Function|void)} doPageContent callback
*/
function getPage(file, t, SVG) {
new mw.Api().get({
prop: 'revisions',
meta: 'tokens',
rvprop: 'content',
titles: file
}).done(function (r) {
r = r.query;
let pages = r.pages,
id;
const tokens = r.tokens;
mw.log('Processing… got page contents from ' + file);
mw.notify('Got page contents from ' + file, { title: 'Processing…' });
if ('csrftoken' in tokens)
token = tokens.csrftoken;
for (id in pages) {
if (id in pages) {
pages = pages[id];
r = pages.revisions[0]['*'];
return doPageContent(r, pages.title, t, SVG);
}
}
}).fail(apiFail);
}
function toSummary(tag) { // In edit mode
const summary = document.editform.wpSummary;
let sum = summary.value,
dblSum = sum.indexOf(SLink);
tag = SLink + sts + tag;
// don't add double cmt
if (!dblSum)
sum = sum.slice(sum.indexOf('}}') + 2);
else
sum = (dblSum === -1) ? sum : sum.slice(0, dblSum);
summary.value = sum.trim() + ' ' + tag;
}
function toSVGpage(e) { // In edit mode
let type = e,
textarea,
txt;
const editform = document.editform;
if (!editform)
return;
if (typeof e !== 'string') {
if (e.preventDefault)
e.preventDefault();
e = e.target;
type = $(e).text();
// if (e.nodeName === "A") return;
}
textarea = editform.wpTextbox1;
txt = replaceTag(textarea.value, type, textarea);
type = txt[1];
txt = txt[0];
toSummary(type);
$(textarea).val(txt); // textarea.value = txt; not working with CodeMirror :-o
editform.wpMinoredit.checked = true;
if (!$('#ca-unwatch').length) // don't change watch status
editform.wpWatchthis.checked = false;
/* if (e && typeof e === 'string') {
loadCleanupScript(function () {
callbackTimer($('#wpSave').click);
});
}*/
return false;
}
/**
* Vector version available in edit mode
*
* @param {Object} $textarea Wikieditor Object | $textarea element
* @param {string} s Existing VVA param
* @return {string} txt To input textarea
* @return {string} s To input summary
*/
function toVVApage($textarea, s) {
let txt = '',
c;
if (!s)
$textarea = $textarea.$textarea;
$textarea = $textarea || $('#wpTextbox1');
s = s || $textarea.textSelection('getSelection');
c = $textarea.textSelection('getCaretPosition') || 0;
txt = $textarea.val();
txt = replaceTag(txt.replace(s, ''), $textarea[0]); // Remove templates
if (s) {
txt = doVVA(txt, s);
s = txt[1];
txt = txt[0];
} else {
txt = txt[0];
s = makeVVA();
if (!txt[1]) { // No type?
txt = txt.slice(0, c) + s[0] + txt.slice(c);
}
s = s[1];
}
$textarea.val(txt);
toSummary(s);
}
// Create array list
function makeTypes() {
let types = ['architecture', 'art', 'astronomical map', 'biology', 'chemical', 'chemistry', 'circuit', 'coat of arms', 'diagram', 'emblem', 'flag', 'graph', 'icon', 'locator map', 'logo', 'map', 'math', 'military insignia', 'musical notation', 'physics', 'realistic', 'ribbon', 'sign', 'signature', 'simple', 'sport', 'symbol', 'technology', 'text', 'transport map'],
typec = '',
t;
if (window.convertToSVGTypes) {
typec = convertToSVGTypes;
if (typeof typec === 'string')
typec = typec.split(/,\s*/);
else if (Array.isArray(typec))
types = [];
// reset default
}
// Append default values
for (t = 0; t < typec.length; t++) {
if (types.indexOf(typec[t]) === -1)
types.push(typec[t]);
}
return types.sort().concat('-');
}
function createSection() {
const types = ['vectordata='].concat(makeTypes()),
$textarea = $('#wpTextbox1');
$textarea.wikiEditor('addToToolbar', {
sections: { ToSVG: {
label: 'ToSVG',
type: 'booklet',
pages: { tosvg: {
label: '{' + st, // replaced
layout: 'characters',
characters: types
} }
} },
section: 'main', // Button Vector version available
groups: { SVG: { tools: { vva: {
label: vt,
type: 'button',
icon: '//upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gnome-x-office-drawing.svg/22px-Gnome-x-office-drawing.svg.png',
action: {
type: 'callback',
execute: toVVApage
}
} } } }
});
$('#wikiEditor-ui-toolbar .page.page-tosvg').find('span').off('click').on('click', toSVGpage).css('font-size', '14px'); // HACK (callback API don't work!?) + made smaller
}
function createList(s) {
let types = makeTypes();
const frag = document.createDocumentFragment();
if (s) { // remove current type from sub-cat
s = s[0].toLowerCase() + s.slice(1); // types need to be start lower case
types = $(types).not([s]);
}
frag.appendChild(document.createElement('br'));
for (let t = 0, tyLen = types.length; t < tyLen; t++) { // this is faster than jQuery
const link = document.createElement('a');
link.text = types[t];
frag.appendChild(document.createTextNode('['));
frag.appendChild(link);
frag.appendChild(document.createTextNode('] '));
}
return frag;
}
function doGetType(e) {
e.preventDefault();
let a = e.target,
$a = $(a);
const t = $a.text();
if (a.title && a.href)
return;
a = $a.parent();
$a = a.find('a').first();
$a.attr('target', '_blank');
// console.log(a, $a.attr("title"), t)
return [$a.attr('title'), t, a];
}
function OpenSVGfile(e) {
// if (e.stopPropagation) e.stopPropagation();
if (typeof e !== 'string') {
const type = doGetType(e); // [file , type]
if (type && type[0]) {
type[2].parents('li.gallerybox').remove();
getPage(type[0], type[1]);
// e.href = $a.attr("href") + "&fixconverttosvg=" + encodeURI(t);
}
}
}
function toSVGcategory() {
const frag = createList((catRegx.test(ti) ? RegExp.$1 : ''));
$('#mw-category-media div.gallerytext').children('a').after(function () {
this.parentNode.onclick = OpenSVGfile;
return frag.cloneNode(true);
});
mw.util.addCSS(
'.gallerybox,.gallerybox>div{width:280px !important}\n.gallerytext>a{white-space:nowrap}'
);
}
function doPrompt(text, kind, $element, title, prefill, cb) {
const $input = $('<input>').attr({
value: prefill,
type: 'text',
id: 'SVGdialog',
size: 48
});
if (!text) { // TODO check file exists
title = 'INVALID PARAMETER';
text = 'You must enter a valid name';
} else {
title += '}}';
text = $('<label>').attr({
'for': kind + 'dialog',
'class': 'text'
}).text(text);
}
$input.after($element);
$element = $('<div>').attr({
type: 'text',
id: kind + 'dialog'
}).append($input);
$('<div>').append([text, $element]).dialog({
width: 400,
dialogClass: 'wikiEditor-toolbar-dialog',
resizable: false,
modal: true,
title: title,
close: function () {
$(this).dialog('destroy');
$(this).remove();
},
buttons: [{
text: 'OK',
click: function (e) {
$(this).dialog('close');
e = $input.val();
if (e) { // Check validity then run callback
return cb(e);
}
doPrompt('', kind, $element, title);
}
}
]
});
}
function addSVG() {
let type = $.grep(mw.config.get('wgCategories'), function (i) {
return catRegx.test(i);
});
const prefill = type.length ? type[0].replace(catRegx, '$1') : '';
type = $('<br>').after($('<div>').append(createList(prefill)).on('click', function (e) {
e = doGetType(e);
$('#SVGdialog').val(e[1]);
}));
/* type = ['<br>', $('<a>').attr({
'href': '#',
'title': 'File:' + ti
})
type]; */
doPrompt('Choose a SVG category type:',
'ToSVG',
type,
'Parameter insert for {' + st,
prefill,
function (res) {
if (isEdit) toSVGpage(res);
else getPage('File:' + ti, res);
});
}
function addVVA() {
let prefill,
$textarea;
if (isEdit && ($textarea = $('#wpTextbox1')).length)
prefill = $textarea.textSelection('getSelection');
prefill = prefill ? toSVGname(prefill) : toSVGname();
doPrompt('Choose one (or more) SVG file(s):',
'VVA',
$(),
'Insert file name for {{' + vt,
(prefill || '.svg'),
function (res) {
if (isEdit) toVVApage($textarea, res);
else getPage('File:' + ti, '', res);
});
}
// if (mw.config.get("wgUserGroups").indexOf("autoconfirmed") > -1)
$.when(mw.loader.using(['mediawiki.user', 'mediawiki.util', 'user.options', 'mediawiki.api']), $.ready).then(function () {
if (ns === 14 && ti.indexOf('mages that should use vector graphics') !== -1) {
if (!mw.messages.get('schnark-imagepopups-missing'))
mw.loader.load('https://de.wikipedia.org/w/index.php?title=Benutzer:Schnark/js/imagepopups.js&action=raw&ctype=text/javascript');
mw.loader.using([], toSVGcategory());
} else if (ns === 6 && !/SVG$/i.test(ti)) {
if (isEdit) {
// Omit double run (in live preview)
if (!$('#wikiEditor-ui-toolbar span.tab.tab-ToSVG').length && mw.user.options.get('usebetatoolbar'))
// && mw.user.options.get("showtoolbar")
mw.loader.using('ext.wikiEditor', createSection);
}
mw.loader.using('ext.gadget.editDropdown').always(function () {
mw.libs.commons.ui.addEditLink('#ToSVG', 'ToSVG +', 'e-edit-tosvg', 'Add {' + st + '}}')
.addEventListener('click', addSVG);
mw.libs.commons.ui.addEditLink('#VVA', 'VVA +', 'e-edit-vva', 'Add {{' + vt + '}}')
.addEventListener('click', addVVA);
});
}
});
}());
// EOF </nowiki>