saltfish 0.3.57 → 0.3.59

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.
@@ -6692,6 +6692,55 @@ const DEFAULT_CONFIG = {
6692
6692
  minSize: 1
6693
6693
  // Minimum 1x1px (reject only zero-size elements)
6694
6694
  };
6695
+ function normalizeText(text) {
6696
+ return text.trim().replace(/\s+/g, " ");
6697
+ }
6698
+ function matchesExpectedElement(element, expected) {
6699
+ if (element.tagName.toUpperCase() !== expected.tagName.toUpperCase()) {
6700
+ return false;
6701
+ }
6702
+ const actualText = normalizeText(element.textContent || "");
6703
+ const expectedText = normalizeText(expected.textContent);
6704
+ return actualText === expectedText;
6705
+ }
6706
+ function findElementByTagAndText(elements, expected, config) {
6707
+ const matches = [];
6708
+ for (const element of elements) {
6709
+ const rejection = getBasicRejectionReason(element, config);
6710
+ if (rejection) {
6711
+ continue;
6712
+ }
6713
+ if (matchesExpectedElement(element, expected)) {
6714
+ matches.push(element);
6715
+ }
6716
+ }
6717
+ if (matches.length === 0) {
6718
+ log(`ElementValidator: No tag+text match for tagName='${expected.tagName}', text='${expected.textContent.substring(0, 50)}${expected.textContent.length > 50 ? "..." : ""}'`);
6719
+ return null;
6720
+ }
6721
+ if (matches.length === 1) {
6722
+ return matches[0];
6723
+ }
6724
+ return matches[0];
6725
+ }
6726
+ function findAllElementsByTagAndText(elements, expected, config) {
6727
+ const matches = [];
6728
+ for (const element of elements) {
6729
+ const rejection = getBasicRejectionReason(element, config);
6730
+ if (rejection) {
6731
+ continue;
6732
+ }
6733
+ if (matchesExpectedElement(element, expected)) {
6734
+ matches.push(element);
6735
+ }
6736
+ }
6737
+ if (matches.length > 0) {
6738
+ log(`ElementValidator: Found ${matches.length} element(s) matching tag+text`);
6739
+ } else {
6740
+ log(`ElementValidator: No tag+text match for tagName='${expected.tagName}'`);
6741
+ }
6742
+ return matches;
6743
+ }
6695
6744
  function getBasicRejectionReason(element, config) {
6696
6745
  const rect = element.getBoundingClientRect();
6697
6746
  if (rect.width === 0 || rect.height === 0) {
@@ -6708,20 +6757,32 @@ function calculateSizeScore(element, expected) {
6708
6757
  const heightRatio = Math.min(rect.height, expected.height) / Math.max(rect.height, expected.height);
6709
6758
  return (widthRatio + heightRatio) / 2 * 100;
6710
6759
  }
6711
- function findValidElement(selector, expectedSize, config = DEFAULT_CONFIG) {
6760
+ function findValidElement(selector, expectedElement, expectedSize, config = DEFAULT_CONFIG) {
6712
6761
  const elements = document.querySelectorAll(selector);
6713
6762
  if (elements.length === 0) {
6714
6763
  return null;
6715
6764
  }
6716
- if (!expectedSize) {
6717
- for (const element of elements) {
6718
- const rejection = getBasicRejectionReason(element, config);
6719
- if (!rejection) {
6720
- return element;
6721
- }
6765
+ if (expectedElement) {
6766
+ const tagTextMatch = findElementByTagAndText(elements, expectedElement, config);
6767
+ if (tagTextMatch) {
6768
+ return tagTextMatch;
6769
+ }
6770
+ if (!expectedSize) {
6771
+ return null;
6772
+ }
6773
+ }
6774
+ if (expectedSize) {
6775
+ return findValidElementBySize(selector, elements, expectedSize, config);
6776
+ }
6777
+ for (const element of elements) {
6778
+ const rejection = getBasicRejectionReason(element, config);
6779
+ if (!rejection) {
6780
+ return element;
6722
6781
  }
6723
- return elements[0];
6724
6782
  }
6783
+ return elements[0];
6784
+ }
6785
+ function findValidElementBySize(selector, elements, expectedSize, config) {
6725
6786
  const threshold = (1 - config.tolerance) * 100;
6726
6787
  const scored = [];
6727
6788
  for (const element of elements) {
@@ -6757,49 +6818,63 @@ function findValidElement(selector, expectedSize, config = DEFAULT_CONFIG) {
6757
6818
  log(`ElementValidator: Selected element with ${valid[0].score.toFixed(0)}% match`);
6758
6819
  return valid[0].element;
6759
6820
  }
6760
- function findAllValidElements(selector, expectedSize, config = DEFAULT_CONFIG) {
6821
+ function findAllValidElements(selector, expectedElement, expectedSize, config = DEFAULT_CONFIG) {
6761
6822
  const elements = document.querySelectorAll(selector);
6762
6823
  if (elements.length === 0) {
6763
6824
  return [];
6764
6825
  }
6765
- if (!expectedSize) {
6826
+ if (expectedElement) {
6827
+ const tagTextMatches = findAllElementsByTagAndText(elements, expectedElement, config);
6828
+ if (tagTextMatches.length > 0) {
6829
+ return tagTextMatches;
6830
+ }
6831
+ if (!expectedSize) {
6832
+ return [];
6833
+ }
6834
+ }
6835
+ if (expectedSize) {
6836
+ const threshold = (1 - config.tolerance) * 100;
6766
6837
  const valid2 = [];
6767
6838
  for (const element of elements) {
6768
6839
  const rejection = getBasicRejectionReason(element, config);
6769
- if (!rejection) {
6840
+ if (rejection) {
6841
+ continue;
6842
+ }
6843
+ const score = calculateSizeScore(element, expectedSize);
6844
+ if (score >= threshold) {
6770
6845
  valid2.push(element);
6771
6846
  }
6772
6847
  }
6773
- return valid2.length > 0 ? valid2 : Array.from(elements);
6848
+ if (valid2.length > 0) {
6849
+ log(`ElementValidator: Found ${valid2.length} valid element(s) for '${selector}'`);
6850
+ }
6851
+ return valid2;
6774
6852
  }
6775
- const threshold = (1 - config.tolerance) * 100;
6776
6853
  const valid = [];
6777
6854
  for (const element of elements) {
6778
6855
  const rejection = getBasicRejectionReason(element, config);
6779
- if (rejection) {
6780
- continue;
6781
- }
6782
- const score = calculateSizeScore(element, expectedSize);
6783
- if (score >= threshold) {
6856
+ if (!rejection) {
6784
6857
  valid.push(element);
6785
6858
  }
6786
6859
  }
6787
- if (valid.length > 0) {
6788
- log(`ElementValidator: Found ${valid.length} valid element(s) for '${selector}'`);
6789
- }
6790
- return valid;
6860
+ return valid.length > 0 ? valid : Array.from(elements);
6791
6861
  }
6792
- function isElementValid(element, expectedSize, config = DEFAULT_CONFIG) {
6862
+ function isElementValid(element, expectedElement, expectedSize, config = DEFAULT_CONFIG) {
6793
6863
  const rejection = getBasicRejectionReason(element, config);
6794
6864
  if (rejection) {
6795
6865
  return false;
6796
6866
  }
6797
- if (!expectedSize) {
6798
- return true;
6867
+ if (expectedElement) {
6868
+ if (matchesExpectedElement(element, expectedElement)) {
6869
+ return true;
6870
+ }
6799
6871
  }
6800
- const threshold = (1 - config.tolerance) * 100;
6801
- const score = calculateSizeScore(element, expectedSize);
6802
- return score >= threshold;
6872
+ if (expectedSize) {
6873
+ const threshold = (1 - config.tolerance) * 100;
6874
+ const score = calculateSizeScore(element, expectedSize);
6875
+ return score >= threshold;
6876
+ }
6877
+ return true;
6803
6878
  }
6804
6879
  class CursorManager {
6805
6880
  constructor() {
@@ -6974,9 +7049,10 @@ class CursorManager {
6974
7049
  * Sets up a MutationObserver to wait for an element to appear in the DOM
6975
7050
  * @param selector - CSS selector to wait for
6976
7051
  * @param callback - Callback to execute when element is found
7052
+ * @param expectedElement - Optional expected tag+text for validation
6977
7053
  * @param expectedSize - Optional expected size for validation
6978
7054
  */
6979
- waitForElement(selector, callback, expectedSize) {
7055
+ waitForElement(selector, callback, expectedElement, expectedSize) {
6980
7056
  if (this.targetMutationObserver) {
6981
7057
  this.targetMutationObserver.disconnect();
6982
7058
  this.targetMutationObserver = null;
@@ -7001,7 +7077,7 @@ class CursorManager {
7001
7077
  cleanupWatchers();
7002
7078
  return null;
7003
7079
  }
7004
- return this.findElementAndScrollIntoView(selector, expectedSize);
7080
+ return this.findElementAndScrollIntoView(selector, expectedElement, expectedSize);
7005
7081
  };
7006
7082
  this.targetMutationObserver = new MutationObserver(async () => {
7007
7083
  if (this.isAutoplayBlocked()) {
@@ -7026,7 +7102,7 @@ class CursorManager {
7026
7102
  cleanupWatchers();
7027
7103
  return;
7028
7104
  }
7029
- const found = this.findElement(selector, expectedSize);
7105
+ const found = this.findElement(selector, expectedElement, expectedSize);
7030
7106
  if (found) {
7031
7107
  cleanupWatchers();
7032
7108
  callback(found);
@@ -7080,15 +7156,16 @@ class CursorManager {
7080
7156
  }
7081
7157
  /**
7082
7158
  * Helper function to find an element in the document
7083
- * Uses size validation when expectedSize is provided
7084
- * Falls back to viewport-based selection when no expectedSize or when validation passes
7159
+ * Uses tag+text validation first (if provided), then size validation
7160
+ * Falls back to viewport-based selection when no validation criteria or when validation passes
7085
7161
  * @param selector - CSS selector
7162
+ * @param expectedElement - Optional expected tag+text for validation
7086
7163
  * @param expectedSize - Optional expected size for validation
7087
7164
  * @returns - The found element or null
7088
7165
  */
7089
- findElement(selector, expectedSize) {
7090
- if (expectedSize) {
7091
- const validElement = findValidElement(selector, expectedSize);
7166
+ findElement(selector, expectedElement, expectedSize) {
7167
+ if (expectedElement || expectedSize) {
7168
+ const validElement = findValidElement(selector, expectedElement, expectedSize);
7092
7169
  if (validElement) {
7093
7170
  return validElement;
7094
7171
  }
@@ -7233,11 +7310,12 @@ class CursorManager {
7233
7310
  /**
7234
7311
  * Finds an element and scrolls it into view if necessary
7235
7312
  * @param selector - CSS selector
7313
+ * @param expectedElement - Optional expected tag+text for validation
7236
7314
  * @param expectedSize - Optional expected size for validation
7237
7315
  * @returns - Promise that resolves with the element or null
7238
7316
  */
7239
- async findElementAndScrollIntoView(selector, expectedSize) {
7240
- const element = this.findElement(selector, expectedSize);
7317
+ async findElementAndScrollIntoView(selector, expectedElement, expectedSize) {
7318
+ const element = this.findElement(selector, expectedElement, expectedSize);
7241
7319
  if (!element) {
7242
7320
  return null;
7243
7321
  }
@@ -7577,12 +7655,12 @@ class CursorManager {
7577
7655
  if (this.isAutoplayBlocked()) {
7578
7656
  return;
7579
7657
  }
7580
- const targetElement = await this.findElementAndScrollIntoView(animation.targetSelector, animation.expectedSize);
7658
+ const targetElement = await this.findElementAndScrollIntoView(animation.targetSelector, animation.expectedElement, animation.expectedSize);
7581
7659
  if (!targetElement) {
7582
7660
  console.warn("CursorManager: Target element not found in animate:", animation.targetSelector);
7583
7661
  this.setShouldShowCursor(false);
7584
7662
  this.hideCursorElements();
7585
- this.waitForElement(animation.targetSelector, () => this.animate(animation), animation.expectedSize);
7663
+ this.waitForElement(animation.targetSelector, () => this.animate(animation), animation.expectedElement, animation.expectedSize);
7586
7664
  return;
7587
7665
  }
7588
7666
  this.setShouldShowCursor(true);
@@ -9256,7 +9334,7 @@ class TransitionManager {
9256
9334
  element.addEventListener("click", handler, { capture: true });
9257
9335
  });
9258
9336
  };
9259
- const initialElements = transition.expectedSize ? findAllValidElements(selector, transition.expectedSize) : Array.from(document.querySelectorAll(selector));
9337
+ const initialElements = transition.expectedElement || transition.expectedSize ? findAllValidElements(selector, transition.expectedElement, transition.expectedSize) : Array.from(document.querySelectorAll(selector));
9260
9338
  addClickHandlersToElements(initialElements);
9261
9339
  mutationObserver = new MutationObserver((mutationsList) => {
9262
9340
  for (const mutation of mutationsList) {
@@ -9265,13 +9343,13 @@ class TransitionManager {
9265
9343
  if (node.nodeType === Node.ELEMENT_NODE) {
9266
9344
  const elementNode = node;
9267
9345
  if (elementNode.matches(selector)) {
9268
- if (!transition.expectedSize || isElementValid(elementNode, transition.expectedSize)) {
9346
+ if (!transition.expectedElement && !transition.expectedSize || isElementValid(elementNode, transition.expectedElement, transition.expectedSize)) {
9269
9347
  addClickHandlersToElements([elementNode]);
9270
9348
  }
9271
9349
  }
9272
9350
  const matchingDescendants = elementNode.querySelectorAll(selector);
9273
9351
  if (matchingDescendants.length > 0) {
9274
- const validDescendants = transition.expectedSize ? Array.from(matchingDescendants).filter((el) => isElementValid(el, transition.expectedSize)) : Array.from(matchingDescendants);
9352
+ const validDescendants = transition.expectedElement || transition.expectedSize ? Array.from(matchingDescendants).filter((el) => isElementValid(el, transition.expectedElement, transition.expectedSize)) : Array.from(matchingDescendants);
9275
9353
  if (validDescendants.length > 0) {
9276
9354
  log(`TransitionManager: Found ${validDescendants.length} valid descendants matching '${selector}'`);
9277
9355
  addClickHandlersToElements(validDescendants);
@@ -9607,7 +9685,7 @@ class TransitionManager {
9607
9685
  mutationObserver = null;
9608
9686
  }
9609
9687
  };
9610
- const initialElement = transition.expectedSize ? findValidElement(selector, transition.expectedSize) : document.querySelector(selector);
9688
+ const initialElement = transition.expectedElement || transition.expectedSize ? findValidElement(selector, transition.expectedElement, transition.expectedSize) : document.querySelector(selector);
9611
9689
  if (initialElement) {
9612
9690
  setupIntersectionObserver(initialElement);
9613
9691
  } else {
@@ -9618,12 +9696,12 @@ class TransitionManager {
9618
9696
  if (node.nodeType === Node.ELEMENT_NODE) {
9619
9697
  const elementNode = node;
9620
9698
  if (elementNode.matches(selector)) {
9621
- if (!transition.expectedSize || isElementValid(elementNode, transition.expectedSize)) {
9699
+ if (!transition.expectedElement && !transition.expectedSize || isElementValid(elementNode, transition.expectedElement, transition.expectedSize)) {
9622
9700
  setupIntersectionObserver(elementNode);
9623
9701
  return;
9624
9702
  }
9625
9703
  }
9626
- const matchingDescendant = transition.expectedSize ? findValidElement(selector, transition.expectedSize) : elementNode.querySelector(selector);
9704
+ const matchingDescendant = transition.expectedElement || transition.expectedSize ? findValidElement(selector, transition.expectedElement, transition.expectedSize) : elementNode.querySelector(selector);
9627
9705
  if (matchingDescendant) {
9628
9706
  setupIntersectionObserver(matchingDescendant);
9629
9707
  return;
@@ -9644,7 +9722,7 @@ class TransitionManager {
9644
9722
  }
9645
9723
  return;
9646
9724
  }
9647
- const element = transition.expectedSize ? findValidElement(selector, transition.expectedSize) : document.querySelector(selector);
9725
+ const element = transition.expectedElement || transition.expectedSize ? findValidElement(selector, transition.expectedElement, transition.expectedSize) : document.querySelector(selector);
9648
9726
  if (element && element.offsetWidth > 0 && element.offsetHeight > 0) {
9649
9727
  if (periodicCheck) {
9650
9728
  clearInterval(periodicCheck);
@@ -10106,6 +10184,7 @@ class TriggerManager {
10106
10184
  this.setupElementClickListener(
10107
10185
  playlist.id,
10108
10186
  playlist.triggers.elementClicked,
10187
+ playlist.triggers.elementClickedExpectedElement,
10109
10188
  playlist.triggers.elementClickedExpectedSize
10110
10189
  );
10111
10190
  }
@@ -10115,11 +10194,12 @@ class TriggerManager {
10115
10194
  * Sets up a click event listener for a specific playlist and selector
10116
10195
  * @param playlistId - The playlist ID
10117
10196
  * @param selector - CSS selector for the target element
10197
+ * @param expectedElement - Optional expected tag+text for validation
10118
10198
  * @param expectedSize - Optional expected size for validation
10119
10199
  */
10120
- setupElementClickListener(playlistId, selector, expectedSize) {
10200
+ setupElementClickListener(playlistId, selector, expectedElement, expectedSize) {
10121
10201
  try {
10122
- const element = expectedSize ? findValidElement(selector, expectedSize) : document.querySelector(selector);
10202
+ const element = expectedElement || expectedSize ? findValidElement(selector, expectedElement, expectedSize) : document.querySelector(selector);
10123
10203
  if (!element) {
10124
10204
  log(`TriggerManager: Element not found for selector '${selector}' (playlist: ${playlistId})`);
10125
10205
  return;
@@ -10171,6 +10251,7 @@ class TriggerManager {
10171
10251
  this.setupElementVisibleObserver(
10172
10252
  playlist.id,
10173
10253
  playlist.triggers.elementVisible,
10254
+ playlist.triggers.elementVisibleExpectedElement,
10174
10255
  playlist.triggers.elementVisibleExpectedSize
10175
10256
  );
10176
10257
  }
@@ -10180,9 +10261,10 @@ class TriggerManager {
10180
10261
  * Sets up a visibility observer for a specific playlist and selector
10181
10262
  * @param playlistId - The playlist ID
10182
10263
  * @param selector - CSS selector for the target element
10264
+ * @param expectedElement - Optional expected tag+text for validation
10183
10265
  * @param expectedSize - Optional expected size for validation
10184
10266
  */
10185
- setupElementVisibleObserver(playlistId, selector, expectedSize) {
10267
+ setupElementVisibleObserver(playlistId, selector, expectedElement, expectedSize) {
10186
10268
  try {
10187
10269
  const observerId = `${playlistId}-${selector}`;
10188
10270
  if (this.elementVisibleObservers.has(observerId)) {
@@ -10240,7 +10322,7 @@ class TriggerManager {
10240
10322
  });
10241
10323
  }
10242
10324
  };
10243
- const initialElement = expectedSize ? findValidElement(selector, expectedSize) : document.querySelector(selector);
10325
+ const initialElement = expectedElement || expectedSize ? findValidElement(selector, expectedElement, expectedSize) : document.querySelector(selector);
10244
10326
  if (initialElement) {
10245
10327
  log(`TriggerManager: Found element matching '${selector}' immediately`);
10246
10328
  setupIntersectionObserver(initialElement);
@@ -10253,12 +10335,12 @@ class TriggerManager {
10253
10335
  if (node.nodeType === Node.ELEMENT_NODE) {
10254
10336
  const elementNode = node;
10255
10337
  if (elementNode.matches(selector)) {
10256
- if (!expectedSize || isElementValid(elementNode, expectedSize)) {
10338
+ if (!expectedElement && !expectedSize || isElementValid(elementNode, expectedElement, expectedSize)) {
10257
10339
  log(`TriggerManager: Added node matches '${selector}'`);
10258
10340
  setupIntersectionObserver(elementNode);
10259
10341
  }
10260
10342
  } else {
10261
- const matchingDescendant = expectedSize ? findValidElement(selector, expectedSize) : elementNode.querySelector(selector);
10343
+ const matchingDescendant = expectedElement || expectedSize ? findValidElement(selector, expectedElement, expectedSize) : elementNode.querySelector(selector);
10262
10344
  if (matchingDescendant) {
10263
10345
  log(`TriggerManager: Found descendant matching '${selector}'`);
10264
10346
  setupIntersectionObserver(matchingDescendant);
@@ -12172,7 +12254,7 @@ const SaltfishPlayer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.de
12172
12254
  __proto__: null,
12173
12255
  SaltfishPlayer
12174
12256
  }, Symbol.toStringTag, { value: "Module" }));
12175
- const version = "0.3.57";
12257
+ const version = "0.3.59";
12176
12258
  const packageJson = {
12177
12259
  version
12178
12260
  };