MediaWiki:Common.js: Difference between revisions
No edit summary |
No edit summary |
||
| (3 intermediate revisions by the same user not shown) | |||
| Line 1,728: | Line 1,728: | ||
if (g.authorIAST) el.setAttribute('data-cite-author-iast', g.authorIAST); | if (g.authorIAST) el.setAttribute('data-cite-author-iast', g.authorIAST); | ||
var h = el.querySelector('.teeka-title'); | var h = el.querySelector('.teeka-title'); | ||
if (h && | var label = teekaLabel(g); | ||
if (h && label) h.textContent = label; | |||
var slug = el.getAttribute('data-teeka-slug'); | |||
if (slug && label) { | |||
labelBySlug[slug] = label; | |||
// A footnote section for this commentary may | |||
// already be on screen; rename it too. | |||
document.querySelectorAll( | |||
'.gr-teeka-notes-group[data-teeka-slug="' + slug + | |||
'"] .gr-teeka-notes-title').forEach(function (t) { | |||
t.textContent = label; | |||
}); | |||
} | |||
}); | }); | ||
}($entry[0], entry.teekaGranthaId, entry.teekaPage)); | }($entry[0], entry.teekaGranthaId, entry.teekaPage)); | ||
| Line 3,007: | Line 3,019: | ||
var h = document.createElement('div'); | var h = document.createElement('div'); | ||
h.className = 'gr-teeka-notes-title'; | h.className = 'gr-teeka-notes-title'; | ||
h.textContent = g.title || g.slug; | h.textContent = (window.grLibrary && window.grLibrary.labelFor(g.slug)) | ||
|| g.title || g.slug; | |||
wrap.appendChild(h); | wrap.appendChild(h); | ||
var ol = document.createElement('ol'); | var ol = document.createElement('ol'); | ||
| Line 3,155: | Line 3,168: | ||
// back to the author baked into the page (usually none). | // back to the author baked into the page (usually none). | ||
var API_BASE = window.GR_LIBRARY_API || '/api/granthas'; | var API_BASE = window.GR_LIBRARY_API || '/api/granthas'; | ||
// Set to false while the library is not yet reachable: no request is | |||
// made at all, and citations use whatever the page itself carries. | |||
// Flip it back to true once API_BASE resolves. | |||
var LIBRARY_ENABLED = true; | |||
// ------------------------------------------------------------------- | // ------------------------------------------------------------------- | ||
var API = API_BASE.replace(/\/+$/, ''); | var API = API_BASE.replace(/\/+$/, ''); | ||
var mem = {}; | var mem = {}; | ||
var warned = false; | var warned = false; | ||
| Line 3,163: | Line 3,179: | ||
// the same answer. Stop asking: a wrong or missing API base otherwise | // the same answer. Stop asking: a wrong or missing API base otherwise | ||
// fills the network log with one 404 per work on the page. | // fills the network log with one 404 per work on the page. | ||
var libraryDown = | var libraryDown = !LIBRARY_ENABLED; | ||
// In memory only, for the life of the page. Names used to be kept in | |||
// sessionStorage for six hours, which quietly outlived any correction | |||
// made in the library or on the map page. They now come from the map | |||
// page, which is read once per load, so there is nothing to gain from | |||
// a second cache that can disagree with it. | |||
function cacheGet(id) { return mem[id] || null; } | |||
function cacheSet(id, v) { mem[id] = v; } | |||
function cacheSet(id, v) { | |||
// The library returns camelCase fields; normalise to what the citation | // The library returns camelCase fields; normalise to what the citation | ||
| Line 3,203: | Line 3,210: | ||
var hit = cacheGet(id); | var hit = cacheGet(id); | ||
if (hit) return Promise.resolve(hit); | if (hit) return Promise.resolve(hit); | ||
// The map page carries the names for every work this wiki uses, so | |||
// normally nothing is fetched from the library at all. That matters | |||
// because the library is someone else's server: we cannot add CORS | |||
// headers to it, and a cross-origin call we cannot configure is a | |||
// call that will not work. The live API stays as an enhancement for | |||
// whenever it IS reachable. | |||
return granthaMap().then(function (m) { | |||
var w = (m.works || {})[id]; | |||
if (w && (w.name || w.nameIAST)) { | |||
var v = { id: id, name: w.name || '', nameIAST: w.nameIAST || '', | |||
author: w.author || '', authorIAST: w.authorIAST || '', | |||
category: w.category || '', prasthana: w.prasthana || '' }; | |||
cacheSet(id, v); | |||
return v; | |||
} | |||
return fetchFromLibrary(id); | |||
}); | |||
} | |||
function fetchFromLibrary(id) { | |||
if (libraryDown) return Promise.resolve(null); | if (libraryDown) return Promise.resolve(null); | ||
if (inflight[id]) return inflight[id]; | if (inflight[id]) return inflight[id]; | ||
| Line 3,256: | Line 3,283: | ||
function granthaMap() { | function granthaMap() { | ||
if (mapPromise) return mapPromise; | if (mapPromise) return mapPromise; | ||
// Deliberately NOT cached in sessionStorage. It was, for six hours, | |||
try { | // which meant that editing the map page changed nothing for anyone | ||
// whose tab had already read the old one - the exact failure you hit | |||
// while setting this up. mapPromise already collapses it to one | |||
// request per page load, and the browser's own HTTP cache handles | |||
// the rest; a stale map is far more expensive than a small fetch. | |||
try { sessionStorage.removeItem('gr-lib:map'); } catch (e) { } | |||
var url = (window.mw && mw.util && mw.util.getUrl) | var url = (window.mw && mw.util && mw.util.getUrl) | ||
| Line 3,270: | Line 3,297: | ||
.then(function (r) { return r.ok ? r.json() : {}; }) | .then(function (r) { return r.ok ? r.json() : {}; }) | ||
.then(function (m) { | .then(function (m) { | ||
// Either a flat {page: id} object, or {pages | // Either a flat {page: id} object, or {pages, works, | ||
// locators | // locators} when names and division names are recorded too. | ||
m = m || {}; | m = m || {}; | ||
if (!m.pages) m = { pages: m | if (!m.pages) m = { pages: m }; | ||
m.works = m.works || {}; | |||
m.locators = m.locators || {}; | m.locators = m.locators || {}; | ||
try { | try { | ||
catch (e) { } | if (!Object.keys(m.works).length) { | ||
console.info('[grantha] the map page has no "works" section, ' + | |||
'so names must come from the library API. Re-run ' + | |||
'sync_grantha_map.py to embed them.'); | |||
} | |||
} catch (e) { } | |||
return m; | return m; | ||
}) | }) | ||
| Line 3,316: | Line 3,349: | ||
return lookup(gid).then(function (g) { | return lookup(gid).then(function (g) { | ||
if (!g) return; | if (!g) return; | ||
// On a commentary's own page the heading still reads as the | |||
// docx colophon ("...विरचिता प्रकाशिका"). Name it the way the | |||
// blocks are named instead. | |||
if (t.getAttribute('data-parent-doc')) { | |||
var lab = teekaLabel(g); | |||
if (lab) t.textContent = lab; | |||
} else if (g.name) { | |||
t.textContent = g.name; | |||
} | |||
if (g.name) t.setAttribute('data-doc-title', g.name); | if (g.name) t.setAttribute('data-doc-title', g.name); | ||
if (g.nameIAST) t.setAttribute('data-doc-title-iast', g.nameIAST); | if (g.nameIAST) t.setAttribute('data-doc-title-iast', g.nameIAST); | ||
| Line 3,351: | Line 3,393: | ||
var want = [], seen = {}; | var want = [], seen = {}; | ||
(ids || []).forEach(function (i) { if (i && !seen[i]) { seen[i] = 1; want.push(i); } }); | (ids || []).forEach(function (i) { if (i && !seen[i]) { seen[i] = 1; want.push(i); } }); | ||
// lookup() answers from the map page without a network call, so | |||
// resolving fifty works costs one shared map fetch, not fifty | |||
// requests. Only ids the map does not name reach the library. | |||
return Promise.all(want.map(lookup)).then(function (vals) { | return Promise.all(want.map(lookup)).then(function (vals) { | ||
var out = {}; | var out = {}; | ||
| Line 3,364: | Line 3,401: | ||
return out; | return out; | ||
}); | }); | ||
} | |||
// How a commentary is named on screen: its author, then the work. | |||
// "श्रीवादिराजतीर्थः: काठकोपनिषद्भाष्यप्रकाशिका" - because several | |||
// commentaries on one moola share a work-type name (प्रकाशिका, | |||
// विवरणम्, खण्डार्थः), and the author is what actually tells them | |||
// apart for a reader scanning down the page. | |||
var labelBySlug = {}; | |||
function teekaLabel(g) { | |||
if (!g) return ''; | |||
var name = g.name || g.nameIAST || ''; | |||
var author = g.author || g.authorIAST || ''; | |||
if (!author || !name) return name || author; | |||
// Nothing to prefix if the work already opens with the author. | |||
if (name.indexOf(author) === 0) return name; | |||
return author + ': ' + name; | |||
} | } | ||
| Line 3,405: | Line 3,458: | ||
window.grLibraryVersion = '2026-09-30a'; | window.grLibraryVersion = '2026-09-30a'; | ||
window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue, | window.grLibrary = { lookup: lookup, lookupMany: lookupMany, catalogue: catalogue, | ||
teekaLabel: teekaLabel, | |||
labelFor: function (slug) { return labelBySlug[slug] || ''; }, | |||
idForPage: idForPage, map: granthaMap, api: API, | idForPage: idForPage, map: granthaMap, api: API, | ||
relabelDocLinks: relabelDocLinks }; | relabelDocLinks: relabelDocLinks }; | ||