Note: After publishing, 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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* reFill.js
* Fill bare / [url title] refs only.
* Do not rename or edit existing named refs.
* New duplicate URLs use the next free :N.
*/
(function () {
'use strict';
var CITOID = 'https://en.wikipedia.org/api/rest_v1/data/citation/mediawiki/';
var TYPE_MAP = {
blogPost: 'Cite web', webpage: 'Cite web', email: 'Cite web', forumPost: 'Cite web',
newspaperArticle: 'Cite news', magazineArticle: 'Cite news',
journalArticle: 'Cite journal', conferencePaper: 'Cite journal', presentation: 'Cite journal',
book: 'Cite book', manuscript: 'Cite book', _default: 'Cite web'
};
function $box() { return $('#wpTextbox1'); }
function getWikitext() {
var $t = $box();
if ($t.length && $.fn.textSelection) return $t.textSelection('getContents');
return $t.length ? $t.val() : '';
}
function setWikitext(text) {
var $t = $box();
if ($t.length && $.fn.textSelection) { $t.textSelection('setContents', text); return; }
if ($t.length) $t.val(text).trigger('input');
}
function setSummary(ok, fail) {
var $s = $('#wpSummary');
if (!$s.length) return;
var note = 'Filled in ' + ok + ' bare reference' + (ok === 1 ? '' : 's') + ' with [[Bharatpedia:reFill|reFill]]';
if (fail) note += ' (' + fail + ' failed)';
var cur = ($s.val() || '').trim();
if (!cur) $s.val(note);
else if (cur.toLowerCase().indexOf('refill') === -1) $s.val(cur.replace(/\s*$/, '') + '; ' + note);
$s.trigger('change');
}
function showDiff() {
var btn = document.getElementById('wpDiff');
if (btn) { btn.click(); return; }
var form = document.getElementById('editform');
if (!form) return;
var input = document.createElement('input');
input.type = 'hidden';
input.name = 'wpDiff';
input.value = 'Show changes';
form.appendChild(input);
form.submit();
}
function esc(s) { return String(s || '').replace(/\|/g, '{{!}}').replace(/\n/g, ' ').trim(); }
function hostTitle(url) {
try { return new URL(url).hostname.replace(/^www\./, ''); } catch (e) { return ''; }
}
function normUrl(url) { return String(url || '').trim().replace(/\/+$/, '').toLowerCase(); }
function usedAutoNames(text) {
var used = {}, maxN = -1, m;
var re = /<ref[^>]*\bname\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>/]+))/gi;
while ((m = re.exec(text)) !== null) {
var n = m[1] || m[2] || m[3];
used[n] = true;
var a = String(n).match(/^:(\d+)$/);
if (a) maxN = Math.max(maxN, parseInt(a[1], 10));
}
return { used: used, next: maxN + 1 };
}
function templateFromCitoid(item, fallbackUrl) {
var name = TYPE_MAP[item && item.itemType] || TYPE_MAP._default;
var p = [];
var url = (item && item.url) || fallbackUrl;
if (url) p.push('url=' + esc(url));
var title = (item && item.title) || (url ? hostTitle(url) : '');
if (title) p.push('title=' + esc(title));
var site = item && (item.websiteTitle || item.publicationTitle || item.publisher);
if (site) p.push((name === 'Cite news' || name === 'Cite journal' ? 'work=' : 'website=') + esc(site));
if (item && item.date) p.push('date=' + esc(item.date));
if (item && item.author && item.author[0]) {
var a = item.author[0];
if (a[1]) p.push('last=' + esc(a[1]));
if (a[0]) p.push('first=' + esc(a[0]));
}
p.push('access-date=' + new Date().toISOString().slice(0, 10));
return '{{' + name + '|' + p.join('|') + '}}';
}
function findBare(wikitext) {
var out = [], m, seen = {};
function add(full, attrs, url, index) {
if (seen[index]) return;
if (/\bname\s*=/i.test(attrs || '')) return;
if (/\{\{\s*cite/i.test(full)) return;
seen[index] = true;
out.push({ full: full, url: url, index: index });
}
var reBare = /<ref(\s[^>]*)?>\s*(https?:\/\/[^\s<>[\]|{]+)\s*(?:\{\{[^}]+\}\})?\s*<\/ref>/gi;
while ((m = reBare.exec(wikitext)) !== null) add(m[0], m[1], m[2], m.index);
var reLink = /<ref(\s[^>]*)?>\s*\[(https?:\/\/[^\s\]]+)\s+[^\]]*\]\s*<\/ref>/gi;
while ((m = reLink.exec(wikitext)) !== null) add(m[0], m[1], m[2], m.index);
out.sort(function (a, b) { return a.index - b.index; });
return out;
}
function fetchOne(url) {
function once() {
return fetch(CITOID + encodeURIComponent(url), { headers: { Accept: 'application/json' } })
.then(function (r) {
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(function (data) {
var item = Array.isArray(data) ? data[0] : data;
if (!item) throw new Error('empty');
return item;
});
}
return once().catch(function () {
return new Promise(function (res) { setTimeout(res, 700); }).then(once);
});
}
function run() {
var text = getWikitext();
if (!text) { window.alert('reFill: no editor text.'); return; }
var names = usedAutoNames(text);
var bare = findBare(text);
if (!bare.length) {
window.alert('reFill finished: 0 filled, 0 failed.');
return;
}
var tab = document.querySelector('.tab-refill, [rel="refill"]');
if (tab) tab.textContent = 'Filling…';
var groups = {};
var order = [];
bare.forEach(function (item) {
var key = normUrl(item.url);
if (!groups[key]) {
groups[key] = { url: item.url, count: 0, first: true, name: null };
order.push(key);
}
groups[key].count++;
});
var next = names.next;
order.forEach(function (key) {
if (groups[key].count > 1) {
while (names.used[':' + next]) next++;
groups[key].name = ':' + next;
names.used[':' + next] = true;
next++;
}
});
var metaByUrl = {};
var chain = Promise.resolve();
var fail = 0;
order.forEach(function (key) {
chain = chain.then(function () {
return fetchOne(groups[key].url).then(function (meta) {
metaByUrl[key] = meta;
}).catch(function () {
fail++;
metaByUrl[key] = { url: groups[key].url };
}).then(function () {
return new Promise(function (res) { setTimeout(res, 350); });
});
});
});
chain.then(function () {
var ok = 0;
bare.forEach(function (item) {
var key = normUrl(item.url);
var g = groups[key];
var meta = metaByUrl[key] || { url: item.url };
var neu;
if (!g.name) {
neu = '<ref>' + templateFromCitoid(meta, item.url) + '</ref>';
} else if (g.first) {
g.first = false;
neu = '<ref name="' + g.name + '">' + templateFromCitoid(meta, item.url) + '</ref>';
} else {
neu = '<ref name="' + g.name + '" />';
}
var pos = text.indexOf(item.full);
if (pos !== -1) {
text = text.slice(0, pos) + neu + text.slice(pos + item.full.length);
ok++;
}
});
setWikitext(text);
setSummary(ok, fail);
if (tab) tab.textContent = 'reFill';
window.alert('reFill finished: ' + ok + ' filled, ' + fail + ' failed.');
showDiff();
});
}
function addSection($textarea) {
if (!$textarea || !$textarea.length || typeof $textarea.wikiEditor !== 'function') return;
if ($textarea.data('refillAdded')) return;
try {
$textarea.wikiEditor('addToToolbar', { sections: { refill: { type: 'toolbar', label: 'reFill' } } });
$textarea.data('refillAdded', true);
} catch (e) {}
}
function bindTab() {
var root = document.querySelector('.wikiEditor-ui-toolbar');
if (!root || root.getAttribute('data-refill-bound')) return;
root.setAttribute('data-refill-bound', '1');
root.addEventListener('click', function (e) {
var t = e.target.closest('.tab-refill, [rel="refill"], .wikiEditor-ui-toolbar [rel="refill"]');
if (!t) return;
e.preventDefault();
e.stopPropagation();
run();
}, true);
}
function start() {
addSection($box());
bindTab();
if (mw.hook) mw.hook('wikiEditor.toolbarReady').add(function ($t) { addSection($t); bindTab(); });
setTimeout(function () { addSection($box()); bindTab(); }, 800);
}
mw.loader.using(['jquery', 'jquery.textSelection', 'ext.wikiEditor']).then(start).catch(start);
})();