Spaces:
Running
Running
optional freeform note per ruling
Browse files- review.html +18 -1
review.html
CHANGED
|
@@ -102,6 +102,8 @@ footer .sp { flex: 1; }
|
|
| 102 |
<button class="drop" id="bDrop" onclick="rule('NON_MENTION')">drop <kbd>d</kbd></button>
|
| 103 |
<button class="nav" onclick="move(1)">next →</button>
|
| 104 |
</div>
|
|
|
|
|
|
|
| 105 |
<div id="mine"></div>
|
| 106 |
<footer>
|
| 107 |
<span><kbd>←</kbd><kbd>→</kbd> prev/next mention · <kbd>k</kbd> keep · <kbd>d</kbd> drop · <kbd>space</kbd> skip · <kbd>[</kbd><kbd>]</kbd> slide · <kbd>-</kbd><kbd>+</kbd> size · <kbd>e</kbd> export</span>
|
|
@@ -148,6 +150,8 @@ const USERS = ['paddy', 'aj', 'aivin', 'rafael'];
|
|
| 148 |
const LS = 'review_rulings';
|
| 149 |
let R = JSON.parse(localStorage.getItem(LS) || '{}');
|
| 150 |
let RQ = JSON.parse(localStorage.getItem('review_queues') || '{}');
|
|
|
|
|
|
|
| 151 |
const $ = id => document.getElementById(id);
|
| 152 |
const esc = s => String(s).replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
| 153 |
const QD = document.documentElement;
|
|
@@ -171,6 +175,12 @@ function loadWho() {
|
|
| 171 |
const w = who().toLowerCase();
|
| 172 |
if (USERS.includes(w) && $('ds').value !== w) { $('ds').value = w; loadDs(); }
|
| 173 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
const ht = $('hubtok');
|
| 175 |
if (ht) { ht.value = localStorage.getItem('review_hubtok') || ''; ht.addEventListener('input', () => localStorage.setItem('review_hubtok', ht.value.trim())); }
|
| 176 |
if (localStorage.getItem('review_locked') === '1' && who()) setLocked(true);
|
|
@@ -233,6 +243,7 @@ function render() {
|
|
| 233 |
document.querySelector('#bar > div').style.width = (100 * (i + 1) / ITEMS.length) + '%';
|
| 234 |
$('key').textContent = sp.key;
|
| 235 |
$('surface').textContent = '“' + sp.surface + '”';
|
|
|
|
| 236 |
// sliding assessment window around the active span
|
| 237 |
if (needCenter || wc === null) { wc = (sp.start + sp.end) / 2; needCenter = false; }
|
| 238 |
const W = WSIZES[wi], L = o.text.length;
|
|
@@ -351,6 +362,7 @@ function light() {
|
|
| 351 |
$('prog').textContent = `${i + 1}/${ITEMS.length} · span ${s + 1}/${o.spans.length}`;
|
| 352 |
$('key').textContent = sp.key;
|
| 353 |
$('surface').textContent = '“' + sp.surface + '”';
|
|
|
|
| 354 |
document.querySelectorAll('#passage mark').forEach(m => {
|
| 355 |
m.classList.toggle('active', m.dataset.k === sp.key);
|
| 356 |
m.classList.toggle('done-k', R[m.dataset.k] === 'DATA_MENTION');
|
|
@@ -384,7 +396,9 @@ async function rule(v) {
|
|
| 384 |
localStorage.setItem('review_queues', JSON.stringify(RQ));
|
| 385 |
setLocked(true);
|
| 386 |
paintGate();
|
| 387 |
-
const
|
|
|
|
|
|
|
| 388 |
let saved = 'this browser only';
|
| 389 |
try {
|
| 390 |
const r = await fetch('/api/rulings', {method: 'POST', headers: {'Content-Type': 'application/json'},
|
|
@@ -448,6 +462,7 @@ function exp() {
|
|
| 448 |
const rows = Object.entries(R).map(([key, ruling]) => {
|
| 449 |
const hit = idx[key];
|
| 450 |
const base = {key, ruling, queue: RQ[key] || $('ds').value, annotator: who(),
|
|
|
|
| 451 |
ts: new Date().toISOString().replace(/\.\d+Z$/, '+00:00')};
|
| 452 |
if (!hit) return base;
|
| 453 |
const {o, x} = hit;
|
|
@@ -474,11 +489,13 @@ document.getElementById('impFile').addEventListener('change', async e => {
|
|
| 474 |
if (!it.key || !['DATA_MENTION', 'NON_MENTION'].includes(it.ruling)) continue;
|
| 475 |
R[it.key] = it.ruling;
|
| 476 |
if (it.queue) RQ[it.key] = it.queue;
|
|
|
|
| 477 |
n++;
|
| 478 |
}
|
| 479 |
}
|
| 480 |
localStorage.setItem(LS, JSON.stringify(R));
|
| 481 |
localStorage.setItem('review_queues', JSON.stringify(RQ));
|
|
|
|
| 482 |
// jump to first unruled span
|
| 483 |
let done = false;
|
| 484 |
for (let ni = 0; ni < ITEMS.length && !done; ni++)
|
|
|
|
| 102 |
<button class="drop" id="bDrop" onclick="rule('NON_MENTION')">drop <kbd>d</kbd></button>
|
| 103 |
<button class="nav" onclick="move(1)">next →</button>
|
| 104 |
</div>
|
| 105 |
+
<input id="note" placeholder="note (optional, saved with your ruling)" autocomplete="off"
|
| 106 |
+
style="width:100%;margin-top:10px;font:inherit;color:var(--fg);background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px 12px">
|
| 107 |
<div id="mine"></div>
|
| 108 |
<footer>
|
| 109 |
<span><kbd>←</kbd><kbd>→</kbd> prev/next mention · <kbd>k</kbd> keep · <kbd>d</kbd> drop · <kbd>space</kbd> skip · <kbd>[</kbd><kbd>]</kbd> slide · <kbd>-</kbd><kbd>+</kbd> size · <kbd>e</kbd> export</span>
|
|
|
|
| 150 |
const LS = 'review_rulings';
|
| 151 |
let R = JSON.parse(localStorage.getItem(LS) || '{}');
|
| 152 |
let RQ = JSON.parse(localStorage.getItem('review_queues') || '{}');
|
| 153 |
+
let RN = JSON.parse(localStorage.getItem('review_notes') || '{}');
|
| 154 |
+
function saveNotes() { localStorage.setItem('review_notes', JSON.stringify(RN)); }
|
| 155 |
const $ = id => document.getElementById(id);
|
| 156 |
const esc = s => String(s).replace(/[&<>]/g, c => ({'&':'&','<':'<','>':'>'}[c]));
|
| 157 |
const QD = document.documentElement;
|
|
|
|
| 175 |
const w = who().toLowerCase();
|
| 176 |
if (USERS.includes(w) && $('ds').value !== w) { $('ds').value = w; loadDs(); }
|
| 177 |
});
|
| 178 |
+
$('note').addEventListener('input', () => {
|
| 179 |
+
if (!ITEMS.length) return;
|
| 180 |
+
const v = $('note').value;
|
| 181 |
+
if (v) RN[span().key] = v; else delete RN[span().key];
|
| 182 |
+
saveNotes();
|
| 183 |
+
});
|
| 184 |
const ht = $('hubtok');
|
| 185 |
if (ht) { ht.value = localStorage.getItem('review_hubtok') || ''; ht.addEventListener('input', () => localStorage.setItem('review_hubtok', ht.value.trim())); }
|
| 186 |
if (localStorage.getItem('review_locked') === '1' && who()) setLocked(true);
|
|
|
|
| 243 |
document.querySelector('#bar > div').style.width = (100 * (i + 1) / ITEMS.length) + '%';
|
| 244 |
$('key').textContent = sp.key;
|
| 245 |
$('surface').textContent = '“' + sp.surface + '”';
|
| 246 |
+
if (document.activeElement !== $('note')) $('note').value = RN[sp.key] || '';
|
| 247 |
// sliding assessment window around the active span
|
| 248 |
if (needCenter || wc === null) { wc = (sp.start + sp.end) / 2; needCenter = false; }
|
| 249 |
const W = WSIZES[wi], L = o.text.length;
|
|
|
|
| 362 |
$('prog').textContent = `${i + 1}/${ITEMS.length} · span ${s + 1}/${o.spans.length}`;
|
| 363 |
$('key').textContent = sp.key;
|
| 364 |
$('surface').textContent = '“' + sp.surface + '”';
|
| 365 |
+
if (document.activeElement !== $('note')) $('note').value = RN[sp.key] || '';
|
| 366 |
document.querySelectorAll('#passage mark').forEach(m => {
|
| 367 |
m.classList.toggle('active', m.dataset.k === sp.key);
|
| 368 |
m.classList.toggle('done-k', R[m.dataset.k] === 'DATA_MENTION');
|
|
|
|
| 396 |
localStorage.setItem('review_queues', JSON.stringify(RQ));
|
| 397 |
setLocked(true);
|
| 398 |
paintGate();
|
| 399 |
+
const note = ($('note').value || '').trim();
|
| 400 |
+
if (note) { RN[k] = note; saveNotes(); }
|
| 401 |
+
const entry = {key: k, ruling: v, queue: $('ds').value, annotator: who(), note: note || undefined};
|
| 402 |
let saved = 'this browser only';
|
| 403 |
try {
|
| 404 |
const r = await fetch('/api/rulings', {method: 'POST', headers: {'Content-Type': 'application/json'},
|
|
|
|
| 462 |
const rows = Object.entries(R).map(([key, ruling]) => {
|
| 463 |
const hit = idx[key];
|
| 464 |
const base = {key, ruling, queue: RQ[key] || $('ds').value, annotator: who(),
|
| 465 |
+
note: RN[key] || undefined,
|
| 466 |
ts: new Date().toISOString().replace(/\.\d+Z$/, '+00:00')};
|
| 467 |
if (!hit) return base;
|
| 468 |
const {o, x} = hit;
|
|
|
|
| 489 |
if (!it.key || !['DATA_MENTION', 'NON_MENTION'].includes(it.ruling)) continue;
|
| 490 |
R[it.key] = it.ruling;
|
| 491 |
if (it.queue) RQ[it.key] = it.queue;
|
| 492 |
+
if (it.note) RN[it.key] = it.note;
|
| 493 |
n++;
|
| 494 |
}
|
| 495 |
}
|
| 496 |
localStorage.setItem(LS, JSON.stringify(R));
|
| 497 |
localStorage.setItem('review_queues', JSON.stringify(RQ));
|
| 498 |
+
saveNotes();
|
| 499 |
// jump to first unruled span
|
| 500 |
let done = false;
|
| 501 |
for (let ni = 0; ni < ITEMS.length && !done; ni++)
|