easter-egg-quest 1.0.25 → 1.0.27

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();
@@ -680,31 +683,8 @@ class HiddenEntry {
680
683
  for (const candidate of ordered) {
681
684
  const host = candidate;
682
685
  if (!this._canHostInjectedTrigger(host)) continue;
683
- const isInline = host.tagName === "A" || host.tagName === "BUTTON" || host.tagName === "LI" || host.closest("nav") !== null;
684
- const span = document.createElement("span");
685
- span.textContent = isInline ? " · start hunt" : " start hunt";
686
- span.setAttribute("data-eeq", "trigger");
687
- span.classList.add("eeq-inline-trigger");
688
- span.style.cssText = `
689
- font: inherit;
690
- color: inherit;
691
- letter-spacing: inherit;
692
- cursor: pointer;
693
- white-space: nowrap;
694
- transition: filter 0.18s ease, opacity 0.18s ease, text-shadow 0.18s ease;
695
- `;
696
- const inserted = !isInline && this._tryInsertBetweenWords(host, span);
697
- if (!inserted) {
698
- const heightBefore = host.getBoundingClientRect().height;
699
- const widthBefore = host.getBoundingClientRect().width;
700
- host.appendChild(span);
701
- const heightAfter = host.getBoundingClientRect().height;
702
- const widthAfter = host.getBoundingClientRect().width;
703
- if (Math.abs(heightAfter - heightBefore) > 2 || Math.abs(widthAfter - widthBefore) > 24 || this._isTriggerClipped(host, span)) {
704
- host.removeChild(span);
705
- continue;
706
- }
707
- }
686
+ const span = this._replaceTextCandidate(host);
687
+ if (!span) continue;
708
688
  const idx = sorted.indexOf(candidate);
709
689
  if (idx >= 0) {
710
690
  usedSet.add(prints[idx]);
@@ -1009,6 +989,7 @@ class HiddenEntry {
1009
989
  if (this._injectedElement && document.body.contains(this._injectedElement)) {
1010
990
  this._injectedElement.remove();
1011
991
  }
992
+ this._restoreReplacedText();
1012
993
  this._injectedElement = null;
1013
994
  this.targetElement = null;
1014
995
  this._startBootstrapWatch();
@@ -1067,6 +1048,7 @@ class HiddenEntry {
1067
1048
  const after = text.slice(splitAt);
1068
1049
  const rectBefore = host.getBoundingClientRect();
1069
1050
  const afterNode = document.createTextNode(after);
1051
+ this._applyTriggerTypography(span, parent);
1070
1052
  textNode.textContent = before;
1071
1053
  parent.insertBefore(span, textNode.nextSibling);
1072
1054
  parent.insertBefore(afterNode, span.nextSibling);
@@ -1088,6 +1070,104 @@ class HiddenEntry {
1088
1070
  }
1089
1071
  return arr;
1090
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);
1081
+ const rectBefore = host.getBoundingClientRect();
1082
+ target.textNode.textContent = "";
1083
+ target.parent.insertBefore(span, target.textNode.nextSibling);
1084
+ const rectAfter = host.getBoundingClientRect();
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;
1090
+ }
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 minReplaceLength = 5;
1098
+ const textTargets = [];
1099
+ const walker = document.createTreeWalker(host, NodeFilter.SHOW_TEXT, {
1100
+ acceptNode: (node) => {
1101
+ const parent = node.parentElement;
1102
+ const text = this._normalizeComparableText(node.textContent ?? "");
1103
+ if (!parent || text.length === 0) return NodeFilter.FILTER_REJECT;
1104
+ if (parent.closest("[data-eeq], script, style, noscript")) return NodeFilter.FILTER_REJECT;
1105
+ if (this._isIconLikeTarget(parent)) return NodeFilter.FILTER_REJECT;
1106
+ if (!isElementVisible(parent)) return NodeFilter.FILTER_REJECT;
1107
+ if (text.length < minReplaceLength) return NodeFilter.FILTER_REJECT;
1108
+ if (Math.abs(text.length - triggerLength) > 10) return NodeFilter.FILTER_REJECT;
1109
+ return NodeFilter.FILTER_ACCEPT;
1110
+ }
1111
+ });
1112
+ let current = walker.nextNode();
1113
+ while (current) {
1114
+ const textNode = current;
1115
+ const parent = textNode.parentElement;
1116
+ const text = textNode.textContent ?? "";
1117
+ if (parent) {
1118
+ textTargets.push({ textNode, parent, text });
1119
+ }
1120
+ current = walker.nextNode();
1121
+ }
1122
+ if (!textTargets.length) return null;
1123
+ const shuffled = this._shuffleTextTargets(textTargets);
1124
+ return shuffled[0] ?? null;
1125
+ }
1126
+ _shuffleTextTargets(values) {
1127
+ const arr = [...values];
1128
+ for (let i = arr.length - 1; i > 0; i--) {
1129
+ const j = Math.floor(Math.random() * (i + 1));
1130
+ [arr[i], arr[j]] = [arr[j], arr[i]];
1131
+ }
1132
+ return arr;
1133
+ }
1134
+ _normalizeComparableText(text) {
1135
+ return text.replace(/\s+/g, " ").trim();
1136
+ }
1137
+ _isIconLikeTarget(parent) {
1138
+ const tag = parent.tagName.toLowerCase();
1139
+ const className = parent.className.toString().toLowerCase();
1140
+ const role = (parent.getAttribute("role") ?? "").toLowerCase();
1141
+ const ariaHidden = parent.getAttribute("aria-hidden");
1142
+ if (tag === "mat-icon" || tag === "svg" || tag === "i") return true;
1143
+ if (className.includes("icon") || className.includes("mat-icon")) return true;
1144
+ if (role === "img" || ariaHidden === "true") return true;
1145
+ if (parent.querySelector("svg, mat-icon")) return true;
1146
+ return false;
1147
+ }
1148
+ _restoreReplacedText() {
1149
+ if (this._replacedTextNode) {
1150
+ this._replacedTextNode.textContent = this._replacedTextContent;
1151
+ }
1152
+ this._replacedTextNode = null;
1153
+ this._replacedTextContent = "";
1154
+ }
1155
+ _applyTriggerTypography(span, source) {
1156
+ const style = getComputedStyle(source);
1157
+ span.style.cssText = `
1158
+ font-family: ${style.fontFamily};
1159
+ font-size: ${style.fontSize};
1160
+ font-weight: ${style.fontWeight};
1161
+ font-style: ${style.fontStyle};
1162
+ line-height: ${style.lineHeight};
1163
+ letter-spacing: ${style.letterSpacing};
1164
+ text-transform: ${style.textTransform};
1165
+ color: ${style.color};
1166
+ cursor: pointer;
1167
+ white-space: nowrap;
1168
+ transition: color 0.18s ease;
1169
+ `;
1170
+ }
1091
1171
  // ─── Shared ───────────────────────────────────────────────────────────
1092
1172
  _attachToElement(el) {
1093
1173
  this.clickHandler = (e) => {
@@ -1279,9 +1359,7 @@ class HiddenEntry {
1279
1359
  animation: eeq-shimmer 3s ease-in-out infinite !important;
1280
1360
  }
1281
1361
  .eeq-inline-trigger:hover {
1282
- filter: brightness(1.35);
1283
- opacity: 1;
1284
- text-shadow: 0 0 10px rgba(255, 248, 220, 0.35);
1362
+ color: ${this.config.theme.accent};
1285
1363
  }
1286
1364
  @keyframes eeq-shimmer {
1287
1365
  0%, 100% { opacity: 1; }