javascript.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. 'use strict';
  2. const { updateElementReadonly, updateElementInvalid } = require('../utils/assets');
  3. const { createReadStream } = require('fs');
  4. const ReadLine = require('readline');
  5. const MAX_LINES = 400;
  6. const MAX_LENGTH = 20000;
  7. exports.template = /* html */`
  8. <section class="asset-javascript">
  9. <ui-prop>
  10. <ui-label slot="label"
  11. tooltip="i18n:ENGINE.assets.javascript.pluginTip"
  12. value="i18n:ENGINE.assets.javascript.plugin"
  13. ></ui-label>
  14. <ui-checkbox slot="content"
  15. class="content"
  16. id="is-plugin"
  17. ></ui-checkbox>
  18. </ui-prop>
  19. <div class="detail">
  20. <ui-prop id="executionScope">
  21. <ui-label slot="label" value="i18n:ENGINE.assets.javascript.globalThisAlias"></ui-label>
  22. <ui-checkbox slot="content"
  23. id="simulateGlobalsCheckBox"
  24. ></ui-checkbox>
  25. </ui-prop>
  26. <ui-prop id="simulateGlobals">
  27. <ui-label slot="label"></ui-label>
  28. <ui-input slot="content"
  29. id="simulateGlobalsInput"
  30. placeholder="self;window;global;globalThis"
  31. tooltip="i18n:ENGINE.assets.javascript.globalThisAliasTip"
  32. ></ui-input>
  33. </ui-prop>
  34. <ui-prop>
  35. <ui-label slot="label"
  36. tooltip="i18n:ENGINE.assets.javascript.loadPluginInWebTip"
  37. value="i18n:ENGINE.assets.javascript.loadPluginInWeb"
  38. ></ui-label>
  39. <ui-checkbox slot="content"
  40. id="load-plugin-in-web"
  41. ></ui-checkbox>
  42. </ui-prop>
  43. <ui-prop>
  44. <ui-label slot="label"
  45. tooltip="i18n:ENGINE.assets.javascript.loadPluginInNativeTip"
  46. value="i18n:ENGINE.assets.javascript.loadPluginInNative"
  47. ></ui-label>
  48. <ui-checkbox slot="content"
  49. id="load-plugin-in-native"
  50. ></ui-checkbox>
  51. </ui-prop>
  52. <ui-prop>
  53. <ui-label slot="label"
  54. tooltip="i18n:ENGINE.assets.javascript.loadPluginInMiniGameTip"
  55. value="i18n:ENGINE.assets.javascript.loadPluginInMiniGame"
  56. ></ui-label>
  57. <ui-checkbox slot="content"
  58. id="load-plugin-in-mini-game"
  59. ></ui-checkbox>
  60. </ui-prop>
  61. <ui-prop >
  62. <ui-label slot="label"
  63. tooltip="i18n:ENGINE.assets.javascript.loadPluginInEditorTip"
  64. value="i18n:ENGINE.assets.javascript.loadPluginInEditor"
  65. ></ui-label>
  66. <ui-checkbox slot="content"
  67. id="load-plugin-in-editor"
  68. ></ui-checkbox>
  69. </ui-prop>
  70. </div>
  71. <ui-code language="javascript"
  72. id="code"
  73. ></ui-code>
  74. </section>
  75. `;
  76. exports.style = /* css */`
  77. .asset-javascript {
  78. flex: 1;
  79. display: flex;
  80. flex-direction: column;
  81. overflow: auto;
  82. /* it is necessary */
  83. height: 0px;
  84. }
  85. .asset-javascript ui-prop[hidden] {
  86. display: none;
  87. }
  88. .asset-javascript .assets > ui-asset {
  89. margin-top: 8px;
  90. margin-bottom: 4px;
  91. width: 100%;
  92. }
  93. .asset-javascript ui-code {
  94. flex: 1;
  95. margin-top: 8px;
  96. }
  97. `;
  98. exports.$ = {
  99. isPluginCheckBox: '#is-plugin',
  100. detail: '.detail',
  101. loadPluginInEditorCheckBox: '#load-plugin-in-editor',
  102. loadPluginInWebCheckBox: '#load-plugin-in-web',
  103. loadPluginInNativeCheckBox: '#load-plugin-in-native',
  104. loadPluginInMiniGameCheckBox: '#load-plugin-in-mini-game',
  105. simulateGlobals: '#simulateGlobals',
  106. simulateGlobalsInput: '#simulateGlobalsInput',
  107. simulateGlobalsCheckBox: '#simulateGlobalsCheckBox',
  108. executionScope: '#executionScope',
  109. code: '#code',
  110. };
  111. const Elements = {
  112. isPlugin: {
  113. ready() {
  114. this.$.isPluginCheckBox.addEventListener('confirm', (event) => {
  115. this.change('isPlugin', event);
  116. if (event.target.value) {
  117. this.metaList.forEach((meta) => {
  118. const defaultConfig = {
  119. loadPluginInEditor: false,
  120. loadPluginInWeb: true,
  121. loadPluginInNative: true,
  122. loadPluginInMiniGame: true,
  123. };
  124. meta.userData = Object.assign(defaultConfig, meta.userData);
  125. })
  126. }
  127. Elements.detail.update.call(this);
  128. });
  129. },
  130. update() {
  131. this.$.isPluginCheckBox.value = this.meta.userData.isPlugin;
  132. updateElementInvalid.call(this, this.$.isPluginCheckBox, 'isPlugin');
  133. updateElementReadonly.call(this, this.$.isPluginCheckBox);
  134. },
  135. },
  136. detail: {
  137. update() {
  138. let display = 'none';
  139. if (this.meta.userData.isPlugin) {
  140. display = 'block';
  141. }
  142. this.$.detail.style.display = display;
  143. },
  144. },
  145. executionScope: {
  146. ready() {
  147. this.$.simulateGlobalsCheckBox.addEventListener('confirm', (event) => {
  148. const value = event.target.value ? 'enclosed' : 'global';
  149. this.changeUserData('executionScope', value);
  150. Elements.executionScope.update.call(this);
  151. Elements.simulateGlobals.update.call(this);
  152. });
  153. },
  154. update() {
  155. this.$.simulateGlobalsCheckBox.value = (this.meta.userData.executionScope === 'enclosed');
  156. updateElementInvalid.call(this, this.$.executionScope, 'executionScope');
  157. updateElementReadonly.call(this, this.$.executionScope);
  158. },
  159. },
  160. simulateGlobals: {
  161. ready() {
  162. this.$.simulateGlobalsInput.addEventListener('confirm', (event) => {
  163. const value = event.target.value;
  164. if (typeof value === 'string') {
  165. let globalNames = [];
  166. if (value.length !== 0) {
  167. globalNames = value
  168. .split(';')
  169. .map((globalName) => globalName.trim())
  170. .filter((globalName) => globalName.length !== 0);
  171. }
  172. this.metaList.forEach((meta) => (meta.userData.simulateGlobals = globalNames.length === 0 ? true : globalNames));
  173. this.dispatch('change');
  174. this.dispatch('snapshot');
  175. }
  176. });
  177. },
  178. update() {
  179. let display = 'none';
  180. if (this.meta.userData.executionScope === 'enclosed') {
  181. display = 'block';
  182. }
  183. this.$.simulateGlobals.style.display = display;
  184. if (display === 'none') {
  185. return;
  186. }
  187. updateElementReadonly.call(this, this.$.simulateGlobalsInput);
  188. this.$.simulateGlobalsInput.value = Array.isArray(this.meta.userData.simulateGlobals)
  189. ? this.meta.userData.simulateGlobals.join(';')
  190. : '';
  191. },
  192. },
  193. loadPluginInWebCheckBox: {
  194. ready() {
  195. this.$.loadPluginInWebCheckBox.addEventListener('confirm', (event) => {
  196. this.change('loadPluginInWeb', event);
  197. Elements.loadPluginInEditorCheckBox.update.call(this);
  198. });
  199. },
  200. update() {
  201. this.$.loadPluginInWebCheckBox.value = this.meta.userData.loadPluginInWeb ?? true;
  202. updateElementInvalid.call(this, this.$.loadPluginInWebCheckBox, 'loadPluginInWeb');
  203. updateElementReadonly.call(this, this.$.loadPluginInWebCheckBox);
  204. },
  205. },
  206. loadPluginInNativeCheckBox: {
  207. ready() {
  208. this.$.loadPluginInNativeCheckBox.addEventListener('confirm', this.change.bind(this, 'loadPluginInNative'));
  209. },
  210. update() {
  211. this.$.loadPluginInNativeCheckBox.value = this.meta.userData.loadPluginInNative ?? true;
  212. updateElementInvalid.call(this, this.$.loadPluginInNativeCheckBox, 'loadPluginInNative');
  213. updateElementReadonly.call(this, this.$.loadPluginInNativeCheckBox);
  214. },
  215. },
  216. loadPluginInMiniGameCheckBox: {
  217. ready() {
  218. this.$.loadPluginInMiniGameCheckBox.addEventListener('confirm', this.change.bind(this, 'loadPluginInMiniGame'));
  219. },
  220. update() {
  221. this.$.loadPluginInMiniGameCheckBox.value = this.meta.userData.loadPluginInMiniGame ?? true;
  222. updateElementInvalid.call(this, this.$.loadPluginInMiniGameCheckBox, 'loadPluginInMiniGame');
  223. updateElementReadonly.call(this, this.$.loadPluginInMiniGameCheckBox);
  224. },
  225. },
  226. loadPluginInEditorCheckBox: {
  227. ready() {
  228. this.$.loadPluginInEditorCheckBox.addEventListener('confirm', this.change.bind(this, 'loadPluginInEditor'));
  229. },
  230. update() {
  231. this.$.loadPluginInEditorCheckBox.value = this.meta.userData.loadPluginInEditor ?? false;
  232. updateElementInvalid.call(this, this.$.loadPluginInEditorCheckBox, 'loadPluginInEditor');
  233. updateElementReadonly.call(this, this.$.loadPluginInEditorCheckBox);
  234. },
  235. },
  236. code: {
  237. update() {
  238. let display = 'none';
  239. if (this.assetList.length === 1) {
  240. display = 'flex';
  241. }
  242. this.$.code.style.display = display;
  243. if (display === 'none') {
  244. return;
  245. }
  246. let remainLines = MAX_LINES;
  247. let remainLength = MAX_LENGTH;
  248. let text = '';
  249. const readStream = createReadStream(this.asset.file, {
  250. encoding: 'utf-8',
  251. });
  252. const readLineStream = ReadLine.createInterface({
  253. input: readStream,
  254. setEncoding: 'utf-8',
  255. });
  256. readLineStream.on('line', (line) => {
  257. const lineLength = line.length;
  258. if (lineLength > remainLength) {
  259. line = line.substr(0, remainLength);
  260. remainLength = 0;
  261. } else {
  262. remainLength -= lineLength;
  263. }
  264. remainLines--;
  265. text += `${line}\n`;
  266. if (remainLines <= 0 || remainLength <= 0) {
  267. text += '...\n';
  268. readLineStream.close();
  269. readStream.close();
  270. }
  271. });
  272. readLineStream.on('close', (err) => {
  273. if (err) {
  274. throw err;
  275. }
  276. if (this.$.code) {
  277. this.$.code.textContent = text;
  278. }
  279. });
  280. },
  281. },
  282. };
  283. exports.methods = {
  284. t(key) {
  285. return Editor.I18n.t(`ENGINE.assets.javascript.${key}`);
  286. },
  287. change(key, event) {
  288. this.changeUserData(key, event.target.value);
  289. },
  290. changeUserData(key, value) {
  291. this.metaList.forEach((meta) => {
  292. meta.userData[key] = value;
  293. });
  294. this.dispatch('change');
  295. this.dispatch('snapshot');
  296. }
  297. };
  298. exports.ready = function() {
  299. for (const key in Elements) {
  300. const element = Elements[key];
  301. if (element.ready) {
  302. element.ready.call(this);
  303. }
  304. }
  305. };
  306. exports.update = function(assetList, metaList) {
  307. this.assetList = assetList;
  308. this.metaList = metaList;
  309. this.asset = assetList[0];
  310. this.meta = metaList[0];
  311. for (const key in Elements) {
  312. const element = Elements[key];
  313. if (element.update) {
  314. element.update.call(this);
  315. }
  316. }
  317. };