Skip to content

Commit f684a75

Browse files
committed
fix: Linters
1 parent 6588b09 commit f684a75

File tree

3 files changed

+55
-59
lines changed

3 files changed

+55
-59
lines changed

manifest.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,11 @@
8888
"description": "Decodes base64-encoded fileuri parameters in resolve URLs to show human-readable S3/GCS/Azure paths",
8989
"path": "decode-s3-paths",
9090
"private": false
91+
},
92+
{
93+
"title": "Countdown timer",
94+
"description": "Shows a countdown progress bar that tracks time spent per task with optional submit blocking",
95+
"path": "countdown-timer",
96+
"private": false
9197
}
9298
]

src/countdown-timer/data.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"data": {
3+
"image": "/static/plugins/src/different-images-per-label/img/demo-sample.png"
4+
}
5+
}
6+

src/countdown-timer/plugin.js

Lines changed: 43 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ async function initCountdownTimer() {
3838
await LSI;
3939

4040
// ── Configuration ──────────────────────────────────────────────────────────
41-
var DURATION_SEC = 300;
42-
var DISABLE_SUBMIT = false;
41+
const DURATION_SEC = 300;
42+
const DISABLE_SUBMIT = false;
4343

4444
// ── Cleanup previous instance ──────────────────────────────────────────────
4545
if (window.__cdtInterval) clearInterval(window.__cdtInterval);
@@ -50,41 +50,40 @@ async function initCountdownTimer() {
5050

5151
// ── Resolve current project & task IDs ─────────────────────────────────────
5252
function getProjectId() {
53-
var m = window.location.pathname.match(/projects\/(\d+)/);
53+
const m = window.location.pathname.match(/projects\/(\d+)/);
5454
return m ? m[1] : "unknown";
5555
}
5656

5757
function getTaskId() {
58-
// LSI.task.id is the most reliable source (works in label stream)
59-
if (LSI.task && LSI.task.id) return String(LSI.task.id);
60-
var params = new URLSearchParams(window.location.search);
58+
if (LSI.task?.id) return String(LSI.task.id);
59+
const params = new URLSearchParams(window.location.search);
6160
return params.get("task") || "unknown";
6261
}
6362

64-
var projectId = getProjectId();
65-
var taskId = getTaskId();
66-
var storageKey = "cdt_" + projectId + "_" + taskId;
63+
const projectId = getProjectId();
64+
const taskId = getTaskId();
65+
const storageKey = `cdt_${projectId}_${taskId}`;
6766

6867
// ── Restore or create remaining seconds ────────────────────────────────────
69-
var saved = localStorage.getItem(storageKey);
70-
var remaining = (saved !== null) ? Math.max(0, parseInt(saved, 10)) : DURATION_SEC;
71-
var BAR_ID = "cdt-timer-bar";
68+
const saved = localStorage.getItem(storageKey);
69+
let remaining = (saved !== null) ? Math.max(0, parseInt(saved, 10)) : DURATION_SEC;
70+
const BAR_ID = "cdt-timer-bar";
7271

7372
// ── Progress bar DOM ───────────────────────────────────────────────────────
74-
var bar = document.createElement("div");
73+
const bar = document.createElement("div");
7574
bar.id = BAR_ID;
7675
bar.style.cssText =
7776
"position:relative;flex:1 1 auto;min-width:120px;height:26px;min-height:26px;" +
7877
"background:#e0e0e0;font-family:system-ui,sans-serif;border-radius:4px;" +
7978
"margin:0 8px;cursor:default;display:flex;align-items:center;justify-content:center;";
8079

81-
var fill = document.createElement("div");
80+
const fill = document.createElement("div");
8281
fill.style.cssText =
8382
"position:absolute;left:0;top:0;height:100%;width:100%;" +
8483
"transition:width 1s linear,background .6s;" +
8584
"background:#43a047;border-radius:4px;";
8685

87-
var label = document.createElement("span");
86+
const label = document.createElement("span");
8887
label.style.cssText =
8988
"position:relative;z-index:1;font-size:12px;font-weight:700;" +
9089
"color:#fff;pointer-events:none;text-shadow:0 1px 2px rgba(0,0,0,.4);";
@@ -94,36 +93,28 @@ async function initCountdownTimer() {
9493
window.__cdtBar = bar;
9594

9695
// ── Inject bar — universal mount chain ───────────────────────────────────
97-
// Mounts INSIDE the annotation panel (left column), not across the full
98-
// page width. Tries anchors in order:
99-
// 1. MIG pagination row (before copy-prev btn or after pagination)
100-
// 2. Annotation panel content area (prepended as first child)
101-
// 3. Fallback: top of document body
10296
function injectBar() {
103-
var old = document.getElementById(BAR_ID);
97+
const old = document.getElementById(BAR_ID);
10498
if (old && old !== bar) old.remove();
10599

106-
// 1. MIG pagination row — insert before copy-prev button or after pagination
107-
var copyBtn = document.getElementById("cpf-copy-btn");
100+
const copyBtn = document.getElementById("cpf-copy-btn");
108101
if (copyBtn) { copyBtn.before(bar); return; }
109102

110-
var pagination = document.querySelector(".lsf-pagination");
111-
if (pagination && pagination.parentElement) {
103+
const pagination = document.querySelector(".lsf-pagination");
104+
if (pagination?.parentElement) {
112105
pagination.parentElement.appendChild(bar);
113106
return;
114107
}
115108

116-
// 2. Annotation panel — the left column that holds image + controls.
117-
// These selectors target the annotation area only (not the side panel).
118-
var selectors = [
109+
const selectors = [
119110
".lsf-main-view__annotation",
120111
"[class*='main-view__annotation']",
121112
".lsf-main-content__task",
122113
"[class*='content__task']",
123114
".lsf-panel__content",
124115
];
125-
for (var i = 0; i < selectors.length; i++) {
126-
var panel = document.querySelector(selectors[i]);
116+
for (let i = 0; i < selectors.length; i++) {
117+
const panel = document.querySelector(selectors[i]);
127118
if (panel) {
128119
bar.style.flex = "none";
129120
bar.style.width = "100%";
@@ -132,17 +123,16 @@ async function initCountdownTimer() {
132123
}
133124
}
134125

135-
// 3. Last resort
136126
document.body.prepend(bar);
137127
}
138128

139129
window.__cdtInjectTimer = setTimeout(injectBar, 600);
140130

141131
// ── Helpers ────────────────────────────────────────────────────────────────
142132
function fmt(sec) {
143-
var m = Math.floor(sec / 60);
144-
var s = sec % 60;
145-
return (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s;
133+
const m = Math.floor(sec / 60);
134+
const s = sec % 60;
135+
return `${m < 10 ? "0" : ""}${m}:${s < 10 ? "0" : ""}${s}`;
146136
}
147137

148138
function barColor(fraction) {
@@ -154,15 +144,15 @@ async function initCountdownTimer() {
154144

155145
function updateTooltip() {
156146
if (remaining > 0) {
157-
var minLeft = Math.ceil(remaining / 60);
158-
bar.title = minLeft + " min remaining.\nThe Submit button will be blocked when time runs out.";
147+
const minLeft = Math.ceil(remaining / 60);
148+
bar.title = `${minLeft} min remaining.\nThe Submit button will be blocked when time runs out.`;
159149
} else {
160150
bar.title = "Time limit exceeded for this task.";
161151
}
162152
}
163153

164154
// ── Submit blocking (only when DISABLE_SUBMIT = true) ──────────────────────
165-
var expired = remaining <= 0;
155+
let expired = remaining <= 0;
166156

167157
if (DISABLE_SUBMIT) {
168158
LSI.on("beforeSaveAnnotation", function () {
@@ -177,11 +167,10 @@ async function initCountdownTimer() {
177167
}
178168

179169
// ── Task change detection (label stream) ───────────────────────────────────
180-
// Poll every 2s: if LSI.task.id changed, re-init the whole plugin.
181170
window.__cdtTaskPoll = setInterval(function () {
182-
var currentTaskId = getTaskId();
171+
const currentTaskId = getTaskId();
183172
if (currentTaskId !== taskId && currentTaskId !== "unknown") {
184-
console.log("[CountdownTimer] Task changed: " + taskId + " → " + currentTaskId + ". Re-initializing.");
173+
console.log(`[CountdownTimer] Task changed: ${taskId}${currentTaskId}. Re-initializing.`);
185174
initCountdownTimer();
186175
}
187176
}, 2000);
@@ -192,8 +181,8 @@ async function initCountdownTimer() {
192181
if (remaining < 0) remaining = 0;
193182
localStorage.setItem(storageKey, String(remaining));
194183

195-
var fraction = remaining / DURATION_SEC;
196-
fill.style.width = (fraction * 100).toFixed(2) + "%";
184+
const fraction = remaining / DURATION_SEC;
185+
fill.style.width = `${(fraction * 100).toFixed(2)}%`;
197186
fill.style.background = barColor(fraction);
198187
label.textContent = fmt(remaining);
199188
updateTooltip();
@@ -215,7 +204,6 @@ async function initCountdownTimer() {
215204
}
216205
}
217206

218-
// Warning-only mode: red bar with exceeded message, submit still works
219207
function onExpiredWarning() {
220208
fill.style.width = "100%";
221209
fill.style.background = "#d32f2f";
@@ -225,7 +213,7 @@ async function initCountdownTimer() {
225213
label.style.letterSpacing = "0.05em";
226214
bar.style.animation = "cdt-pulse 2s ease-in-out 3";
227215

228-
var style = document.getElementById("cdt-pulse-style");
216+
let style = document.getElementById("cdt-pulse-style");
229217
if (!style) {
230218
style = document.createElement("style");
231219
style.id = "cdt-pulse-style";
@@ -235,35 +223,34 @@ async function initCountdownTimer() {
235223

236224
console.log("[CountdownTimer] Time exceeded — warning shown (submit NOT blocked).");
237225
}
238-
239-
// Blocking mode: overlay + submit disabled
226+
240227
function onExpiredBlocking() {
241228
bar.style.display = "none";
242229

243-
var overlay = document.createElement("div");
230+
const overlay = document.createElement("div");
244231
overlay.style.cssText =
245232
"position:fixed;top:0;left:0;width:100%;height:100%;z-index:100000;" +
246233
"background:rgba(0,0,0,.55);display:flex;align-items:center;" +
247234
"justify-content:center;font-family:system-ui,sans-serif;";
248235

249-
var box = document.createElement("div");
236+
const box = document.createElement("div");
250237
box.style.cssText =
251238
"background:#fff;border-radius:12px;padding:32px 48px;text-align:center;" +
252239
"box-shadow:0 8px 32px rgba(0,0,0,.3);max-width:420px;";
253240

254-
var icon = document.createElement("div");
241+
const icon = document.createElement("div");
255242
icon.textContent = "\u23F0";
256243
icon.style.cssText = "font-size:48px;margin-bottom:12px;";
257244

258-
var title = document.createElement("div");
245+
const title = document.createElement("div");
259246
title.textContent = "Time is up!";
260247
title.style.cssText = "font-size:22px;font-weight:800;color:#d32f2f;margin-bottom:8px;";
261248

262-
var msg = document.createElement("div");
249+
const msg = document.createElement("div");
263250
msg.textContent = "Your time has expired. You can no longer submit this task.";
264251
msg.style.cssText = "font-size:14px;color:#555;line-height:1.5;margin-bottom:20px;";
265252

266-
var okBtn = document.createElement("button");
253+
const okBtn = document.createElement("button");
267254
okBtn.textContent = "OK";
268255
okBtn.style.cssText =
269256
"background:#1890ff;color:#fff;border:none;border-radius:6px;" +
@@ -294,20 +281,17 @@ async function initCountdownTimer() {
294281
label.textContent = "00:00";
295282
setTimeout(function () { onExpired(); }, 700);
296283
} else {
297-
var initFraction = remaining / DURATION_SEC;
298-
fill.style.width = (initFraction * 100).toFixed(2) + "%";
284+
const initFraction = remaining / DURATION_SEC;
285+
fill.style.width = `${(initFraction * 100).toFixed(2)}%`;
299286
fill.style.background = barColor(initFraction);
300287
label.textContent = fmt(remaining);
301288
updateTooltip();
302289
window.__cdtInterval = setInterval(tick, 1000);
303290
}
304291

305292
console.log(
306-
"[CountdownTimer] Plugin loaded. Key=" + storageKey +
307-
", remaining=" + fmt(remaining) +
308-
", DISABLE_SUBMIT=" + DISABLE_SUBMIT +
309-
(expired ? " (EXPIRED)" : "") + "."
293+
`[CountdownTimer] Plugin loaded. Key=${storageKey}, remaining=${fmt(remaining)}, DISABLE_SUBMIT=${DISABLE_SUBMIT}${expired ? " (EXPIRED)" : ""}.`
310294
);
311295
}
312296

313-
initCountdownTimer();
297+
initCountdownTimer();

0 commit comments

Comments
 (0)