User:Smasongarrison/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:Smasongarrison/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
* @author [[c:User:Smasongarrison]], 2022
* 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 = ['simple','text'],
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>