MediaWiki:Common.js: Difference between revisions
Appearance
Created page with "→Any JavaScript here will be loaded for all users on every page load.: →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(/\/$/, ""); } func..." |
No edit summary |
||
| Line 1: | Line 1: | ||
/* Any JavaScript here will be loaded for all users on every page load. */ | /* 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. | /* SIGNAL structured-term hovercards. Add to MediaWiki:Common.js. | ||
Revision as of 14:59, 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 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);
placeCard(card, target);
})
.catch(function () {
if (activeTarget !== target) return;
renderBasic(card, label, type, id);
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();
}
}());