選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

481 行
20KB

  1. <template>
  2. <div class="columns is-fullheight">
  3. <div class="column">
  4. <nav class="navbar is-black" role="navigation" aria-label="main navigation">
  5. <div class="navbar-brand">
  6. <a class="navbar-item" href="https://cheerp.cppse.nl">
  7. <img src="cheerp.png" height="28">
  8. </a>
  9. <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
  10. <span aria-hidden="true"></span>
  11. <span aria-hidden="true"></span>
  12. <span aria-hidden="true"></span>
  13. </a>
  14. </div>
  15. <div id="navbarBasicExample" class="navbar-menu">
  16. <div class="navbar-start">
  17. <!--
  18. <a class="navbar-item">Home</a>
  19. <a class="navbar-item">Documentation</a>
  20. -->
  21. <div class="navbar-item has-dropdown is-hoverable">
  22. <a class="navbar-link">
  23. Load example
  24. </a>
  25. <div class="navbar-dropdown">
  26. <div v-for="item in examples">
  27. <a class="navbar-item" v-on:click="load_example(item)">{{ item.title }}</a>
  28. </div>
  29. </div>
  30. </div>
  31. </div>
  32. <div class="navbar-end">
  33. <div class="share">
  34. <a target="_blank" href="https://github.com/rayburgemeestre/cheerpweb" class="button github">
  35. <span class="icon">
  36. <svg class="svg-inline--fa fa-github fa-w-16" aria-hidden="true" data-prefix="fab" data-icon="github" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512" data-fa-i2svg=""><path fill="currentColor" d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg><!-- <i class="fab fa-github"></i> -->
  37. </span>
  38. <span>View on GitHub</span>
  39. </a>
  40. <a href="https://paypal.me/cppse?locale.x=en_US" target="_blank"><img class="donate" src="donate.png"></a>
  41. <input class="input" type="text" v-on:focus="select_link($event)" v-model="share_link" placeholder="Compile before sharing link!">
  42. </div>
  43. <div class="navbar-item">
  44. <div class="buttons">
  45. <a class="button is-primary" v-on:click="compile" v-shortkey.once="['ctrl', 'f9']" @shortkey="compile()">
  46. <strong>Compile [CTRL+F9]</strong>
  47. </a>
  48. <a class="button is-light" v-on:click="run" v-shortkey.once="['ctrl', 'f10']" @shortkey="run()">
  49. Run [CTRL+F10]
  50. </a>
  51. </div>
  52. </div>
  53. </div>
  54. </div>
  55. </nav>
  56. <div class="columns is-fullheight">
  57. <div class="column">
  58. <div class="rows">
  59. <div class="row">
  60. <div class="tabs">
  61. <ul>
  62. <li v-bind:class="{'is-active': editor_tabs == 1}" v-on:click="editor_tabs = 1"><a>Code editor</a></li>
  63. <li v-bind:class="{'is-active': editor_tabs == 2}" v-on:click="editor_tabs = 2"><a>Compiler</a></li>
  64. </ul>
  65. </div>
  66. <editor-component v-if="editor_tabs == 1" v-model="cpp_code" name="cpp" language="cpp" height="50vh"/>
  67. <div v-if="editor_tabs == 2" class="settings">
  68. <textarea class="textarea" v-model="compiler_flags"></textarea>
  69. </div>
  70. </div>
  71. <div class="row">
  72. <div class="tabs">
  73. <ul>
  74. <li class="is-active"><a>HTML</a></li>
  75. </ul>
  76. </div>
  77. <editor-component v-model="html_code" name="html" language="html" height="50vh"/>
  78. </div>
  79. </div>
  80. </div>
  81. <div class="column">
  82. <div class="rows">
  83. <div class="row">
  84. <div class="tabs">
  85. <ul>
  86. <li v-bind:class="{'is-active': js_tabs == 1}" v-on:click="js_tabs = 1"><a>Javascript</a></li>
  87. <li v-bind:class="{'is-active': js_tabs == 2}" v-on:click="js_tabs = 2" v-if="wasm_code"><a>Webassembly</a></li>
  88. </ul>
  89. </div>
  90. <editor-component v-if="js_tabs == 1" v-model="js_code" name="js" language="javascript" height="50vh"/>
  91. <editor-component v-if="js_tabs == 2 && wasm_code" v-model="wasm_code" name="wasm" language="javascript" height="50vh"/>
  92. </div>
  93. <div class="row">
  94. <div class="tabs">
  95. <ul>
  96. <li v-bind:class="{'is-active': output_tabs == 1}" v-on:click="output_tabs = 1"><a>Compiler output</a></li>
  97. <li v-bind:class="{'is-active': output_tabs == 2}" v-on:click="output_tabs = 2"><a>Run output</a></li>
  98. <li v-bind:class="{'is-active': output_tabs == 3}" v-on:click="output_tabs = 3"><a>Resulting HTML</a></li>
  99. </ul>
  100. </div>
  101. <textarea v-if="output_tabs == 1" class="textarea" placeholder="Compiler output will be displayed here.">{{ compiler_output }}</textarea>
  102. <iframe v-if="output_tabs == 2" src="about:blank" v-on:load="onLoadIframe" name="myIframe"></iframe>
  103. <textarea v-if="output_tabs == 3" class="textarea" placeholder="Resulting HTML will be displayed here">{{ generated_html }}</textarea>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. <!-- for development commented out <script src="main.js"></script> -->
  109. </div>
  110. </div>
  111. </template>
  112. <script>
  113. import EditorComponent from './components/EditorComponent.vue'
  114. const _ = require('lodash');
  115. const axios = require('axios');
  116. var context = require.context("./examples", true, /\.js$/);
  117. var examples = [];
  118. var dom_example_index = -1;
  119. context.keys().forEach(function (key) {
  120. if (key.indexOf('dom.js') != -1) {
  121. dom_example_index = examples.length;
  122. console.log("FOUND: " + dom_example_index);
  123. }
  124. examples.push(context(key)['example'])
  125. });
  126. let cpp_code = examples[dom_example_index].cpp_code,
  127. js_code = examples[dom_example_index].js_code,
  128. wasm_code = examples[dom_example_index].wasm_code,
  129. html_code = examples[dom_example_index].html_code,
  130. compiler_flags = examples[dom_example_index].flags;
  131. function findIframeByName(name) {
  132. return _.find(window.frames, frame => frame.name === name);
  133. }
  134. export default {
  135. props: {
  136. uuid: {
  137. type: String,
  138. default: '',
  139. },
  140. version: {
  141. type: String,
  142. default: '',
  143. },
  144. editor_tabs: {
  145. type: Number,
  146. default: 1,
  147. },
  148. output_tabs: {
  149. type: Number,
  150. default: 1,
  151. },
  152. js_tabs: {
  153. type: Number,
  154. default: 1,
  155. },
  156. cpp_code: {
  157. type: String,
  158. default: cpp_code,
  159. },
  160. html_code: {
  161. type: String,
  162. default: html_code,
  163. },
  164. js_code: {
  165. type: String,
  166. },
  167. wasm_code: {
  168. type: String,
  169. },
  170. compiler_output: {
  171. type: String,
  172. },
  173. compiler_flags: {
  174. type: String,
  175. default: compiler_flags
  176. },
  177. generated_html: {
  178. type: String,
  179. default: ''
  180. },
  181. do_update_iframe: {
  182. type: Boolean,
  183. default: false
  184. },
  185. examples: {
  186. type: Array,
  187. default: function () {
  188. return examples
  189. }
  190. },
  191. share_link: {
  192. type: String,
  193. default: '',
  194. },
  195. hash_set: {
  196. type: Boolean,
  197. default: false,
  198. },
  199. },
  200. components: {
  201. EditorComponent
  202. },
  203. methods: {
  204. update_iframe(name) {
  205. if (false) {
  206. const iframe = findIframeByName(name);
  207. iframe.document.body.innerHTML = ''
  208. var head = this.html_code.indexOf("<head>");
  209. if (head != -1) {
  210. iframe.document.write(this.html_code.substr(0, head + 6 /* len(<head>) */));
  211. }
  212. else {
  213. iframe.document.write(this.html_code);
  214. }
  215. iframe.document.write("<script>");
  216. // wasm
  217. iframe.document.write("function fetchBuffer(path) {\n");
  218. iframe.document.write(" return new Promise( (resolve, reject) => {\n");
  219. iframe.document.write(" var wasm = '" + this.wasm_code.trim() + "';\n");
  220. iframe.document.write(" wasm = atob(wasm)\n");
  221. iframe.document.write("\n");
  222. iframe.document.write(" var len = wasm.length;\n");
  223. iframe.document.write(" var bytes = new Uint8Array(len);\n");
  224. iframe.document.write(" for (var i = 0; i < len; i++) {\n");
  225. iframe.document.write(" bytes[i] = wasm.charCodeAt(i);\n");
  226. iframe.document.write(" }\n");
  227. iframe.document.write(" resolve(bytes.buffer);\n");
  228. iframe.document.write(" });\n");
  229. iframe.document.write("}\n");
  230. iframe.document.write(this.js_code);
  231. iframe.document.write("<\/script>");
  232. if (head != -1) {
  233. iframe.document.write(this.html_code.substr(head + 6 /* len(<head>) */ + 1));
  234. }
  235. } else {
  236. // new experimental rendering
  237. //const iframe = findIframeByName(name);
  238. const iframe = document.getElementsByTagName('iframe')[0];
  239. //iframe.document.body.innerHTML = ''
  240. var marker = '<!-- MARKER: Include javascript here. -->';
  241. var m = this.html_code.indexOf(marker);
  242. var s = "";
  243. if (m != -1) {
  244. s += this.html_code.substr(0, m);
  245. } else {
  246. var head = this.html_code.indexOf("<head>");
  247. if (head != -1) {
  248. s += this.html_code.substr(0, head + 6 /* len(<head>) */);
  249. }
  250. else {
  251. s += this.html_code;
  252. }
  253. }
  254. s += "<script>";
  255. if (this.compiler_flags.indexOf('-cheerp-mode=wasm') != -1) {
  256. // wasm
  257. s += "function fetchBuffer(path) {\n";
  258. s += " return new Promise( (resolve, reject) => {\n";
  259. s += " var wasm = '" + this.wasm_code.trim() + "';\n";
  260. s += " wasm = atob(wasm)\n";
  261. s += "\n";
  262. s += " var len = wasm.length;\n";
  263. s += " var bytes = new Uint8Array(len);\n";
  264. s += " for (var i = 0; i < len; i++) {\n";
  265. s += " bytes[i] = wasm.charCodeAt(i);\n";
  266. s += " }\n";
  267. s += " resolve(bytes.buffer);\n";
  268. s += " });\n";
  269. s += "}\n";
  270. }
  271. s += this.js_code;
  272. s += "<\/script>";
  273. if (m != -1) {
  274. s += this.html_code.substr(m + marker.length);
  275. } else if (head != -1) {
  276. s += this.html_code.substr(head + 6 /* len(<head>) */ + 1);
  277. }
  278. iframe.srcdoc = s;
  279. this.generated_html = s;
  280. }
  281. },
  282. onLoadIframe(event) {
  283. // iframe ready, set flag?
  284. console.log("loaded iframe");
  285. if (this.do_update_iframe) {
  286. this.update_iframe('myIframe');
  287. this.do_update_iframe = false;
  288. }
  289. },
  290. compile() {
  291. this.output_tabs = 1;
  292. var url = 'https://cheerp.cppse.nl/api/compile';
  293. if (window.location.href.indexOf('cheerp.cppse.nl') == -1) {
  294. // assuming local dev env
  295. url = '//localhost:5000/compile';
  296. }
  297. axios.post(url, {
  298. flags: this.compiler_flags,
  299. source: this.cpp_code,
  300. html: this.html_code,
  301. uuid: this.uuid,
  302. })
  303. .then(function (response) {
  304. const str =
  305. 'COMMAND: ' + response.data.command + '\n' +
  306. 'EXIT_CODE: ' + response.data.retcode + '\n' +
  307. 'STDOUT:\n------------------------------\n' +
  308. response.data.stdout +
  309. 'STDERR:\n------------------------------\n' +
  310. response.data.stderr;
  311. this.compiler_output = str;
  312. var js = response.data.javascript;
  313. var idx = js.indexOf('function fetchBuffer'), jdx = -1;
  314. if (idx != -1) {
  315. var level = 0;
  316. for (var i=idx; ; i++) {
  317. if (js[i] == '{') {
  318. level++;
  319. }
  320. else if (js[i] == '}') {
  321. level--;
  322. if (!level) {
  323. jdx = i;
  324. break;
  325. }
  326. }
  327. }
  328. }
  329. this.js_code = js.substr(0, idx) + js.substr(jdx + 1);
  330. this.wasm_code = response.data.wasm;
  331. this.uuid = response.data.uuid
  332. this.version = response.data.version
  333. this.share_link = 'https://cheerp.cppse.nl/#' + this.uuid + ":" + this.version
  334. this.hash_set = true
  335. window.location.hash = '#' + this.uuid + ":" + this.version
  336. console.log(this.uuid)
  337. console.log(this.version)
  338. }.bind(this))
  339. .catch(function (error) {
  340. console.log(error);
  341. });
  342. },
  343. run() {
  344. this.do_update_iframe = true;
  345. this.output_tabs = 2;
  346. try {
  347. this.update_iframe('myIframe');
  348. }
  349. catch (e) {}
  350. },
  351. load_example(ex) {
  352. this.cpp_code = ex.cpp_code
  353. this.js_code = ex.js_code
  354. this.wasm_code = ex.wasm_code
  355. this.html_code = ex.html_code
  356. this.compiler_flags = ex.flags
  357. this.uuid = ''
  358. this.version = ''
  359. this.share_link = ''
  360. window.location.hash = ''
  361. return true;
  362. },
  363. select_link(event) {
  364. setTimeout(function() {
  365. this.select();
  366. }.bind(event.target), 10);
  367. },
  368. },
  369. watch: {
  370. html_code(new_val) {},
  371. cpp_code(new_val) {},
  372. js_code(new_val) {},
  373. },
  374. created: function() {
  375. var hashchange_fun = function() {
  376. if (this.hash_set) {
  377. this.hash_set = false;
  378. return;
  379. }
  380. if (window.location.hash.length > 3) {
  381. var h = window.location.hash.substr(1).split(':')
  382. if (h.length == 2) {
  383. var load_hash = h[0];
  384. var load_version = h[1];
  385. console.log("Should load: ",load_hash," ", load_version);
  386. var url = 'https://cheerp.cppse.nl/api/retrieve';
  387. if (window.location.href.indexOf('cheerp.cppse.nl') == -1) {
  388. // assuming local dev env
  389. url = '//localhost:5000/retrieve';
  390. }
  391. // Keeping this POST on purpose, just to keep things HARD for crawlers
  392. axios.post(url, {
  393. uuid: load_hash,
  394. version: load_version,
  395. })
  396. .then(function (response) {
  397. var js = response.data.source;
  398. if (js == '') {
  399. const str =
  400. 'Loading hash: '+ load_hash + '\n' +
  401. 'With version: '+ load_version + '\n' +
  402. 'FAILED.'
  403. this.compiler_output = str;
  404. return false;
  405. }
  406. const str =
  407. 'Loading hash: '+ load_hash + '\n' +
  408. 'With version: '+ load_version + '\n' +
  409. 'SUCCEEDED.'
  410. this.compiler_output = str;
  411. var code = response.data.source;
  412. var html = response.data.html;
  413. var flags = response.data.flags;
  414. this.cpp_code = code;
  415. this.html_code = html;
  416. this.compiler_flags = flags;
  417. this.uuid = load_hash
  418. this.version = load_version
  419. this.share_link = 'https://cheerp.cppse.nl/#' + this.uuid + ":" + this.version
  420. }.bind(this))
  421. .catch(function (error) {
  422. const str =
  423. 'Loading hash: '+ load_hash + '\n' +
  424. 'With version: '+ load_version + '\n' +
  425. 'FAILED: ' + error
  426. this.compiler_output = str;
  427. });
  428. }
  429. }
  430. }.bind(this);
  431. window.onhashchange = hashchange_fun;
  432. hashchange_fun();
  433. }
  434. }
  435. </script>
  436. <style scoped>
  437. .full-width {
  438. width: 100%;
  439. }
  440. .center-content {
  441. display: flex;
  442. justify-content: center;
  443. align-items: center;
  444. }
  445. .settings {
  446. height: calc(50vh - 6rem);
  447. }
  448. </style>