@eko-ai/eko 1.0.2 → 1.0.4

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.
package/dist/web.cjs.js CHANGED
@@ -8,15 +8,16 @@
8
8
  * @returns { element_str, selector_map }
9
9
  */
10
10
  function get_clickable_elements(doHighlightElements = true, includeAttributes) {
11
+ window.clickable_elements = {};
11
12
  let page_tree = build_dom_tree(doHighlightElements);
12
13
  let element_tree = parse_node(page_tree);
13
14
  let selector_map = create_selector_map(element_tree);
14
15
  let element_str = clickable_elements_to_string(element_tree, includeAttributes);
15
16
  return { element_str, selector_map };
16
17
  }
17
- /**
18
- * Remove highlight
19
- */
18
+ function get_highlight_element(highlightIndex) {
19
+ return window.clickable_elements[highlightIndex];
20
+ }
20
21
  function remove_highlight() {
21
22
  let highlight = document.getElementById('playwright-highlight-container');
22
23
  if (highlight) {
@@ -539,6 +540,7 @@ function build_dom_tree(doHighlightElements) {
539
540
  // Highlight if element meets all criteria and highlighting is enabled
540
541
  if (isInteractive && isVisible && isTop) {
541
542
  nodeData.highlightIndex = highlightIndex++;
543
+ window.clickable_elements[nodeData.highlightIndex] = node;
542
544
  if (doHighlightElements) {
543
545
  highlightElement(node, nodeData.highlightIndex, parentIframe);
544
546
  }
@@ -579,198 +581,9 @@ function build_dom_tree(doHighlightElements) {
579
581
  return buildDomTree(document.body);
580
582
  }
581
583
  window.get_clickable_elements = get_clickable_elements;
584
+ window.get_highlight_element = get_highlight_element;
582
585
  window.remove_highlight = remove_highlight;
583
586
 
584
- function exportFile(filename, type, content) {
585
- const blob = new Blob([content], { type: type });
586
- const link = document.createElement('a');
587
- link.href = URL.createObjectURL(blob);
588
- link.download = filename;
589
- document.body.appendChild(link);
590
- link.click();
591
- document.body.removeChild(link);
592
- URL.revokeObjectURL(link.href);
593
- }
594
- function xpath(element) {
595
- if (element.id !== '') {
596
- return '//*[@id=\"' + element.id + '\"]';
597
- }
598
- if (element == document.body) {
599
- return '/html/' + element.tagName.toLowerCase();
600
- }
601
- var ix = 1, siblings = element.parentNode.childNodes;
602
- for (var i = 0, l = siblings.length; i < l; i++) {
603
- var sibling = siblings[i];
604
- if (sibling == element) {
605
- return xpath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + ix + ']';
606
- }
607
- else if (sibling.nodeType == 1 && sibling.tagName == element.tagName) {
608
- ix++;
609
- }
610
- }
611
- return '';
612
- }
613
- function getDropdownOptions(xpath) {
614
- const select = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
615
- .singleNodeValue;
616
- if (!select) {
617
- return null;
618
- }
619
- return {
620
- options: Array.from(select.options).map((opt) => ({
621
- index: opt.index,
622
- text: opt.text.trim(),
623
- value: opt.value,
624
- })),
625
- id: select.id,
626
- name: select.name,
627
- };
628
- }
629
- function selectDropdownOption(xpath, text) {
630
- const select = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
631
- .singleNodeValue;
632
- if (!select || select.tagName.toUpperCase() !== 'SELECT') {
633
- return { success: false, error: 'Select not found or invalid element type' };
634
- }
635
- const option = Array.from(select.options).find((opt) => opt.text.trim() === text);
636
- if (!option) {
637
- return {
638
- success: false,
639
- error: 'Option not found',
640
- availableOptions: Array.from(select.options).map((o) => o.text.trim()),
641
- };
642
- }
643
- select.value = option.value;
644
- select.dispatchEvent(new Event('change'));
645
- return {
646
- success: true,
647
- selectedValue: option.value,
648
- selectedText: option.text.trim(),
649
- };
650
- }
651
- /**
652
- * Extract the elements related to html operability and wrap them into pseudo-html code.
653
- */
654
- function extractOperableElements() {
655
- // visible
656
- const isElementVisible = (element) => {
657
- const style = window.getComputedStyle(element);
658
- return (style.display !== 'none' &&
659
- style.visibility !== 'hidden' &&
660
- style.opacity !== '0' &&
661
- element.offsetWidth > 0 &&
662
- element.offsetHeight > 0);
663
- };
664
- // element original index
665
- const getElementIndex = (element) => {
666
- const xpath = document.evaluate('preceding::*', element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
667
- return xpath.snapshotLength;
668
- };
669
- // exclude
670
- const addExclude = (excludes, children) => {
671
- for (let i = 0; i < children.length; i++) {
672
- excludes.push(children[i]);
673
- if (children[i].children) {
674
- addExclude(excludes, children[i].children);
675
- }
676
- }
677
- };
678
- // { pseudoId: element }
679
- let elementMap = {};
680
- let nextId = 1;
681
- let elements = [];
682
- let excludes = [];
683
- // operable element
684
- const operableSelectors = 'a, button, input, textarea, select';
685
- document.querySelectorAll(operableSelectors).forEach((element) => {
686
- if (isElementVisible(element) && excludes.indexOf(element) == -1) {
687
- const id = nextId++;
688
- elementMap[id.toString()] = element;
689
- const tagName = element.tagName.toLowerCase();
690
- const attributes = Array.from(element.attributes)
691
- .filter((attr) => ['id', 'name', 'type', 'value', 'href', 'title', 'placeholder'].includes(attr.name))
692
- .map((attr) => `${attr.name == 'id' ? 'target' : attr.name}="${attr.value}"`)
693
- .join(' ');
694
- elements.push({
695
- originalIndex: getElementIndex(element),
696
- id: id,
697
- html: `<${tagName} id="${id}" ${attributes}>${tagName == 'select' ? element.innerHTML : element.innerText || ''}</${tagName}>`,
698
- });
699
- addExclude(excludes, element.children);
700
- }
701
- });
702
- // short text element
703
- const textWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
704
- acceptNode: function (node) {
705
- var _a;
706
- if (node.matches(operableSelectors) || excludes.indexOf(node) != -1) {
707
- // skip
708
- return NodeFilter.FILTER_SKIP;
709
- }
710
- // text <= 100
711
- const text = (_a = node.innerText) === null || _a === void 0 ? void 0 : _a.trim();
712
- if (isElementVisible(node) &&
713
- text &&
714
- text.length <= 100 &&
715
- text.length > 0 &&
716
- node.children.length === 0) {
717
- return NodeFilter.FILTER_ACCEPT;
718
- }
719
- // skip
720
- return NodeFilter.FILTER_SKIP;
721
- },
722
- });
723
- let currentNode;
724
- while ((currentNode = textWalker.nextNode())) {
725
- const id = nextId++;
726
- elementMap[id.toString()] = currentNode;
727
- const tagName = currentNode.tagName.toLowerCase();
728
- elements.push({
729
- originalIndex: getElementIndex(currentNode),
730
- id: id,
731
- html: `<${tagName} id="${id}">${currentNode.innerText.trim()}</${tagName}>`,
732
- });
733
- }
734
- // element sort
735
- elements.sort((a, b) => a.originalIndex - b.originalIndex);
736
- // cache
737
- window.operableElementMap = elementMap;
738
- // pseudo html
739
- return elements.map((e) => e.html).join('\n');
740
- }
741
- function clickOperableElement(id) {
742
- let element = window.operableElementMap[id];
743
- if (!element) {
744
- return false;
745
- }
746
- if (element.click) {
747
- element.click();
748
- }
749
- else {
750
- element.dispatchEvent(new MouseEvent('click', {
751
- view: window,
752
- bubbles: true,
753
- cancelable: true,
754
- }));
755
- }
756
- return true;
757
- }
758
- function getOperableElementRect(id) {
759
- let element = window.operableElementMap[id];
760
- if (!element) {
761
- return null;
762
- }
763
- const rect = element.getBoundingClientRect();
764
- return {
765
- left: rect.left + window.scrollX,
766
- top: rect.top + window.scrollY,
767
- right: rect.right + window.scrollX,
768
- bottom: rect.bottom + window.scrollY,
769
- width: rect.right - rect.left,
770
- height: rect.bottom - rect.top,
771
- };
772
- }
773
-
774
587
  /*!
775
588
  * html2canvas 1.4.1 <https://html2canvas.hertzen.com>
776
589
  * Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
@@ -8591,20 +8404,20 @@ var parseBackgroundColor = function (context, element, backgroundColorOverride)
8591
8404
  : defaultBackgroundColor;
8592
8405
  };
8593
8406
 
8594
- function type(xpath, text) {
8595
- return do_input(xpath, text);
8407
+ function type(text, xpath, highlightIndex) {
8408
+ return do_input(text, xpath, highlightIndex);
8596
8409
  }
8597
- function clear_input(xpath) {
8598
- return do_input(xpath, '');
8410
+ function clear_input(xpath, highlightIndex) {
8411
+ return do_input('', xpath, highlightIndex);
8599
8412
  }
8600
- function left_click(xpath) {
8601
- return simulateMouseEvent(xpath, ['mousedown', 'mouseup', 'click'], 0);
8413
+ function left_click(xpath, highlightIndex) {
8414
+ return simulateMouseEvent(['mousedown', 'mouseup', 'click'], 0, xpath, highlightIndex);
8602
8415
  }
8603
- function right_click(xpath) {
8604
- return simulateMouseEvent(xpath, ['mousedown', 'mouseup', 'contextmenu'], 2);
8416
+ function right_click(xpath, highlightIndex) {
8417
+ return simulateMouseEvent(['mousedown', 'mouseup', 'contextmenu'], 2, xpath, highlightIndex);
8605
8418
  }
8606
- function double_click(xpath) {
8607
- return simulateMouseEvent(xpath, ['mousedown', 'mouseup', 'click', 'mousedown', 'mouseup', 'click', 'dblclick'], 0);
8419
+ function double_click(xpath, highlightIndex) {
8420
+ return simulateMouseEvent(['mousedown', 'mouseup', 'click', 'mousedown', 'mouseup', 'click', 'dblclick'], 0, xpath, highlightIndex);
8608
8421
  }
8609
8422
  async function screenshot() {
8610
8423
  const [width, height] = size();
@@ -8634,12 +8447,72 @@ async function screenshot() {
8634
8447
  },
8635
8448
  };
8636
8449
  }
8637
- function scroll_to(xpath) {
8638
- let result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
8639
- let element = result.singleNodeValue;
8640
- return element.scrollIntoView({
8450
+ function scroll_to(xpath, highlightIndex) {
8451
+ let element = null;
8452
+ if (highlightIndex != null) {
8453
+ element = window.get_highlight_element(highlightIndex);
8454
+ }
8455
+ else if (xpath) {
8456
+ element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
8457
+ .singleNodeValue;
8458
+ }
8459
+ if (!element) {
8460
+ return false;
8461
+ }
8462
+ element.scrollIntoView({
8641
8463
  behavior: 'smooth',
8642
8464
  });
8465
+ return true;
8466
+ }
8467
+ function get_dropdown_options(xpath, highlightIndex) {
8468
+ let select = null;
8469
+ if (highlightIndex != null) {
8470
+ select = window.get_highlight_element(highlightIndex);
8471
+ }
8472
+ else if (xpath) {
8473
+ select = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
8474
+ .singleNodeValue;
8475
+ }
8476
+ if (!select) {
8477
+ return null;
8478
+ }
8479
+ return {
8480
+ options: Array.from(select.options).map((opt) => ({
8481
+ index: opt.index,
8482
+ text: opt.text.trim(),
8483
+ value: opt.value,
8484
+ })),
8485
+ id: select.id,
8486
+ name: select.name,
8487
+ };
8488
+ }
8489
+ function select_dropdown_option(text, xpath, highlightIndex) {
8490
+ let select = null;
8491
+ if (highlightIndex != null) {
8492
+ select = window.get_highlight_element(highlightIndex);
8493
+ }
8494
+ else if (xpath) {
8495
+ select = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
8496
+ .singleNodeValue;
8497
+ }
8498
+ if (!select || select.tagName.toUpperCase() !== 'SELECT') {
8499
+ return { success: false, error: 'Select not found or invalid element type' };
8500
+ }
8501
+ const option = Array.from(select.options).find((opt) => opt.text.trim() === text);
8502
+ if (!option) {
8503
+ return {
8504
+ success: false,
8505
+ error: 'Option not found',
8506
+ availableOptions: Array.from(select.options).map((o) => o.text.trim()),
8507
+ };
8508
+ }
8509
+ select.value = option.value;
8510
+ select.dispatchEvent(new Event('change'));
8511
+ return {
8512
+ success: true,
8513
+ selectedValue: option.value,
8514
+ selectedText: option.text.trim(),
8515
+ };
8643
8516
  }
8644
8517
  function extractHtmlContent() {
8645
8518
  let element = document.body;
@@ -8676,11 +8549,26 @@ function size() {
8676
8549
  window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight,
8677
8550
  ];
8678
8551
  }
8679
- function do_input(xpath, text) {
8680
- let query_result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
8681
- let element = query_result.singleNodeValue;
8552
+ function do_input(text, xpath, highlightIndex) {
8553
+ let element = null;
8554
+ if (highlightIndex != null) {
8555
+ element = window.get_highlight_element(highlightIndex);
8556
+ }
8557
+ else if (xpath) {
8558
+ element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
8559
+ .singleNodeValue;
8560
+ }
8682
8561
  if (!element) {
8683
- return;
8562
+ return false;
8563
+ }
8564
+ let enter = false;
8565
+ if (text.endsWith('\\n')) {
8566
+ enter = true;
8567
+ text = text.substring(0, text.length - 2);
8568
+ }
8569
+ else if (text.endsWith('\n')) {
8570
+ enter = true;
8571
+ text = text.substring(0, text.length - 1);
8684
8572
  }
8685
8573
  let input;
8686
8574
  if (element.tagName == 'INPUT' ||
@@ -8699,13 +8587,33 @@ function do_input(xpath, text) {
8699
8587
  input.value += text;
8700
8588
  }
8701
8589
  let result = input.dispatchEvent(new Event('input', { bubbles: true }));
8590
+ if (enter) {
8591
+ ['keydown', 'keypress', 'keyup'].forEach((eventType) => {
8592
+ const event = new KeyboardEvent(eventType, {
8593
+ key: 'Enter',
8594
+ code: 'Enter',
8595
+ keyCode: 13,
8596
+ bubbles: true,
8597
+ cancelable: true,
8598
+ });
8599
+ input.dispatchEvent(event);
8600
+ });
8601
+ }
8702
8602
  console.log('type', input, result);
8703
- return result;
8603
+ return true;
8704
8604
  }
8705
- function simulateMouseEvent(xpath, eventTypes, button) {
8706
- let query_result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
8707
- let element = query_result.singleNodeValue;
8708
- let result = false;
8605
+ function simulateMouseEvent(eventTypes, button, xpath, highlightIndex) {
8606
+ let element = null;
8607
+ if (highlightIndex != null) {
8608
+ element = window.get_highlight_element(highlightIndex);
8609
+ }
8610
+ else if (xpath) {
8611
+ element = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)
8612
+ .singleNodeValue;
8613
+ }
8614
+ if (!element) {
8615
+ return false;
8616
+ }
8709
8617
  for (let i = 0; i < eventTypes.length; i++) {
8710
8618
  const event = new MouseEvent(eventTypes[i], {
8711
8619
  view: window,
@@ -8713,10 +8621,10 @@ function simulateMouseEvent(xpath, eventTypes, button) {
8713
8621
  cancelable: true,
8714
8622
  button, // 0 left; 2 right
8715
8623
  });
8716
- result = element.dispatchEvent(event);
8624
+ let result = element.dispatchEvent(event);
8717
8625
  console.log('simulateMouse', element, { xpath, eventTypes, button }, result);
8718
8626
  }
8719
- return result;
8627
+ return true;
8720
8628
  }
8721
8629
 
8722
8630
  var browser = /*#__PURE__*/Object.freeze({
@@ -8724,10 +8632,12 @@ var browser = /*#__PURE__*/Object.freeze({
8724
8632
  clear_input: clear_input,
8725
8633
  double_click: double_click,
8726
8634
  extractHtmlContent: extractHtmlContent,
8635
+ get_dropdown_options: get_dropdown_options,
8727
8636
  left_click: left_click,
8728
8637
  right_click: right_click,
8729
8638
  screenshot: screenshot,
8730
8639
  scroll_to: scroll_to,
8640
+ select_dropdown_option: select_dropdown_option,
8731
8641
  size: size,
8732
8642
  type: type
8733
8643
  });
@@ -8817,7 +8727,7 @@ class BrowserUse {
8817
8727
  if (params === null || !params.action) {
8818
8728
  throw new Error('Invalid parameters. Expected an object with a "action" property.');
8819
8729
  }
8820
- let selector_map = context.variables.get('selector_map');
8730
+ let selector_map = context.selector_map;
8821
8731
  let selector_xpath;
8822
8732
  if (params.index != null && selector_map) {
8823
8733
  selector_xpath = (_a = selector_map[params.index]) === null || _a === void 0 ? void 0 : _a.xpath;
@@ -8834,42 +8744,42 @@ class BrowserUse {
8834
8744
  if (params.text == null) {
8835
8745
  throw new Error('text parameter is required');
8836
8746
  }
8837
- result = await type(selector_xpath, params.text);
8747
+ result = await type(params.text, selector_xpath, params.index);
8838
8748
  await sleep(200);
8839
8749
  break;
8840
8750
  case 'clear_text':
8841
8751
  if (params.index == null) {
8842
8752
  throw new Error('index parameter is required');
8843
8753
  }
8844
- result = await clear_input(selector_xpath);
8754
+ result = await clear_input(selector_xpath, params.index);
8845
8755
  await sleep(100);
8846
8756
  break;
8847
8757
  case 'click':
8848
8758
  if (params.index == null) {
8849
8759
  throw new Error('index parameter is required');
8850
8760
  }
8851
- result = await left_click(selector_xpath);
8761
+ result = await left_click(selector_xpath, params.index);
8852
8762
  await sleep(100);
8853
8763
  break;
8854
8764
  case 'right_click':
8855
8765
  if (params.index == null) {
8856
8766
  throw new Error('index parameter is required');
8857
8767
  }
8858
- result = await right_click(selector_xpath);
8768
+ result = await right_click(selector_xpath, params.index);
8859
8769
  await sleep(100);
8860
8770
  break;
8861
8771
  case 'double_click':
8862
8772
  if (params.index == null) {
8863
8773
  throw new Error('index parameter is required');
8864
8774
  }
8865
- result = await double_click(selector_xpath);
8775
+ result = await double_click(selector_xpath, params.index);
8866
8776
  await sleep(100);
8867
8777
  break;
8868
8778
  case 'scroll_to':
8869
8779
  if (params.index == null) {
8870
8780
  throw new Error('index parameter is required');
8871
8781
  }
8872
- result = await scroll_to(selector_xpath);
8782
+ result = await scroll_to(selector_xpath, params.index);
8873
8783
  await sleep(500);
8874
8784
  break;
8875
8785
  case 'extract_content':
@@ -8884,7 +8794,7 @@ class BrowserUse {
8884
8794
  if (params.index == null) {
8885
8795
  throw new Error('index parameter is required');
8886
8796
  }
8887
- result = getDropdownOptions(selector_xpath);
8797
+ result = get_dropdown_options(selector_xpath, params.index);
8888
8798
  break;
8889
8799
  case 'select_dropdown_option':
8890
8800
  if (params.index == null) {
@@ -8893,12 +8803,12 @@ class BrowserUse {
8893
8803
  if (params.text == null) {
8894
8804
  throw new Error('text parameter is required');
8895
8805
  }
8896
- result = selectDropdownOption(selector_xpath, params.text);
8806
+ result = select_dropdown_option(params.text, selector_xpath, params.index);
8897
8807
  break;
8898
8808
  case 'screenshot_extract_element':
8899
8809
  await sleep(100);
8900
8810
  let element_result = get_clickable_elements(true, null);
8901
- context.variables.set('selector_map', element_result.selector_map);
8811
+ context.selector_map = element_result.selector_map;
8902
8812
  let screenshot$1 = await screenshot();
8903
8813
  remove_highlight();
8904
8814
  result = { image: screenshot$1.image, text: element_result.element_str };
@@ -8917,11 +8827,187 @@ class BrowserUse {
8917
8827
  return { success: false, error: e === null || e === void 0 ? void 0 : e.message };
8918
8828
  }
8919
8829
  }
8830
+ destroy(context) {
8831
+ delete context.selector_map;
8832
+ }
8920
8833
  }
8921
8834
  function sleep(time) {
8922
8835
  return new Promise((resolve) => setTimeout(() => resolve(), time));
8923
8836
  }
8924
8837
 
8838
+ function exportFile(filename, type, content) {
8839
+ const blob = new Blob([content], { type: type });
8840
+ const link = document.createElement('a');
8841
+ link.href = URL.createObjectURL(blob);
8842
+ link.download = filename;
8843
+ document.body.appendChild(link);
8844
+ link.click();
8845
+ document.body.removeChild(link);
8846
+ URL.revokeObjectURL(link.href);
8847
+ }
8848
+ function xpath(element) {
8849
+ if (element == document.body) {
8850
+ return '/html/' + element.tagName.toLowerCase();
8851
+ }
8852
+ if (element.parentNode instanceof ShadowRoot) {
8853
+ let shadowRoot = element.parentNode;
8854
+ let parent = shadowRoot.getRootNode().host;
8855
+ return xpath(parent) + '//' + element.tagName.toLowerCase();
8856
+ }
8857
+ else {
8858
+ let sp;
8859
+ let parent;
8860
+ if (element.parentNode instanceof ShadowRoot) {
8861
+ sp = '//';
8862
+ let shadowRoot = element.parentNode;
8863
+ parent = shadowRoot.getRootNode().host;
8864
+ }
8865
+ else {
8866
+ sp = '/';
8867
+ parent = element.parentNode;
8868
+ }
8869
+ let siblings = parent.childNodes;
8870
+ if (siblings.length == 1) {
8871
+ return xpath(parent) + sp + element.tagName.toLowerCase();
8872
+ }
8873
+ else {
8874
+ let ix = 1;
8875
+ for (let i = 0, l = siblings.length; i < l; i++) {
8876
+ let sibling = siblings[i];
8877
+ if (sibling == element) {
8878
+ return xpath(parent) + sp + element.tagName.toLowerCase() + '[' + ix + ']';
8879
+ }
8880
+ else if (sibling.nodeType == 1 && sibling.tagName == element.tagName) {
8881
+ ix++;
8882
+ }
8883
+ }
8884
+ return '';
8885
+ }
8886
+ }
8887
+ }
8888
+ /**
8889
+ * Extract the elements related to html operability and wrap them into pseudo-html code.
8890
+ */
8891
+ function extractOperableElements() {
8892
+ // visible
8893
+ const isElementVisible = (element) => {
8894
+ const style = window.getComputedStyle(element);
8895
+ return (style.display !== 'none' &&
8896
+ style.visibility !== 'hidden' &&
8897
+ style.opacity !== '0' &&
8898
+ element.offsetWidth > 0 &&
8899
+ element.offsetHeight > 0);
8900
+ };
8901
+ // element original index
8902
+ const getElementIndex = (element) => {
8903
+ const xpath = document.evaluate('preceding::*', element, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
8904
+ return xpath.snapshotLength;
8905
+ };
8906
+ // exclude
8907
+ const addExclude = (excludes, children) => {
8908
+ for (let i = 0; i < children.length; i++) {
8909
+ excludes.push(children[i]);
8910
+ if (children[i].children) {
8911
+ addExclude(excludes, children[i].children);
8912
+ }
8913
+ }
8914
+ };
8915
+ // { pseudoId: element }
8916
+ let elementMap = {};
8917
+ let nextId = 1;
8918
+ let elements = [];
8919
+ let excludes = [];
8920
+ // operable element
8921
+ const operableSelectors = 'a, button, input, textarea, select';
8922
+ document.querySelectorAll(operableSelectors).forEach((element) => {
8923
+ if (isElementVisible(element) && excludes.indexOf(element) == -1) {
8924
+ const id = nextId++;
8925
+ elementMap[id.toString()] = element;
8926
+ const tagName = element.tagName.toLowerCase();
8927
+ const attributes = Array.from(element.attributes)
8928
+ .filter((attr) => ['id', 'name', 'type', 'value', 'href', 'title', 'placeholder'].includes(attr.name))
8929
+ .map((attr) => `${attr.name == 'id' ? 'target' : attr.name}="${attr.value}"`)
8930
+ .join(' ');
8931
+ elements.push({
8932
+ originalIndex: getElementIndex(element),
8933
+ id: id,
8934
+ html: `<${tagName} id="${id}" ${attributes}>${tagName == 'select' ? element.innerHTML : element.innerText || ''}</${tagName}>`,
8935
+ });
8936
+ addExclude(excludes, element.children);
8937
+ }
8938
+ });
8939
+ // short text element
8940
+ const textWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
8941
+ acceptNode: function (node) {
8942
+ var _a;
8943
+ if (node.matches(operableSelectors) || excludes.indexOf(node) != -1) {
8944
+ // skip
8945
+ return NodeFilter.FILTER_SKIP;
8946
+ }
8947
+ // text <= 100
8948
+ const text = (_a = node.innerText) === null || _a === void 0 ? void 0 : _a.trim();
8949
+ if (isElementVisible(node) &&
8950
+ text &&
8951
+ text.length <= 100 &&
8952
+ text.length > 0 &&
8953
+ node.children.length === 0) {
8954
+ return NodeFilter.FILTER_ACCEPT;
8955
+ }
8956
+ // skip
8957
+ return NodeFilter.FILTER_SKIP;
8958
+ },
8959
+ });
8960
+ let currentNode;
8961
+ while ((currentNode = textWalker.nextNode())) {
8962
+ const id = nextId++;
8963
+ elementMap[id.toString()] = currentNode;
8964
+ const tagName = currentNode.tagName.toLowerCase();
8965
+ elements.push({
8966
+ originalIndex: getElementIndex(currentNode),
8967
+ id: id,
8968
+ html: `<${tagName} id="${id}">${currentNode.innerText.trim()}</${tagName}>`,
8969
+ });
8970
+ }
8971
+ // element sort
8972
+ elements.sort((a, b) => a.originalIndex - b.originalIndex);
8973
+ // cache
8974
+ window.operableElementMap = elementMap;
8975
+ // pseudo html
8976
+ return elements.map((e) => e.html).join('\n');
8977
+ }
8978
+ function clickOperableElement(id) {
8979
+ let element = window.operableElementMap[id];
8980
+ if (!element) {
8981
+ return false;
8982
+ }
8983
+ if (element.click) {
8984
+ element.click();
8985
+ }
8986
+ else {
8987
+ element.dispatchEvent(new MouseEvent('click', {
8988
+ view: window,
8989
+ bubbles: true,
8990
+ cancelable: true,
8991
+ }));
8992
+ }
8993
+ return true;
8994
+ }
8995
+ function getOperableElementRect(id) {
8996
+ let element = window.operableElementMap[id];
8997
+ if (!element) {
8998
+ return null;
8999
+ }
9000
+ const rect = element.getBoundingClientRect();
9001
+ return {
9002
+ left: rect.left + window.scrollX,
9003
+ top: rect.top + window.scrollY,
9004
+ right: rect.right + window.scrollX,
9005
+ bottom: rect.bottom + window.scrollY,
9006
+ width: rect.right - rect.left,
9007
+ height: rect.bottom - rect.top,
9008
+ };
9009
+ }
9010
+
8925
9011
  /**
8926
9012
  * Element click
8927
9013
  */