User:Most2dot0/common.js
From Angelina Jordan Wiki
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.
//syntax highlighter
mw.loader.load('//www.mediawiki.org/w/index.php?title=MediaWiki:Gadget-DotsSyntaxHighlighter.js&action=raw&ctype=text/javascript');
syntaxHighlighterConfig = { parameterColor: "#FFDD99" }
// Auto-reload page content if marker element is present
$(document).ready(function() {
const marker = document.getElementById('auto-reload-trigger');
if (!marker) return;
const period = Math.max(5, parseInt(marker.dataset.period || "30", 10)); // seconds
const limit = parseInt(marker.dataset.limit || "10", 10); // max reloads
let count = 0;
// Create and insert status display
const statusBox = $('<div id="auto-reload-status" style="margin:10px 0;padding:5px;border:1px solid #ccc;font-size:90%;width:fit-content;background:#f8f8f8;"></div>');
statusBox.text(`Reloads done: ${count} / ${limit}`);
$('#mw-content-text').before(statusBox);
function reloadContent() {
if (count >= limit) {
statusBox.text(`Reload complete (${count}/${limit})`);
return;
}
count++;
statusBox.text(`Reloads done: ${count} / ${limit}`);
$.ajax({
url: location.href,
cache: false,
success: function(data) {
const newContent = $(data).find('#mw-content-text');
if (newContent.length) {
$('#mw-content-text').replaceWith(newContent);
mw.hook('wikipage.content').fire($('#mw-content-text'));
}
}
});
}
setInterval(reloadContent, period * 1000);
});
/* Optimized CargoDynamic gadget */
window.CargoDynamic = (function () {
'use strict';
/* ---------- Utilities ---------- */
const cache = new Map();
const precompiled = new Map();
function resolvePath(obj, path) {
return path.split('.').reduce((o, k) => (o && o[k] !== undefined ? o[k] : null), obj);
}
function compileRegex(p) {
if (!p || precompiled.has(p)) return precompiled.get(p);
try {
const r = new RegExp(String(p).replace(/\\\\/g, '\\').replace(/\\\//g, '/'), 'g');
precompiled.set(p, r);
return r;
} catch {
console.warn('Invalid regex:', p);
return null;
}
}
/* ---------- Formula evaluation ---------- */
function computeField(item, formula) {
// process {field|if|ref} ... {/endif}
function processConditionals(text) {
let out = '', pos = 0;
while (true) {
const m = text.slice(pos).match(/\{([^|}]+)\|if\|([^}]+)\}/);
if (!m) { out += text.slice(pos); break; }
const si = pos + m.index, sj = si + m[0].length;
const field = m[1], ref = m[2];
out += text.slice(pos, si);
let depth = 1, scan = sj, elseStart = null, elseEnd = null, endPos = null;
while (scan < text.length) {
const t = text.slice(scan).match(/\{([^}]+)\}/);
if (!t) break;
const ti = scan + t.index, te = ti + t[0].length, tag = t[1];
if (/^[^|}]+\|if\|/.test(tag)) depth++;
else if (tag === 'else' && depth === 1) { elseStart = ti; elseEnd = te; }
else if (tag === '/endif') {
if (--depth === 0) {
endPos = te;
const thenPart = elseStart ? text.slice(sj, elseStart) : text.slice(sj, ti);
const elsePart = elseStart ? text.slice(elseEnd, ti) : '';
const val = resolvePath(item, field);
const ok = ref === '' ? !!val :
(!isNaN(+val) && !isNaN(+ref) ? +val === +ref : String(val) === ref);
out += processConditionals(ok ? thenPart : elsePart);
pos = te;
break;
}
}
scan = te;
}
if (!endPos) { out += text.slice(si); break; }
}
return out;
}
formula = processConditionals(formula);
// simple {field|pattern|repl|toTitleCase|...}
return formula.replace(/\{([^}]+)\}/g, (_, content) => {
if (/^[^|}]+\|if\|/.test(content) || content === 'else' || content === '/endif') return '';
const parts = content.split('|');
let value = resolvePath(item, parts[0]);
value = value != null ? String(value) : '';
for (let i = 1; i < parts.length; i += 2) {
const pattern = parts[i], repl = parts[i + 1];
if (!pattern) continue;
if (pattern === 'toUpper') value = value.toUpperCase();
else if (pattern === 'toLower') value = value.toLowerCase();
else if (pattern === 'toTitleCase')
value = value.replace(/\b(\w)(\w*)/g, (_, f, r) => f.toUpperCase() + r.toLowerCase());
else if (pattern === 'limit' && repl)
value = value.slice(0, +repl || 0);
else if (repl) {
const regex = compileRegex(pattern);
if (regex)
value = value.replace(regex, (...args) =>
repl.replace(/\$(\d+)/g, (_, n) => args[+n] ?? '')
);
}
}
return value;
});
}
/* ---------- Core ---------- */
function readConfig() {
const div = document.getElementById('cargo-config');
if (!div) return null;
try { return JSON.parse(div.textContent); }
catch (e) { console.error('CargoDynamic: bad JSON', e); return null; }
}
async function loadCargoFor($row) {
const song = $row.data('song');
const qname = $row.data('query');
const rname = $row.data('render');
const cfg = readConfig();
const q = cfg?.queries?.[qname];
const r = cfg?.renderers?.[rname];
const $td = $row.find('td').first();
if (!q || !r) { $td.html('<em>Missing query or renderer.</em>'); return; }
const key = `${qname}|${song}`;
if (cache.has(key)) { $td.html(cache.get(key)); return; }
const api = new URL(mw.util.wikiScript('api'), location.origin);
api.searchParams.set('action', 'cargoquery');
api.searchParams.set('format', 'json');
for (const k in q) api.searchParams.set(k, String(q[k]).replace(/\$song/g, song || ''));
$td.html('<em>Loading…</em>');
try {
const data = await $.getJSON(api.toString());
const rows = (data?.cargoquery || []).map(e => e.title);
if (!rows.length) { $td.html('<em>No results.</em>'); return; }
const table = document.createElement('table');
table.className = 'wikitable sortable';
table.style.width = '100%';
const head = document.createElement('tr');
r.headers.forEach(h => {
const th = document.createElement('th');
th.textContent = h;
head.appendChild(th);
});
table.appendChild(head);
for (const row of rows) {
const tr = document.createElement('tr');
for (const col of r.columns) {
const td = document.createElement('td');
td.innerHTML = computeField(row, col); // preserve HTML
tr.appendChild(td);
}
table.appendChild(tr);
}
cache.set(key, table.outerHTML);
$td.html(table);
} catch {
$td.html('<em>Error loading data.</em>');
}
}
/* ---------- Single observer for all ---------- */
function init() {
const placeholders = document.querySelectorAll('.cargo-placeholder');
if (!placeholders.length) return;
const observer = new MutationObserver(muts => {
for (const m of muts) {
const el = m.target;
if (el.matches('.cargo-placeholder') && $(el).is(':visible') && !el.dataset.loaded) {
el.dataset.loaded = '1';
loadCargoFor($(el));
}
}
});
placeholders.forEach(el => observer.observe(el, {
attributes: true,
attributeFilter: ['style', 'class']
}));
// also trigger load on direct click
$(document).on('click', '.mw-customtoggle', e => {
const id = 'mw-customcollapsible-' + e.target.className.match(/mw-customtoggle-(\S+)/)[1];
const $row = $('#' + id);
setTimeout(() => { if ($row.is(':visible') && !$row.data('loaded')) loadCargoFor($row); }, 100);
});
}
//$(init);
mw.hook('wikipage.content').add(init);
return { computeField };
})();