MediaWiki:Gadget-figureSource.js: Difference between revisions
Content deleted Content added
Created page with "/* Gadget-figureSource — click-to-source provenance for archived documents. * * Two modes, keyed on the page: * * ARCHIVE PAGES (any page with a published figures sidecar, * Data:<page>.figures.json): every located figure becomes clickable. Click → * an anchored popover showing a CROP of the source PDF with the figure's * stored rectangle boxed in coral, plus its tags (row label, column header, * caption, p./t. address). The wiki row stays visible beside the..." |
No edit summary |
||
Line 36:
var coral = (getComputedStyle(document.documentElement)
.getPropertyValue('--ed-accent') || '#f07662').trim() || '#f07662';
// ── zero-dependency stand-ins for mw.util / mw.loader ──────────────────
// Deliberate: on this wiki mediawiki.util can hang at "loading" under the
// page's heavy module soup (VE, popups, SMW), and a gadget waits FOREVER
// for a declared dependency (found live: the gadget sat at "loaded" while
// its dependency never resolved). This gadget therefore depends on
// nothing beyond the startup config, which is always present before
// gadgets execute.
var SCRIPT = mw.config.get('wgScript'); // "/w/index.php"
var ARTICLE = mw.config.get('wgArticlePath'); // "/wiki/$1"
function esc(s) {
return String(s).replace(/[&<>"']/g, function (ch) {
return '&#' + ch.charCodeAt(0) + ';';
}
function rawUrl(title) {
return SCRIPT + '?title=' + encodeURIComponent(title) + '&action=raw';
}
function pageUrl(title, params) {
if (!params) {
return ARTICLE.replace('$1', encodeURIComponent(title)
.replace(/%2F/g, '/').replace(/%3A/g, ':'));
}
var q = Object.keys(params).map(function (k) {
return k + '=' + encodeURIComponent(params[k]);
return SCRIPT + '?title=' + encodeURIComponent(title) + '&' + q;
}
function addStyle(css) {
var el = document.createElement('style');
el.textContent = css;
document.head.appendChild(el);
}
function getScript(url) {
return new Promise(function (resolve, reject) {
var el = document.createElement('script');
el.src = url;
el.onload = resolve;
el.onerror = reject;
document.head.appendChild(el);
});
}
// ── shared: pdf.js + page-canvas cache ─────────────────────────────────
Line 41 ⟶ 83:
function loadPdfJs() {
if (!pdfjsReady) {
pdfjsReady =
window.pdfjsLib.GlobalWorkerOptions.workerSrc = PDFJS_WORKER;
return window.pdfjsLib;
Line 51 ⟶ 93:
function mirrorUrl(pageTitle) {
var file = pageTitle.replace(/\//g, '-') + '.pdf';
return
}
Line 182 ⟶ 224:
}
if (idx < list.length) {
(list.length - idx) + ' prose figure(s) not found');
}
Line 208 ⟶ 250:
if (!el) { skipped++; return; }
if (cellText(el).indexOf(entry.text) === -1) {
entry.loc.r + 'c' + entry.loc.c +
': DOM says "' + cellText(el) + '", sidecar "' +
Line 223 ⟶ 265:
}), tag);
skipped + ' skipped');
return byEl;
Line 269 ⟶ 311:
}
return parts.map(function (p) {
return '<div class="fig-tag">' +
}).join('');
}
Line 280 ⟶ 322:
? 'no source location (ambiguous ×' + entry.candidates + ')'
: 'no source location (' + (entry.reason || 'unmatched') + ')');
var openHref =
doc: mw.config.get('wgPageName'), fig: entry.id || '' });
pop.innerHTML =
Line 286 ⟶ 328:
(entry.rect ? '<canvas></canvas>' : '') + '</div>' +
tagsHtml(el, entry) +
'<div class="fig-foot"><span>' +
'</span>' + (entry.id ? '<a href="' + openHref +
'" target="_blank">open ↗</a>' : '') + '</div>';
Line 332 ⟶ 374:
// NB: this wiki serves EMPTY 200s for missing action=raw pages —
// existence must be tested on the body, never on r.ok.
fetch(
.then(function (r) { return r.text(); })
.then(function (t) {
Line 347 ⟶ 388:
var page = fig ? fig.page : parseInt(q.get('page') || '1', 10);
root.innerHTML =
'<div class="fig-viewer-bar"><b>' +
'</b> <a href="' +
'">← back</a><span class="fig-viewer-page"></span>' +
'<button class="fig-prev">‹ prev</button>' +
'<button class="fig-next">next ›</button></div>' +
'<div class="fig-stage"
'<canvas></canvas><div class="fig-box"></div></div></div>';
var render = function () {
pdf.page(page).then(function (pg) {
Line 391 ⟶ 433:
function runArchive() {
var title = mw.config.get('wgPageName').replace(/_/g, ' ');
fetch(
.then(function (r) { return r.text(); })
.then(function (t) { // empty 200 = no sidecar here
Line 409 ⟶ 450:
}
'.fig-src{cursor:pointer}' +
'.fig-src:hover{text-decoration:underline;' +
Line 425 ⟶ 466:
'.fig-viewer-bar button{font:inherit;background:#fff;' +
'border:1px solid #0d0d0d;padding:2px 12px;cursor:pointer}' +
'.fig-stage{
// the page frame: ink border so the white page reads as an OBJECT on
'.fig-stage canvas{display:block;margin:0 auto}' +▼
// the white wiki background; position:relative so the coral box is
// canvas-anchored (absolute children resolve against the padding box,
// so the border adds no offset) — centering via the stage no longer
// drifts the box on wide windows
'.fig-page{position:relative;display:inline-block;' +
'border:1px solid #0d0d0d;background:#fff}' +
'.fig-box{position:absolute;display:none;border:3px solid ' + coral +
';pointer-events:none}');
function boot() {
if (mw.config.get('wgPageName') === VIEWER_PAGE.replace(/ /g, '_')) {
▲ runViewer();
runViewer();
} else if (mw.config.get('wgNamespaceNumber') === 0) {
▲ runArchive();
runArchive();
}
}
// A dependency-free gadget can execute before the content is parsed.
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
}());
| |||