Bot:Gadget-figureSource.js: Difference between revisions

Content deleted Content added
Guard classes updated to the peek surfaces
Prose address: section h2 + chunk title (title lives in the marker paragraph)
 
(3 intermediate revisions by the same user not shown)
Line 203:
var idx = 0, node;
while (idx < list.length && (node = walker.nextNode())) {
if (node.parentElement.closest('table,// .fig-card,src .figin the guard = idempotency: re-banner')) {running continue;the }tagger
// (staging-copy injection during verification) must never
// nest a second wrapper inside an already-tagged figure
if (to && tonode.parentElement.closest &&(
'table, .fig-card, .fig-banner, .fig-src')) { continue; }
if (chunkOf(node) !== parseInt(chunk, 10)) { break; }
var at, from = 0;
Line 300 ⟶ 304:
if (label && label !== el) { parts.push(cellText(label)); }
} else {
// Address for a prose figure, outer -> inner: the enclosing
parts.push('chunk ' + entry.loc.chunk);
// section heading, then the CHUNK TITLE — which lives INSIDE the
// marker's own <p> ("[c. 3; p. 1] Gross written premiums and
// underlying earnings"), so it is the paragraph text minus the
// .ed-chunk span. "chunk 3" appears nowhere: readers get words.
var anchor = document.querySelector(
parts.push( 'span.ed-chunk [id$="-c' + entry.loc.chunk + '"]');
var blk = anchor && anchor.closest('p');
},if true(blk); {
var toup = eblk.relatedTargetpreviousElementSibling;
while (up && !/^H[1-4]$/.test(up.tagName)) {
'_blank')up = up.previousElementSibling;
});
closeif (up); {
parts.push(up.textContent.replace(/\s+/g, ' ').trim());
});
var entryclone = byElblk.getcloneNode(cuetrue);
clone.querySelectorAll('.ed-chunk').forEach(function (s) {
var cue = cueOf s.remove(e);
if (box) { close(}); }
var title = clone.textContent.replace(/\s+/g, ' ').trim();
if (!cuetitle) { returnparts.push(title); }
}
}
// one line, middle-dot separated ("Solvency II ratio (%) · FY24 · …")
Line 336 ⟶ 362:
}
 
function renderCrop(container, entry, pdf, onDone) {
if (!entry.rect) { return; }
pdf.page(entry.page).then(function (pg) {
if (onDone) { setTimeout(onDone, 0); }
var s = pg.viewport.scale;
var x0 = Math.max(0, (entry.rect[0] - CROP_MARGIN_X) * s);
Line 372 ⟶ 397:
}
function onAway(e) {
// clicks INSIDE the surface never dismiss it — the open link
// must be clickable on both surfaces
if (box && if box.contains(!activeCuee.target)) { return; }
if (!cueOf(e)) { close(); }
}
Line 404 ⟶ 432:
}, false);
} else {
// hoverDesktop: CLICK -> peekanchored card besidebelow the cuecell (never covering the cell)original
// UI, which the user preferred over a hover peek (and which the
document.addEventListener('mouseover', function (e) {
// touch banner mirrors). The card carries the open link, so
// unlike the footnote hover popover it is interactive.
document.addEventListener('mouseoverclick', function (e) {
if (!entrybox && box.idcontains(e.target)) { return; }
var cue = cueOf(e);
if (!cue || cue === activeCue) { return; }
e.preventDefault();
e.stopPropagation();
close();
activeCue = cue;
Line 413 ⟶ 447:
box = document.createElement('div');
box.className = 'fig-card';
box.innerHTML = peekHtml(cue, entry, falsetrue);
document.body.appendChild(box);
var// placepage = functioncoordinates (absolute), so the card scrolls WITH {the
// page like the iforiginal; (!boxleft ||clamped activeCuefor !== cue) { return;narrow }viewports
var r = cue.getBoundingClientRect();
var box.style.left = Math.max(8, Math.min(r.left + window.scrollX,
window.innerWidthscrollX -+ boxdocument.offsetWidthdocumentElement.clientWidth - 8));
var top = r.top - box.offsetHeightoffsetWidth - 8;)) + // above the cue…'px';
if (top < 8) { box.style.top = (r.bottom + 8;window.scrollY }+ 6) + // …or below'px';
renderCrop(box.style.left = left, +entry, 'px'pdf);
setTimeout(function () box.style.top = top + 'px';{
} document.addEventListener('click', onAway, true);
place(}, 0);
}, false);
// the crop arrives async and changes the card's height —
document.addEventListener('clickkeydown', function (e) {
// re-place so an above-the-cue card grows UP, not down
//if over(e.key the=== cell'Escape') being{ comparedclose(); }
renderCrop(box, entry, pdf, place);
});
document.addEventListener('mouseout', function (e) {
if (!activeCue) { return; }
var to = e.relatedTarget;
if (to && to.closest &&
to.closest('.fig-src') === activeCue) { return; }
close();
});
window.addEventListener('scroll', function () {
if (box) { close(); }
}, true);
// click = drill into the full viewer (desktop click is reliable)
document.addEventListener('click', function (e) {
var cue = cueOf(e);
if (!cue) { return; }
var entry = byEl.get(cue);
if (!entry.id) { return; }
window.open(pageUrl(VIEWER_PAGE, {
doc: mw.config.get('wgPageName'), fig: entry.id }),
'_blank');
});
}
Line 557 ⟶ 570:
// --text-dark because body-attached elements inherit the SKIN's
// #202122, not the content area's ink.
// absolute (page coords) so it scrolls with the page like the
'.fig-card{position:fixed;z-index:2147483647;' +
// original popover; interactive because it carries the open link
'.fig-card{position:fixedabsolute;z-index:2147483647;' +
'max-width:min(420px,90vw);background:#fff;padding:0;' +
'border:1px solid #0d0d0d;color:var(--text-dark,#0d0d0d);' +
'box-shadow:0 4px 16px rgba(43,41,38,.18);pointer-events:noneauto}' +
// full-bleed crop in both surfaces (declare border/margin — an
// omitted property lets a stale stacked stylesheet rule survive)