diff --git a/.vscode/launch.json b/.vscode/launch.json index 5efbc3b..cd9d3a8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,8 +11,8 @@ "type": "node-terminal" }, { - "command": "npm run server", - "name": "2. Start server (w. hot)", + "command": "node run-servers.js", + "name": "2. Start servers (w. hot)", "request": "launch", "type": "node-terminal" }, diff --git a/app/elements/code-editor/injects/editor-init.txt b/app/elements/code-editor/injects/editor-init.txt deleted file mode 100644 index ad86b2e..0000000 --- a/app/elements/code-editor/injects/editor-init.txt +++ /dev/null @@ -1 +0,0 @@ -console.log("hello"); \ No newline at end of file diff --git a/app/elements/notification-bubbles/notification-bubbles.ts b/app/elements/notification-bubbles/notification-bubbles.ts deleted file mode 100644 index 44703e6..0000000 --- a/app/elements/notification-bubbles/notification-bubbles.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BaseElement } from "../_base"; -import { NotificationBubble } from "./notification-bubble"; -import './notification-bubbles.less'; - -export class NotificationBubbles extends BaseElement { - add(text: string, type: 'log' | 'info' | 'warn' | 'error' | 'syntax-error') { - var bubble = document.createElement("notification-bubble"); - bubble.innerText = text; - bubble.classList.add(type); - this.appendChild(bubble); - } - - reset() { - this.innerHTML = ""; - } -} \ No newline at end of file diff --git a/app/elements/output-frame/file.ts b/app/elements/output-frame/file.ts deleted file mode 100644 index ec4532c..0000000 --- a/app/elements/output-frame/file.ts +++ /dev/null @@ -1,3 +0,0 @@ -export function hello() { - -} \ No newline at end of file diff --git a/app/elements/output-frame/injects/on-error.txt b/app/elements/output-frame/injects/on-error.txt deleted file mode 100644 index 24875ba..0000000 --- a/app/elements/output-frame/injects/on-error.txt +++ /dev/null @@ -1,3 +0,0 @@ - window.onerror = function (error) { - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(error, 'syntax-error'); -} \ No newline at end of file diff --git a/app/elements/output-frame/injects/try-catch.txt b/app/elements/output-frame/injects/try-catch.txt deleted file mode 100644 index 4ce9ccd..0000000 --- a/app/elements/output-frame/injects/try-catch.txt +++ /dev/null @@ -1,9 +0,0 @@ -try { - setTimeout(() => { - [replace] - }); - -} -catch (ex) { - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(ex, 'syntax-error'); -} \ No newline at end of file diff --git a/app/elements/output-frame/injects/watch-console.txt b/app/elements/output-frame/injects/watch-console.txt deleted file mode 100644 index f169602..0000000 --- a/app/elements/output-frame/injects/watch-console.txt +++ /dev/null @@ -1,24 +0,0 @@ -// define a new console -var console=(function(oldCons){ - return { - log: function(text){ - oldCons.log(text); - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(text, 'log'); - }, - info: function (text) { - oldCons.info(text); - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(text, 'info'); - }, - warn: function (text) { - oldCons.warn(text); - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(text, 'warn'); - }, - error: function (text) { - oldCons.error(text); - parent.document.body.getElementsByTagName("notification-bubbles")[0].add(text, 'error'); - } - }; -}(window.console)); - -//Then redefine the old console -window.console = console; \ No newline at end of file diff --git a/app/elements/output-frame/output-frame.ts b/app/elements/output-frame/output-frame.ts deleted file mode 100644 index abfd67e..0000000 --- a/app/elements/output-frame/output-frame.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { BaseElement } from "../_base"; -import './output-frame.less'; -import watchConsole from './injects/watch-console.txt'; -import tryCatch from './injects/try-catch.txt'; -import onError from './injects/on-error.txt'; -import windowStopScript from './injects/window-stop.txt'; -import { NotificationBubbles } from "../notification-bubbles/notification-bubbles"; - -export class OutputFrame extends BaseElement { - public Iframe: HTMLIFrameElement | null = null; - private watchConsoleScript: string; - - constructor() { - super(); - this.watchConsoleScript = watchConsole; - } - - onInit(): void { - this.reset(); - } - - onUpdate(): void { - } - - reset() { - if (this.Iframe && this.hasChild(this.Iframe)) { - this.removeChild(this.Iframe); - } - - this.Iframe = document.createElement("iframe"); - this.Iframe.setAttribute("sandbox", "allow-pointer-lock allow-same-origin allow-scripts"); - - this.appendChild(this.Iframe); - - var bubbles = this.find("notification-bubbles"); - bubbles.reset(); - - this.setInitScript(windowStopScript); - this.setInitScript(onError); - this.setInitScript(watchConsole); - } - - preventInfiniteLoop(value: string) { - return value.replace("while (true)", "while (false)"); - } - - private setInitScript(value: string) { - var script: HTMLScriptElement = document.createElement("script"); - script.setAttribute("async", ""); - script.innerHTML = this.preventInfiniteLoop(value); - - this.Iframe?.contentWindow?.document.body.appendChild(script); - } - - setScript(value: string) { - this.reset(); - - var script: HTMLScriptElement = document.createElement("script"); - script.setAttribute("async", ""); - script.innerHTML = this.preventInfiniteLoop(value); - - this.Iframe?.contentWindow?.document.body.appendChild(script); - } - - surroundTryCatch(value: string) { - return tryCatch.replace("[replace]", value); - } -} \ No newline at end of file diff --git a/app/app.html b/codeeditor-app/app.html similarity index 80% rename from app/app.html rename to codeeditor-app/app.html index dc61b44..615f549 100644 --- a/app/app.html +++ b/codeeditor-app/app.html @@ -3,7 +3,7 @@ code editor
- +
methods: rect, box, center, width, height
diff --git a/app/app.less b/codeeditor-app/app.less similarity index 84% rename from app/app.less rename to codeeditor-app/app.less index be2b7d2..7374e60 100644 --- a/app/app.less +++ b/codeeditor-app/app.less @@ -1,4 +1,4 @@ -html, body, body > * { +html, body { margin: 0; width: 100%; height: 100%; @@ -22,6 +22,7 @@ html, body, body > * { flex-grow: 1; flex-shrink: 1; flex-basis: 1px; + position: relative; } .flex-horiz .flex-shrink, .flex-vert .flex-shrink { @@ -34,6 +35,15 @@ html, body, body > * { flex-shrink: 0; } +.flex-reset { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; +} body { font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; diff --git a/app/boot.ts b/codeeditor-app/boot.ts similarity index 87% rename from app/boot.ts rename to codeeditor-app/boot.ts index 402750d..abb1d96 100644 --- a/app/boot.ts +++ b/codeeditor-app/boot.ts @@ -13,7 +13,4 @@ window.customElements.define('notification-bubbles', NotificationBubbles); window.customElements.define('notification-bubble', NotificationBubble); -var appEle = document.createElement("div"); -appEle.innerHTML = bootHtml; - -document.body.appendChild(appEle); \ No newline at end of file +document.body.innerHTML += bootHtml; \ No newline at end of file diff --git a/app/elements/_base.ts b/codeeditor-app/elements/_base.ts similarity index 100% rename from app/elements/_base.ts rename to codeeditor-app/elements/_base.ts diff --git a/app/elements/app-root/app-root.less b/codeeditor-app/elements/app-root/app-root.less similarity index 100% rename from app/elements/app-root/app-root.less rename to codeeditor-app/elements/app-root/app-root.less diff --git a/app/elements/app-root/app-root.ts b/codeeditor-app/elements/app-root/app-root.ts similarity index 100% rename from app/elements/app-root/app-root.ts rename to codeeditor-app/elements/app-root/app-root.ts diff --git a/app/elements/code-editor/code-editor.less b/codeeditor-app/elements/code-editor/code-editor.less similarity index 100% rename from app/elements/code-editor/code-editor.less rename to codeeditor-app/elements/code-editor/code-editor.less diff --git a/app/elements/code-editor/code-editor.ts b/codeeditor-app/elements/code-editor/code-editor.ts similarity index 60% rename from app/elements/code-editor/code-editor.ts rename to codeeditor-app/elements/code-editor/code-editor.ts index f8e4ae2..23d4398 100644 --- a/app/elements/code-editor/code-editor.ts +++ b/codeeditor-app/elements/code-editor/code-editor.ts @@ -13,6 +13,7 @@ export class CodeEditor extends BaseElement { this.initWorkers(); + var editor = monaco.editor.create(this, { value: this.input, language: 'javascript', @@ -20,9 +21,35 @@ export class CodeEditor extends BaseElement { contextmenu: false, minimap: { enabled: false - } + }, + autoIndent: "full" }); + monaco.languages.registerCompletionItemProvider("javascript", { + triggerCharacters: ["."], + provideCompletionItems: (model, position, context, token) => ( + { + suggestions: [ + { + label: 'for', + kind: monaco.languages.CompletionItemKind.Function, + insertText: 'for (let i = 0; i < array.length; i++) {\n\n}', + range: null, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet + }, + { + label: 'forr', + kind: monaco.languages.CompletionItemKind.Function, + insertText: 'for (var i = array.length - 1; i >= 0; i--) {\n\n}', + range: null, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet + } + ] + } + ) + }); + + editor.onDidChangeModelContent((e) => { this.input = editor.getValue(); this.outputFrame.setScript(this.input); diff --git a/codeeditor-app/elements/code-editor/injects/editor-init.txt b/codeeditor-app/elements/code-editor/injects/editor-init.txt new file mode 100644 index 0000000..b6a8b43 --- /dev/null +++ b/codeeditor-app/elements/code-editor/injects/editor-init.txt @@ -0,0 +1,16 @@ +var myDiv = document.createElement("div"); +myDiv.innerText = "hello moto"; + +document.body.appendChild(myDiv); + + +var i = 0; + +while (i < 10) { + console.log("hello " + i); + i++; +} + +var obj = { hello: true, moto: "enabled" }; + +console.log(obj); \ No newline at end of file diff --git a/app/elements/notification-bubbles/notification-bubble.less b/codeeditor-app/elements/notification-bubbles/notification-bubble.less similarity index 77% rename from app/elements/notification-bubbles/notification-bubble.less rename to codeeditor-app/elements/notification-bubbles/notification-bubble.less index fef5528..8579645 100644 --- a/app/elements/notification-bubbles/notification-bubble.less +++ b/codeeditor-app/elements/notification-bubbles/notification-bubble.less @@ -3,6 +3,7 @@ notification-bubble { padding: 10px; color: black; background-color: #a0ffa7; + border: 1px solid #8ddf92; display: block; position: relative; box-shadow: 1px 1px 1px 1px rgba(0,0,0,0.19); @@ -11,22 +12,27 @@ notification-bubble { notification-bubble.log { background-color: #ffffff; + border-color: #dfdfdf; } notification-bubble.info { background-color: #e7e7b3; + border-color: #d4d4a9; } notification-bubble.warn { background-color: #fff9a0; + border-color: #e6e094; } notification-bubble.error { background-color: #ffa0a0; + border-color: #df8e8e; } notification-bubble.syntax-error { background-color: #ff6262; + background-color: #d15353; font-size: 22px; padding: 30px; } \ No newline at end of file diff --git a/app/elements/notification-bubbles/notification-bubble.ts b/codeeditor-app/elements/notification-bubbles/notification-bubble.ts similarity index 100% rename from app/elements/notification-bubbles/notification-bubble.ts rename to codeeditor-app/elements/notification-bubbles/notification-bubble.ts diff --git a/app/elements/notification-bubbles/notification-bubbles.less b/codeeditor-app/elements/notification-bubbles/notification-bubbles.less similarity index 100% rename from app/elements/notification-bubbles/notification-bubbles.less rename to codeeditor-app/elements/notification-bubbles/notification-bubbles.less diff --git a/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts b/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts new file mode 100644 index 0000000..9575ca1 --- /dev/null +++ b/codeeditor-app/elements/notification-bubbles/notification-bubbles.ts @@ -0,0 +1,31 @@ +import { messageListener } from "../../../shared/message-listener"; +import { BaseElement } from "../_base"; +import { NotificationBubble } from "./notification-bubble"; +import './notification-bubbles.less'; + +export class NotificationBubbles extends BaseElement { + add(text: string, type: 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'script' | 'time' | 'response') { + + if (text && type) { + var bubble = document.createElement("notification-bubble"); + bubble.innerText = text; + bubble.classList.add(type); + this.appendChild(bubble); + + if (this.children.length > 50) { + this.removeChild(this.children[0]); + } + } + } + + reset() { + this.innerHTML = ""; + } + + onInit(): void { + messageListener((type, value) => { + if (type != 'script' && type != 'response') + this.add(value, type); + }); + } +} \ No newline at end of file diff --git a/app/elements/output-frame/output-frame.less b/codeeditor-app/elements/output-frame/output-frame.less similarity index 100% rename from app/elements/output-frame/output-frame.less rename to codeeditor-app/elements/output-frame/output-frame.less diff --git a/codeeditor-app/elements/output-frame/output-frame.ts b/codeeditor-app/elements/output-frame/output-frame.ts new file mode 100644 index 0000000..bd850f0 --- /dev/null +++ b/codeeditor-app/elements/output-frame/output-frame.ts @@ -0,0 +1,84 @@ +import { BaseElement } from "../_base"; +import './output-frame.less'; +import { NotificationBubbles } from "../notification-bubbles/notification-bubbles"; +import { GetOutputFrameUrl } from "../../../shared/url-helpers"; +import { GetNotificationBubbles } from "../../../shared/getdom"; +import { postMessage } from "../../../shared/post-message"; + +export class OutputFrame extends BaseElement { + public Iframe: HTMLIFrameElement | null = null; + public CurrentIframeScriptRemoveResponseListener: Function | null = null; + + constructor() { + super(); + } + + onInit(): void { + this.reset(); + } + + onUpdate(): void { + } + + reset(resetNotificationBubbles: boolean = true) { + if (this.CurrentIframeScriptRemoveResponseListener != null) { + this.CurrentIframeScriptRemoveResponseListener(); + this.CurrentIframeScriptRemoveResponseListener = null; + } + + if (this.Iframe && this.hasChild(this.Iframe)) { + this.removeChild(this.Iframe); + } + + this.Iframe = document.createElement("iframe"); + this.Iframe.setAttribute("sandbox", "allow-pointer-lock allow-same-origin allow-scripts"); + this.Iframe.src = GetOutputFrameUrl(); + this.appendChild(this.Iframe); + + if (resetNotificationBubbles) { + var bubbles = GetNotificationBubbles(); + if (bubbles && bubbles.reset) { + bubbles.reset(); + } + } + } + + setError() { + this.reset(); + if (this.Iframe) { + this.Iframe.src = GetOutputFrameUrl() + "execution-time-error.html"; + } + } + + preventInfiniteLoop(value: string) { + return value.replace("while (true)", "while (false)"); + } + + setScript(value: string) { + this.reset(); + this.onIframeLoaded(() => { + this.CurrentIframeScriptRemoveResponseListener = postMessage(this.Iframe?.contentWindow, "script", value, (executeTimeInMs, exceedTimeInMs) => { + if (executeTimeInMs === -1) { + this.setError(); + } + else { + var bubbles = GetNotificationBubbles(); + bubbles.add("code executed in: " + executeTimeInMs + "ms", "info"); + } + }); + }); + } + + onIframeLoaded(fn: Function) { + if (this.Iframe) { + this.Iframe.onload = () => { + fn(); + } + } + else { + this.waitFor(this.Iframe, () => { + this.onIframeLoaded(fn); + }); + } + } +} \ No newline at end of file diff --git a/static/Web.config b/codeeditor-app/static/Web.config similarity index 100% rename from static/Web.config rename to codeeditor-app/static/Web.config diff --git a/static/index.html b/codeeditor-app/static/index.html similarity index 100% rename from static/index.html rename to codeeditor-app/static/index.html diff --git a/static/logo.png b/codeeditor-app/static/logo.png similarity index 100% rename from static/logo.png rename to codeeditor-app/static/logo.png diff --git a/static/manifest.json b/codeeditor-app/static/manifest.json similarity index 100% rename from static/manifest.json rename to codeeditor-app/static/manifest.json diff --git a/outputframe-app/app.html b/outputframe-app/app.html new file mode 100644 index 0000000..e69de29 diff --git a/outputframe-app/app.less b/outputframe-app/app.less new file mode 100644 index 0000000..643672f --- /dev/null +++ b/outputframe-app/app.less @@ -0,0 +1,11 @@ +html, body { + margin: 0; + width: 100%; + height: 100%; + overflow: hidden; +} + +body { + font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif; + padding: 10px; +} \ No newline at end of file diff --git a/outputframe-app/boot.ts b/outputframe-app/boot.ts new file mode 100644 index 0000000..777ba65 --- /dev/null +++ b/outputframe-app/boot.ts @@ -0,0 +1,22 @@ +import './app.less'; +import bootHtml from './app.html'; +import { watchConsole } from './injects/watch-console'; +import { setOnErrorListener } from './injects/on-error'; +import { createScript } from '../shared/create-script'; +import { messageListener } from '../shared/message-listener'; +import { postMessage, postResponseMessage } from '../shared/post-message'; + + +document.body.innerHTML += bootHtml; +watchConsole((type, value) => { + postMessage(parent, type, value); +}); +messageListener((type, value, id) => { + if (type == "script") { + createScript(value); + postResponseMessage(parent, id, value); + } +}); +setOnErrorListener((type, value) => { + postMessage(parent, type, value); +}); \ No newline at end of file diff --git a/outputframe-app/injects/on-error.ts b/outputframe-app/injects/on-error.ts new file mode 100644 index 0000000..c22f8da --- /dev/null +++ b/outputframe-app/injects/on-error.ts @@ -0,0 +1,7 @@ +import { NotificationBubbles } from "../../codeeditor-app/elements/notification-bubbles/notification-bubbles"; + +export function setOnErrorListener(fn: (type: 'syntax-error', value: any) => void) { + window.onerror = function (error) { + fn('syntax-error', error); + } +} \ No newline at end of file diff --git a/outputframe-app/injects/try-catch.ts b/outputframe-app/injects/try-catch.ts new file mode 100644 index 0000000..b786067 --- /dev/null +++ b/outputframe-app/injects/try-catch.ts @@ -0,0 +1,12 @@ +import { NotificationBubbles } from "../../codeeditor-app/elements/notification-bubbles/notification-bubbles"; + +export function tryCatch(fn: Function) { + try { + fn(); + } + catch (err) { + var bubbles = parent.document.body.getElementsByTagName("notification-bubbles")[0]; + //@ts-ignore + bubbles.add(err, 'syntax-error'); + } +} \ No newline at end of file diff --git a/outputframe-app/injects/watch-console.ts b/outputframe-app/injects/watch-console.ts new file mode 100644 index 0000000..82c29e4 --- /dev/null +++ b/outputframe-app/injects/watch-console.ts @@ -0,0 +1,28 @@ +import { NotificationBubbles } from "../../codeeditor-app/elements/notification-bubbles/notification-bubbles"; + +export function watchConsole(fn: (type: 'log' | 'info' | 'warn' | 'error', value: any) => void) { + // define a new console + var console: any = (function (oldCons) { + return { + log: function (text: any) { + oldCons.log(text); + fn('log', text); + }, + info: function (text: any) { + oldCons.info(text); + fn('info', text); + }, + warn: function (text: any) { + oldCons.warn(text); + fn('warn', text); + }, + error: function (text: any) { + oldCons.error(text); + fn('error', text); + } + }; + }(window.console)); + + //Then redefine the old console + window.console = console; +} \ No newline at end of file diff --git a/app/elements/output-frame/injects/window-stop.txt b/outputframe-app/injects/window-stop.ts similarity index 100% rename from app/elements/output-frame/injects/window-stop.txt rename to outputframe-app/injects/window-stop.ts diff --git a/outputframe-app/static/execution-time-error.html b/outputframe-app/static/execution-time-error.html new file mode 100644 index 0000000..56326b2 --- /dev/null +++ b/outputframe-app/static/execution-time-error.html @@ -0,0 +1,36 @@ + + + + + + output error + + + + + +
+ took too long to run?
+ why you infinite loopin? +
+ + + \ No newline at end of file diff --git a/outputframe-app/static/index.html b/outputframe-app/static/index.html new file mode 100644 index 0000000..106c903 --- /dev/null +++ b/outputframe-app/static/index.html @@ -0,0 +1,16 @@ + + + + + + output + + + + + + + + + \ No newline at end of file diff --git a/outputframe/output.frame.bundle.js b/outputframe/output.frame.bundle.js new file mode 100644 index 0000000..c38d2e2 --- /dev/null +++ b/outputframe/output.frame.bundle.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={8971:(e,n,t)=>{t.d(n,{Z:()=>s});var r=t(8081),o=t.n(r),a=t(3645),i=t.n(a)()(o());i.push([e.id,'html,\nbody {\n margin: 0;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\nbody {\n font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif;\n padding: 10px;\n}\n',""]);const s=i},3645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",r=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),r&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),r&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,r,o,a){"string"==typeof e&&(e=[[null,e,void 0]]);var i={};if(r)for(var s=0;s0?" ".concat(d[5]):""," {").concat(d[1],"}")),d[5]=a),t&&(d[2]?(d[1]="@media ".concat(d[2]," {").concat(d[1],"}"),d[2]=t):d[2]=t),o&&(d[4]?(d[1]="@supports (".concat(d[4],") {").concat(d[1],"}"),d[4]=o):d[4]="".concat(o)),n.push(d))}},n}},8081:e=>{e.exports=function(e){return e[1]}},3379:e=>{var n=[];function t(e){for(var t=-1,r=0;r{var n={};e.exports=function(e,t){var r=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!r)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");r.appendChild(t)}},9216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},3565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},7795:e=>{e.exports=function(e){var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var r="";t.supports&&(r+="@supports (".concat(t.supports,") {")),t.media&&(r+="@media ".concat(t.media," {"));var o=void 0!==t.layer;o&&(r+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),r+=t.css,o&&(r+="}"),t.media&&(r+="}"),t.supports&&(r+="}");var a=t.sourceMap;a&&"undefined"!=typeof btoa&&(r+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),n.styleTagTransform(r,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},4589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(r){var o=n[r];if(void 0!==o)return o.exports;var a=n[r]={id:r,exports:{}};return e[r](a,a.exports,t),a.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var r in n)t.o(n,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:n[r]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),(()=>{var e,n,r,o=t(3379),a=t.n(o),i=t(7795),s=t.n(i),c=t(569),u=t.n(c),d=t(3565),l=t.n(d),p=t(9216),f=t.n(p),v=t(4589),m=t.n(v),y=t(8971),h={};function g(e){var n=function(n){n.data&&""!==n.data&&"webpackOk"!==n.data.type&&e(n.data.type,n.data.value,n.data.id)};return window.addEventListener("message",n),()=>{window.removeEventListener("message",n)}}function w(e,n,t,r){var o=+new Date,a=5e3,i=!0;if(e&&n&&t){if(r){var s=null,c=g(((e,n,t)=>{"response"==e&&t==o&&(null!=s&&clearTimeout(s),c(),i&&r(+new Date-t,a))}));s=setTimeout((()=>{c(),null!=s&&(clearTimeout(s),i&&r(-1,a))}),a)}e.postMessage({type:n,value:"string"==typeof t||t instanceof String?t:JSON.stringify(t),id:o},"*")}return()=>{r&&(i=!1)}}h.styleTagTransform=m(),h.setAttributes=l(),h.insert=u().bind(null,"head"),h.domAPI=s(),h.insertStyleElement=f(),a()(y.Z,h),y.Z&&y.Z.locals&&y.Z.locals,document.body.innerHTML+="",e=(e,n)=>{w(parent,e,n)},n=window.console,r={log:function(t){n.log(t),e("log",t)},info:function(t){n.info(t),e("info",t)},warn:function(t){n.warn(t),e("warn",t)},error:function(t){n.error(t),e("error",t)}},window.console=r,g(((e,n,t)=>{"script"==e&&(function(e){var n=document.createElement("script");n.setAttribute("async",""),n.innerHTML=e,document.body.appendChild(n)}(n),function(e,n,t){e&&t&&e.postMessage({type:"response",value:JSON.stringify(t),id:n},"*")}(parent,t,n))})),window.onerror=function(e){w(parent,"syntax-error",e)}})()})(); \ No newline at end of file diff --git a/run-servers.js b/run-servers.js new file mode 100644 index 0000000..a5900b5 --- /dev/null +++ b/run-servers.js @@ -0,0 +1,22 @@ +const WebpackDevServer = require("webpack-dev-server"); +const webpack = require("webpack"); + +const codeeditorConfig = require("./webpack.dev.js"); +const outputframeConfig = require("./webpack.dev-outputframe.js"); + +const codeeditorCompiler = webpack(codeeditorConfig); +const outputframeCompiler = webpack(outputframeConfig); + + +const server1 = new WebpackDevServer(codeeditorCompiler, { + hot: true, + historyApiFallback: true +}) + +const server2 = new WebpackDevServer(outputframeCompiler, { + hot: true, + historyApiFallback: true +}) + +server1.listen(8020, "localhost", function() { console.log("server 1 rdy"); }) +server2.listen(8021, "localhost", function() { console.log("server 2 rdy"); }) \ No newline at end of file diff --git a/shared/create-script.ts b/shared/create-script.ts new file mode 100644 index 0000000..0eed6df --- /dev/null +++ b/shared/create-script.ts @@ -0,0 +1,7 @@ +export function createScript(value: string) { + var script: HTMLScriptElement = document.createElement("script"); + script.setAttribute("async", ""); + script.innerHTML = value; + + document.body.appendChild(script); +} \ No newline at end of file diff --git a/shared/getdom.ts b/shared/getdom.ts new file mode 100644 index 0000000..36b666b --- /dev/null +++ b/shared/getdom.ts @@ -0,0 +1,10 @@ +import { NotificationBubbles } from "../codeeditor-app/elements/notification-bubbles/notification-bubbles"; +import { OutputFrame } from "../codeeditor-app/elements/output-frame/output-frame"; + +export function GetNotificationBubbles(): NotificationBubbles { + return document.getElementsByTagName("notification-bubbles")[0]; +} + +export function GetOutputFrame(): OutputFrame { + return document.getElementsByTagName("output-frame")[0]; +} \ No newline at end of file diff --git a/shared/message-listener.ts b/shared/message-listener.ts new file mode 100644 index 0000000..75c3a29 --- /dev/null +++ b/shared/message-listener.ts @@ -0,0 +1,16 @@ +export function messageListener(fn: (type: 'script' | 'log' | 'info' | 'warn' | 'error' | 'syntax-error' | 'time' | 'response', value: string, id: number) => void): Function { + + var evt = function(e: any) { + if (!e.data || e.data === '' || e.data.type === 'webpackOk') { + return; + } + + fn(e.data.type, e.data.value, e.data.id); + }; + + window.addEventListener('message', evt); + + return () => { + window.removeEventListener('message', evt); + } +} \ No newline at end of file diff --git a/shared/post-message.ts b/shared/post-message.ts new file mode 100644 index 0000000..4d50a7a --- /dev/null +++ b/shared/post-message.ts @@ -0,0 +1,48 @@ +import { messageListener } from "./message-listener"; + +export function postMessage(window: Window | null | undefined, type: string, value: any, onResponse?: (executeTimeInMs: number, exceedTimeInMs: number) => void): Function { + var postId = +new Date(); + var responseTimeout = 5000; + var execResponse = true; + + if (window && type && value) { + if (onResponse) { + var timeoutHandle: number | null = null; + + var remListener = messageListener((type, value, id) => { + if (type == "response" && id == postId) { + if (timeoutHandle != null) + clearTimeout(timeoutHandle); + + remListener(); + if (execResponse) + onResponse(+new Date() - id, responseTimeout); + } + }); + + timeoutHandle = setTimeout(() => { + remListener(); + if (timeoutHandle != null) { + clearTimeout(timeoutHandle); + if (execResponse) + onResponse(-1, responseTimeout); + } + }, responseTimeout); + } + + window.postMessage({ type: type, value: (typeof value === 'string' || value instanceof String) ? value : JSON.stringify(value), id: postId }, "*"); + } + + return () => { + if (onResponse) { + execResponse = false; + } + }; + +} + +export function postResponseMessage(window: Window | null | undefined, id: number, value: any) { + if (window && value) { + window.postMessage({ type: "response", value: JSON.stringify(value), id: id }, "*"); + } +} \ No newline at end of file diff --git a/shared/url-helpers.ts b/shared/url-helpers.ts new file mode 100644 index 0000000..72c6907 --- /dev/null +++ b/shared/url-helpers.ts @@ -0,0 +1,16 @@ +export function GetUrl(): string { + return location.protocol + "//" + location.host + "/"; +} + +export function IsLocalhost(): boolean { + return location.href.indexOf("://localhost") != -1; +} + +export function GetOutputFrameUrl(): string { + if (IsLocalhost()) { + return "http://127.0.0.1:8021/"; + } + else { + return "https://outputframe.davidmeincke.dk/"; + } +} \ No newline at end of file diff --git a/webpack.common.js b/webpack.common.js index 6b04ac8..7800114 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,12 +1,13 @@ const path = require('path'); module.exports = { entry: { - 'main': path.resolve(__dirname, 'App', 'boot.ts'), + 'main': path.resolve(__dirname, 'codeeditor-app', 'boot.ts'), 'editor.worker': 'monaco-editor/esm/vs/editor/editor.worker.js', 'json.worker': 'monaco-editor/esm/vs/language/json/json.worker', 'css.worker': 'monaco-editor/esm/vs/language/css/css.worker', 'html.worker': 'monaco-editor/esm/vs/language/html/html.worker', - 'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker' + 'ts.worker': 'monaco-editor/esm/vs/language/typescript/ts.worker', + '../outputframe/output.frame': path.resolve(__dirname, 'outputframe-app', 'boot.ts') }, resolve: { extensions: [".ts", ".js", ".json"] diff --git a/webpack.dev-outputframe.js b/webpack.dev-outputframe.js new file mode 100644 index 0000000..12ac5b6 --- /dev/null +++ b/webpack.dev-outputframe.js @@ -0,0 +1,35 @@ +const path = require('path'); +const { merge } = require('webpack-merge'); +const copyPlugin = require('copy-webpack-plugin'); +const common = require('./webpack.common.js'); + +const devFolderName = "dev-dist"; + +module.exports = merge(common, { + mode: 'development', + output: { + globalObject: 'self', + filename: '[name].bundle.js', + path: path.resolve(__dirname, devFolderName, 'outputframe'), + publicPath: "/" + }, + devServer: { + historyApiFallback: true + }, + plugins: [ + new copyPlugin( + { + patterns: [ + { + from: path.resolve(__dirname, 'codeeditor-app/static'), + to: path.resolve(__dirname, devFolderName, 'codeeditor', '[name][ext]') + }, + { + from: path.resolve(__dirname, 'outputframe-app/static'), + to: path.resolve(__dirname, devFolderName, 'outputframe', '[name][ext]') + } + ] + } + ) + ] +}); \ No newline at end of file diff --git a/webpack.dev.js b/webpack.dev.js index ff7ce6a..190a0e1 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -2,7 +2,6 @@ const path = require('path'); const { merge } = require('webpack-merge'); const copyPlugin = require('copy-webpack-plugin'); const common = require('./webpack.common.js'); -const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const devFolderName = "dev-dist"; @@ -11,13 +10,10 @@ module.exports = merge(common, { output: { globalObject: 'self', filename: '[name].bundle.js', - path: path.resolve(__dirname, devFolderName), + path: path.resolve(__dirname, devFolderName, 'codeeditor'), publicPath: "/" }, devServer: { - static: { - directory: path.join(__dirname, devFolderName), - }, historyApiFallback: true }, plugins: [ @@ -25,8 +21,12 @@ module.exports = merge(common, { { patterns: [ { - from: path.resolve(__dirname, 'static'), - to: path.resolve(__dirname, devFolderName, '[name][ext]') + from: path.resolve(__dirname, 'codeeditor-app/static'), + to: path.resolve(__dirname, devFolderName, 'codeeditor', '[name][ext]') + }, + { + from: path.resolve(__dirname, 'outputframe-app/static'), + to: path.resolve(__dirname, devFolderName, 'outputframe', '[name][ext]') } ] } diff --git a/webpack.prod.js b/webpack.prod.js index 46de967..9599b43 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -3,7 +3,6 @@ const common = require('./webpack.common.js'); const path = require('path'); const ReplaceInFileWebpackPlugin = require('replace-in-file-webpack-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); const prodFolderName = "prod-dist"; @@ -18,28 +17,29 @@ const replaceVersionInHtmlOption = { return match.toString().substr(0, 3) + +new Date(); } - } + } ] }; module.exports = merge(common, { mode: "production", output: { - path: path.resolve(__dirname, prodFolderName), - publicPath: "/", - filename: 'main.js' + globalObject: 'self', + filename: '[name].bundle.js', + path: path.resolve(__dirname, prodFolderName, "codeeditor"), + publicPath: "/" }, plugins: [ new CopyWebpackPlugin( { patterns: [ { - from: path.resolve(__dirname, 'index.html'), - to: path.resolve(__dirname, prodFolderName, '[name][ext]') + from: path.resolve(__dirname, 'codeeditor-app/static'), + to: path.resolve(__dirname, prodFolderName, 'codeeditor', '[name][ext]') }, { - from: path.resolve(__dirname, 'manifest.json'), - to: path.resolve(__dirname, devFolderName, '[name][ext]') + from: path.resolve(__dirname, 'outputframe-app/static'), + to: path.resolve(__dirname, prodFolderName, 'outputframe', '[name][ext]') } ] }