@funnycaptcha/embed 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/embed.global.js +715 -117
- package/dist/embed.global.js.map +1 -1
- package/dist/embed.js +3 -0
- package/dist/embed.js.map +1 -1
- package/package.json +15 -12
package/dist/embed.global.js
CHANGED
|
@@ -92,11 +92,12 @@ var FunnyCaptcha = (() => {
|
|
|
92
92
|
return c.answer === userAnswer;
|
|
93
93
|
}
|
|
94
94
|
var STR = {
|
|
95
|
-
zh: { title: "\u8BF7\u8BA1\u7B97", placeholder: "\u8F93\u5165\u7B54\u6848", submit: "\u9A8C\u8BC1", fail: "\u7B54\u9519\u4E86\uFF0C\u6362\u4E00\u9898" },
|
|
96
|
-
en: { title: "Solve", placeholder: "Enter answer", submit: "Verify", fail: "Wrong, try again" }
|
|
95
|
+
zh: { title: "\u8BF7\u8BA1\u7B97", placeholder: "\u8F93\u5165\u7B54\u6848", submit: "\u9A8C\u8BC1", fail: "\u7B54\u9519\u4E86\uFF0C\u6362\u4E00\u9898", refresh: "\u5237\u65B0" },
|
|
96
|
+
en: { title: "Solve", placeholder: "Enter answer", submit: "Verify", fail: "Wrong, try again", refresh: "Refresh" }
|
|
97
97
|
};
|
|
98
98
|
function createMathInstance(container, config) {
|
|
99
99
|
const t = STR[config.locale];
|
|
100
|
+
const theme = config.theme ?? "light";
|
|
100
101
|
let current;
|
|
101
102
|
let listeners = [];
|
|
102
103
|
let startTime = Date.now();
|
|
@@ -104,16 +105,36 @@ var FunnyCaptcha = (() => {
|
|
|
104
105
|
current = generateChallenge();
|
|
105
106
|
startTime = Date.now();
|
|
106
107
|
container.innerHTML = `
|
|
107
|
-
<
|
|
108
|
+
<style>
|
|
109
|
+
.fc-math{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
110
|
+
.fc-math[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
111
|
+
.fc-math[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
112
|
+
.fc-math{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
113
|
+
.fc-math-q{display:block;font-size:14px;font-weight:600;color:var(--fc-text);margin-bottom:12px}
|
|
114
|
+
.fc-math-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
115
|
+
.fc-math-input{flex:1;min-width:0;padding:8px 12px;font-size:14px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text);border-radius:8px;box-sizing:border-box;outline:none}
|
|
116
|
+
.fc-math-input:focus{border-color:var(--fc-accent);box-shadow:0 0 0 3px var(--fc-accent-soft)}
|
|
117
|
+
.fc-math-btn{padding:8px 16px;font-size:14px;border:1px solid var(--fc-accent);background:var(--fc-accent);color:#fff;border-radius:8px;cursor:pointer;transition:opacity .15s}
|
|
118
|
+
.fc-math-btn:hover{opacity:.9}
|
|
119
|
+
.fc-math-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
120
|
+
.fc-math-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
121
|
+
.fc-math-msg{font-size:13px;min-height:18px;margin-top:10px;color:var(--fc-danger)}
|
|
122
|
+
</style>
|
|
123
|
+
<div class="fc-math" data-theme="${theme}">
|
|
108
124
|
<label class="fc-math-q">${t.title}: ${current.question}</label>
|
|
109
|
-
<
|
|
110
|
-
|
|
125
|
+
<div class="fc-math-row">
|
|
126
|
+
<input class="fc-math-input" type="text" inputmode="numeric" placeholder="${t.placeholder}" />
|
|
127
|
+
<button class="fc-math-btn">${t.submit}</button>
|
|
128
|
+
<button class="fc-math-refresh">${t.refresh}</button>
|
|
129
|
+
</div>
|
|
111
130
|
<div class="fc-math-msg"></div>
|
|
112
131
|
</div>
|
|
113
132
|
`;
|
|
114
133
|
const btn = container.querySelector(".fc-math-btn");
|
|
115
134
|
const input = container.querySelector(".fc-math-input");
|
|
116
135
|
const msg = container.querySelector(".fc-math-msg");
|
|
136
|
+
const refreshBtn = container.querySelector(".fc-math-refresh");
|
|
137
|
+
refreshBtn.addEventListener("click", render2);
|
|
117
138
|
btn.addEventListener("click", async () => {
|
|
118
139
|
const val = Number(input.value);
|
|
119
140
|
const success = verifyAnswer(current, val);
|
|
@@ -172,12 +193,16 @@ var FunnyCaptcha = (() => {
|
|
|
172
193
|
zh: { placeholder: "\u8F93\u5165\u56FE\u4E2D\u5B57\u7B26", submit: "\u9A8C\u8BC1", fail: "\u770B\u4E0D\u6E05\uFF1F\u6362\u4E00\u5F20", refresh: "\u5237\u65B0" },
|
|
173
194
|
en: { placeholder: "Type the text", submit: "Verify", fail: "Wrong? Try another", refresh: "Refresh" }
|
|
174
195
|
};
|
|
175
|
-
function drawDistorted(canvas, code) {
|
|
196
|
+
function drawDistorted(canvas, code, surface, text, border) {
|
|
176
197
|
const ctx = canvas.getContext("2d");
|
|
177
198
|
if (!ctx) return;
|
|
178
199
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
179
|
-
ctx.fillStyle =
|
|
200
|
+
ctx.fillStyle = surface;
|
|
180
201
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
202
|
+
for (let i = 0; i < 80; i++) {
|
|
203
|
+
ctx.fillStyle = `rgba(0,0,0,${Math.random() * 0.12})`;
|
|
204
|
+
ctx.fillRect(Math.random() * canvas.width, Math.random() * canvas.height, 1.5, 1.5);
|
|
205
|
+
}
|
|
181
206
|
const chars = code.split("");
|
|
182
207
|
const step = canvas.width / (chars.length + 1);
|
|
183
208
|
chars.forEach((ch, i) => {
|
|
@@ -187,22 +212,34 @@ var FunnyCaptcha = (() => {
|
|
|
187
212
|
ctx.translate(x, y);
|
|
188
213
|
ctx.rotate((Math.random() - 0.5) * 0.5);
|
|
189
214
|
ctx.font = `${24 + Math.floor(Math.random() * 8)}px sans-serif`;
|
|
190
|
-
ctx.fillStyle =
|
|
215
|
+
ctx.fillStyle = text;
|
|
191
216
|
ctx.textAlign = "center";
|
|
192
217
|
ctx.textBaseline = "middle";
|
|
193
218
|
ctx.fillText(ch, 0, 0);
|
|
194
219
|
ctx.restore();
|
|
195
220
|
});
|
|
196
|
-
for (let i = 0; i <
|
|
197
|
-
ctx.strokeStyle =
|
|
221
|
+
for (let i = 0; i < 6; i++) {
|
|
222
|
+
ctx.strokeStyle = border;
|
|
223
|
+
ctx.lineWidth = 1 + Math.random() * 1.5;
|
|
198
224
|
ctx.beginPath();
|
|
199
|
-
|
|
200
|
-
|
|
225
|
+
const x1 = Math.random() * canvas.width;
|
|
226
|
+
const y1 = Math.random() * canvas.height;
|
|
227
|
+
const x2 = Math.random() * canvas.width;
|
|
228
|
+
const y2 = Math.random() * canvas.height;
|
|
229
|
+
const cpx = Math.random() * canvas.width;
|
|
230
|
+
const cpy = Math.random() * canvas.height;
|
|
231
|
+
ctx.moveTo(x1, y1);
|
|
232
|
+
ctx.quadraticCurveTo(cpx, cpy, x2, y2);
|
|
201
233
|
ctx.stroke();
|
|
202
234
|
}
|
|
203
235
|
}
|
|
236
|
+
function readVar(root, name, fallback) {
|
|
237
|
+
const v = getComputedStyle(root).getPropertyValue(name).trim();
|
|
238
|
+
return v || fallback;
|
|
239
|
+
}
|
|
204
240
|
function createTextDistortInstance(container, config) {
|
|
205
241
|
const t = STR2[config.locale];
|
|
242
|
+
const theme = config.theme ?? "light";
|
|
206
243
|
let current;
|
|
207
244
|
let listeners = [];
|
|
208
245
|
let startTime = Date.now();
|
|
@@ -210,16 +247,37 @@ var FunnyCaptcha = (() => {
|
|
|
210
247
|
current = generateChallenge2();
|
|
211
248
|
startTime = Date.now();
|
|
212
249
|
container.innerHTML = `
|
|
213
|
-
<
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
250
|
+
<style>
|
|
251
|
+
.fc-text{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
252
|
+
.fc-text[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
253
|
+
.fc-text[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
254
|
+
.fc-text{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
255
|
+
.fc-text-canvas{display:block;width:100%;max-width:240px;height:60px;border-radius:8px;border:1px solid var(--fc-border);margin-bottom:10px}
|
|
256
|
+
.fc-text-row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
|
|
257
|
+
.fc-text-input{flex:1;min-width:0;padding:8px 12px;font-size:14px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text);border-radius:8px;box-sizing:border-box;outline:none}
|
|
258
|
+
.fc-text-input:focus{border-color:var(--fc-accent);box-shadow:0 0 0 3px var(--fc-accent-soft)}
|
|
259
|
+
.fc-text-btn{padding:8px 16px;font-size:14px;border:1px solid var(--fc-accent);background:var(--fc-accent);color:#fff;border-radius:8px;cursor:pointer;transition:opacity .15s}
|
|
260
|
+
.fc-text-btn:hover{opacity:.9}
|
|
261
|
+
.fc-text-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
262
|
+
.fc-text-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
263
|
+
.fc-text-msg{font-size:13px;min-height:18px;margin-top:10px;color:var(--fc-danger)}
|
|
264
|
+
</style>
|
|
265
|
+
<div class="fc-text" data-theme="${theme}">
|
|
266
|
+
<canvas class="fc-text-canvas" width="240" height="60"></canvas>
|
|
267
|
+
<div class="fc-text-row">
|
|
268
|
+
<input class="fc-text-input" placeholder="${t.placeholder}" />
|
|
269
|
+
<button class="fc-text-btn">${t.submit}</button>
|
|
270
|
+
<button class="fc-text-refresh">${t.refresh}</button>
|
|
271
|
+
</div>
|
|
218
272
|
<div class="fc-text-msg"></div>
|
|
219
273
|
</div>
|
|
220
274
|
`;
|
|
275
|
+
const root = container.querySelector(".fc-text");
|
|
221
276
|
const canvas = container.querySelector(".fc-text-canvas");
|
|
222
|
-
|
|
277
|
+
const surface = readVar(root, "--fc-surface", "#f6f7f9");
|
|
278
|
+
const text = readVar(root, "--fc-text", "#0f172a");
|
|
279
|
+
const border = readVar(root, "--fc-border", "#e2e8f0");
|
|
280
|
+
drawDistorted(canvas, current.code, surface, text, border);
|
|
223
281
|
container.querySelector(".fc-text-refresh").addEventListener("click", render2);
|
|
224
282
|
const btn = container.querySelector(".fc-text-btn");
|
|
225
283
|
const input = container.querySelector(".fc-text-input");
|
|
@@ -274,11 +332,12 @@ var FunnyCaptcha = (() => {
|
|
|
274
332
|
return `${c.token}:completed`;
|
|
275
333
|
}
|
|
276
334
|
var STR3 = {
|
|
277
|
-
zh: { title: "\u62D6\u52A8\u6ED1\u5757\u5230\u6700\u53F3\u7AEF", tip: "\u5411\u53F3\u62D6\u52A8", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u8BF7\u62D6\u5230\u5C3D\u5934" },
|
|
278
|
-
en: { title: "Drag the slider to the right end", tip: "Drag right", success: "Verified", fail: "Drag to the end" }
|
|
335
|
+
zh: { title: "\u62D6\u52A8\u6ED1\u5757\u5230\u6700\u53F3\u7AEF", tip: "\u5411\u53F3\u62D6\u52A8", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u8BF7\u62D6\u5230\u5C3D\u5934", refresh: "\u5237\u65B0" },
|
|
336
|
+
en: { title: "Drag the slider to the right end", tip: "Drag right", success: "Verified", fail: "Drag to the end", refresh: "Refresh" }
|
|
279
337
|
};
|
|
280
338
|
function createSliderInstance(container, config) {
|
|
281
339
|
const t = STR3[config.locale];
|
|
340
|
+
const theme = config.theme ?? "light";
|
|
282
341
|
let current;
|
|
283
342
|
let listeners = [];
|
|
284
343
|
let startTime = Date.now();
|
|
@@ -289,17 +348,26 @@ var FunnyCaptcha = (() => {
|
|
|
289
348
|
done = false;
|
|
290
349
|
container.innerHTML = `
|
|
291
350
|
<style>
|
|
292
|
-
.fc-slider{font-family:-apple-system,system-ui,sans-serif;width:
|
|
293
|
-
.fc-slider-
|
|
294
|
-
.fc-slider-
|
|
295
|
-
.fc-slider
|
|
296
|
-
.fc-slider-
|
|
351
|
+
.fc-slider{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
352
|
+
.fc-slider[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
353
|
+
.fc-slider[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
354
|
+
.fc-slider{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
355
|
+
.fc-slider-title{font-size:14px;color:var(--fc-text);margin-bottom:12px;text-align:center}
|
|
356
|
+
.fc-slider-track{position:relative;height:40px;background:var(--fc-surface);border:1px solid var(--fc-border);border-radius:20px}
|
|
357
|
+
.fc-slider-progress{position:absolute;left:0;top:0;height:100%;width:0;background:var(--fc-accent);border-radius:20px;transition:width .05s,background .15s}
|
|
358
|
+
.fc-slider-handle{position:absolute;left:0;top:50%;transform:translateY(-50%);width:50px;height:50px;background:var(--fc-bg);border:2px solid var(--fc-accent);border-radius:50%;cursor:grab;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(0,0,0,.15);user-select:none;touch-action:none;transition:border-color .15s,color .15s}
|
|
297
359
|
.fc-slider-handle:active{cursor:grabbing}
|
|
298
|
-
.fc-slider-handle::after{content:'\\2192';color
|
|
299
|
-
.fc-slider-tip{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;color
|
|
300
|
-
.fc-slider-msg{font-size:13px;min-height:18px;text-align:center;margin-top:10px;color
|
|
360
|
+
.fc-slider-handle::after{content:'\\2192';color:var(--fc-accent);font-size:18px;font-weight:bold;transition:color .15s}
|
|
361
|
+
.fc-slider-tip{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:13px;color:var(--fc-text-soft);pointer-events:none;transition:opacity .2s}
|
|
362
|
+
.fc-slider-msg{font-size:13px;min-height:18px;text-align:center;margin-top:10px;color:var(--fc-success)}
|
|
363
|
+
.fc-slider-row{display:flex;justify-content:center;margin-top:10px}
|
|
364
|
+
.fc-slider-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
365
|
+
.fc-slider-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
366
|
+
.fc-slider-done .fc-slider-handle{border-color:var(--fc-success)}
|
|
367
|
+
.fc-slider-done .fc-slider-handle::after{color:var(--fc-success)}
|
|
368
|
+
.fc-slider-done .fc-slider-progress{background:var(--fc-success)}
|
|
301
369
|
</style>
|
|
302
|
-
<div class="fc-slider">
|
|
370
|
+
<div class="fc-slider" data-theme="${theme}">
|
|
303
371
|
<div class="fc-slider-title">${t.title}</div>
|
|
304
372
|
<div class="fc-slider-track">
|
|
305
373
|
<div class="fc-slider-progress"></div>
|
|
@@ -307,13 +375,19 @@ var FunnyCaptcha = (() => {
|
|
|
307
375
|
<span class="fc-slider-tip">${t.tip}</span>
|
|
308
376
|
</div>
|
|
309
377
|
<div class="fc-slider-msg"></div>
|
|
378
|
+
<div class="fc-slider-row">
|
|
379
|
+
<button class="fc-slider-refresh">${t.refresh}</button>
|
|
380
|
+
</div>
|
|
310
381
|
</div>
|
|
311
382
|
`;
|
|
383
|
+
const root = container.querySelector(".fc-slider");
|
|
312
384
|
const track = container.querySelector(".fc-slider-track");
|
|
313
385
|
const handle = container.querySelector(".fc-slider-handle");
|
|
314
386
|
const progress = container.querySelector(".fc-slider-progress");
|
|
315
387
|
const tip = container.querySelector(".fc-slider-tip");
|
|
316
388
|
const msg = container.querySelector(".fc-slider-msg");
|
|
389
|
+
const refreshBtn = container.querySelector(".fc-slider-refresh");
|
|
390
|
+
refreshBtn.addEventListener("click", render2);
|
|
317
391
|
let dragging = false;
|
|
318
392
|
let startX = 0;
|
|
319
393
|
let startOffset = 0;
|
|
@@ -349,6 +423,7 @@ var FunnyCaptcha = (() => {
|
|
|
349
423
|
if (currentOffset / maxX >= 0.95) {
|
|
350
424
|
done = true;
|
|
351
425
|
applyPos(maxX);
|
|
426
|
+
root.classList.add("fc-slider-done");
|
|
352
427
|
const result = {
|
|
353
428
|
success: true,
|
|
354
429
|
proof: await hashProof(proofInput(current)),
|
|
@@ -414,17 +489,41 @@ var FunnyCaptcha = (() => {
|
|
|
414
489
|
return c.order.join(",");
|
|
415
490
|
}
|
|
416
491
|
var STR4 = {
|
|
417
|
-
zh: { title: "\u6309 1 \u2192 2 \u2192 3 \u2192 4 \u987A\u5E8F\u70B9\u51FB", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u987A\u5E8F\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5" },
|
|
418
|
-
en: { title: "Click in order 1 \u2192 2 \u2192 3 \u2192 4", success: "Verified", fail: "Wrong order, try again" }
|
|
492
|
+
zh: { title: "\u6309 1 \u2192 2 \u2192 3 \u2192 4 \u987A\u5E8F\u70B9\u51FB", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u987A\u5E8F\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5", refresh: "\u5237\u65B0" },
|
|
493
|
+
en: { title: "Click in order 1 \u2192 2 \u2192 3 \u2192 4", success: "Verified", fail: "Wrong order, try again", refresh: "Refresh" }
|
|
419
494
|
};
|
|
420
|
-
var
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
495
|
+
var AREA_W = 280;
|
|
496
|
+
var AREA_H = 180;
|
|
497
|
+
var BOX_W = 100;
|
|
498
|
+
var BOX_H = 60;
|
|
499
|
+
var MIN_DIST = 110;
|
|
500
|
+
function generateSlots(count) {
|
|
501
|
+
const maxX = AREA_W - BOX_W;
|
|
502
|
+
const maxY = AREA_H - BOX_H;
|
|
503
|
+
const slots = [];
|
|
504
|
+
let attempts = 0;
|
|
505
|
+
while (slots.length < count && attempts < 1e3) {
|
|
506
|
+
attempts++;
|
|
507
|
+
const candidate = { x: Math.random() * maxX, y: Math.random() * maxY };
|
|
508
|
+
let ok = true;
|
|
509
|
+
for (const s of slots) {
|
|
510
|
+
const dx = candidate.x - s.x;
|
|
511
|
+
const dy = candidate.y - s.y;
|
|
512
|
+
if (Math.sqrt(dx * dx + dy * dy) < MIN_DIST) {
|
|
513
|
+
ok = false;
|
|
514
|
+
break;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (ok) slots.push(candidate);
|
|
518
|
+
}
|
|
519
|
+
while (slots.length < count) {
|
|
520
|
+
slots.push({ x: Math.random() * maxX, y: Math.random() * maxY });
|
|
521
|
+
}
|
|
522
|
+
return slots;
|
|
523
|
+
}
|
|
426
524
|
function createClickOrderInstance(container, config) {
|
|
427
525
|
const t = STR4[config.locale];
|
|
526
|
+
const theme = config.theme ?? "light";
|
|
428
527
|
let current;
|
|
429
528
|
let listeners = [];
|
|
430
529
|
let startTime = Date.now();
|
|
@@ -435,30 +534,42 @@ var FunnyCaptcha = (() => {
|
|
|
435
534
|
clicked = [];
|
|
436
535
|
finished = false;
|
|
437
536
|
startTime = Date.now();
|
|
537
|
+
const slots = generateSlots(current.targets.length);
|
|
438
538
|
const boxes = current.targets.map((num, i) => {
|
|
439
|
-
const slot =
|
|
440
|
-
return `<div class="fc-click-box" data-num="${num}" style="left:${slot.x}px;top:${slot.y}px;">${num}</div>`;
|
|
539
|
+
const slot = slots[i];
|
|
540
|
+
return `<div class="fc-click-box" data-num="${num}" style="left:${Math.round(slot.x)}px;top:${Math.round(slot.y)}px;">${num}</div>`;
|
|
441
541
|
}).join("");
|
|
442
542
|
container.innerHTML = `
|
|
443
543
|
<style>
|
|
444
|
-
.fc-click{font-family:-apple-system,system-ui,sans-serif;width:
|
|
445
|
-
.fc-click-
|
|
446
|
-
.fc-click-
|
|
447
|
-
.fc-click
|
|
544
|
+
.fc-click{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
545
|
+
.fc-click[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
546
|
+
.fc-click[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
547
|
+
.fc-click{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
548
|
+
.fc-click-title{font-size:14px;color:var(--fc-text);margin-bottom:12px;text-align:center}
|
|
549
|
+
.fc-click-area{position:relative;width:${AREA_W}px;height:${AREA_H}px;background:var(--fc-surface);border-radius:8px;border:1px solid var(--fc-border);overflow:hidden}
|
|
550
|
+
.fc-click-box{position:absolute;width:${BOX_W}px;height:${BOX_H}px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:bold;color:var(--fc-accent);background:var(--fc-bg);border:2px solid var(--fc-accent);border-radius:8px;cursor:pointer;user-select:none;transition:transform .15s,background .15s,color .15s,border-color .15s}
|
|
448
551
|
.fc-click-box:hover{transform:scale(1.05)}
|
|
449
|
-
.fc-click-box-active{background
|
|
450
|
-
.fc-click-box-done{background
|
|
552
|
+
.fc-click-box-active{background:var(--fc-accent);color:var(--fc-bg)}
|
|
553
|
+
.fc-click-box-done{background:var(--fc-success);border-color:var(--fc-success);color:#fff;cursor:default}
|
|
451
554
|
.fc-click-box-done:hover{transform:none}
|
|
452
|
-
.fc-click-
|
|
555
|
+
.fc-click-row{display:flex;justify-content:center;margin-top:10px}
|
|
556
|
+
.fc-click-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
557
|
+
.fc-click-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
558
|
+
.fc-click-msg{font-size:13px;min-height:18px;text-align:center;margin-top:10px;color:var(--fc-success)}
|
|
453
559
|
</style>
|
|
454
|
-
<div class="fc-click">
|
|
560
|
+
<div class="fc-click" data-theme="${theme}">
|
|
455
561
|
<div class="fc-click-title">${t.title}</div>
|
|
456
562
|
<div class="fc-click-area">${boxes}</div>
|
|
457
563
|
<div class="fc-click-msg"></div>
|
|
564
|
+
<div class="fc-click-row">
|
|
565
|
+
<button class="fc-click-refresh">${t.refresh}</button>
|
|
566
|
+
</div>
|
|
458
567
|
</div>
|
|
459
568
|
`;
|
|
460
569
|
const area = container.querySelector(".fc-click-area");
|
|
461
570
|
const msg = container.querySelector(".fc-click-msg");
|
|
571
|
+
const refreshBtn = container.querySelector(".fc-click-refresh");
|
|
572
|
+
refreshBtn.addEventListener("click", render2);
|
|
462
573
|
area.querySelectorAll(".fc-click-box").forEach((el) => {
|
|
463
574
|
el.addEventListener("click", async () => {
|
|
464
575
|
if (finished) return;
|
|
@@ -527,11 +638,12 @@ var FunnyCaptcha = (() => {
|
|
|
527
638
|
return `${c.angle}:aligned`;
|
|
528
639
|
}
|
|
529
640
|
var STR5 = {
|
|
530
|
-
zh: { title: "\u62D6\u52A8\u6ED1\u5757\u5C06\u56FE\u5F62\u8F6C\u6B63", success: "\u9A8C\u8BC1\u6210\u529F", hint: "\u8BA9\u7BAD\u5934\u6307\u5411\u4E0A\u65B9" },
|
|
531
|
-
en: { title: "Drag to rotate the figure upright", success: "Verified", hint: "Point the arrow up" }
|
|
641
|
+
zh: { title: "\u62D6\u52A8\u6ED1\u5757\u5C06\u56FE\u5F62\u8F6C\u6B63", success: "\u9A8C\u8BC1\u6210\u529F", hint: "\u8BA9\u7BAD\u5934\u6307\u5411\u4E0A\u65B9", refresh: "\u5237\u65B0" },
|
|
642
|
+
en: { title: "Drag to rotate the figure upright", success: "Verified", hint: "Point the arrow up", refresh: "Refresh" }
|
|
532
643
|
};
|
|
533
644
|
function createRotateInstance(container, config) {
|
|
534
645
|
const t = STR5[config.locale];
|
|
646
|
+
const theme = config.theme ?? "light";
|
|
535
647
|
let current;
|
|
536
648
|
let listeners = [];
|
|
537
649
|
let startTime = Date.now();
|
|
@@ -542,31 +654,42 @@ var FunnyCaptcha = (() => {
|
|
|
542
654
|
done = false;
|
|
543
655
|
container.innerHTML = `
|
|
544
656
|
<style>
|
|
545
|
-
.fc-rotate{font-family:-apple-system,system-ui,sans-serif;width:
|
|
546
|
-
.fc-rotate-
|
|
547
|
-
.fc-rotate-
|
|
657
|
+
.fc-rotate{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box;display:flex;flex-direction:column;align-items:center;gap:12px}
|
|
658
|
+
.fc-rotate[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
659
|
+
.fc-rotate[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
660
|
+
.fc-rotate{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
661
|
+
.fc-rotate-title{font-size:14px;color:var(--fc-text);text-align:center}
|
|
662
|
+
.fc-rotate-stage{position:relative;width:140px;height:140px;display:flex;align-items:center;justify-content:center;border:2px dashed var(--fc-border);border-radius:50%}
|
|
548
663
|
.fc-rotate-figure{width:100px;height:100px;transform-origin:center;transition:transform .05s}
|
|
549
|
-
.fc-rotate-marker{position:absolute;top:-6px;left:50%;transform:translateX(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-top:12px solid
|
|
550
|
-
.fc-rotate-hint{font-size:12px;color
|
|
551
|
-
.fc-rotate-range{width:240px;accent-color
|
|
552
|
-
.fc-rotate-msg{font-size:13px;min-height:18px;text-align:center;color
|
|
664
|
+
.fc-rotate-marker{position:absolute;top:-6px;left:50%;transform:translateX(-50%);width:0;height:0;border-left:8px solid transparent;border-right:8px solid transparent;border-top:12px solid var(--fc-danger)}
|
|
665
|
+
.fc-rotate-hint{font-size:12px;color:var(--fc-text-soft)}
|
|
666
|
+
.fc-rotate-range{width:240px;accent-color:var(--fc-accent)}
|
|
667
|
+
.fc-rotate-msg{font-size:13px;min-height:18px;text-align:center;color:var(--fc-success)}
|
|
668
|
+
.fc-rotate-row{display:flex;justify-content:center}
|
|
669
|
+
.fc-rotate-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
670
|
+
.fc-rotate-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
553
671
|
</style>
|
|
554
|
-
<div class="fc-rotate">
|
|
672
|
+
<div class="fc-rotate" data-theme="${theme}">
|
|
555
673
|
<div class="fc-rotate-title">${t.title}</div>
|
|
556
674
|
<div class="fc-rotate-stage">
|
|
557
675
|
<div class="fc-rotate-figure" style="transform: rotate(${current.angle}deg)">
|
|
558
|
-
<svg viewBox="0 0 100 100" width="100" height="100"><
|
|
676
|
+
<svg viewBox="0 0 100 100" width="100" height="100"><path d="M 50 8 L 82 42 L 62 42 L 62 92 L 38 92 L 38 42 Z" fill="var(--fc-accent)"/></svg>
|
|
559
677
|
</div>
|
|
560
678
|
<div class="fc-rotate-marker"></div>
|
|
561
679
|
</div>
|
|
562
680
|
<div class="fc-rotate-hint">${t.hint}</div>
|
|
563
681
|
<input class="fc-rotate-range" type="range" min="0" max="360" step="1" value="0" />
|
|
564
682
|
<div class="fc-rotate-msg"></div>
|
|
683
|
+
<div class="fc-rotate-row">
|
|
684
|
+
<button class="fc-rotate-refresh">${t.refresh}</button>
|
|
685
|
+
</div>
|
|
565
686
|
</div>
|
|
566
687
|
`;
|
|
567
688
|
const figure = container.querySelector(".fc-rotate-figure");
|
|
568
689
|
const range = container.querySelector(".fc-rotate-range");
|
|
569
690
|
const msg = container.querySelector(".fc-rotate-msg");
|
|
691
|
+
const refreshBtn = container.querySelector(".fc-rotate-refresh");
|
|
692
|
+
refreshBtn.addEventListener("click", render2);
|
|
570
693
|
const update = async () => {
|
|
571
694
|
if (done) return;
|
|
572
695
|
const v = Number(range.value);
|
|
@@ -649,11 +772,12 @@ var FunnyCaptcha = (() => {
|
|
|
649
772
|
return sorted.every((v, i) => v === c.diffs[i]);
|
|
650
773
|
}
|
|
651
774
|
var STR6 = {
|
|
652
|
-
zh: { title: "\u627E\u51FA\u4E24\u5904\u4E0D\u540C", hint: "\u70B9\u51FB\u53F3\u4FA7\u7F51\u683C\u4E2D\u4E0D\u540C\u7684\u683C\u5B50", found: "\u5DF2\u627E\u5230", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u627E\u5F97\u4E0D\u5BF9\uFF0C\u518D\u6765\u4E00\u6B21" },
|
|
653
|
-
en: { title: "Spot 2 differences", hint: "Click the differing cells on the right grid", found: "Found", success: "Verified", fail: "Not quite, try again" }
|
|
775
|
+
zh: { title: "\u627E\u51FA\u4E24\u5904\u4E0D\u540C", hint: "\u70B9\u51FB\u53F3\u4FA7\u7F51\u683C\u4E2D\u4E0D\u540C\u7684\u683C\u5B50", found: "\u5DF2\u627E\u5230", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u627E\u5F97\u4E0D\u5BF9\uFF0C\u518D\u6765\u4E00\u6B21", refresh: "\u5237\u65B0" },
|
|
776
|
+
en: { title: "Spot 2 differences", hint: "Click the differing cells on the right grid", found: "Found", success: "Verified", fail: "Not quite, try again", refresh: "Refresh" }
|
|
654
777
|
};
|
|
655
778
|
function createSpotDiffInstance(container, config) {
|
|
656
779
|
const t = STR6[config.locale];
|
|
780
|
+
const theme = config.theme ?? "light";
|
|
657
781
|
let current;
|
|
658
782
|
let listeners = [];
|
|
659
783
|
let startTime = Date.now();
|
|
@@ -670,20 +794,26 @@ var FunnyCaptcha = (() => {
|
|
|
670
794
|
startTime = Date.now();
|
|
671
795
|
container.innerHTML = `
|
|
672
796
|
<style>
|
|
673
|
-
.fc-spot-diff{font-family:-apple-system,system-ui,sans-serif;width:360px;padding:16px;box-sizing:border-box}
|
|
674
|
-
.fc-spot-diff-
|
|
675
|
-
.fc-spot-diff-
|
|
797
|
+
.fc-spot-diff{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
798
|
+
.fc-spot-diff[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
799
|
+
.fc-spot-diff[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
800
|
+
.fc-spot-diff{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
801
|
+
.fc-spot-diff-title{font-size:14px;color:var(--fc-text);margin-bottom:6px;text-align:center;font-weight:600}
|
|
802
|
+
.fc-spot-diff-hint{font-size:12px;color:var(--fc-text-soft);margin-bottom:12px;text-align:center}
|
|
676
803
|
.fc-spot-diff-grids{display:flex;gap:16px;justify-content:center}
|
|
677
|
-
.fc-spot-diff-grid{display:grid;grid-template-columns:repeat(3,48px);grid-template-rows:repeat(3,48px);gap:4px;background
|
|
678
|
-
.fc-spot-diff-cell{display:flex;align-items:center;justify-content:center;font-size:26px;background
|
|
804
|
+
.fc-spot-diff-grid{display:grid;grid-template-columns:repeat(3,48px);grid-template-rows:repeat(3,48px);gap:4px;background:var(--fc-surface);padding:8px;border-radius:8px;border:1px solid var(--fc-border)}
|
|
805
|
+
.fc-spot-diff-cell{display:flex;align-items:center;justify-content:center;font-size:26px;background:var(--fc-bg);border-radius:6px;user-select:none}
|
|
679
806
|
.fc-spot-diff-clickable{cursor:pointer;transition:transform .12s,box-shadow .12s}
|
|
680
807
|
.fc-spot-diff-clickable:hover{transform:scale(1.06)}
|
|
681
|
-
.fc-spot-diff-marked{box-shadow:0 0 0 3px
|
|
682
|
-
.fc-spot-diff-wrong{box-shadow:0 0 0 3px
|
|
683
|
-
.fc-spot-diff-status{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color
|
|
684
|
-
.fc-spot-diff-status-fail{color
|
|
808
|
+
.fc-spot-diff-marked{box-shadow:0 0 0 3px var(--fc-accent) inset;background:var(--fc-accent-soft)}
|
|
809
|
+
.fc-spot-diff-wrong{box-shadow:0 0 0 3px var(--fc-danger) inset;background:var(--fc-danger)}
|
|
810
|
+
.fc-spot-diff-status{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}
|
|
811
|
+
.fc-spot-diff-status-fail{color:var(--fc-danger)}
|
|
812
|
+
.fc-spot-diff-row{display:flex;justify-content:center;margin-top:8px}
|
|
813
|
+
.fc-spot-diff-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
814
|
+
.fc-spot-diff-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
685
815
|
</style>
|
|
686
|
-
<div class="fc-spot-diff">
|
|
816
|
+
<div class="fc-spot-diff" data-theme="${theme}">
|
|
687
817
|
<div class="fc-spot-diff-title">${t.title}</div>
|
|
688
818
|
<div class="fc-spot-diff-hint">${t.hint}</div>
|
|
689
819
|
<div class="fc-spot-diff-grids">
|
|
@@ -691,10 +821,15 @@ var FunnyCaptcha = (() => {
|
|
|
691
821
|
<div class="fc-spot-diff-grid fc-spot-diff-grid-b">${renderGrid(current.gridB, true)}</div>
|
|
692
822
|
</div>
|
|
693
823
|
<div class="fc-spot-diff-status">${t.found} 0/${current.diffs.length}</div>
|
|
824
|
+
<div class="fc-spot-diff-row">
|
|
825
|
+
<button class="fc-spot-diff-refresh">${t.refresh}</button>
|
|
826
|
+
</div>
|
|
694
827
|
</div>
|
|
695
828
|
`;
|
|
696
829
|
const gridB = container.querySelector(".fc-spot-diff-grid-b");
|
|
697
830
|
const status = container.querySelector(".fc-spot-diff-status");
|
|
831
|
+
const refreshBtn = container.querySelector(".fc-spot-diff-refresh");
|
|
832
|
+
refreshBtn.addEventListener("click", render2);
|
|
698
833
|
gridB.querySelectorAll(".fc-spot-diff-clickable").forEach((el) => {
|
|
699
834
|
el.addEventListener("click", async () => {
|
|
700
835
|
if (finished) return;
|
|
@@ -789,11 +924,12 @@ var FunnyCaptcha = (() => {
|
|
|
789
924
|
return c.correct === userPick;
|
|
790
925
|
}
|
|
791
926
|
var STR7 = {
|
|
792
|
-
zh: { title: "\u9009\u51FA\u7B26\u5408\u63CF\u8FF0\u7684\u8868\u60C5", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u9009\u9519\u4E86\uFF0C\u518D\u8BD5\u4E00\u6B21" },
|
|
793
|
-
en: { title: "Pick the face matching the description", success: "Verified", fail: "Wrong, try again" }
|
|
927
|
+
zh: { title: "\u9009\u51FA\u7B26\u5408\u63CF\u8FF0\u7684\u8868\u60C5", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u9009\u9519\u4E86\uFF0C\u518D\u8BD5\u4E00\u6B21", refresh: "\u5237\u65B0" },
|
|
928
|
+
en: { title: "Pick the face matching the description", success: "Verified", fail: "Wrong, try again", refresh: "Refresh" }
|
|
794
929
|
};
|
|
795
930
|
function createEmojiMatchInstance(container, config) {
|
|
796
931
|
const t = STR7[config.locale];
|
|
932
|
+
const theme = config.theme ?? "light";
|
|
797
933
|
let current;
|
|
798
934
|
let listeners = [];
|
|
799
935
|
let startTime = Date.now();
|
|
@@ -807,24 +943,36 @@ var FunnyCaptcha = (() => {
|
|
|
807
943
|
).join("");
|
|
808
944
|
container.innerHTML = `
|
|
809
945
|
<style>
|
|
810
|
-
.fc-emoji-match{font-family:-apple-system,system-ui,sans-serif;width:
|
|
811
|
-
.fc-emoji-match-
|
|
812
|
-
.fc-emoji-match-
|
|
946
|
+
.fc-emoji-match{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
947
|
+
.fc-emoji-match[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
948
|
+
.fc-emoji-match[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
949
|
+
.fc-emoji-match{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
950
|
+
.fc-emoji-match-title{font-size:14px;color:var(--fc-text);margin-bottom:6px;text-align:center;font-weight:600}
|
|
951
|
+
.fc-emoji-match-desc{font-size:15px;color:var(--fc-accent);margin-bottom:14px;text-align:center;font-weight:600}
|
|
813
952
|
.fc-emoji-match-opts{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
|
|
814
|
-
.fc-emoji-match-opt{font-size:30px;height:64px;border:2px solid
|
|
815
|
-
.fc-emoji-match-opt:hover{transform:scale(1.06);border-color
|
|
816
|
-
.fc-emoji-match-opt-wrong{border-color
|
|
817
|
-
.fc-emoji-match-opt-correct{border-color
|
|
818
|
-
.fc-emoji-match-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color
|
|
953
|
+
.fc-emoji-match-opt{font-size:30px;height:64px;border:2px solid var(--fc-border);background:var(--fc-bg);border-radius:10px;cursor:pointer;transition:transform .12s,border-color .12s,background .12s;display:flex;align-items:center;justify-content:center;color:var(--fc-text)}
|
|
954
|
+
.fc-emoji-match-opt:hover{transform:scale(1.06);border-color:var(--fc-accent)}
|
|
955
|
+
.fc-emoji-match-opt-wrong{border-color:var(--fc-danger);background:var(--fc-danger)}
|
|
956
|
+
.fc-emoji-match-opt-correct{border-color:var(--fc-success);background:var(--fc-accent-soft)}
|
|
957
|
+
.fc-emoji-match-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-danger)}
|
|
958
|
+
.fc-emoji-match-msg-ok{color:var(--fc-success)}
|
|
959
|
+
.fc-emoji-match-row{display:flex;justify-content:center;margin-top:8px}
|
|
960
|
+
.fc-emoji-match-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
961
|
+
.fc-emoji-match-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
819
962
|
</style>
|
|
820
|
-
<div class="fc-emoji-match">
|
|
963
|
+
<div class="fc-emoji-match" data-theme="${theme}">
|
|
821
964
|
<div class="fc-emoji-match-title">${t.title}</div>
|
|
822
965
|
<div class="fc-emoji-match-desc">${current.description}</div>
|
|
823
966
|
<div class="fc-emoji-match-opts">${buttons}</div>
|
|
824
967
|
<div class="fc-emoji-match-msg"></div>
|
|
968
|
+
<div class="fc-emoji-match-row">
|
|
969
|
+
<button class="fc-emoji-match-refresh">${t.refresh}</button>
|
|
970
|
+
</div>
|
|
825
971
|
</div>
|
|
826
972
|
`;
|
|
827
973
|
const msg = container.querySelector(".fc-emoji-match-msg");
|
|
974
|
+
const refreshBtn = container.querySelector(".fc-emoji-match-refresh");
|
|
975
|
+
refreshBtn.addEventListener("click", render2);
|
|
828
976
|
container.querySelectorAll(".fc-emoji-match-opt").forEach((el) => {
|
|
829
977
|
el.addEventListener("click", async () => {
|
|
830
978
|
if (finished) return;
|
|
@@ -838,7 +986,7 @@ var FunnyCaptcha = (() => {
|
|
|
838
986
|
proof: await hashProof(current.correct),
|
|
839
987
|
duration: Date.now() - startTime
|
|
840
988
|
};
|
|
841
|
-
msg.
|
|
989
|
+
msg.classList.add("fc-emoji-match-msg-ok");
|
|
842
990
|
msg.textContent = t.success;
|
|
843
991
|
config.onVerify?.(result);
|
|
844
992
|
listeners.forEach((cb) => cb(result));
|
|
@@ -906,11 +1054,12 @@ var FunnyCaptcha = (() => {
|
|
|
906
1054
|
return c.correct === userPick;
|
|
907
1055
|
}
|
|
908
1056
|
var STR8 = {
|
|
909
|
-
zh: { title: "\u6897\u56FE\u95EE\u7B54", hint: "\u9009\u51FA\u6B63\u786E\u7684\u6897\u540D", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u7B54\u9519\u4E86\uFF0C\u6362\u4E00\u9898" },
|
|
910
|
-
en: { title: "Meme Quiz", hint: "Pick the correct meme name", success: "Verified", fail: "Wrong, try again" }
|
|
1057
|
+
zh: { title: "\u6897\u56FE\u95EE\u7B54", hint: "\u9009\u51FA\u6B63\u786E\u7684\u6897\u540D", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u7B54\u9519\u4E86\uFF0C\u6362\u4E00\u9898", refresh: "\u5237\u65B0" },
|
|
1058
|
+
en: { title: "Meme Quiz", hint: "Pick the correct meme name", success: "Verified", fail: "Wrong, try again", refresh: "Refresh" }
|
|
911
1059
|
};
|
|
912
1060
|
function createMemeQuizInstance(container, config) {
|
|
913
1061
|
const t = STR8[config.locale];
|
|
1062
|
+
const theme = config.theme ?? "light";
|
|
914
1063
|
let current;
|
|
915
1064
|
let listeners = [];
|
|
916
1065
|
let startTime = Date.now();
|
|
@@ -924,26 +1073,38 @@ var FunnyCaptcha = (() => {
|
|
|
924
1073
|
).join("");
|
|
925
1074
|
container.innerHTML = `
|
|
926
1075
|
<style>
|
|
927
|
-
.fc-meme-quiz{font-family:-apple-system,system-ui,sans-serif;width:
|
|
928
|
-
.fc-meme-quiz-
|
|
929
|
-
.fc-meme-quiz-
|
|
930
|
-
.fc-meme-quiz
|
|
1076
|
+
.fc-meme-quiz{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
1077
|
+
.fc-meme-quiz[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1078
|
+
.fc-meme-quiz[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1079
|
+
.fc-meme-quiz{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
1080
|
+
.fc-meme-quiz-title{font-size:14px;color:var(--fc-text);margin-bottom:6px;text-align:center;font-weight:600}
|
|
1081
|
+
.fc-meme-quiz-hint{font-size:12px;color:var(--fc-text-soft);margin-bottom:10px;text-align:center}
|
|
1082
|
+
.fc-meme-quiz-q{font-size:24px;color:var(--fc-accent);margin-bottom:14px;text-align:center;letter-spacing:2px}
|
|
931
1083
|
.fc-meme-quiz-opts{display:grid;grid-template-columns:1fr 1fr;gap:10px}
|
|
932
|
-
.fc-meme-quiz-opt{font-size:15px;height:48px;border:2px solid
|
|
933
|
-
.fc-meme-quiz-opt:hover{transform:scale(1.03);border-color
|
|
934
|
-
.fc-meme-quiz-opt-wrong{border-color
|
|
935
|
-
.fc-meme-quiz-opt-correct{border-color
|
|
936
|
-
.fc-meme-quiz-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color
|
|
1084
|
+
.fc-meme-quiz-opt{font-size:15px;height:48px;border:2px solid var(--fc-border);background:var(--fc-bg);border-radius:8px;cursor:pointer;transition:transform .12s,border-color .12s,background .12s;color:var(--fc-text)}
|
|
1085
|
+
.fc-meme-quiz-opt:hover{transform:scale(1.03);border-color:var(--fc-accent)}
|
|
1086
|
+
.fc-meme-quiz-opt-wrong{border-color:var(--fc-danger);background:var(--fc-danger)}
|
|
1087
|
+
.fc-meme-quiz-opt-correct{border-color:var(--fc-success);background:var(--fc-accent-soft)}
|
|
1088
|
+
.fc-meme-quiz-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-danger)}
|
|
1089
|
+
.fc-meme-quiz-msg-ok{color:var(--fc-success)}
|
|
1090
|
+
.fc-meme-quiz-row{display:flex;justify-content:center;margin-top:8px}
|
|
1091
|
+
.fc-meme-quiz-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
1092
|
+
.fc-meme-quiz-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
937
1093
|
</style>
|
|
938
|
-
<div class="fc-meme-quiz">
|
|
1094
|
+
<div class="fc-meme-quiz" data-theme="${theme}">
|
|
939
1095
|
<div class="fc-meme-quiz-title">${t.title}</div>
|
|
940
1096
|
<div class="fc-meme-quiz-hint">${t.hint}</div>
|
|
941
1097
|
<div class="fc-meme-quiz-q">${current.question}</div>
|
|
942
1098
|
<div class="fc-meme-quiz-opts">${buttons}</div>
|
|
943
1099
|
<div class="fc-meme-quiz-msg"></div>
|
|
1100
|
+
<div class="fc-meme-quiz-row">
|
|
1101
|
+
<button class="fc-meme-quiz-refresh">${t.refresh}</button>
|
|
1102
|
+
</div>
|
|
944
1103
|
</div>
|
|
945
1104
|
`;
|
|
946
1105
|
const msg = container.querySelector(".fc-meme-quiz-msg");
|
|
1106
|
+
const refreshBtn = container.querySelector(".fc-meme-quiz-refresh");
|
|
1107
|
+
refreshBtn.addEventListener("click", render2);
|
|
947
1108
|
container.querySelectorAll(".fc-meme-quiz-opt").forEach((el) => {
|
|
948
1109
|
el.addEventListener("click", async () => {
|
|
949
1110
|
if (finished) return;
|
|
@@ -957,7 +1118,7 @@ var FunnyCaptcha = (() => {
|
|
|
957
1118
|
proof: await hashProof(current.correct),
|
|
958
1119
|
duration: Date.now() - startTime
|
|
959
1120
|
};
|
|
960
|
-
msg.
|
|
1121
|
+
msg.classList.add("fc-meme-quiz-msg-ok");
|
|
961
1122
|
msg.textContent = t.success;
|
|
962
1123
|
config.onVerify?.(result);
|
|
963
1124
|
listeners.forEach((cb) => cb(result));
|
|
@@ -1014,7 +1175,8 @@ var FunnyCaptcha = (() => {
|
|
|
1014
1175
|
time: "\u5269\u4F59",
|
|
1015
1176
|
sec: "\u79D2",
|
|
1016
1177
|
success: "\u9A8C\u8BC1\u6210\u529F\uFF01\u4F60\u679C\u7136\u662F\u4EBA\u7C7B",
|
|
1017
|
-
fail: "\u65F6\u95F4\u5230\uFF0C\u6CA1\u8FBE\u5230\u76EE\u6807\u5206\u6570"
|
|
1178
|
+
fail: "\u65F6\u95F4\u5230\uFF0C\u6CA1\u8FBE\u5230\u76EE\u6807\u5206\u6570",
|
|
1179
|
+
restart: "\u91CD\u65B0\u5F00\u59CB"
|
|
1018
1180
|
},
|
|
1019
1181
|
en: {
|
|
1020
1182
|
title: "Whack-a-Mole",
|
|
@@ -1023,12 +1185,16 @@ var FunnyCaptcha = (() => {
|
|
|
1023
1185
|
time: "Time",
|
|
1024
1186
|
sec: "s",
|
|
1025
1187
|
success: "Verified! You are human.",
|
|
1026
|
-
fail: "Time's up, target not reached"
|
|
1188
|
+
fail: "Time's up, target not reached",
|
|
1189
|
+
restart: "Restart"
|
|
1027
1190
|
}
|
|
1028
1191
|
};
|
|
1029
1192
|
var HOLE_COUNT = 9;
|
|
1193
|
+
var MOLE_MIN_DELAY = 600;
|
|
1194
|
+
var MOLE_MAX_DELAY = 1200;
|
|
1030
1195
|
function createMiniGameInstance(container, config) {
|
|
1031
1196
|
const t = STR9[config.locale];
|
|
1197
|
+
const theme = config.theme ?? "light";
|
|
1032
1198
|
let current;
|
|
1033
1199
|
let listeners = [];
|
|
1034
1200
|
let startTime = Date.now();
|
|
@@ -1045,7 +1211,7 @@ var FunnyCaptcha = (() => {
|
|
|
1045
1211
|
countdownTimer = null;
|
|
1046
1212
|
}
|
|
1047
1213
|
if (moleTimer) {
|
|
1048
|
-
|
|
1214
|
+
clearTimeout(moleTimer);
|
|
1049
1215
|
moleTimer = null;
|
|
1050
1216
|
}
|
|
1051
1217
|
if (hideTimer) {
|
|
@@ -1093,6 +1259,14 @@ var FunnyCaptcha = (() => {
|
|
|
1093
1259
|
if (activeHole === next) activeHole = -1;
|
|
1094
1260
|
}, 700);
|
|
1095
1261
|
}
|
|
1262
|
+
function scheduleMole() {
|
|
1263
|
+
if (ended) return;
|
|
1264
|
+
const delay = MOLE_MIN_DELAY + Math.random() * (MOLE_MAX_DELAY - MOLE_MIN_DELAY);
|
|
1265
|
+
moleTimer = setTimeout(() => {
|
|
1266
|
+
showMole();
|
|
1267
|
+
scheduleMole();
|
|
1268
|
+
}, delay);
|
|
1269
|
+
}
|
|
1096
1270
|
function startGame() {
|
|
1097
1271
|
clearTimers();
|
|
1098
1272
|
score = 0;
|
|
@@ -1114,8 +1288,8 @@ var FunnyCaptcha = (() => {
|
|
|
1114
1288
|
void endGame(verifyScore(current, score));
|
|
1115
1289
|
}
|
|
1116
1290
|
}, 100);
|
|
1117
|
-
moleTimer = setInterval(showMole, 850);
|
|
1118
1291
|
showMole();
|
|
1292
|
+
scheduleMole();
|
|
1119
1293
|
}
|
|
1120
1294
|
function render2() {
|
|
1121
1295
|
current = generateChallenge9();
|
|
@@ -1129,19 +1303,25 @@ var FunnyCaptcha = (() => {
|
|
|
1129
1303
|
).join("");
|
|
1130
1304
|
container.innerHTML = `
|
|
1131
1305
|
<style>
|
|
1132
|
-
.fc-mini-game{font-family:-apple-system,system-ui,sans-serif;width:
|
|
1133
|
-
.fc-mini-game-
|
|
1134
|
-
.fc-mini-game-
|
|
1135
|
-
.fc-mini-game
|
|
1136
|
-
.fc-mini-game-
|
|
1137
|
-
.fc-mini-game-
|
|
1306
|
+
.fc-mini-game{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
1307
|
+
.fc-mini-game[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1308
|
+
.fc-mini-game[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1309
|
+
.fc-mini-game{background:var(--fc-bg);border:1px solid var(--fc-border);border-radius:10px;color:var(--fc-text)}
|
|
1310
|
+
.fc-mini-game-title{font-size:15px;color:var(--fc-text);margin-bottom:4px;text-align:center;font-weight:600}
|
|
1311
|
+
.fc-mini-game-rule{font-size:12px;color:var(--fc-text-soft);margin-bottom:10px;text-align:center}
|
|
1312
|
+
.fc-mini-game-hud{display:flex;justify-content:space-between;font-size:13px;color:var(--fc-text-soft);margin-bottom:10px}
|
|
1313
|
+
.fc-mini-game-hud b{color:var(--fc-accent)}
|
|
1314
|
+
.fc-mini-game-board{display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);gap:8px;width:300px;height:300px;margin:0 auto;background:var(--fc-surface);border:1px solid var(--fc-border);border-radius:10px;padding:8px;box-sizing:border-box}
|
|
1138
1315
|
.fc-mini-game-hole{position:relative;background:radial-gradient(circle at 50% 60%,#6d4c41,#4e342e);border-radius:50%;display:flex;align-items:flex-end;justify-content:center;overflow:hidden;cursor:pointer;user-select:none}
|
|
1139
1316
|
.fc-mini-game-hole:hover{background:radial-gradient(circle at 50% 60%,#7d5c51,#5e443e)}
|
|
1140
1317
|
.fc-mini-game-mole{font-size:34px;line-height:1;transform:translateY(120%);transition:transform .12s ease-out;pointer-events:none}
|
|
1141
1318
|
.fc-mini-game-mole-active .fc-mini-game-mole{transform:translateY(0)}
|
|
1142
|
-
.fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color
|
|
1319
|
+
.fc-mini-game-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}
|
|
1320
|
+
.fc-mini-game-row{display:flex;justify-content:center;margin-top:8px}
|
|
1321
|
+
.fc-mini-game-restart{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer}
|
|
1322
|
+
.fc-mini-game-restart:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
1143
1323
|
</style>
|
|
1144
|
-
<div class="fc-mini-game">
|
|
1324
|
+
<div class="fc-mini-game" data-theme="${theme}">
|
|
1145
1325
|
<div class="fc-mini-game-title">${t.title}</div>
|
|
1146
1326
|
<div class="fc-mini-game-rule">${t.rule}</div>
|
|
1147
1327
|
<div class="fc-mini-game-hud">
|
|
@@ -1150,8 +1330,13 @@ var FunnyCaptcha = (() => {
|
|
|
1150
1330
|
</div>
|
|
1151
1331
|
<div class="fc-mini-game-board">${holes}</div>
|
|
1152
1332
|
<div class="fc-mini-game-msg"></div>
|
|
1333
|
+
<div class="fc-mini-game-row">
|
|
1334
|
+
<button class="fc-mini-game-restart">${t.restart}</button>
|
|
1335
|
+
</div>
|
|
1153
1336
|
</div>
|
|
1154
1337
|
`;
|
|
1338
|
+
const restartBtn = container.querySelector(".fc-mini-game-restart");
|
|
1339
|
+
restartBtn.addEventListener("click", startGame);
|
|
1155
1340
|
container.querySelectorAll(".fc-mini-game-hole").forEach((el) => {
|
|
1156
1341
|
el.addEventListener("click", () => {
|
|
1157
1342
|
if (ended) return;
|
|
@@ -1233,6 +1418,7 @@ var FunnyCaptcha = (() => {
|
|
|
1233
1418
|
};
|
|
1234
1419
|
function createAntiBotInstance(container, config) {
|
|
1235
1420
|
const t = STR10[config.locale];
|
|
1421
|
+
const theme = config.theme ?? "light";
|
|
1236
1422
|
let current;
|
|
1237
1423
|
let listeners = [];
|
|
1238
1424
|
let startTime = Date.now();
|
|
@@ -1270,17 +1456,20 @@ var FunnyCaptcha = (() => {
|
|
|
1270
1456
|
}
|
|
1271
1457
|
container.innerHTML = `
|
|
1272
1458
|
<style>
|
|
1273
|
-
.fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;width:
|
|
1274
|
-
.fc-anti-bot-
|
|
1275
|
-
.fc-anti-bot-
|
|
1276
|
-
.fc-anti-bot
|
|
1277
|
-
.fc-anti-bot-
|
|
1278
|
-
.fc-anti-bot-
|
|
1279
|
-
.fc-anti-bot-
|
|
1280
|
-
.fc-anti-bot-
|
|
1281
|
-
.fc-anti-bot-
|
|
1459
|
+
.fc-anti-bot{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:20px;box-sizing:border-box;border:1px solid var(--fc-border);border-radius:10px;background:var(--fc-surface)}
|
|
1460
|
+
.fc-anti-bot[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1461
|
+
.fc-anti-bot[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1462
|
+
.fc-anti-bot{color:var(--fc-text)}
|
|
1463
|
+
.fc-anti-bot-title{font-size:18px;color:var(--fc-text);text-align:center;font-weight:600;margin-bottom:6px}
|
|
1464
|
+
.fc-anti-bot-subtitle{font-size:12px;color:var(--fc-text-soft);text-align:center;margin-bottom:16px}
|
|
1465
|
+
.fc-anti-bot-btn{display:block;width:100%;padding:12px;font-size:15px;border:1px solid var(--fc-border);background:var(--fc-bg);border-radius:6px;cursor:pointer;color:var(--fc-text);transition:background .15s,border-color .15s}
|
|
1466
|
+
.fc-anti-bot-btn:hover:not(:disabled){background:var(--fc-accent-soft);border-color:var(--fc-accent)}
|
|
1467
|
+
.fc-anti-bot-btn:disabled{cursor:default;color:var(--fc-success);border-color:var(--fc-success);background:var(--fc-accent-soft)}
|
|
1468
|
+
.fc-anti-bot-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-success)}
|
|
1469
|
+
.fc-anti-bot-hint{font-size:11px;color:var(--fc-text-soft);margin-top:14px;text-align:center;line-height:1.5;font-style:italic}
|
|
1470
|
+
.fc-anti-bot-footer{font-size:10px;color:var(--fc-text-soft);margin-top:8px;text-align:center;opacity:.7}
|
|
1282
1471
|
</style>
|
|
1283
|
-
<div class="fc-anti-bot">
|
|
1472
|
+
<div class="fc-anti-bot" data-theme="${theme}">
|
|
1284
1473
|
<div class="fc-anti-bot-title">${t.title}</div>
|
|
1285
1474
|
<div class="fc-anti-bot-subtitle">${t.subtitle}</div>
|
|
1286
1475
|
<button class="fc-anti-bot-btn">${t.btn}</button>
|
|
@@ -1330,6 +1519,415 @@ var FunnyCaptcha = (() => {
|
|
|
1330
1519
|
};
|
|
1331
1520
|
defineCaptcha(antiBotPlugin);
|
|
1332
1521
|
|
|
1522
|
+
// ../captchas/click-text/dist/index.js
|
|
1523
|
+
var COMMON_CHARS = [
|
|
1524
|
+
"\u7684",
|
|
1525
|
+
"\u4E86",
|
|
1526
|
+
"\u662F",
|
|
1527
|
+
"\u5728",
|
|
1528
|
+
"\u6709",
|
|
1529
|
+
"\u4EBA",
|
|
1530
|
+
"\u8FD9",
|
|
1531
|
+
"\u4E2D",
|
|
1532
|
+
"\u5927",
|
|
1533
|
+
"\u4E3A",
|
|
1534
|
+
"\u4E0A",
|
|
1535
|
+
"\u4E2A",
|
|
1536
|
+
"\u56FD",
|
|
1537
|
+
"\u6211",
|
|
1538
|
+
"\u4EE5",
|
|
1539
|
+
"\u8981",
|
|
1540
|
+
"\u4ED6",
|
|
1541
|
+
"\u65F6",
|
|
1542
|
+
"\u6765",
|
|
1543
|
+
"\u7528",
|
|
1544
|
+
"\u4EEC",
|
|
1545
|
+
"\u5730",
|
|
1546
|
+
"\u5230",
|
|
1547
|
+
"\u53EF"
|
|
1548
|
+
];
|
|
1549
|
+
var AREA_W2 = 320;
|
|
1550
|
+
var AREA_H2 = 200;
|
|
1551
|
+
var MARGIN = 30;
|
|
1552
|
+
var MIN_DIST2 = 100;
|
|
1553
|
+
function randInt6(min, max) {
|
|
1554
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1555
|
+
}
|
|
1556
|
+
function shuffle5(arr) {
|
|
1557
|
+
const a = arr.slice();
|
|
1558
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
1559
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1560
|
+
const tmp = a[i];
|
|
1561
|
+
a[i] = a[j];
|
|
1562
|
+
a[j] = tmp;
|
|
1563
|
+
}
|
|
1564
|
+
return a;
|
|
1565
|
+
}
|
|
1566
|
+
function placePositions(count) {
|
|
1567
|
+
const positions = [];
|
|
1568
|
+
let attempts = 0;
|
|
1569
|
+
while (positions.length < count && attempts < 2e3) {
|
|
1570
|
+
attempts++;
|
|
1571
|
+
const x = MARGIN + Math.random() * (AREA_W2 - MARGIN * 2);
|
|
1572
|
+
const y = MARGIN + Math.random() * (AREA_H2 - MARGIN * 2);
|
|
1573
|
+
const ok = positions.every((p) => Math.hypot(p.x - x, p.y - y) >= MIN_DIST2);
|
|
1574
|
+
if (ok) positions.push({ x, y });
|
|
1575
|
+
}
|
|
1576
|
+
return positions;
|
|
1577
|
+
}
|
|
1578
|
+
function generateChallenge11() {
|
|
1579
|
+
const count = randInt6(3, 4);
|
|
1580
|
+
const chars = shuffle5(COMMON_CHARS).slice(0, count);
|
|
1581
|
+
const positions = placePositions(count);
|
|
1582
|
+
return { chars, positions };
|
|
1583
|
+
}
|
|
1584
|
+
function proofInput6(c) {
|
|
1585
|
+
return `${c.chars.join("")}:completed`;
|
|
1586
|
+
}
|
|
1587
|
+
var STR11 = {
|
|
1588
|
+
zh: { title: "\u8BF7\u6309\u987A\u5E8F\u70B9\u51FB", success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u987A\u5E8F\u9519\u8BEF\uFF0C\u8BF7\u91CD\u8BD5", refresh: "\u5237\u65B0" },
|
|
1589
|
+
en: { title: "Click in order", success: "Verified", fail: "Wrong order, try again", refresh: "Refresh" }
|
|
1590
|
+
};
|
|
1591
|
+
function createClickTextInstance(container, config) {
|
|
1592
|
+
const t = STR11[config.locale];
|
|
1593
|
+
let current;
|
|
1594
|
+
let listeners = [];
|
|
1595
|
+
let startTime = Date.now();
|
|
1596
|
+
let clicked = [];
|
|
1597
|
+
let finished = false;
|
|
1598
|
+
function render2() {
|
|
1599
|
+
current = generateChallenge11();
|
|
1600
|
+
clicked = [];
|
|
1601
|
+
finished = false;
|
|
1602
|
+
startTime = Date.now();
|
|
1603
|
+
const prompt = current.chars.join("\u3001");
|
|
1604
|
+
const chars = current.chars.map((ch, i) => {
|
|
1605
|
+
const p = current.positions[i];
|
|
1606
|
+
return `<div class="fc-click-text-char" data-ch="${ch}" style="left:${p.x}px;top:${p.y}px;">${ch}</div>`;
|
|
1607
|
+
}).join("");
|
|
1608
|
+
container.innerHTML = `
|
|
1609
|
+
<style>
|
|
1610
|
+
.fc-click-text{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
1611
|
+
.fc-click-text[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1612
|
+
.fc-click-text[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1613
|
+
.fc-click-text-title{font-size:14px;color:var(--fc-text);margin-bottom:4px;text-align:center}
|
|
1614
|
+
.fc-click-text-prompt{font-size:15px;color:var(--fc-accent);margin-bottom:12px;text-align:center;font-weight:600}
|
|
1615
|
+
.fc-click-text-area{position:relative;width:320px;height:200px;background:var(--fc-surface);border-radius:8px;border:1px solid var(--fc-border);overflow:hidden}
|
|
1616
|
+
.fc-click-text-char{position:absolute;width:44px;height:44px;display:flex;align-items:center;justify-content:center;font-size:22px;font-weight:600;color:var(--fc-text);background:var(--fc-bg);border:2px solid var(--fc-border);border-radius:8px;cursor:pointer;user-select:none;transition:transform .15s,background .15s,color .15s,border-color .15s}
|
|
1617
|
+
.fc-click-text-char:hover{transform:scale(1.08);border-color:var(--fc-accent)}
|
|
1618
|
+
.fc-click-text-char-done{background:var(--fc-success);border-color:var(--fc-success);color:#fff;cursor:default}
|
|
1619
|
+
.fc-click-text-char-done:hover{transform:none}
|
|
1620
|
+
.fc-click-text-char-wrong{background:var(--fc-danger);border-color:var(--fc-danger);color:#fff}
|
|
1621
|
+
.fc-click-text-msg{font-size:13px;min-height:18px;text-align:center;margin-top:10px;color:var(--fc-success)}
|
|
1622
|
+
.fc-click-text-msg-fail{color:var(--fc-danger)}
|
|
1623
|
+
.fc-click-text-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer;margin-top:8px;display:block;margin-left:auto;margin-right:auto}
|
|
1624
|
+
.fc-click-text-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
1625
|
+
</style>
|
|
1626
|
+
<div class="fc-click-text" data-theme="${config.theme}">
|
|
1627
|
+
<div class="fc-click-text-title">${t.title}</div>
|
|
1628
|
+
<div class="fc-click-text-prompt">${prompt}</div>
|
|
1629
|
+
<div class="fc-click-text-area">${chars}</div>
|
|
1630
|
+
<div class="fc-click-text-msg"></div>
|
|
1631
|
+
<button class="fc-click-text-refresh">${t.refresh}</button>
|
|
1632
|
+
</div>
|
|
1633
|
+
`;
|
|
1634
|
+
const msg = container.querySelector(".fc-click-text-msg");
|
|
1635
|
+
const refreshBtn = container.querySelector(".fc-click-text-refresh");
|
|
1636
|
+
refreshBtn.addEventListener("click", () => render2());
|
|
1637
|
+
container.querySelectorAll(".fc-click-text-char").forEach((el) => {
|
|
1638
|
+
el.addEventListener("click", async () => {
|
|
1639
|
+
if (finished) return;
|
|
1640
|
+
const box = el;
|
|
1641
|
+
if (box.classList.contains("fc-click-text-char-done")) return;
|
|
1642
|
+
const ch = box.dataset.ch;
|
|
1643
|
+
const expected = current.chars[clicked.length];
|
|
1644
|
+
if (ch === expected) {
|
|
1645
|
+
clicked.push(ch);
|
|
1646
|
+
box.classList.add("fc-click-text-char-done");
|
|
1647
|
+
if (clicked.length === current.chars.length) {
|
|
1648
|
+
finished = true;
|
|
1649
|
+
const result = {
|
|
1650
|
+
success: true,
|
|
1651
|
+
proof: await hashProof(proofInput6(current)),
|
|
1652
|
+
duration: Date.now() - startTime
|
|
1653
|
+
};
|
|
1654
|
+
msg.textContent = t.success;
|
|
1655
|
+
config.onVerify?.(result);
|
|
1656
|
+
listeners.forEach((cb) => cb(result));
|
|
1657
|
+
}
|
|
1658
|
+
} else {
|
|
1659
|
+
finished = true;
|
|
1660
|
+
box.classList.add("fc-click-text-char-wrong");
|
|
1661
|
+
msg.classList.add("fc-click-text-msg-fail");
|
|
1662
|
+
msg.textContent = t.fail;
|
|
1663
|
+
setTimeout(render2, 700);
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
return {
|
|
1669
|
+
mount: () => render2(),
|
|
1670
|
+
reset: () => render2(),
|
|
1671
|
+
destroy: () => {
|
|
1672
|
+
container.innerHTML = "";
|
|
1673
|
+
listeners = [];
|
|
1674
|
+
},
|
|
1675
|
+
onResult: (cb) => listeners.push(cb)
|
|
1676
|
+
};
|
|
1677
|
+
}
|
|
1678
|
+
var clickTextPlugin = {
|
|
1679
|
+
id: "click-text",
|
|
1680
|
+
category: "interactive",
|
|
1681
|
+
create: createClickTextInstance,
|
|
1682
|
+
describe: (locale) => ({
|
|
1683
|
+
name: locale === "zh" ? "\u6587\u5B57\u70B9\u9009" : "Click Text",
|
|
1684
|
+
description: locale === "zh" ? "\u6309\u6307\u5B9A\u987A\u5E8F\u70B9\u51FB\u753B\u5E03\u4E0A\u7684\u6C49\u5B57" : "Click characters on the canvas in the specified order.",
|
|
1685
|
+
tags: ["click", "text", "interactive"]
|
|
1686
|
+
})
|
|
1687
|
+
};
|
|
1688
|
+
defineCaptcha(clickTextPlugin);
|
|
1689
|
+
|
|
1690
|
+
// ../captchas/color-pick/dist/index.js
|
|
1691
|
+
var COLORS = [
|
|
1692
|
+
{ name: "red", zh: "\u7EA2\u8272", en: "red", hex: "#ef4444" },
|
|
1693
|
+
{ name: "blue", zh: "\u84DD\u8272", en: "blue", hex: "#3b82f6" },
|
|
1694
|
+
{ name: "green", zh: "\u7EFF\u8272", en: "green", hex: "#22c55e" },
|
|
1695
|
+
{ name: "yellow", zh: "\u9EC4\u8272", en: "yellow", hex: "#eab308" },
|
|
1696
|
+
{ name: "purple", zh: "\u7D2B\u8272", en: "purple", hex: "#a855f7" },
|
|
1697
|
+
{ name: "orange", zh: "\u6A59\u8272", en: "orange", hex: "#f97316" },
|
|
1698
|
+
{ name: "pink", zh: "\u7C89\u8272", en: "pink", hex: "#ec4899" },
|
|
1699
|
+
{ name: "cyan", zh: "\u9752\u8272", en: "cyan", hex: "#06b6d4" }
|
|
1700
|
+
];
|
|
1701
|
+
function randInt7(min, max) {
|
|
1702
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1703
|
+
}
|
|
1704
|
+
function shuffle6(arr) {
|
|
1705
|
+
const a = arr.slice();
|
|
1706
|
+
for (let i = a.length - 1; i > 0; i--) {
|
|
1707
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1708
|
+
const tmp = a[i];
|
|
1709
|
+
a[i] = a[j];
|
|
1710
|
+
a[j] = tmp;
|
|
1711
|
+
}
|
|
1712
|
+
return a;
|
|
1713
|
+
}
|
|
1714
|
+
function colorLabel(name, locale) {
|
|
1715
|
+
const c = COLORS.find((c2) => c2.name === name);
|
|
1716
|
+
return c ? locale === "zh" ? c.zh : c.en : name;
|
|
1717
|
+
}
|
|
1718
|
+
function generateChallenge12() {
|
|
1719
|
+
const target = COLORS[randInt7(0, COLORS.length - 1)];
|
|
1720
|
+
const distractorCount = randInt7(5, 8);
|
|
1721
|
+
const others = COLORS.filter((c) => c.name !== target.name);
|
|
1722
|
+
const distractors = shuffle6(others).slice(0, Math.min(distractorCount, others.length));
|
|
1723
|
+
const options = shuffle6([
|
|
1724
|
+
{ name: target.name, hex: target.hex },
|
|
1725
|
+
...distractors.map((d) => ({ name: d.name, hex: d.hex }))
|
|
1726
|
+
]);
|
|
1727
|
+
return { targetColor: target.name, targetHex: target.hex, options };
|
|
1728
|
+
}
|
|
1729
|
+
function proofInput7(c) {
|
|
1730
|
+
return `${c.targetColor}:completed`;
|
|
1731
|
+
}
|
|
1732
|
+
function verifyAnswer5(c, userPickHex) {
|
|
1733
|
+
return c.targetHex === userPickHex;
|
|
1734
|
+
}
|
|
1735
|
+
var STR12 = {
|
|
1736
|
+
zh: { success: "\u9A8C\u8BC1\u6210\u529F", fail: "\u9009\u9519\u4E86\uFF0C\u518D\u8BD5\u4E00\u6B21", refresh: "\u5237\u65B0" },
|
|
1737
|
+
en: { success: "Verified", fail: "Wrong, try again", refresh: "Refresh" }
|
|
1738
|
+
};
|
|
1739
|
+
function createColorPickInstance(container, config) {
|
|
1740
|
+
const t = STR12[config.locale];
|
|
1741
|
+
let current;
|
|
1742
|
+
let listeners = [];
|
|
1743
|
+
let startTime = Date.now();
|
|
1744
|
+
let finished = false;
|
|
1745
|
+
function render2() {
|
|
1746
|
+
current = generateChallenge12();
|
|
1747
|
+
finished = false;
|
|
1748
|
+
startTime = Date.now();
|
|
1749
|
+
const label = colorLabel(current.targetColor, config.locale);
|
|
1750
|
+
const prompt = config.locale === "zh" ? `\u8BF7\u70B9\u51FB${label}\u7684\u65B9\u5757` : `Click the ${label} square`;
|
|
1751
|
+
const squares = current.options.map(
|
|
1752
|
+
(o) => `<button class="fc-color-pick-opt" data-hex="${o.hex}" style="background:${o.hex}" aria-label="${o.name}"></button>`
|
|
1753
|
+
).join("");
|
|
1754
|
+
container.innerHTML = `
|
|
1755
|
+
<style>
|
|
1756
|
+
.fc-color-pick{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
1757
|
+
.fc-color-pick[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1758
|
+
.fc-color-pick[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1759
|
+
.fc-color-pick-prompt{font-size:15px;color:var(--fc-text);margin-bottom:12px;text-align:center;font-weight:600}
|
|
1760
|
+
.fc-color-pick-opts{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}
|
|
1761
|
+
.fc-color-pick-opt{height:56px;border:2px solid var(--fc-border);border-radius:10px;cursor:pointer;transition:transform .12s,border-color .12s;outline:none;padding:0}
|
|
1762
|
+
.fc-color-pick-opt:hover{transform:scale(1.06);border-color:var(--fc-accent)}
|
|
1763
|
+
.fc-color-pick-opt-correct{border-color:var(--fc-success);transform:scale(1.06)}
|
|
1764
|
+
.fc-color-pick-opt-wrong{border-color:var(--fc-danger)}
|
|
1765
|
+
.fc-color-pick-msg{font-size:13px;min-height:18px;text-align:center;margin-top:12px;color:var(--fc-danger)}
|
|
1766
|
+
.fc-color-pick-msg-success{color:var(--fc-success)}
|
|
1767
|
+
.fc-color-pick-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer;margin-top:8px;display:block;margin-left:auto;margin-right:auto}
|
|
1768
|
+
.fc-color-pick-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
1769
|
+
</style>
|
|
1770
|
+
<div class="fc-color-pick" data-theme="${config.theme}">
|
|
1771
|
+
<div class="fc-color-pick-prompt">${prompt}</div>
|
|
1772
|
+
<div class="fc-color-pick-opts">${squares}</div>
|
|
1773
|
+
<div class="fc-color-pick-msg"></div>
|
|
1774
|
+
<button class="fc-color-pick-refresh">${t.refresh}</button>
|
|
1775
|
+
</div>
|
|
1776
|
+
`;
|
|
1777
|
+
const msg = container.querySelector(".fc-color-pick-msg");
|
|
1778
|
+
const refreshBtn = container.querySelector(".fc-color-pick-refresh");
|
|
1779
|
+
refreshBtn.addEventListener("click", () => render2());
|
|
1780
|
+
container.querySelectorAll(".fc-color-pick-opt").forEach((el) => {
|
|
1781
|
+
el.addEventListener("click", async () => {
|
|
1782
|
+
if (finished) return;
|
|
1783
|
+
const btn = el;
|
|
1784
|
+
const pick2 = btn.dataset.hex;
|
|
1785
|
+
if (verifyAnswer5(current, pick2)) {
|
|
1786
|
+
finished = true;
|
|
1787
|
+
btn.classList.add("fc-color-pick-opt-correct");
|
|
1788
|
+
msg.classList.add("fc-color-pick-msg-success");
|
|
1789
|
+
const result = {
|
|
1790
|
+
success: true,
|
|
1791
|
+
proof: await hashProof(proofInput7(current)),
|
|
1792
|
+
duration: Date.now() - startTime
|
|
1793
|
+
};
|
|
1794
|
+
msg.textContent = t.success;
|
|
1795
|
+
config.onVerify?.(result);
|
|
1796
|
+
listeners.forEach((cb) => cb(result));
|
|
1797
|
+
} else {
|
|
1798
|
+
finished = true;
|
|
1799
|
+
btn.classList.add("fc-color-pick-opt-wrong");
|
|
1800
|
+
msg.textContent = t.fail;
|
|
1801
|
+
setTimeout(render2, 800);
|
|
1802
|
+
}
|
|
1803
|
+
});
|
|
1804
|
+
});
|
|
1805
|
+
}
|
|
1806
|
+
return {
|
|
1807
|
+
mount: () => render2(),
|
|
1808
|
+
reset: () => render2(),
|
|
1809
|
+
destroy: () => {
|
|
1810
|
+
container.innerHTML = "";
|
|
1811
|
+
listeners = [];
|
|
1812
|
+
},
|
|
1813
|
+
onResult: (cb) => listeners.push(cb)
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
var colorPickPlugin = {
|
|
1817
|
+
id: "color-pick",
|
|
1818
|
+
category: "recognize",
|
|
1819
|
+
create: createColorPickInstance,
|
|
1820
|
+
describe: (locale) => ({
|
|
1821
|
+
name: locale === "zh" ? "\u989C\u8272\u9009\u62E9" : "Color Pick",
|
|
1822
|
+
description: locale === "zh" ? "\u4ECE\u591A\u4E2A\u5F69\u8272\u65B9\u5757\u4E2D\u70B9\u51FB\u6307\u5B9A\u989C\u8272" : "Pick the square of the specified color.",
|
|
1823
|
+
tags: ["color", "pick", "recognize"]
|
|
1824
|
+
})
|
|
1825
|
+
};
|
|
1826
|
+
defineCaptcha(colorPickPlugin);
|
|
1827
|
+
|
|
1828
|
+
// ../captchas/puzzle/dist/index.js
|
|
1829
|
+
var TOLERANCE = 5;
|
|
1830
|
+
function randInt8(min, max) {
|
|
1831
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
1832
|
+
}
|
|
1833
|
+
function generateChallenge13() {
|
|
1834
|
+
return { gapPosition: randInt8(10, 90) };
|
|
1835
|
+
}
|
|
1836
|
+
function proofInput8(c) {
|
|
1837
|
+
return `${c.gapPosition}:completed`;
|
|
1838
|
+
}
|
|
1839
|
+
function verifyPosition(c, userPos) {
|
|
1840
|
+
return Math.abs(userPos - c.gapPosition) <= TOLERANCE;
|
|
1841
|
+
}
|
|
1842
|
+
var STR13 = {
|
|
1843
|
+
zh: { title: "\u62D6\u52A8\u6ED1\u5757\u628A\u62FC\u56FE\u5757\u79FB\u5230\u7F3A\u53E3", success: "\u9A8C\u8BC1\u6210\u529F", refresh: "\u5237\u65B0" },
|
|
1844
|
+
en: { title: "Drag the slider to fit the puzzle", success: "Verified", refresh: "Refresh" }
|
|
1845
|
+
};
|
|
1846
|
+
var STAGE_W = 300;
|
|
1847
|
+
var PIECE_W = 50;
|
|
1848
|
+
var MAX_OFFSET = STAGE_W - PIECE_W;
|
|
1849
|
+
function createPuzzleInstance(container, config) {
|
|
1850
|
+
const t = STR13[config.locale];
|
|
1851
|
+
let current;
|
|
1852
|
+
let listeners = [];
|
|
1853
|
+
let startTime = Date.now();
|
|
1854
|
+
let done = false;
|
|
1855
|
+
function render2() {
|
|
1856
|
+
current = generateChallenge13();
|
|
1857
|
+
startTime = Date.now();
|
|
1858
|
+
done = false;
|
|
1859
|
+
const gapLeft = current.gapPosition / 100 * MAX_OFFSET;
|
|
1860
|
+
container.innerHTML = `
|
|
1861
|
+
<style>
|
|
1862
|
+
.fc-puzzle{font-family:-apple-system,system-ui,sans-serif;max-width:360px;width:100%;padding:16px;box-sizing:border-box}
|
|
1863
|
+
.fc-puzzle[data-theme="light"]{--fc-bg:#ffffff;--fc-surface:#f6f7f9;--fc-text:#0f172a;--fc-text-soft:#64748b;--fc-border:#e2e8f0;--fc-accent:#6366f1;--fc-accent-soft:#eef2ff;--fc-success:#16a34a;--fc-danger:#dc2626}
|
|
1864
|
+
.fc-puzzle[data-theme="dark"]{--fc-bg:#1e2544;--fc-surface:#171c36;--fc-text:#e5e9f0;--fc-text-soft:#94a3b8;--fc-border:#2a3358;--fc-accent:#818cf8;--fc-accent-soft:#252b5c;--fc-success:#4ade80;--fc-danger:#f87171}
|
|
1865
|
+
.fc-puzzle-title{font-size:14px;color:var(--fc-text);margin-bottom:12px;text-align:center}
|
|
1866
|
+
.fc-puzzle-stage{position:relative;width:${STAGE_W}px;height:160px;border-radius:8px;overflow:hidden;border:1px solid var(--fc-border);background:linear-gradient(135deg,var(--fc-accent-soft),var(--fc-surface) 60%,var(--fc-accent-soft))}
|
|
1867
|
+
.fc-puzzle-gap{position:absolute;top:55px;width:${PIECE_W}px;height:${PIECE_W}px;background:var(--fc-bg);border:2px dashed var(--fc-text-soft);border-radius:6px;box-shadow:inset 0 0 8px rgba(0,0,0,.15)}
|
|
1868
|
+
.fc-puzzle-piece{position:absolute;top:55px;left:0;width:${PIECE_W}px;height:${PIECE_W}px;border-radius:6px;background:linear-gradient(135deg,var(--fc-accent),var(--fc-accent-soft));border:2px solid var(--fc-bg);box-shadow:0 2px 6px rgba(0,0,0,.25)}
|
|
1869
|
+
.fc-puzzle-range{width:${STAGE_W}px;margin-top:14px;accent-color:var(--fc-accent)}
|
|
1870
|
+
.fc-puzzle-msg{font-size:13px;min-height:18px;text-align:center;margin-top:10px;color:var(--fc-success)}
|
|
1871
|
+
.fc-puzzle-refresh{padding:4px 12px;font-size:12px;border:1px solid var(--fc-border);background:var(--fc-surface);color:var(--fc-text-soft);border-radius:6px;cursor:pointer;margin-top:8px;display:block;margin-left:auto;margin-right:auto}
|
|
1872
|
+
.fc-puzzle-refresh:hover{border-color:var(--fc-accent);color:var(--fc-accent)}
|
|
1873
|
+
</style>
|
|
1874
|
+
<div class="fc-puzzle" data-theme="${config.theme}">
|
|
1875
|
+
<div class="fc-puzzle-title">${t.title}</div>
|
|
1876
|
+
<div class="fc-puzzle-stage">
|
|
1877
|
+
<div class="fc-puzzle-gap" style="left:${gapLeft}px"></div>
|
|
1878
|
+
<div class="fc-puzzle-piece" style="left:0"></div>
|
|
1879
|
+
</div>
|
|
1880
|
+
<input class="fc-puzzle-range" type="range" min="0" max="100" step="1" value="0" />
|
|
1881
|
+
<div class="fc-puzzle-msg"></div>
|
|
1882
|
+
<button class="fc-puzzle-refresh">${t.refresh}</button>
|
|
1883
|
+
</div>
|
|
1884
|
+
`;
|
|
1885
|
+
const piece = container.querySelector(".fc-puzzle-piece");
|
|
1886
|
+
const range = container.querySelector(".fc-puzzle-range");
|
|
1887
|
+
const msg = container.querySelector(".fc-puzzle-msg");
|
|
1888
|
+
const refreshBtn = container.querySelector(".fc-puzzle-refresh");
|
|
1889
|
+
refreshBtn.addEventListener("click", () => render2());
|
|
1890
|
+
range.addEventListener("input", async () => {
|
|
1891
|
+
if (done) return;
|
|
1892
|
+
const v = Number(range.value);
|
|
1893
|
+
piece.style.left = `${v / 100 * MAX_OFFSET}px`;
|
|
1894
|
+
if (verifyPosition(current, v)) {
|
|
1895
|
+
done = true;
|
|
1896
|
+
range.disabled = true;
|
|
1897
|
+
piece.style.left = `${gapLeft}px`;
|
|
1898
|
+
const result = {
|
|
1899
|
+
success: true,
|
|
1900
|
+
proof: await hashProof(proofInput8(current)),
|
|
1901
|
+
duration: Date.now() - startTime
|
|
1902
|
+
};
|
|
1903
|
+
msg.textContent = t.success;
|
|
1904
|
+
config.onVerify?.(result);
|
|
1905
|
+
listeners.forEach((cb) => cb(result));
|
|
1906
|
+
}
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
return {
|
|
1910
|
+
mount: () => render2(),
|
|
1911
|
+
reset: () => render2(),
|
|
1912
|
+
destroy: () => {
|
|
1913
|
+
container.innerHTML = "";
|
|
1914
|
+
listeners = [];
|
|
1915
|
+
},
|
|
1916
|
+
onResult: (cb) => listeners.push(cb)
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
var puzzlePlugin = {
|
|
1920
|
+
id: "puzzle",
|
|
1921
|
+
category: "interactive",
|
|
1922
|
+
create: createPuzzleInstance,
|
|
1923
|
+
describe: (locale) => ({
|
|
1924
|
+
name: locale === "zh" ? "\u62FC\u56FE\u7F3A\u53E3" : "Puzzle Gap",
|
|
1925
|
+
description: locale === "zh" ? "\u62D6\u52A8\u6ED1\u5757\u628A\u62FC\u56FE\u5757\u79FB\u5230\u7F3A\u53E3\u4F4D\u7F6E" : "Drag the slider to fit the puzzle piece into the gap.",
|
|
1926
|
+
tags: ["puzzle", "drag", "interactive"]
|
|
1927
|
+
})
|
|
1928
|
+
};
|
|
1929
|
+
defineCaptcha(puzzlePlugin);
|
|
1930
|
+
|
|
1333
1931
|
// src/index.ts
|
|
1334
1932
|
var instances = /* @__PURE__ */ new Map();
|
|
1335
1933
|
function render(root = document) {
|