MediaWiki:Common.js: Difference between revisions
Appearance
No edit summary |
No edit summary |
||
| Line 262: | Line 262: | ||
} | } | ||
}()); | }()); | ||
/* SIGNAL Earth: collapse Vector 2022 Appearance panel by default, | |||
while preserving the native control that lets users reopen it. */ | |||
(function () { | |||
function collapseAppearancePanel() { | |||
var appearance = | |||
document.querySelector('#p-appearance') || | |||
document.querySelector('.vector-appearance-landmark') || | |||
document.querySelector('[aria-labelledby="p-appearance-label"]'); | |||
if (!appearance) return; | |||
var buttons = appearance.querySelectorAll('button, input[type="button"], a'); | |||
var hideButton = null; | |||
buttons.forEach(function (button) { | |||
var text = (button.textContent || button.getAttribute('aria-label') || '').trim().toLowerCase(); | |||
if (text === 'hide' || text.includes('hide appearance')) { | |||
hideButton = button; | |||
} | |||
}); | |||
if (hideButton) { | |||
hideButton.click(); | |||
} | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', function () { | |||
setTimeout(collapseAppearancePanel, 250); | |||
}); | |||
} else { | |||
setTimeout(collapseAppearancePanel, 250); | |||
} | |||
})(); | |||
Revision as of 16:29, 26 May 2026
/* Any JavaScript here will be loaded for all users on every page load. */
window.SIGNAL_EARTH_APP_BASE_URL = "http://localhost:3000";
window.SIGNAL_EARTH_API_BASE_URL = "http://localhost:3000";
/* SIGNAL structured-term hovercards. Add to MediaWiki:Common.js.
* Optional configuration before this script runs:
* window.SIGNAL_EARTH_APP_BASE_URL = "http://localhost:3000";
* window.SIGNAL_EARTH_API_BASE_URL = "http://localhost:3000";
*/
(function () {
function getAppBaseUrl() {
return (window.SIGNAL_EARTH_APP_BASE_URL || "http://localhost:3000").replace(/\/$/, "");
}
function getApiBaseUrl() {
return (window.SIGNAL_EARTH_API_BASE_URL || getAppBaseUrl()).replace(/\/$/, "");
}
function escapeHtml(value) {
return String(value == null ? "" : value)
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
function normalizeKey(value) {
return String(value == null ? "" : value)
.toLowerCase()
.replace(/[^a-z0-9]+/g, " ")
.trim();
}
function findStructuredDataTable() {
var tables = Array.prototype.slice.call(document.querySelectorAll('table'));
for (var i = 0; i < tables.length; i += 1) {
var table = tables[i];
var caption = table.querySelector('caption');
var captionText = caption ? caption.textContent || '' : '';
if (/SIGNAL\s+Earth\s+Structured\s+Data/i.test(captionText)) return table;
var text = (table.textContent || '').slice(0, 250);
if (/SIGNAL\s+Earth\s+Structured\s+Data/i.test(text)) return table;
}
return null;
}
function readStructuredDataFromPage() {
var table = findStructuredDataTable();
if (!table) return null;
var data = {};
Array.prototype.slice.call(table.querySelectorAll('tr')).forEach(function (row) {
var th = row.querySelector('th');
var td = row.querySelector('td');
if (!th || !td) return;
var key = normalizeKey(th.textContent || '');
var value = String(td.textContent || '').replace(/\s+/g, ' ').trim();
if (!key || !value || value === '—') return;
data[key] = value;
});
return Object.keys(data).length ? data : null;
}
function payloadFromPageMetadata(fallback) {
var data = readStructuredDataFromPage();
if (!data) return null;
var objectType = data['object type'] || '';
var id = data['signal earth id'] || fallback.id;
var subtitle = objectType || (fallback.type === 'DS' ? 'Damage Signal' : fallback.type === 'DSI' ? 'Damage Signal Instance' : 'SIGNAL structured object');
return {
term: {
type: fallback.type,
id: id,
label: fallback.label || id,
subtitle: subtitle,
observableType: data['observable type'] || null,
unit: data['unit'] || null,
temporalStructure: data['temporal structure'] || null,
monitoringBackbone: data['monitoring backbone'] || null,
geography: data['geography'] || null,
appPath: basicAppPath(fallback.type, id),
wikiUrl: window.location.href.split('#')[0]
}
};
}
function basicAppPath(type, id) {
if (type === "DS") return "/signals/" + encodeURIComponent(id) + "?tab=wiki";
if (type === "DSI") return "/signals/instances/" + encodeURIComponent(id);
return null;
}
function createCard() {
var card = document.createElement("div");
card.className = "signal-term-hovercard";
card.hidden = true;
document.body.appendChild(card);
return card;
}
function placeCard(card, target) {
var rect = target.getBoundingClientRect();
var margin = 10;
var top = rect.bottom + margin;
var left = Math.min(rect.left, window.innerWidth - card.offsetWidth - 16);
left = Math.max(16, left);
if (top + card.offsetHeight > window.innerHeight - 16) {
top = Math.max(16, rect.top - card.offsetHeight - margin);
}
card.style.left = left + "px";
card.style.top = top + "px";
}
function renderLoading(card, label, type, id) {
card.innerHTML =
'<div class="signal-term-hovercard-title">' + escapeHtml(label || id || "SIGNAL object") + '</div>' +
'<div class="signal-term-hovercard-subtitle">SIGNAL structured object' + (type || id ? ': ' + escapeHtml([type, id].filter(Boolean).join(' ')) : '') + '</div>' +
'<div class="signal-term-hovercard-detail">Loading structured object context…</div>';
}
function renderBasic(card, label, type, id) {
var appPath = basicAppPath(type, id);
var links = [];
if (appPath) {
links.push('<a href="' + escapeHtml(getAppBaseUrl() + appPath) + '" target="_blank" rel="noopener noreferrer">Open SIGNAL object</a>');
}
links.push('<a href="' + escapeHtml(window.location.href.split('#')[0]) + '" target="_blank" rel="noopener noreferrer">Open wiki article</a>');
card.innerHTML =
'<div class="signal-term-hovercard-title">' + escapeHtml(label || id || "SIGNAL object") + '</div>' +
'<div class="signal-term-hovercard-subtitle">' + escapeHtml([type, id].filter(Boolean).join(' · ') || 'SIGNAL structured object') + '</div>' +
'<div class="signal-term-hovercard-detail">Structured object metadata is unavailable from the SIGNAL app API.</div>' +
'<div class="signal-term-hovercard-links">' + links.join('') + '</div>';
}
function renderTerm(card, fallback, payload) {
var term = payload && payload.term ? payload.term : null;
if (!term) {
renderBasic(card, fallback.label, fallback.type, fallback.id);
return;
}
var appUrl = term.appPath ? getAppBaseUrl() + term.appPath : null;
var wikiUrl = term.wikiUrl || window.location.href.split('#')[0];
var details = [];
if (term.observableType) details.push(['Observable type', term.observableType]);
if (term.unit) details.push(['Unit', term.unit]);
if (term.temporalStructure) details.push(['Temporal structure', term.temporalStructure]);
if (term.monitoringBackbone) details.push(['Monitoring backbone', term.monitoringBackbone]);
if (term.geography) details.push(['Geography', term.geography]);
var detailHtml = details.length
? details.map(function (row) {
return '<div class="signal-term-hovercard-detail"><strong>' + escapeHtml(row[0]) + ':</strong> ' + escapeHtml(row[1]) + '</div>';
}).join('')
: '<div class="signal-term-hovercard-detail">' + escapeHtml(term.description || 'Structured SIGNAL object.') + '</div>';
var links = [];
if (appUrl) links.push('<a href="' + escapeHtml(appUrl) + '" target="_blank" rel="noopener noreferrer">Open SIGNAL object</a>');
if (wikiUrl) links.push('<a href="' + escapeHtml(wikiUrl) + '" target="_blank" rel="noopener noreferrer">Open wiki article</a>');
card.innerHTML =
'<div class="signal-term-hovercard-title">' + escapeHtml(term.label || fallback.label || fallback.id) + '</div>' +
'<div class="signal-term-hovercard-subtitle">' + escapeHtml(term.subtitle || [fallback.type, fallback.id].filter(Boolean).join(' · ')) + '</div>' +
detailHtml +
'<div class="signal-term-hovercard-links">' + links.join('') + '</div>';
}
function init() {
var card = createCard();
var hideTimer = null;
var activeTarget = null;
function showFor(target) {
window.clearTimeout(hideTimer);
activeTarget = target;
var type = (target.getAttribute('data-signal-type') || '').trim().toUpperCase();
var id = (target.getAttribute('data-signal-id') || '').trim();
var label = (target.getAttribute('data-signal-label') || target.textContent || id || '').trim();
var fallback = { type: type, id: id, label: label };
renderLoading(card, label, type, id);
card.hidden = false;
placeCard(card, target);
if (!type || !id) {
renderBasic(card, label, type, id);
placeCard(card, target);
return;
}
fetch(getApiBaseUrl() + '/api/wiki/signal-term?type=' + encodeURIComponent(type) + '&id=' + encodeURIComponent(id), {
method: 'GET',
mode: 'cors',
credentials: 'omit'
})
.then(function (response) { return response.ok ? response.json() : null; })
.then(function (payload) {
if (activeTarget !== target) return;
renderTerm(card, fallback, payload || payloadFromPageMetadata(fallback));
placeCard(card, target);
})
.catch(function () {
if (activeTarget !== target) return;
renderTerm(card, fallback, payloadFromPageMetadata(fallback));
placeCard(card, target);
});
}
function scheduleHide() {
window.clearTimeout(hideTimer);
hideTimer = window.setTimeout(function () {
card.hidden = true;
activeTarget = null;
}, 150);
}
document.addEventListener('mouseover', function (event) {
var target = event.target && event.target.closest ? event.target.closest('.signal-term') : null;
if (target) showFor(target);
});
document.addEventListener('focusin', function (event) {
var target = event.target && event.target.closest ? event.target.closest('.signal-term') : null;
if (target) showFor(target);
});
document.addEventListener('mouseout', function (event) {
var target = event.target && event.target.closest ? event.target.closest('.signal-term') : null;
if (target && !target.contains(event.relatedTarget) && !card.contains(event.relatedTarget)) scheduleHide();
});
document.addEventListener('focusout', function (event) {
var target = event.target && event.target.closest ? event.target.closest('.signal-term') : null;
if (target) scheduleHide();
});
card.addEventListener('mouseover', function () { window.clearTimeout(hideTimer); });
card.addEventListener('mouseout', function (event) {
if (!card.contains(event.relatedTarget)) scheduleHide();
});
window.addEventListener('scroll', function () {
if (!card.hidden) card.hidden = true;
}, { passive: true });
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
}());
/* SIGNAL Earth: collapse Vector 2022 Appearance panel by default,
while preserving the native control that lets users reopen it. */
(function () {
function collapseAppearancePanel() {
var appearance =
document.querySelector('#p-appearance') ||
document.querySelector('.vector-appearance-landmark') ||
document.querySelector('[aria-labelledby="p-appearance-label"]');
if (!appearance) return;
var buttons = appearance.querySelectorAll('button, input[type="button"], a');
var hideButton = null;
buttons.forEach(function (button) {
var text = (button.textContent || button.getAttribute('aria-label') || '').trim().toLowerCase();
if (text === 'hide' || text.includes('hide appearance')) {
hideButton = button;
}
});
if (hideButton) {
hideButton.click();
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
setTimeout(collapseAppearancePanel, 250);
});
} else {
setTimeout(collapseAppearancePanel, 250);
}
})();