easter-egg-quest 1.0.24 → 1.0.26
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.
|
@@ -567,6 +567,8 @@ class HiddenEntry {
|
|
|
567
567
|
this.clickHandler = null;
|
|
568
568
|
this.fallbackButton = null;
|
|
569
569
|
this._injectedElement = null;
|
|
570
|
+
this._replacedTextNode = null;
|
|
571
|
+
this._replacedTextContent = "";
|
|
570
572
|
this._destroyed = false;
|
|
571
573
|
this._hintVisibleElapsed = 0;
|
|
572
574
|
this._hintVisibleStart = 0;
|
|
@@ -610,6 +612,7 @@ class HiddenEntry {
|
|
|
610
612
|
}
|
|
611
613
|
this._stopWatchdog();
|
|
612
614
|
this._stopBootstrapWatch();
|
|
615
|
+
this._restoreReplacedText();
|
|
613
616
|
(_a2 = this._injectedElement) == null ? void 0 : _a2.remove();
|
|
614
617
|
this._injectedElement = null;
|
|
615
618
|
(_b2 = this.hintContainer) == null ? void 0 : _b2.remove();
|
|
@@ -662,12 +665,9 @@ class HiddenEntry {
|
|
|
662
665
|
const navEls = sorted.filter(
|
|
663
666
|
(el) => el.closest("nav") !== null || el.closest("header") !== null
|
|
664
667
|
);
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
...bodyEls.slice(0, offset),
|
|
669
|
-
...navEls
|
|
670
|
-
];
|
|
668
|
+
const rotated = this._shuffleCandidates(bodyEls, visitCount).concat(
|
|
669
|
+
this._shuffleCandidates(navEls, visitCount + 17)
|
|
670
|
+
);
|
|
671
671
|
const rotatedPrints = rotated.map(
|
|
672
672
|
(el) => {
|
|
673
673
|
var _a3;
|
|
@@ -682,26 +682,9 @@ class HiddenEntry {
|
|
|
682
682
|
const ordered = unused.length > 0 ? [...unused, ...rotated.filter((_, i) => usedSet.has(rotatedPrints[i]))] : rotated;
|
|
683
683
|
for (const candidate of ordered) {
|
|
684
684
|
const host = candidate;
|
|
685
|
-
|
|
686
|
-
const span =
|
|
687
|
-
span
|
|
688
|
-
span.setAttribute("data-eeq", "trigger");
|
|
689
|
-
span.style.cssText = `
|
|
690
|
-
font: inherit;
|
|
691
|
-
color: inherit;
|
|
692
|
-
letter-spacing: inherit;
|
|
693
|
-
cursor: pointer;
|
|
694
|
-
`;
|
|
695
|
-
const inserted = !isInline && this._tryInsertBetweenWords(host, span);
|
|
696
|
-
if (!inserted) {
|
|
697
|
-
const heightBefore = host.getBoundingClientRect().height;
|
|
698
|
-
host.appendChild(span);
|
|
699
|
-
const heightAfter = host.getBoundingClientRect().height;
|
|
700
|
-
if (Math.abs(heightAfter - heightBefore) > 2) {
|
|
701
|
-
host.removeChild(span);
|
|
702
|
-
continue;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
685
|
+
if (!this._canHostInjectedTrigger(host)) continue;
|
|
686
|
+
const span = this._replaceTextCandidate(host);
|
|
687
|
+
if (!span) continue;
|
|
705
688
|
const idx = sorted.indexOf(candidate);
|
|
706
689
|
if (idx >= 0) {
|
|
707
690
|
usedSet.add(prints[idx]);
|
|
@@ -814,9 +797,53 @@ class HiddenEntry {
|
|
|
814
797
|
if (childElementCount > 12) return false;
|
|
815
798
|
const rect = el.getBoundingClientRect();
|
|
816
799
|
if (rect.width < 18 || rect.height < 12) return false;
|
|
800
|
+
if (!this._canHostInjectedTrigger(el)) return false;
|
|
817
801
|
return true;
|
|
818
802
|
});
|
|
819
803
|
}
|
|
804
|
+
_shuffleCandidates(candidates, seed) {
|
|
805
|
+
if (candidates.length < 2) return [...candidates];
|
|
806
|
+
const arr = [...candidates];
|
|
807
|
+
let state = (seed || 1) ^ Date.now() & 65535;
|
|
808
|
+
const rand = () => {
|
|
809
|
+
state ^= state << 13;
|
|
810
|
+
state ^= state >>> 17;
|
|
811
|
+
state ^= state << 5;
|
|
812
|
+
return (state >>> 0) % 1e4 / 1e4;
|
|
813
|
+
};
|
|
814
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
815
|
+
const j = Math.floor(rand() * (i + 1));
|
|
816
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
817
|
+
}
|
|
818
|
+
return arr;
|
|
819
|
+
}
|
|
820
|
+
_canHostInjectedTrigger(host) {
|
|
821
|
+
const style = getComputedStyle(host);
|
|
822
|
+
const textOverflow = style.textOverflow;
|
|
823
|
+
const whiteSpace = style.whiteSpace;
|
|
824
|
+
const overflowX = style.overflowX;
|
|
825
|
+
const overflow = style.overflow;
|
|
826
|
+
if (textOverflow === "ellipsis") return false;
|
|
827
|
+
if (whiteSpace === "nowrap" && ["hidden", "clip"].includes(overflowX || overflow)) return false;
|
|
828
|
+
if (host.scrollWidth > host.clientWidth + 2 && host.clientWidth > 0) return false;
|
|
829
|
+
if (host.scrollHeight > host.clientHeight + 2 && host.clientHeight > 0 && whiteSpace === "nowrap") return false;
|
|
830
|
+
return true;
|
|
831
|
+
}
|
|
832
|
+
_isTriggerClipped(host, span) {
|
|
833
|
+
if (!document.body.contains(span)) return true;
|
|
834
|
+
const hostRect = host.getBoundingClientRect();
|
|
835
|
+
const spanRect = span.getBoundingClientRect();
|
|
836
|
+
const style = getComputedStyle(host);
|
|
837
|
+
const clipsHorizontally = ["hidden", "clip"].includes(style.overflowX || style.overflow);
|
|
838
|
+
const clipsVertically = ["hidden", "clip"].includes(style.overflowY || style.overflow);
|
|
839
|
+
if (host.scrollWidth > host.clientWidth + 2 && host.clientWidth > 0) return true;
|
|
840
|
+
if (style.textOverflow === "ellipsis") return true;
|
|
841
|
+
if (clipsHorizontally && spanRect.right > hostRect.right - 1) return true;
|
|
842
|
+
if (clipsHorizontally && spanRect.left < hostRect.left + 1) return true;
|
|
843
|
+
if (clipsVertically && spanRect.bottom > hostRect.bottom - 1) return true;
|
|
844
|
+
if (clipsVertically && spanRect.top < hostRect.top + 1) return true;
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
820
847
|
_startBootstrapWatch() {
|
|
821
848
|
this._stopBootstrapWatch();
|
|
822
849
|
const root = this._getSearchRoot();
|
|
@@ -962,6 +989,7 @@ class HiddenEntry {
|
|
|
962
989
|
if (this._injectedElement && document.body.contains(this._injectedElement)) {
|
|
963
990
|
this._injectedElement.remove();
|
|
964
991
|
}
|
|
992
|
+
this._restoreReplacedText();
|
|
965
993
|
this._injectedElement = null;
|
|
966
994
|
this.targetElement = null;
|
|
967
995
|
this._startBootstrapWatch();
|
|
@@ -982,8 +1010,8 @@ class HiddenEntry {
|
|
|
982
1010
|
const walker = document.createTreeWalker(host, NodeFilter.SHOW_TEXT, {
|
|
983
1011
|
acceptNode: (node) => {
|
|
984
1012
|
var _a2;
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
1013
|
+
const text = ((_a2 = node.textContent) == null ? void 0 : _a2.trim()) ?? "";
|
|
1014
|
+
if (text.length <= 5) return NodeFilter.FILTER_REJECT;
|
|
987
1015
|
const parent = node.parentElement;
|
|
988
1016
|
if (!parent) return NodeFilter.FILTER_REJECT;
|
|
989
1017
|
if (parent.closest("[data-eeq], script, style, noscript")) return NodeFilter.FILTER_REJECT;
|
|
@@ -996,33 +1024,135 @@ class HiddenEntry {
|
|
|
996
1024
|
current = walker.nextNode();
|
|
997
1025
|
}
|
|
998
1026
|
if (!textNodes.length) return false;
|
|
999
|
-
const
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1027
|
+
const textNodesOrdered = this._shuffleCandidates(
|
|
1028
|
+
textNodes.map((node) => node.parentElement).filter((el) => !!el),
|
|
1029
|
+
Date.now()
|
|
1030
|
+
);
|
|
1031
|
+
const uniqueTextNodes = textNodesOrdered.map((parent) => textNodes.find((node) => node.parentElement === parent)).filter((node) => !!node);
|
|
1032
|
+
for (const textNode of uniqueTextNodes) {
|
|
1033
|
+
const text = textNode.textContent ?? "";
|
|
1034
|
+
const parent = textNode.parentElement;
|
|
1035
|
+
if (!parent) continue;
|
|
1036
|
+
const spacePositions = [];
|
|
1037
|
+
for (let i = 1; i < text.length - 1; i++) {
|
|
1038
|
+
if (text[i] === " ") spacePositions.push(i);
|
|
1039
|
+
}
|
|
1040
|
+
if (spacePositions.length < 2) continue;
|
|
1041
|
+
const start = Math.floor(spacePositions.length * 0.2);
|
|
1042
|
+
const end = Math.ceil(spacePositions.length * 0.8);
|
|
1043
|
+
const candidateBreaks = this._shuffleNumbers(
|
|
1044
|
+
spacePositions.slice(start, Math.max(start + 1, end))
|
|
1045
|
+
);
|
|
1046
|
+
for (const splitAt of candidateBreaks) {
|
|
1047
|
+
const before = text.slice(0, splitAt);
|
|
1048
|
+
const after = text.slice(splitAt);
|
|
1049
|
+
const rectBefore = host.getBoundingClientRect();
|
|
1050
|
+
const afterNode = document.createTextNode(after);
|
|
1051
|
+
this._applyTriggerTypography(span, parent);
|
|
1052
|
+
textNode.textContent = before;
|
|
1053
|
+
parent.insertBefore(span, textNode.nextSibling);
|
|
1054
|
+
parent.insertBefore(afterNode, span.nextSibling);
|
|
1055
|
+
const rectAfter = host.getBoundingClientRect();
|
|
1056
|
+
const invalid = Math.abs(rectAfter.height - rectBefore.height) > 8 || Math.abs(rectAfter.width - rectBefore.width) > 24 || this._isTriggerClipped(host, span);
|
|
1057
|
+
if (!invalid) return true;
|
|
1058
|
+
span.remove();
|
|
1059
|
+
afterNode.remove();
|
|
1060
|
+
textNode.textContent = text;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
return false;
|
|
1064
|
+
}
|
|
1065
|
+
_shuffleNumbers(values) {
|
|
1066
|
+
const arr = [...values];
|
|
1067
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
1068
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1069
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
1070
|
+
}
|
|
1071
|
+
return arr;
|
|
1072
|
+
}
|
|
1073
|
+
_replaceTextCandidate(host) {
|
|
1074
|
+
const target = this._findReplacementTextTarget(host);
|
|
1075
|
+
if (!target) return null;
|
|
1076
|
+
const span = document.createElement("span");
|
|
1077
|
+
span.textContent = "start hunt";
|
|
1078
|
+
span.setAttribute("data-eeq", "trigger");
|
|
1079
|
+
span.classList.add("eeq-inline-trigger");
|
|
1080
|
+
this._applyTriggerTypography(span, target.parent);
|
|
1013
1081
|
const rectBefore = host.getBoundingClientRect();
|
|
1014
|
-
|
|
1015
|
-
textNode.
|
|
1016
|
-
host.insertBefore(afterNode, textNode.nextSibling);
|
|
1017
|
-
host.insertBefore(span, afterNode);
|
|
1082
|
+
target.textNode.textContent = "";
|
|
1083
|
+
target.parent.insertBefore(span, target.textNode.nextSibling);
|
|
1018
1084
|
const rectAfter = host.getBoundingClientRect();
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
textNode.textContent = text;
|
|
1023
|
-
return
|
|
1085
|
+
const invalid = Math.abs(rectAfter.height - rectBefore.height) > 6 || Math.abs(rectAfter.width - rectBefore.width) > 20 || this._isTriggerClipped(host, span);
|
|
1086
|
+
if (invalid) {
|
|
1087
|
+
span.remove();
|
|
1088
|
+
target.textNode.textContent = target.text;
|
|
1089
|
+
return null;
|
|
1024
1090
|
}
|
|
1025
|
-
|
|
1091
|
+
this._replacedTextNode = target.textNode;
|
|
1092
|
+
this._replacedTextContent = target.text;
|
|
1093
|
+
return span;
|
|
1094
|
+
}
|
|
1095
|
+
_findReplacementTextTarget(host) {
|
|
1096
|
+
const triggerLength = "start hunt".length;
|
|
1097
|
+
const textTargets = [];
|
|
1098
|
+
const walker = document.createTreeWalker(host, NodeFilter.SHOW_TEXT, {
|
|
1099
|
+
acceptNode: (node) => {
|
|
1100
|
+
const parent = node.parentElement;
|
|
1101
|
+
const text = this._normalizeComparableText(node.textContent ?? "");
|
|
1102
|
+
if (!parent || text.length === 0) return NodeFilter.FILTER_REJECT;
|
|
1103
|
+
if (parent.closest("[data-eeq], script, style, noscript")) return NodeFilter.FILTER_REJECT;
|
|
1104
|
+
if (!isElementVisible(parent)) return NodeFilter.FILTER_REJECT;
|
|
1105
|
+
if (Math.abs(text.length - triggerLength) > 10) return NodeFilter.FILTER_REJECT;
|
|
1106
|
+
return NodeFilter.FILTER_ACCEPT;
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
1109
|
+
let current = walker.nextNode();
|
|
1110
|
+
while (current) {
|
|
1111
|
+
const textNode = current;
|
|
1112
|
+
const parent = textNode.parentElement;
|
|
1113
|
+
const text = textNode.textContent ?? "";
|
|
1114
|
+
if (parent) {
|
|
1115
|
+
textTargets.push({ textNode, parent, text });
|
|
1116
|
+
}
|
|
1117
|
+
current = walker.nextNode();
|
|
1118
|
+
}
|
|
1119
|
+
if (!textTargets.length) return null;
|
|
1120
|
+
const shuffled = this._shuffleTextTargets(textTargets);
|
|
1121
|
+
return shuffled[0] ?? null;
|
|
1122
|
+
}
|
|
1123
|
+
_shuffleTextTargets(values) {
|
|
1124
|
+
const arr = [...values];
|
|
1125
|
+
for (let i = arr.length - 1; i > 0; i--) {
|
|
1126
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
1127
|
+
[arr[i], arr[j]] = [arr[j], arr[i]];
|
|
1128
|
+
}
|
|
1129
|
+
return arr;
|
|
1130
|
+
}
|
|
1131
|
+
_normalizeComparableText(text) {
|
|
1132
|
+
return text.replace(/\s+/g, " ").trim();
|
|
1133
|
+
}
|
|
1134
|
+
_restoreReplacedText() {
|
|
1135
|
+
if (this._replacedTextNode) {
|
|
1136
|
+
this._replacedTextNode.textContent = this._replacedTextContent;
|
|
1137
|
+
}
|
|
1138
|
+
this._replacedTextNode = null;
|
|
1139
|
+
this._replacedTextContent = "";
|
|
1140
|
+
}
|
|
1141
|
+
_applyTriggerTypography(span, source) {
|
|
1142
|
+
const style = getComputedStyle(source);
|
|
1143
|
+
span.style.cssText = `
|
|
1144
|
+
font-family: ${style.fontFamily};
|
|
1145
|
+
font-size: ${style.fontSize};
|
|
1146
|
+
font-weight: ${style.fontWeight};
|
|
1147
|
+
font-style: ${style.fontStyle};
|
|
1148
|
+
line-height: ${style.lineHeight};
|
|
1149
|
+
letter-spacing: ${style.letterSpacing};
|
|
1150
|
+
text-transform: ${style.textTransform};
|
|
1151
|
+
color: ${style.color};
|
|
1152
|
+
cursor: pointer;
|
|
1153
|
+
white-space: nowrap;
|
|
1154
|
+
transition: color 0.18s ease;
|
|
1155
|
+
`;
|
|
1026
1156
|
}
|
|
1027
1157
|
// ─── Shared ───────────────────────────────────────────────────────────
|
|
1028
1158
|
_attachToElement(el) {
|
|
@@ -1214,6 +1344,9 @@ class HiddenEntry {
|
|
|
1214
1344
|
.eeq-entry-target {
|
|
1215
1345
|
animation: eeq-shimmer 3s ease-in-out infinite !important;
|
|
1216
1346
|
}
|
|
1347
|
+
.eeq-inline-trigger:hover {
|
|
1348
|
+
color: ${this.config.theme.accent};
|
|
1349
|
+
}
|
|
1217
1350
|
@keyframes eeq-shimmer {
|
|
1218
1351
|
0%, 100% { opacity: 1; }
|
|
1219
1352
|
50% { opacity: 0.55; }
|