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);
});
/* test AJAX load of cargo query templates */
// Reusable function to load one Cargo table
/* global mw, $ */
window.CargoDynamic = (function() {
'use strict';
function computeField(item, formula, resolvePath, matchesFilter) {
resolvePath = resolvePath || function(obj, path) {
return path.split('.').reduce((o, k) => (o && o[k] !== undefined ? o[k] : null), obj);
};
matchesFilter = matchesFilter || function(value, filter) {
if (!filter) return true;
return String(value).includes(filter);
};
function processConditionals(text) {
let out = '', pos = 0;
while (true) {
const match = text.slice(pos).match(/<([^:>]+):if:([^>]+)>/);
if (!match) { out += text.slice(pos); break; }
const si = pos + match.index;
const sj = si + match[0].length;
const field = match[1];
const ref = match[2];
out += text.slice(pos, si);
let depth = 1, scan = sj, elseStart = null, elseEnd = null, endPos = null;
while (scan < text.length) {
const tagMatch = text.slice(scan).match(/<([^>]+)>/);
if (!tagMatch) break;
const ti = scan + tagMatch.index, te = ti + tagMatch[0].length, tag = tagMatch[1];
if (/^[^:>]+:if:/.test(tag)) depth++;
else if (tag === 'else' && depth === 1) { elseStart = ti; elseEnd = te; }
else if (tag === 'endif') {
depth--;
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);
let ok;
if (ref === '') ok = val != null && val !== '';
else {
const v = String(val ?? '');
const vn = Number(v), rn = Number(ref);
ok = (!isNaN(vn) && !isNaN(rn)) ? (vn === rn) : (v === ref);
}
out += processConditionals(ok ? thenPart : elsePart);
pos = te;
break;
}
}
scan = te;
}
if (!endPos) { out += text.slice(si); break; }
}
return out;
}
formula = processConditionals(formula);
formula = formula.replace(/<([^>]+)>/g, function(_, content) {
if (/^[^:>]+:if:/.test(content) || content === 'else' || content === 'endif') return '';
let [fieldPart, filterPart] = content.split('|');
filterPart = filterPart || null;
const parts = fieldPart.split(':');
const fieldPath = parts[0];
let value = resolvePath(item, fieldPath);
value = String(value ?? '');
if (filterPart && !matchesFilter(value, filterPart)) return '';
for (let i = 1; i < parts.length; i += 2) {
const pattern = parts[i], repl = parts[i + 1];
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);
else if (pattern === 'limit' && repl) {
const n = Number(repl);
if (!isNaN(n)) value = value.slice(0, n);
} else if (repl) value = value.replace(new RegExp(pattern, 'g'), repl);
}
return value;
});
return formula;
}
function readConfig() {
var div = document.getElementById('cargo-config');
if (!div) return null;
try {
return JSON.parse(div.textContent);
} catch (e) {
console.error('CargoDynamic: invalid JSON in #cargo-config', e);
return null;
}
}
function loadCargoFor($div) {
var $content = $div.find('.mw-collapsible-content');
var song = $div.data('song');
var queryName = $div.data('query');
var renderName = $div.data('render');
var config = readConfig();
var query = (config && config.queries && config.queries[queryName]) || null;
var renderer = (config && config.renderers && config.renderers[renderName]) || null;
if (!query || !renderer) {
$content.html('<em>Missing query or renderer definition.</em>');
return;
}
var apiUrl = new URL(mw.util.wikiScript('api'), location.origin);
apiUrl.searchParams.set('action', 'cargoquery');
apiUrl.searchParams.set('format', 'json');
Object.keys(query).forEach(function(k) {
// replace $song placeholder in query values
var v = String(query[k]).replace(/\$song/g, song || '');
apiUrl.searchParams.set(k, v);
});
$content.html('<em>Loading…</em>');
$.getJSON(apiUrl.toString()).done(function(data) {
var results = (data && data.cargoquery) ? data.cargoquery.map(function(e) { return e.title; }) : [];
if (!results.length) {
$content.html('<em>No results found.</em>');
return;
}
var rows = results.map(function(r) {
return '<tr>' + renderer.columns.map(function(c) {
return '<td>' + computeField(r, c) + '</td>';
}).join('') + '</tr>';
});
var table = '<table class="wikitable sortable" style="width:100%;">' +
'<tr>' + renderer.headers.map(function(h) { return '<th>' + h + '</th>'; }).join('') + '</tr>' +
rows.join('') + '</table>';
$content.html(table);
}).fail(function() {
$content.html('<em>Error loading data.</em>');
});
}
function init() {
$('.cargo-placeholder').each(function() {
const $div = $(this);
const $content = $div.find('.mw-collapsible-content');
function triggerLoad() {
if ($div.data('loaded')) return;
// diagnostic feedback
$content.html('<em>Loading…</em>');
$div.data('loaded', true);
loadCargoFor($div);
}
// watch visibility change
const observer = new MutationObserver(() => {
if ($content.is(':visible')) triggerLoad();
});
observer.observe(this, {
attributes: true,
attributeFilter: ['class', 'style'],
subtree: true
});
// also bind to click as fallback (in case collapsible does not use style changes)
$div.on('click', function() {
// check short delay after click
setTimeout(() => {
if ($content.is(':visible')) triggerLoad();
}, 400);
});
// initial check
if ($content.is(':visible')) triggerLoad();
});
}
$(init);
return { computeField: computeField, readConfig: readConfig };
})();