manuscript: Baum's own words shown in green
This commit is contained in:
parent
aa8597bedd
commit
cb0f3a6b1f
1 changed files with 49 additions and 4 deletions
|
|
@ -24,6 +24,10 @@
|
|||
.para.sel{border-color:var(--accent);background:#10141d}
|
||||
.para.changed{border-left:3px solid #3fae74}
|
||||
.para .pn{position:absolute;left:-30px;top:14px;font:11px var(--mono,monospace);color:var(--muted)}
|
||||
.orig{color:#5fd38d}
|
||||
.new{color:var(--ink)}
|
||||
.legend{font-size:12px;color:var(--muted);margin:0 0 8px}
|
||||
.legend .orig{font-weight:600}
|
||||
mark{border-radius:4px;padding:0 2px;cursor:pointer;color:inherit}
|
||||
mark.open{background:#5a4a12}
|
||||
mark.kept{background:#2a2f3a}
|
||||
|
|
@ -189,14 +193,55 @@
|
|||
POLL = setTimeout(function () { openChapter(CH.n, PAGE); }, 4000);
|
||||
}
|
||||
|
||||
// Which words of the paragraph are Baum's. Longest common subsequence on
|
||||
// words (punctuation ignored for the match, kept in the output).
|
||||
function tok(t) { return String(t || "").split(/(\s+)/).filter(function (x) { return x.length; }); }
|
||||
function key(w) { return w.toLowerCase().replace(/[^a-z0-9']/g, ""); }
|
||||
function originalMask(p) {
|
||||
if (p.text === p.original) return null;
|
||||
var a = tok(p.original).filter(function (x) { return !/^\s+$/.test(x); }), b = tok(p.text);
|
||||
var bw = b.map(function (x, i) { return /^\s+$/.test(x) ? null : i; }).filter(function (x) { return x !== null; });
|
||||
var A = a.map(key), B = bw.map(function (i) { return key(b[i]); });
|
||||
var n = A.length, m = B.length, L = [];
|
||||
for (var i = 0; i <= n; i++) { L.push(new Uint16Array(m + 1)); }
|
||||
for (i = n - 1; i >= 0; i--) for (var j = m - 1; j >= 0; j--) L[i][j] = A[i] === B[j] ? L[i + 1][j + 1] + 1 : Math.max(L[i + 1][j], L[i][j + 1]);
|
||||
var mask = b.map(function () { return false; });
|
||||
i = 0; j = 0;
|
||||
while (i < n && j < m) {
|
||||
if (A[i] === B[j]) { mask[bw[j]] = true; i++; j++; }
|
||||
else if (L[i + 1][j] >= L[i][j + 1]) i++; else j++;
|
||||
}
|
||||
for (var k = 0; k < b.length; k++) if (/^\s+$/.test(b[k])) mask[k] = (k > 0 && mask[k - 1]) && (k + 1 < b.length && mask[k + 1]);
|
||||
return { parts: b, mask: mask };
|
||||
}
|
||||
function greened(p) {
|
||||
var om = originalMask(p);
|
||||
if (!om) return '<span class="orig">' + esc(p.text) + '</span>';
|
||||
var out = "", cur = null, buf = "";
|
||||
om.parts.forEach(function (w, i) {
|
||||
var cls = om.mask[i] ? "orig" : "new";
|
||||
if (cls !== cur) { if (cur !== null) out += '<span class="' + cur + '">' + esc(buf) + '</span>'; cur = cls; buf = ""; }
|
||||
buf += w;
|
||||
});
|
||||
if (cur !== null) out += '<span class="' + cur + '">' + esc(buf) + '</span>';
|
||||
return out;
|
||||
}
|
||||
function marked(p) {
|
||||
var text = p.text, html = esc(text), refs = REFS.filter(function (r) { return r.para === p.i; });
|
||||
var text = p.text, html = greened(p), refs = REFS.filter(function (r) { return r.para === p.i; });
|
||||
refs.forEach(function (r) {
|
||||
var q = esc(r.quote), at = html.indexOf(q);
|
||||
if (at >= 0) html = html.slice(0, at) + '<mark class="' + esc(r.status) + '" data-ref="' + esc(r.id) + '" title="' + esc(r.why || "") + '">' + q + '</mark>' + html.slice(at + q.length);
|
||||
html = markIn(html, r.quote, esc(r.status), esc(r.id), esc(r.why || ""));
|
||||
});
|
||||
return html;
|
||||
}
|
||||
// Wrapping a quote in <mark> inside the green spans: the quote is found in
|
||||
// the plain text; the coloured html is searched for the same run of words.
|
||||
function markIn(html, q, cls, id, title) {
|
||||
var plain = q.split(/\s+/).map(function (w) { return esc(w).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); }).join('(?:\\s|<\\/?span[^>]*>)+');
|
||||
var rx = new RegExp(plain);
|
||||
var m = rx.exec(html);
|
||||
if (!m) return html;
|
||||
return html.slice(0, m.index) + '<mark class="' + cls + '" data-ref="' + id + '" title="' + title + '">' + m[0] + '</mark>' + html.slice(m.index + m[0].length);
|
||||
}
|
||||
|
||||
function paintChapter() {
|
||||
$("chtitle").textContent = (CH.n ? "Chapter " + CH.n + " — " : "") + CH.title;
|
||||
|
|
@ -218,7 +263,7 @@
|
|||
$("pagebar").innerHTML = bar.replace('left in the chapter</span>', 'left in the chapter' + printSpan + '</span>');
|
||||
var platesHere = PLATES.filter(function (x) { return pp.length && x.pg >= Math.min.apply(null, pp) && x.pg <= Math.max.apply(null, pp); });
|
||||
var sceneOf = function (i) { var sc = SCENES.find(function (x) { return i >= x.from && i <= x.to; }); return sc ? '<span class="tiny" style="display:block;color:#f5c04b">Scene ' + sc.n + (sc.title ? ' · ' + esc(sc.title) : '') + '</span>' : ''; };
|
||||
$("paras").innerHTML = (platesHere.length ? '<div style="display:flex;gap:10px;flex-wrap:wrap;margin:0 0 12px">' + platesHere.map(function (x) { return '<a href="' + esc(x.url) + '" target="_blank" title="' + esc(x.alt) + ' (Denslow, 1900, p.' + x.pg + ')"><img src="' + esc(x.url) + '" style="height:150px;border-radius:6px;background:#fff" alt="' + esc(x.alt) + '"></a>'; }).join("") + '</div>' : '') + here.map(function (p) {
|
||||
$("paras").innerHTML = '<p class="legend"><span class="orig">Green is Baum\'s own text, word for word.</span> White is what has been written for this adaptation.</p>' + (platesHere.length ? '<div style="display:flex;gap:10px;flex-wrap:wrap;margin:0 0 12px">' + platesHere.map(function (x) { return '<a href="' + esc(x.url) + '" target="_blank" title="' + esc(x.alt) + ' (Denslow, 1900, p.' + x.pg + ')"><img src="' + esc(x.url) + '" style="height:150px;border-radius:6px;background:#fff" alt="' + esc(x.alt) + '"></a>'; }).join("") + '</div>' : '') + here.map(function (p) {
|
||||
var changed = p.text !== p.original;
|
||||
return (SCENES.some(function (x) { return x.from === p.i; }) ? sceneOf(p.i) : '') + '<div class="para' + (changed ? ' changed' : '') + (SEL === p.i ? ' sel' : '') + '" data-para="' + p.i + '" title="' + (p.print_page ? 'first edition p. ' + p.print_page : '') + '"><span class="pn">' + (p.i + 1) + (p.print_page ? '<br>p' + p.print_page : '') + '</span>' + marked(p)
|
||||
+ (changed ? '<div class="was"><b>As Baum wrote it:</b> ' + esc(p.original) + '<br><span class="tiny">changed ' + esc(when(p.changed_at)) + ' by ' + esc(p.changed_by || "") + ' · ' + esc(p.how || "") + ' · revision ' + p.rev + '</span></div>' : '')
|
||||
|
|
|
|||
Loading…
Reference in a new issue