handleJavascriptBlock.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. if (!keyword) return null;
  37. if (!path) return ${propertyName}.refData[keyword];
  38. return window.$getNestedProperties(${propertyName}.refData, keyword + '.' + path);
  39. }
  40. function automaSetVariable(name, value) {
  41. const variables = ${propertyName}.refData.variables;
  42. if (!variables) ${propertyName}.refData.variables = {}
  43. ${propertyName}.refData.variables[name] = value;
  44. }
  45. function automaNextBlock(data = {}, insert = true) {
  46. ${propertyName}.nextBlock({ data, insert });
  47. }
  48. function automaResetTimeout() {
  49. ${propertyName}.resetTimeout();
  50. }
  51. function automaFetch(type, resource) {
  52. return ${propertyName}.fetch(type, resource);
  53. }
  54. try {
  55. ${data.blockData.code}
  56. } catch (error) {
  57. console.error(error);
  58. automaNextBlock({ $error: true, message: error.message });
  59. }
  60. })();
  61. `;
  62. function cleanUp() {
  63. script.remove();
  64. preloadScripts.forEach((preloadScript) => {
  65. preloadScript.remove();
  66. });
  67. delete window[propertyName];
  68. }
  69. window[propertyName] = {
  70. refData: data.refData,
  71. nextBlock: (result) => {
  72. cleanUp();
  73. window.top.postMessage(
  74. {
  75. id: data.id,
  76. type: 'sandbox',
  77. result: {
  78. variables: data?.refData?.variables,
  79. columns: {
  80. data: result?.data,
  81. insert: result?.insert,
  82. },
  83. },
  84. },
  85. '*'
  86. );
  87. },
  88. resetTimeout: () => {
  89. clearTimeout(timeout);
  90. timeout = setTimeout(cleanUp, data.blockData.timeout);
  91. },
  92. fetch: (type, resource) => {
  93. return new Promise((resolve, reject) => {
  94. const types = ['json', 'text'];
  95. if (!type || !types.includes(type)) {
  96. reject(new Error('The "type" must be "text" or "json"'));
  97. return;
  98. }
  99. window.top.postMessage(
  100. {
  101. type: 'automa-fetch',
  102. data: { id: instanceId, type, resource },
  103. },
  104. '*'
  105. );
  106. const eventName = `automa-fetch-response-${instanceId}`;
  107. const eventListener = ({ detail }) => {
  108. window.removeEventListener(eventName, eventListener);
  109. if (detail.isError) {
  110. reject(new Error(detail.result));
  111. } else {
  112. resolve(detail.result);
  113. }
  114. };
  115. window.addEventListener(eventName, eventListener);
  116. });
  117. },
  118. };
  119. timeout = setTimeout(cleanUp, data.blockData.timeout);
  120. (document.body || document.documentElement).appendChild(script);
  121. }