utils.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. export function getElementRect(target, withAttributes) {
  2. if (!target) return {};
  3. const { x, y, height, width } = target.getBoundingClientRect();
  4. const result = {
  5. width: width + 4,
  6. height: height + 4,
  7. x: x - 2,
  8. y: y - 2,
  9. };
  10. if (withAttributes) {
  11. const attributes = {};
  12. Array.from(target.attributes).forEach(({ name, value }) => {
  13. if (name === 'automa-el-list') return;
  14. attributes[name] = value;
  15. });
  16. result.attributes = attributes;
  17. result.tagName = target.tagName;
  18. }
  19. return result;
  20. }
  21. export function getElementPath(el, root = document.documentElement) {
  22. const path = [el];
  23. /* eslint-disable-next-line */
  24. while ((el = el.parentNode) && !el.isEqualNode(root)) {
  25. path.push(el);
  26. }
  27. return path;
  28. }
  29. export function generateXPath(element, root = document.body) {
  30. if (!element) return null;
  31. if (element.id !== '') return `id("${element.id}")`;
  32. if (element === root) return `//${element.tagName}`;
  33. let ix = 0;
  34. const siblings = element.parentNode.childNodes;
  35. for (let index = 0; index < siblings.length; index += 1) {
  36. const sibling = siblings[index];
  37. if (sibling === element) {
  38. return `${generateXPath(element.parentNode)}/${element.tagName}[${
  39. ix + 1
  40. }]`;
  41. }
  42. if (sibling.nodeType === 1 && sibling.tagName === element.tagName) {
  43. ix += 1;
  44. }
  45. }
  46. return null;
  47. }
  48. export function automaRefDataStr(varName) {
  49. return `
  50. function findData(obj, path) {
  51. const paths = path.split('.');
  52. const isWhitespace = paths.length === 1 && !/\\S/.test(paths[0]);
  53. if (path.startsWith('$last') && Array.isArray(obj)) {
  54. paths[0] = obj.length - 1;
  55. }
  56. if (paths.length === 0 || isWhitespace) return obj;
  57. else if (paths.length === 1) return obj[paths[0]];
  58. let result = obj;
  59. for (let i = 0; i < paths.length; i++) {
  60. if (result[paths[i]] == undefined) {
  61. return undefined;
  62. } else {
  63. result = result[paths[i]];
  64. }
  65. }
  66. return result;
  67. }
  68. function automaRefData(keyword, path = '') {
  69. const data = ${varName}[keyword];
  70. if (!data) return;
  71. return findData(data, path);
  72. }
  73. `;
  74. }
  75. function messageTopFrame(windowCtx) {
  76. return new Promise((resolve) => {
  77. let timeout = null;
  78. let isResolved = false;
  79. const messageListener = ({ data }) => {
  80. if (data.type !== 'automa:the-frame-rect' || isResolved) return;
  81. clearTimeout(timeout);
  82. isResolved = true;
  83. windowCtx.removeEventListener('message', messageListener);
  84. resolve(data.frameRect);
  85. };
  86. timeout = setTimeout(() => {
  87. if (isResolved) return;
  88. isResolved = true;
  89. windowCtx.removeEventListener('message', messageListener);
  90. resolve(null);
  91. }, 5000);
  92. windowCtx.addEventListener('message', messageListener);
  93. windowCtx.top.postMessage({ type: 'automa:get-frame' }, '*');
  94. });
  95. }
  96. export async function getElementPosition(element) {
  97. const elWindow = element.ownerDocument.defaultView;
  98. const isInFrame = elWindow !== window.top;
  99. const { width, height, x, y } = element.getBoundingClientRect();
  100. const position = {
  101. x: x + width / 2,
  102. y: y + height / 2,
  103. };
  104. if (!isInFrame) return position;
  105. try {
  106. const frameEl = elWindow.frameElement;
  107. let frameRect = null;
  108. if (frameEl) {
  109. frameRect = frameEl.getBoundingClientRect();
  110. } else {
  111. frameRect = await messageTopFrame(elWindow);
  112. if (!frameRect) throw new Error('Iframe not found');
  113. }
  114. position.x += frameRect.x;
  115. position.y += frameRect.y;
  116. return position;
  117. } catch (error) {
  118. console.error(error);
  119. return position;
  120. }
  121. }