easter-egg-quest 1.0.12 โ 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 };
|
|
@@ -733,26 +772,42 @@ class HiddenEntry {
|
|
|
733
772
|
this._injectShimmerStyles();
|
|
734
773
|
this._createHintContainer();
|
|
735
774
|
const hints = this.script.hiddenEntryHints;
|
|
736
|
-
const FIRST_HINT_DELAY =
|
|
775
|
+
const FIRST_HINT_DELAY = 6e4;
|
|
737
776
|
const HINT_INTERVAL = 6e4;
|
|
738
|
-
const SHIMMER_DELAY =
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
action: () => {
|
|
751
|
-
if (this.targetElement) {
|
|
752
|
-
this.targetElement.classList.add("eeq-entry-target");
|
|
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
|
+
});
|
|
753
789
|
}
|
|
754
|
-
|
|
755
|
-
|
|
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;
|
|
756
811
|
const getVisibleElapsed = () => this._hintVisibleElapsed + (this._hintVisibleStart ? Date.now() - this._hintVisibleStart : 0);
|
|
757
812
|
const checkThresholds = () => {
|
|
758
813
|
if (this._destroyed) return;
|
|
@@ -788,6 +843,47 @@ class HiddenEntry {
|
|
|
788
843
|
document.addEventListener("visibilitychange", this._hintVisibilityHandler);
|
|
789
844
|
if (document.visibilityState === "visible") startTimer();
|
|
790
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
|
+
}
|
|
791
887
|
_stopHintTimer() {
|
|
792
888
|
if (this._hintCheckInterval) {
|
|
793
889
|
clearInterval(this._hintCheckInterval);
|
|
@@ -1447,7 +1543,7 @@ class ThreeRenderer {
|
|
|
1447
1543
|
const egg2 = this.eggs[bestIdx];
|
|
1448
1544
|
this._dragRotX = egg2.rotation.x;
|
|
1449
1545
|
this._dragRotY = egg2.rotation.y;
|
|
1450
|
-
|
|
1546
|
+
if (this.canvas) this.canvas.style.cursor = "grabbing";
|
|
1451
1547
|
return;
|
|
1452
1548
|
}
|
|
1453
1549
|
if (this._interactiveEgg === null) return;
|
|
@@ -1460,7 +1556,7 @@ class ThreeRenderer {
|
|
|
1460
1556
|
if (dist > 120) return;
|
|
1461
1557
|
e.preventDefault();
|
|
1462
1558
|
e.stopPropagation();
|
|
1463
|
-
if (this.canvas)
|
|
1559
|
+
if (this.canvas) this.canvas.style.cursor = "grabbing";
|
|
1464
1560
|
this._isDragging = true;
|
|
1465
1561
|
this._dragMoved = false;
|
|
1466
1562
|
this._dragStartX = e.clientX;
|
|
@@ -1510,7 +1606,7 @@ class ThreeRenderer {
|
|
|
1510
1606
|
this._finaleDragTarget = null;
|
|
1511
1607
|
this._cancelLongPress();
|
|
1512
1608
|
if (this._interactiveEgg !== null || this._finaleInteractive) {
|
|
1513
|
-
|
|
1609
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1514
1610
|
}
|
|
1515
1611
|
};
|
|
1516
1612
|
this._onWheel = (e) => {
|
|
@@ -1652,7 +1748,7 @@ class ThreeRenderer {
|
|
|
1652
1748
|
/** Make an egg interactively draggable. */
|
|
1653
1749
|
enableInteraction(index) {
|
|
1654
1750
|
this._interactiveEgg = index;
|
|
1655
|
-
|
|
1751
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1656
1752
|
}
|
|
1657
1753
|
/** Disable drag interaction. */
|
|
1658
1754
|
disableInteraction() {
|
|
@@ -1661,7 +1757,7 @@ class ThreeRenderer {
|
|
|
1661
1757
|
this._finaleDragTarget = null;
|
|
1662
1758
|
this._finaleInteractive = false;
|
|
1663
1759
|
this._cancelLongPress();
|
|
1664
|
-
|
|
1760
|
+
if (this.canvas) this.canvas.style.cursor = "";
|
|
1665
1761
|
}
|
|
1666
1762
|
/** Trigger spectacular egg reveal. */
|
|
1667
1763
|
revealEgg(index) {
|
|
@@ -1805,7 +1901,7 @@ class ThreeRenderer {
|
|
|
1805
1901
|
this._finaleInteractive = true;
|
|
1806
1902
|
this._finaleDragTarget = null;
|
|
1807
1903
|
this._finalePausedRotation = [false, false, false];
|
|
1808
|
-
|
|
1904
|
+
if (this.canvas) this.canvas.style.cursor = "grab";
|
|
1809
1905
|
for (let i = 0; i < 3; i++) {
|
|
1810
1906
|
const state = this._eggStates[i];
|
|
1811
1907
|
state.phase = "finale";
|
|
@@ -1829,14 +1925,27 @@ class ThreeRenderer {
|
|
|
1829
1925
|
fontFamily: "'Georgia', 'Times New Roman', serif",
|
|
1830
1926
|
textAlign: "center"
|
|
1831
1927
|
});
|
|
1832
|
-
this._greetingOverlay.innerHTML =
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
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);
|
|
1840
1949
|
document.body.appendChild(this._greetingOverlay);
|
|
1841
1950
|
requestAnimationFrame(() => {
|
|
1842
1951
|
requestAnimationFrame(() => {
|
|
@@ -3273,7 +3382,7 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3273
3382
|
this.shadow.appendChild(style);
|
|
3274
3383
|
const panel = document.createElement("div");
|
|
3275
3384
|
panel.className = "eeq-results";
|
|
3276
|
-
|
|
3385
|
+
this._buildContentDOM(panel, score);
|
|
3277
3386
|
this.shadow.appendChild(panel);
|
|
3278
3387
|
requestAnimationFrame(() => {
|
|
3279
3388
|
requestAnimationFrame(() => {
|
|
@@ -3293,7 +3402,7 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3293
3402
|
this.shadow = null;
|
|
3294
3403
|
}
|
|
3295
3404
|
// โโโ Content โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3296
|
-
|
|
3405
|
+
_buildContentDOM(parent, s) {
|
|
3297
3406
|
const bp = s.behaviorProfile;
|
|
3298
3407
|
const actionIcons = {
|
|
3299
3408
|
clicker: "๐ฑ",
|
|
@@ -3314,31 +3423,56 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3314
3423
|
explorer: "๐งญ",
|
|
3315
3424
|
philosopher: "๐"
|
|
3316
3425
|
};
|
|
3317
|
-
const
|
|
3318
|
-
|
|
3319
|
-
const
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
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);
|
|
3342
3476
|
}
|
|
3343
3477
|
// โโโ 2D Egg Drawing (type-accurate) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3344
3478
|
_makeRand(seed) {
|
|
@@ -3703,9 +3837,6 @@ const _ResultsRenderer = class _ResultsRenderer {
|
|
|
3703
3837
|
span.textContent = orig;
|
|
3704
3838
|
}, 1500);
|
|
3705
3839
|
}
|
|
3706
|
-
_escapeHtml(s) {
|
|
3707
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3708
|
-
}
|
|
3709
3840
|
// โโโ Styles โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
3710
3841
|
_getStyles() {
|
|
3711
3842
|
const t = this.config.theme;
|
|
@@ -4092,7 +4223,11 @@ class Persistence {
|
|
|
4092
4223
|
try {
|
|
4093
4224
|
const raw = localStorage.getItem(STORAGE_KEY);
|
|
4094
4225
|
if (!raw) return [];
|
|
4095
|
-
|
|
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
|
+
);
|
|
4096
4231
|
} catch {
|
|
4097
4232
|
return [];
|
|
4098
4233
|
}
|