handleJavascriptBlock.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import { nanoid } from 'nanoid/non-secure';
  2. export default function (data) {
  3. let timeout;
  4. const instanceId = nanoid();
  5. const scriptId = `script${data.id}`;
  6. const propertyName = `automa${data.id}`;
  7. const isScriptExists = document.querySelector(`#${scriptId}`);
  8. if (isScriptExists) {
  9. window.top.postMessage(
  10. {
  11. id: data.id,
  12. type: 'sandbox',
  13. result: {
  14. columns: {},
  15. variables: {},
  16. },
  17. },
  18. '*'
  19. );
  20. return;
  21. }
  22. const preloadScripts = data.preloadScripts.map((item) => {
  23. const scriptEl = document.createElement('script');
  24. scriptEl.textContent = item.script;
  25. (document.body || document.documentElement).appendChild(scriptEl);
  26. return scriptEl;
  27. });
  28. if (!data.blockData.code.includes('automaNextBlock')) {
  29. data.blockData.code += `\n automaNextBlock()`;
  30. }
  31. const script = document.createElement('script');
  32. script.id = scriptId;
  33. script.textContent = `
  34. (() => {
  35. function automaRefData(keyword, path = '') {
  36. return window.$getNestedProperties(${propertyName}.refData, keyword + '.' + path);
  37. }
  38. function automaSetVariable(name, value) {
  39. const variables = ${propertyName}.refData.variables;
  40. if (!variables) ${propertyName}.refData.variables = {}
  41. ${propertyName}.refData.variables[name] = value;
  42. }
  43. function automaNextBlock(data = {}, insert = true) {
  44. ${propertyName}.nextBlock({ data, insert });
  45. }
  46. function automaResetTimeout() {
  47. ${propertyName}.resetTimeout();
  48. }
  49. function automaFetch(type, resource) {
  50. return ${propertyName}.fetch(type, resource);
  51. }
  52. try {
  53. ${data.blockData.code}
  54. } catch (error) {
  55. console.error(error);
  56. automaNextBlock({ $error: true, message: error.message });
  57. }
  58. })();
  59. `;
  60. function cleanUp() {
  61. script.remove();
  62. preloadScripts.forEach((preloadScript) => {
  63. preloadScript.remove();
  64. });
  65. delete window[propertyName];
  66. }
  67. window[propertyName] = {
  68. refData: data.refData,
  69. nextBlock: (result) => {
  70. cleanUp();
  71. window.top.postMessage(
  72. {
  73. id: data.id,
  74. type: 'sandbox',
  75. result: {
  76. variables: data?.refData?.variables,
  77. columns: {
  78. data: result?.data,
  79. insert: result?.insert,
  80. },
  81. },
  82. },
  83. '*'
  84. );
  85. },
  86. resetTimeout: () => {
  87. clearTimeout(timeout);
  88. timeout = setTimeout(cleanUp, data.blockData.timeout);
  89. },
  90. fetch: (type, resource) => {
  91. return new Promise((resolve, reject) => {
  92. const types = ['json', 'text'];
  93. if (!type || !types.includes(type)) {
  94. reject(new Error('The "type" must be "text" or "json"'));
  95. return;
  96. }
  97. window.top.postMessage(
  98. {
  99. type: 'automa-fetch',
  100. data: { id: instanceId, type, resource },
  101. },
  102. '*'
  103. );
  104. const eventName = `automa-fetch-response-${instanceId}`;
  105. const eventListener = ({ detail }) => {
  106. window.removeEventListener(eventName, eventListener);
  107. if (detail.isError) {
  108. reject(new Error(detail.result));
  109. } else {
  110. resolve(detail.result);
  111. }
  112. };
  113. window.addEventListener(eventName, eventListener);
  114. });
  115. },
  116. };
  117. timeout = setTimeout(cleanUp, data.blockData.timeout);
  118. (document.body || document.documentElement).appendChild(script);
  119. }