easter-egg-quest 1.0.11 โ 1.0.13
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.
|
@@ -141,19 +141,21 @@ function resolveConfig(raw = {}) {
|
|
|
141
141
|
theme,
|
|
142
142
|
hiddenEntry: {
|
|
143
143
|
mode: ((_b2 = raw.hiddenEntry) == null ? void 0 : _b2.mode) ?? "auto",
|
|
144
|
-
selector: (_c = raw.hiddenEntry) == null ? void 0 : _c.selector,
|
|
144
|
+
selector: sanitizeSelector((_c = raw.hiddenEntry) == null ? void 0 : _c.selector),
|
|
145
145
|
excludeSelectors: [
|
|
146
146
|
...DEFAULT_EXCLUDE_SELECTORS,
|
|
147
|
-
...((_d = raw.hiddenEntry) == null ? void 0 : _d.excludeSelectors) ?? []
|
|
147
|
+
...(((_d = raw.hiddenEntry) == null ? void 0 : _d.excludeSelectors) ?? []).filter(
|
|
148
|
+
(s) => typeof s === "string" && s.length < 200
|
|
149
|
+
)
|
|
148
150
|
]
|
|
149
151
|
},
|
|
150
152
|
hud: typeof raw.hud === "boolean" ? { enabled: raw.hud, position: "top-left" } : { enabled: ((_e = raw.hud) == null ? void 0 : _e.enabled) ?? true, position: ((_f = raw.hud) == null ? void 0 : _f.position) ?? "top-left" },
|
|
151
153
|
shrine: typeof raw.shrine === "boolean" ? { enabled: raw.shrine, position: "bottom-right" } : { enabled: ((_g = raw.shrine) == null ? void 0 : _g.enabled) ?? true, position: ((_h = raw.shrine) == null ? void 0 : _h.position) ?? "bottom-right" },
|
|
152
154
|
sounds: raw.sounds ?? false,
|
|
153
155
|
stageDurations: {
|
|
154
|
-
stillnessMs: ((_i = raw.stageDurations) == null ? void 0 : _i.stillnessMs
|
|
155
|
-
motionMs: ((_j = raw.stageDurations) == null ? void 0 : _j.motionMs
|
|
156
|
-
rhythmCycles: ((_k = raw.stageDurations) == null ? void 0 : _k.rhythmCycles
|
|
156
|
+
stillnessMs: clampDuration((_i = raw.stageDurations) == null ? void 0 : _i.stillnessMs, 2e4),
|
|
157
|
+
motionMs: clampDuration((_j = raw.stageDurations) == null ? void 0 : _j.motionMs, 2e4),
|
|
158
|
+
rhythmCycles: clampInt((_k = raw.stageDurations) == null ? void 0 : _k.rhythmCycles, 6, 1, 50)
|
|
157
159
|
},
|
|
158
160
|
renderer: raw.renderer ?? "auto",
|
|
159
161
|
scoring: {
|
|
@@ -169,11 +171,48 @@ function resolveConfig(raw = {}) {
|
|
|
169
171
|
callbacks: raw.callbacks ?? {}
|
|
170
172
|
};
|
|
171
173
|
}
|
|
174
|
+
function clampDuration(val, def) {
|
|
175
|
+
if (val === void 0) return def;
|
|
176
|
+
const n = Number(val);
|
|
177
|
+
if (!Number.isFinite(n) || n < 1e3 || n > 3e5) return def;
|
|
178
|
+
return n;
|
|
179
|
+
}
|
|
180
|
+
function clampInt(val, def, min, max) {
|
|
181
|
+
if (val === void 0) return def;
|
|
182
|
+
const n = Math.round(Number(val));
|
|
183
|
+
if (!Number.isFinite(n) || n < min || n > max) return def;
|
|
184
|
+
return n;
|
|
185
|
+
}
|
|
186
|
+
function sanitizeSelector(sel) {
|
|
187
|
+
if (!sel || typeof sel !== "string") return void 0;
|
|
188
|
+
if (sel.length > 200) return void 0;
|
|
189
|
+
try {
|
|
190
|
+
document.querySelector(sel);
|
|
191
|
+
} catch {
|
|
192
|
+
return void 0;
|
|
193
|
+
}
|
|
194
|
+
return sel;
|
|
195
|
+
}
|
|
172
196
|
function resolveTheme(input) {
|
|
173
197
|
if (!input || input === "auto" || input === "light" || input === "dark") {
|
|
174
198
|
return { ...THEME_DEFAULTS };
|
|
175
199
|
}
|
|
176
|
-
|
|
200
|
+
const allowed = [
|
|
201
|
+
"overlayBg",
|
|
202
|
+
"textColor",
|
|
203
|
+
"textSecondary",
|
|
204
|
+
"accent",
|
|
205
|
+
"accentGlow",
|
|
206
|
+
"hudBg",
|
|
207
|
+
"hudText"
|
|
208
|
+
];
|
|
209
|
+
const filtered = {};
|
|
210
|
+
for (const key of allowed) {
|
|
211
|
+
if (key in input && typeof input[key] === "string") {
|
|
212
|
+
filtered[key] = input[key];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return { ...THEME_DEFAULTS, ...filtered };
|
|
177
216
|
}
|
|
178
217
|
function resolveNarrative(overrides) {
|
|
179
218
|
if (!overrides) return { ...DEFAULT_SCRIPT };
|
|
@@ -524,6 +563,10 @@ class HiddenEntry {
|
|
|
524
563
|
this.fallbackButton = null;
|
|
525
564
|
this._injectedElement = null;
|
|
526
565
|
this._destroyed = false;
|
|
566
|
+
this._hintVisibleElapsed = 0;
|
|
567
|
+
this._hintVisibleStart = 0;
|
|
568
|
+
this._hintCheckInterval = null;
|
|
569
|
+
this._hintVisibilityHandler = null;
|
|
527
570
|
this.config = config;
|
|
528
571
|
this.script = script;
|
|
529
572
|
this.onFound = onFound;
|
|
@@ -547,6 +590,7 @@ class HiddenEntry {
|
|
|
547
590
|
cleanup() {
|
|
548
591
|
var _a2, _b2, _c, _d;
|
|
549
592
|
this._destroyed = true;
|
|
593
|
+
this._stopHintTimer();
|
|
550
594
|
for (const t of this.hintTimers) clearTimeout(t);
|
|
551
595
|
this.hintTimers = [];
|
|
552
596
|
if (this.clickHandler && this.targetElement) {
|
|
@@ -728,45 +772,126 @@ class HiddenEntry {
|
|
|
728
772
|
this._injectShimmerStyles();
|
|
729
773
|
this._createHintContainer();
|
|
730
774
|
const hints = this.script.hiddenEntryHints;
|
|
731
|
-
const FIRST_HINT_DELAY =
|
|
775
|
+
const FIRST_HINT_DELAY = 6e4;
|
|
732
776
|
const HINT_INTERVAL = 6e4;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
}, shimmerDelay);
|
|
746
|
-
this.hintTimers.push(shimmerTimer);
|
|
747
|
-
}
|
|
748
|
-
/** Show hint only when the page is visible. If hidden, wait for visibility change. */
|
|
749
|
-
_showHintWhenVisible(text) {
|
|
750
|
-
if (this._destroyed) return;
|
|
751
|
-
if (document.visibilityState === "visible" && document.hasFocus()) {
|
|
752
|
-
this._showHint(text);
|
|
753
|
-
} else {
|
|
754
|
-
const handler = () => {
|
|
755
|
-
if (this._destroyed) {
|
|
756
|
-
document.removeEventListener("visibilitychange", handler);
|
|
757
|
-
window.removeEventListener("focus", handler);
|
|
758
|
-
return;
|
|
777
|
+
const SHIMMER_DELAY = 12e4;
|
|
778
|
+
const showFirstHint = () => {
|
|
779
|
+
this._stopHintTimer();
|
|
780
|
+
this._showFirstHintWithConfirm(hints[0] ?? "", () => {
|
|
781
|
+
const remaining = hints.slice(1);
|
|
782
|
+
const thresholds = [];
|
|
783
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
784
|
+
thresholds.push({
|
|
785
|
+
at: (i + 1) * HINT_INTERVAL,
|
|
786
|
+
fired: false,
|
|
787
|
+
action: () => this._showHint(remaining[i])
|
|
788
|
+
});
|
|
759
789
|
}
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (
|
|
765
|
-
|
|
790
|
+
thresholds.push({
|
|
791
|
+
at: SHIMMER_DELAY,
|
|
792
|
+
fired: false,
|
|
793
|
+
action: () => {
|
|
794
|
+
if (this.targetElement) {
|
|
795
|
+
this.targetElement.classList.add("eeq-entry-target");
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
if (thresholds.length === 0) return;
|
|
800
|
+
this._startVisibilityTimer(thresholds);
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
const phase1 = [
|
|
804
|
+
{ at: FIRST_HINT_DELAY, fired: false, action: showFirstHint }
|
|
805
|
+
];
|
|
806
|
+
this._startVisibilityTimer(phase1);
|
|
807
|
+
}
|
|
808
|
+
_startVisibilityTimer(thresholds) {
|
|
809
|
+
this._hintVisibleElapsed = 0;
|
|
810
|
+
this._hintVisibleStart = 0;
|
|
811
|
+
const getVisibleElapsed = () => this._hintVisibleElapsed + (this._hintVisibleStart ? Date.now() - this._hintVisibleStart : 0);
|
|
812
|
+
const checkThresholds = () => {
|
|
813
|
+
if (this._destroyed) return;
|
|
814
|
+
const elapsed = getVisibleElapsed();
|
|
815
|
+
for (const t of thresholds) {
|
|
816
|
+
if (!t.fired && elapsed >= t.at) {
|
|
817
|
+
t.fired = true;
|
|
818
|
+
t.action();
|
|
766
819
|
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
|
|
820
|
+
}
|
|
821
|
+
if (thresholds.every((t) => t.fired)) this._stopHintTimer();
|
|
822
|
+
};
|
|
823
|
+
const startTimer = () => {
|
|
824
|
+
if (this._hintCheckInterval) return;
|
|
825
|
+
this._hintVisibleStart = Date.now();
|
|
826
|
+
this._hintCheckInterval = setInterval(checkThresholds, 1e3);
|
|
827
|
+
};
|
|
828
|
+
const pauseTimer = () => {
|
|
829
|
+
if (this._hintVisibleStart) {
|
|
830
|
+
this._hintVisibleElapsed += Date.now() - this._hintVisibleStart;
|
|
831
|
+
this._hintVisibleStart = 0;
|
|
832
|
+
}
|
|
833
|
+
if (this._hintCheckInterval) {
|
|
834
|
+
clearInterval(this._hintCheckInterval);
|
|
835
|
+
this._hintCheckInterval = null;
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
this._hintVisibilityHandler = () => {
|
|
839
|
+
if (this._destroyed) return;
|
|
840
|
+
if (document.visibilityState === "visible") startTimer();
|
|
841
|
+
else pauseTimer();
|
|
842
|
+
};
|
|
843
|
+
document.addEventListener("visibilitychange", this._hintVisibilityHandler);
|
|
844
|
+
if (document.visibilityState === "visible") startTimer();
|
|
845
|
+
}
|
|
846
|
+
_showFirstHintWithConfirm(text, onConfirm) {
|
|
847
|
+
if (!this.hintContainer) return;
|
|
848
|
+
const snack = document.createElement("div");
|
|
849
|
+
snack.className = "eeq-snackbar";
|
|
850
|
+
const msg = document.createElement("span");
|
|
851
|
+
msg.textContent = text;
|
|
852
|
+
snack.appendChild(msg);
|
|
853
|
+
const goBtn = document.createElement("button");
|
|
854
|
+
goBtn.className = "eeq-snackbar-btn";
|
|
855
|
+
goBtn.textContent = "Let's go!";
|
|
856
|
+
Object.assign(goBtn.style, {
|
|
857
|
+
background: "rgba(76,175,80,0.85)",
|
|
858
|
+
color: "#fff"
|
|
859
|
+
});
|
|
860
|
+
goBtn.addEventListener("click", () => {
|
|
861
|
+
dismiss();
|
|
862
|
+
onConfirm();
|
|
863
|
+
});
|
|
864
|
+
snack.appendChild(goBtn);
|
|
865
|
+
const noBtn = document.createElement("button");
|
|
866
|
+
noBtn.className = "eeq-snackbar-btn";
|
|
867
|
+
noBtn.textContent = "Not interested";
|
|
868
|
+
noBtn.addEventListener("click", () => {
|
|
869
|
+
try {
|
|
870
|
+
localStorage.setItem("eeq_optout", "1");
|
|
871
|
+
} catch {
|
|
872
|
+
}
|
|
873
|
+
this.cleanup();
|
|
874
|
+
});
|
|
875
|
+
snack.appendChild(noBtn);
|
|
876
|
+
this.hintContainer.appendChild(snack);
|
|
877
|
+
const dismiss = () => {
|
|
878
|
+
snack.style.transform = "translateY(20px)";
|
|
879
|
+
snack.style.opacity = "0";
|
|
880
|
+
setTimeout(() => snack.remove(), 400);
|
|
881
|
+
};
|
|
882
|
+
requestAnimationFrame(() => {
|
|
883
|
+
snack.style.transform = "translateY(0)";
|
|
884
|
+
snack.style.opacity = "1";
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
_stopHintTimer() {
|
|
888
|
+
if (this._hintCheckInterval) {
|
|
889
|
+
clearInterval(this._hintCheckInterval);
|
|
890
|
+
this._hintCheckInterval = null;
|
|
891
|
+
}
|
|
892
|
+
if (this._hintVisibilityHandler) {
|
|
893
|
+
document.removeEventListener("visibilitychange", this._hintVisibilityHandler);
|
|
894
|
+
this._hintVisibilityHandler = null;
|
|
770
895
|
}
|
|
771
896
|
}
|
|
772
897
|
_showHint(text) {
|
|
@@ -1418,7 +1543,7 @@ class ThreeRenderer {
|
|
|
1418
1543
|
const egg2 = this.eggs[bestIdx];
|
|
1419
1544
|
this._dragRotX = egg2.rotation.x;
|
|
1420
1545
|
this._dragRotY = egg2.rotation.y;
|
|
1421
|
-
|
|
1546
|
+
if (this.canvas) this.canvas.style.cursor = "grabbing";
|
|
1422
1547
|
return;
|
|
1423
1548
|
}
|
|
1424
1549
|
if (this._interactiveEgg === null) return;
|
|
@@ -1431,7 +1556,7 @@ class ThreeRenderer {
|
|
|
1431
1556
|
if (dist > 120) return;
|
|
1432
1557
|
e.preventDefault();
|
|
1433
1558
|
e.stopPropagation();
|
|
1434
|
-
if (this.canvas)
|
|
1559
|
+
if (this.canvas) this.canvas.style.cursor = "grabbing";
|
|
1435
1560
|
this._isDragging = true;
|
|
1436
1561
|
this._dragMoved = false;
|
|
1437
1562
|
this._dragStartX = e.clientX;
|
|
@@ -1481,7 +1606,7 @@ class ThreeRenderer {
|
|
|
1481
1606
|
this._finaleDragTarget = null;
|
|
1482
1607
|
this._cancelLongPress();
|
|
1483
1608
|
if (this._interactiveEgg !== null || this._finaleInteractive) {
|
|
1484
|
-
|
|
1609
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1485
1610
|
}
|
|
1486
1611
|
};
|
|
1487
1612
|
this._onWheel = (e) => {
|
|
@@ -1623,7 +1748,7 @@ class ThreeRenderer {
|
|
|
1623
1748
|
/** Make an egg interactively draggable. */
|
|
1624
1749
|
enableInteraction(index) {
|
|
1625
1750
|
this._interactiveEgg = index;
|
|
1626
|
-
|
|
1751
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1627
1752
|
}
|
|
1628
1753
|
/** Disable drag interaction. */
|
|
1629
1754
|
disableInteraction() {
|
|
@@ -1632,7 +1757,7 @@ class ThreeRenderer {
|
|
|
1632
1757
|
this._finaleDragTarget = null;
|
|
1633
1758
|
this._finaleInteractive = false;
|
|
1634
1759
|
this._cancelLongPress();
|
|
1635
|
-
|
|
1760
|
+
if (this.canvas) this.canvas.style.cursor = "";
|
|
1636
1761
|
}
|
|
1637
1762
|
/** Trigger spectacular egg reveal. */
|
|
1638
1763
|
revealEgg(index) {
|
|
@@ -1776,7 +1901,7 @@ class ThreeRenderer {
|
|
|
1776
1901
|
this._finaleInteractive = true;
|
|
1777
1902
|
this._finaleDragTarget = null;
|
|
1778
1903
|
this._finalePausedRotation = [false, false, false];
|
|
1779
|
-
|
|
1904
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1780
1905
|
for (let i = 0; i < 3; i++) {
|
|
1781
1906
|
const state = this._eggStates[i];
|
|
1782
1907
|
state.phase = "finale";
|
|
@@ -1800,14 +1925,27 @@ class ThreeRenderer {
|
|
|
1800
1925
|
fontFamily: "'Georgia', 'Times New Roman', serif",
|
|
1801
1926
|
textAlign: "center"
|
|
1802
1927
|
});
|
|
1803
|
-
this._greetingOverlay.innerHTML =
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1928
|
+
this._greetingOverlay.innerHTML = "";
|
|
1929
|
+
const headingDiv = document.createElement("div");
|
|
1930
|
+
Object.assign(headingDiv.style, {
|
|
1931
|
+
fontSize: "48px",
|
|
1932
|
+
color: "#fff8e8",
|
|
1933
|
+
textShadow: "0 0 60px rgba(212,165,80,0.7), 0 0 20px rgba(212,165,80,0.4), 0 2px 8px rgba(0,0,0,0.6)",
|
|
1934
|
+
marginBottom: "10px",
|
|
1935
|
+
letterSpacing: "0.03em",
|
|
1936
|
+
fontWeight: "bold"
|
|
1937
|
+
});
|
|
1938
|
+
headingDiv.textContent = "Happy Easter! ๐ฐ๐ฅ";
|
|
1939
|
+
this._greetingOverlay.appendChild(headingDiv);
|
|
1940
|
+
const subDiv = document.createElement("div");
|
|
1941
|
+
Object.assign(subDiv.style, {
|
|
1942
|
+
fontSize: "17px",
|
|
1943
|
+
color: "rgba(245,240,230,0.85)",
|
|
1944
|
+
fontStyle: "italic",
|
|
1945
|
+
textShadow: "0 0 12px rgba(212,165,80,0.3), 0 1px 6px rgba(0,0,0,0.4)"
|
|
1946
|
+
});
|
|
1947
|
+
subDiv.textContent = "Wishing you joy, peace, and light";
|
|
1948
|
+
this._greetingOverlay.appendChild(subDiv);
|
|
1811
1949
|
document.body.appendChild(this._greetingOverlay);
|
|
1812
1950
|
requestAnimationFrame(() => {
|
|
1813
1951
|
requestAnimationFrame(() => {
|
|
@@ -3244,7 +3382,7 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3244
3382
|
this.shadow.appendChild(style);
|
|
3245
3383
|
const panel = document.createElement("div");
|
|
3246
3384
|
panel.className = "eeq-results";
|
|
3247
|
-
|
|
3385
|
+
this._buildContentDOM(panel, score);
|
|
3248
3386
|
this.shadow.appendChild(panel);
|
|
3249
3387
|
requestAnimationFrame(() => {
|
|
3250
3388
|
requestAnimationFrame(() => {
|
|
@@ -3264,7 +3402,7 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3264
3402
|
this.shadow = null;
|
|
3265
3403
|
}
|
|
3266
3404
|
// โโโ Content โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3267
|
-
|
|
3405
|
+
_buildContentDOM(parent, s) {
|
|
3268
3406
|
const bp = s.behaviorProfile;
|
|
3269
3407
|
const actionIcons = {
|
|
3270
3408
|
clicker: "๐ฑ",
|
|
@@ -3285,31 +3423,56 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3285
3423
|
explorer: "๐งญ",
|
|
3286
3424
|
philosopher: "๐"
|
|
3287
3425
|
};
|
|
3288
|
-
const
|
|
3289
|
-
|
|
3290
|
-
const
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3426
|
+
const inner = document.createElement("div");
|
|
3427
|
+
inner.className = "eeq-results-inner";
|
|
3428
|
+
const title = document.createElement("div");
|
|
3429
|
+
title.className = "eeq-results-title";
|
|
3430
|
+
title.textContent = `ยซ ${bp.title} ยป`;
|
|
3431
|
+
inner.appendChild(title);
|
|
3432
|
+
const time = document.createElement("div");
|
|
3433
|
+
time.className = "eeq-time";
|
|
3434
|
+
time.textContent = formatTime(s.totalTime);
|
|
3435
|
+
inner.appendChild(time);
|
|
3436
|
+
const badgesDiv = document.createElement("div");
|
|
3437
|
+
badgesDiv.className = "eeq-badges";
|
|
3438
|
+
const icons = [
|
|
3439
|
+
{ icon: actionIcons[bp.dominantAction], label: bp.dominantAction },
|
|
3440
|
+
{ icon: mouseIcons[bp.mousePersonality], label: bp.mousePersonality },
|
|
3441
|
+
{ icon: paceIcons[bp.pace], label: bp.pace }
|
|
3442
|
+
];
|
|
3443
|
+
for (const { icon, label } of icons) {
|
|
3444
|
+
if (!icon) continue;
|
|
3445
|
+
const span = document.createElement("span");
|
|
3446
|
+
span.className = "eeq-badge";
|
|
3447
|
+
span.title = label;
|
|
3448
|
+
span.textContent = icon;
|
|
3449
|
+
badgesDiv.appendChild(span);
|
|
3450
|
+
}
|
|
3451
|
+
inner.appendChild(badgesDiv);
|
|
3452
|
+
const invite = document.createElement("div");
|
|
3453
|
+
invite.className = "eeq-share-invite";
|
|
3454
|
+
invite.textContent = "share your result with friends ๐ฅ";
|
|
3455
|
+
inner.appendChild(invite);
|
|
3456
|
+
const actions = document.createElement("div");
|
|
3457
|
+
actions.className = "eeq-results-actions";
|
|
3458
|
+
const shareBtn = document.createElement("button");
|
|
3459
|
+
shareBtn.className = "eeq-btn eeq-btn-share";
|
|
3460
|
+
shareBtn.title = "share result";
|
|
3461
|
+
shareBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4.5 10.5a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm7-4a2 2 0 1 1 0-4 2 2 0 0 1 0 4zm0 8a2 2 0 1 1 0-4 2 2 0 0 1 0 4z" stroke="currentColor" stroke-width="1.2"/><path d="M6.3 9.2l3.4 1.6M6.3 7.8l3.4-1.6" stroke="currentColor" stroke-width="1.2"/></svg>';
|
|
3462
|
+
const shareLabel = document.createElement("span");
|
|
3463
|
+
shareLabel.textContent = "share";
|
|
3464
|
+
shareBtn.appendChild(shareLabel);
|
|
3465
|
+
actions.appendChild(shareBtn);
|
|
3466
|
+
const finishBtn = document.createElement("button");
|
|
3467
|
+
finishBtn.className = "eeq-btn eeq-btn-finish";
|
|
3468
|
+
finishBtn.title = "finish";
|
|
3469
|
+
finishBtn.innerHTML = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 8.5l3 3 5-6.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
|
3470
|
+
const finishLabel = document.createElement("span");
|
|
3471
|
+
finishLabel.textContent = "finish";
|
|
3472
|
+
finishBtn.appendChild(finishLabel);
|
|
3473
|
+
actions.appendChild(finishBtn);
|
|
3474
|
+
inner.appendChild(actions);
|
|
3475
|
+
parent.appendChild(inner);
|
|
3313
3476
|
}
|
|
3314
3477
|
// โโโ 2D Egg Drawing (type-accurate) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3315
3478
|
_makeRand(seed) {
|
|
@@ -3674,9 +3837,6 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3674
3837
|
span.textContent = orig;
|
|
3675
3838
|
}, 1500);
|
|
3676
3839
|
}
|
|
3677
|
-
_escapeHtml(s) {
|
|
3678
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3679
|
-
}
|
|
3680
3840
|
// โโโ Styles โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3681
3841
|
_getStyles() {
|
|
3682
3842
|
const t = this.config.theme;
|
|
@@ -4063,7 +4223,11 @@ class Persistence {
|
|
|
4063
4223
|
try {
|
|
4064
4224
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
4065
4225
|
if (!raw) return [];
|
|
4066
|
-
|
|
4226
|
+
const parsed = JSON.parse(raw);
|
|
4227
|
+
if (!Array.isArray(parsed)) return [];
|
|
4228
|
+
return parsed.filter(
|
|
4229
|
+
(item) => typeof item === "object" && item !== null && typeof item.totalTime === "number" && Number.isFinite(item.totalTime)
|
|
4230
|
+
);
|
|
4067
4231
|
} catch {
|
|
4068
4232
|
return [];
|
|
4069
4233
|
}
|