yourjs-box 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/yourjs-box.full.js +1891 -0
- package/dist/yourjs-box.js +470 -0
- package/dist/yourjs-box.min.js +2 -0
- package/package.json +44 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/*! yourjs-box v1.0.0 | (c) 2023-present Christopher West | MIT License | https://github.com/westc/yourjs-box */
|
|
2
|
+
(() => {
|
|
3
|
+
/**
|
|
4
|
+
* Viewer IFRAME's CSS code
|
|
5
|
+
* @type {string}
|
|
6
|
+
*/
|
|
7
|
+
const VIEWER_IFRAME_CSS = "[v-cloak]{display:none}body,html{height:100%;margin:0}body{background-color:var(--console-bg);font-family:system-ui,-apple-system,'Segoe UI',Roboto,Helvetica,Arial,sans-serif}#vueApp{display:flex;flex-direction:column;inset:0;position:fixed}#main{display:grid;flex-grow:1;gap:0;min-height:0;position:relative}#main.is-moving-divider{-webkit-user-select:none;user-select:none}#main.col-orient.is-moving-divider{cursor:col-resize}#main.row-orient.is-moving-divider{cursor:row-resize}#main.is-moving-divider::after{background-color:var(--accent);content:'';position:absolute;z-index:99}#main.col-orient.is-moving-divider::after{bottom:0;left:calc(100% - var(--temp-editor-pct) - var(--divider-size)/ 2 - 1px);top:0;width:2px}#main.row-orient.is-moving-divider::after{height:2px;left:0;right:0;top:calc(100% - var(--temp-editor-pct) - var(--divider-size)/ 2 - 1px)}#main.col-orient{grid-template-columns:1fr var(--divider-size) calc(var(--editor-pct) - var(--divider-size)/ 2);grid-template-rows:1fr}#main.row-orient{grid-template-columns:1fr;grid-template-rows:1fr var(--divider-size) calc(var(--editor-pct) - var(--divider-size)/ 2)}.divider{background-color:var(--divider-bg);box-sizing:border-box;position:relative;transition:background-color .15s}#main.col-orient .divider{border-left:1px solid var(--toolbar-border);border-right:1px solid var(--toolbar-border);cursor:col-resize}#main.row-orient .divider{border-bottom:1px solid var(--toolbar-border);border-top:1px solid var(--toolbar-border);cursor:row-resize}.divider::after{background-color:var(--divider-grip);border-radius:2px;content:'';left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);transition:background-color .15s}#main.col-orient .divider::after{height:32px;width:2px}#main.row-orient .divider::after{height:2px;width:32px}#main.is-moving-divider .divider,.divider:hover{background-color:var(--divider-hover-bg)}#main.is-moving-divider .divider::after,.divider:hover::after{background-color:var(--accent)}#displays{position:relative}#displays>div{position:absolute;inset:0;overflow:auto}#bottomNav{align-items:center;background-color:var(--toolbar-bg);border-top:1px solid var(--toolbar-border);color:var(--toolbar-text);display:flex;flex:0 0 auto;font-size:12px;gap:8px;height:32px;padding:0 6px 0 8px;-webkit-user-select:none;user-select:none}#bottomNav>.brand{align-items:center;display:flex;flex-grow:1;gap:8px}.runner-badge{border:1px solid var(--toolbar-border);border-radius:999px;color:var(--muted-text);font-size:10px;letter-spacing:.04em;line-height:15px;padding:0 6px;text-transform:uppercase}#bottomNav>.buttons{align-items:center;display:flex;gap:2px}#bottomNav .separator{background-color:var(--toolbar-border);height:16px;margin:0 4px;width:1px}#bottomNav button{align-items:center;background:0 0;border:0;border-radius:4px;color:inherit;cursor:pointer;display:inline-flex;font:inherit;gap:5px;height:24px;justify-content:center;min-width:26px;padding:0 6px}#bottomNav button>span:not(.label){display:inline-flex;font-size:14px}#bottomNav button svg:not(.spin){transform:none!important}#bottomNav button:hover:not(:disabled){background-color:var(--button-hover)}#bottomNav button:disabled{cursor:not-allowed;opacity:.45}#bottomNav button.primary{background-color:var(--accent);color:var(--accent-text);font-weight:600;margin-left:2px;padding:0 10px 0 8px}#bottomNav button.primary>span:not(.label){font-size:11px}#bottomNav button.primary:hover:not(:disabled){background-color:var(--accent-hover)}#bottomNav button:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.spin{animation:spin .8s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.logo{align-items:center;color:var(--toolbar-text);display:inline-flex;font-weight:700;gap:5px;letter-spacing:-.01em;line-height:1}.logo-mark{align-items:flex-end;background-color:#f7df1e;border-radius:3px;box-sizing:border-box;color:#1a1a1a;display:inline-flex;font-size:9.5px;font-weight:800;height:18px;justify-content:flex-end;letter-spacing:-.02em;padding:0 2px 2px 0;width:18px}.logo-text{font-size:14px}.logo-large{gap:12px}.logo-large>.logo-mark{border-radius:10px;box-shadow:0 8px 24px rgb(0 0 0 / .18);font-size:24px;height:56px;padding:0 6px 5px 0;width:56px}.logo-large>.logo-text{font-size:36px}#splash{align-items:center;background-color:var(--console-bg);display:flex;inset:0;justify-content:center;position:fixed;transition:opacity .35s ease,visibility .35s;z-index:1000}#splash.hidden{opacity:0;visibility:hidden}.splash-content{align-items:center;animation:splash-in .4s ease-out both;display:flex;flex-direction:column;gap:24px}.splash-progress{background-color:var(--toolbar-border);border-radius:3px;height:3px;overflow:hidden;width:140px}.splash-progress>div{animation:splash-progress 1.1s ease-in-out infinite;background-color:var(--accent);border-radius:inherit;height:100%;width:40%}@keyframes splash-in{from{opacity:0;transform:translateY(6px)}}@keyframes splash-progress{from{transform:translateX(-100%)}to{transform:translateX(250%)}}.dialog-backdrop{align-items:center;animation:fade-in .12s ease-out;background-color:var(--backdrop);display:flex;inset:0;justify-content:center;position:fixed;z-index:900}.dialog{animation:dialog-in .15s ease-out;background-color:var(--dialog-bg);border:1px solid var(--toolbar-border);border-radius:8px;box-shadow:0 12px 40px rgb(0 0 0 / .3);color:var(--console-text);font-size:13px;padding:16px;width:min(360px,calc(100% - 32px))}.dialog-title{font-size:14px;font-weight:600;margin-bottom:6px}.dialog-message{color:var(--muted-text);line-height:1.45}.dialog-buttons{display:flex;gap:8px;justify-content:flex-end;margin-top:16px}.dialog-button{background:0 0;border:1px solid var(--toolbar-border);border-radius:4px;color:inherit;cursor:pointer;font:inherit;height:28px;padding:0 12px}.dialog-button:hover{background-color:var(--button-hover)}.dialog-button.primary{background-color:var(--accent);border-color:var(--accent);color:var(--accent-text);font-weight:600}.dialog-button.primary:hover{background-color:var(--accent-hover)}.dialog-button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}@keyframes fade-in{from{opacity:0}}@keyframes dialog-in{from{opacity:0;transform:scale(.96)}}.rotated-90deg{transform:rotate(90deg)}.align-top{vertical-align:top!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.no-select{-webkit-user-select:none;user-select:none}:root{color-scheme:light;--console-font:ui-monospace,Menlo,Monaco,Consolas,'Liberation Mono','Courier New',monospace;--console-bg:#fff;--console-text:#1f1f1f;--row-border:#f0f0f0;--arrow:#727272;--chevron:#9aa0a6;--entry-key:#881391;--preview-key:#5f6368;--string:#c41a16;--number:#1a1aa6;--null:#80868b;--header-bg:#f1f3f4;--header-text:#3c4043;--warn-bg:#fffbe5;--warn-border:#fff5c2;--warn-text:#5c3c00;--warn-icon:#e8a600;--error-bg:#fff0f0;--error-border:#ffd6d6;--error-text:#dc362e;--error-icon:#dc362e;--table-header-bg:#f3f3f3;--table-border:#d0d0d0;--toolbar-bg:#f3f3f3;--toolbar-border:#d6d6d6;--toolbar-text:#333;--muted-text:#5f6368;--button-hover:rgb(0 0 0 / 0.08);--accent:#1a73e8;--accent-hover:#1765cc;--accent-text:#fff;--divider-bg:#f3f3f3;--divider-hover-bg:#e8eaed;--divider-grip:#b0b0b0;--dialog-bg:#fff;--backdrop:rgb(0 0 0 / 0.25)}:root[data-theme=dark]{color-scheme:dark;--console-bg:#242424;--console-text:#e3e3e3;--row-border:#3a3a3a;--arrow:#9aa0a6;--chevron:#80868b;--entry-key:#5db0d7;--preview-key:#9aa0a6;--string:#f28b54;--number:#9980ff;--null:#8e8e8e;--header-bg:#2d2e30;--header-text:#c4c7c5;--warn-bg:#332b00;--warn-border:#665500;--warn-text:#ffd17a;--warn-icon:#ffd17a;--error-bg:#290000;--error-border:#5c0000;--error-text:#ff8080;--error-icon:#ff6b6b;--table-header-bg:#2e2e2e;--table-border:#4a4a4a;--toolbar-bg:#2b2b2b;--toolbar-border:#474747;--toolbar-text:#e3e3e3;--muted-text:#9aa0a6;--button-hover:rgb(255 255 255 / 0.1);--accent:#8ab4f8;--accent-hover:#aecbfa;--accent-text:#202124;--divider-bg:#2b2b2b;--divider-hover-bg:#333;--divider-grip:#6b6b6b;--dialog-bg:#2d2e30;--backdrop:rgb(0 0 0 / 0.5)}#displays{background-color:var(--console-bg);color:var(--console-text)}#displays>div{font-family:var(--console-font);font-size:12px;line-height:16px}.console-row{border-bottom:1px solid var(--row-border);padding:2px 8px 2px 24px;position:relative;white-space:pre-wrap;word-break:break-word}.console-row>.row-icon{left:6px;line-height:0;position:absolute;top:4px}.log-error,.log-warn{margin-top:-1px}.log-warn{background-color:var(--warn-bg);border-bottom-color:var(--warn-border);border-top:1px solid var(--warn-border);color:var(--warn-text)}.log-warn>.row-icon{color:var(--warn-icon)}.log-error{background-color:var(--error-bg);border-bottom-color:var(--error-border);border-top:1px solid var(--error-border);color:var(--error-text)}.log-error>.row-icon{color:var(--error-icon)}.code-header{background-color:var(--header-bg);border-bottom:1px solid var(--row-border);color:var(--header-text);font-weight:700;padding:2px 8px;white-space:pre-wrap}.code-header.toggleable{cursor:pointer}.code-row .copy-to-editor-button{align-items:center;background-color:var(--console-bg);border:1px solid var(--toolbar-border);border-radius:4px;color:var(--toolbar-text);cursor:pointer;display:inline-flex;height:22px;justify-content:center;opacity:0;padding:0;position:absolute;right:6px;top:2px;transition:opacity .15s;width:22px}.code-row .copy-to-editor-button:focus-visible,.code-row:hover .copy-to-editor-button{opacity:1}.code-row .copy-to-editor-button:hover{background-color:var(--toolbar-bg)}@media (hover:none){.code-row .copy-to-editor-button{opacity:.8}}.notice{color:var(--null);font-style:italic}.code-row>.row-icon{color:var(--chevron)}.code-row code,.code-row pre{background:0 0!important;font-family:var(--console-font)!important;font-size:12px!important;line-height:16px!important;padding:0!important;text-shadow:none!important}.js-value{max-width:100%;vertical-align:top}.row-content>.js-value+.js-value{margin-left:1ch}.js-value-header.expandable{cursor:default}.js-value .expansion{padding-left:12px}.js-value .loading{color:var(--null);padding-left:12px}.arrow{display:inline-block;height:10px;position:relative;width:12px}.arrow.expandable::before{border-color:transparent transparent transparent var(--arrow);border-style:solid;border-width:4px 0 4px 6px;content:'';left:2px;position:absolute;top:1px;transform-origin:3px 4px;transition:transform .1s}.arrow.expandable.expanded::before{transform:rotate(90deg)}.entry-key{color:var(--entry-key)}.entry-key.dim{opacity:.6}.t-key{color:var(--preview-key)}.t-regexp,.t-string,.t-symbol{color:var(--string)}.t-number{color:var(--number)}.t-null{color:var(--null)}.t-function{font-style:italic}.t-node{color:var(--entry-key)}.console-table{border:1px solid var(--table-border);border-collapse:collapse;margin:2px 0 4px;white-space:nowrap}.console-table td,.console-table th{border-left:1px solid var(--table-border);max-width:300px;overflow:hidden;padding:1px 4px;text-align:left;text-overflow:ellipsis}.console-table th{background-color:var(--table-header-bg);border-bottom:1px solid var(--table-border);font-weight:400}";
|
|
8
|
+
/**
|
|
9
|
+
* Viewer IFRAME's HTML code
|
|
10
|
+
* @type {string}
|
|
11
|
+
*/
|
|
12
|
+
const VIEWER_IFRAME_HTML = "<div id=\"splash\" aria-label=\"Loading\"><div class=\"splash-content\"><span class=\"logo logo-large\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span><div class=\"splash-progress\"><div></div></div></div></div><div id=\"vueApp\" v-cloak><div id=\"main\" ref=\"main\" :class=\"mainElemClassNames\" :style=\"mainElemStyles\" @mousedown=\"onMainElemMouseDown\"><div id=\"displays\"><div ref=\"displaysScroller\" @scroll=\"onDisplaysScroll\"><template v-for=\"display in displays\"><div v-if=\"display.type === 'prism'\"><div v-if=\"display.isHidden\" class=\"code-header toggleable no-select\" @click=\"display.isCodeShown = !display.isCodeShown\" :title=\"display.isCodeShown ? 'Hide Code' : 'Show Code'\"><span :class=\"['arrow', 'expandable', display.isCodeShown ? 'expanded' : '']\"></span>{{ display.header || 'Hidden code' }}</div><div v-else-if=\"display.header\" class=\"code-header\">{{ display.header }}</div><div v-if=\"display.isCodeShown\" class=\"console-row code-row\"><span class=\"row-icon\"><icon name=\"chevron\"></icon></span><prism language=\"javascript\" :code=\"display.value\" :is-dark=\"theme === 'dark'\" match-braces></prism><button class=\"copy-to-editor-button\" title=\"Copy to editor\" @click=\"copyToEditor(display.value)\"><icon name=\"copyToEditor\"></icon></button></div></div><div v-if=\"display.type === 'log'\" :class=\"['console-row', 'log-' + display.key]\" :title=\"display.name\"><span v-if=\"display.key === 'error'\" class=\"row-icon\"><icon name=\"consoleError\"></icon></span><span v-else-if=\"display.key === 'warn'\" class=\"row-icon\"><icon name=\"consoleWarning\"></icon></span><div class=\"row-content\"><table v-if=\"display.table\" class=\"console-table\"><thead><tr><th v-for=\"header in display.table.headers\">{{ header }}</th></tr></thead><tbody><tr v-for=\"row in display.table.rows\"><td>{{ row.index }}</td><td v-for=\"cell in row.cells\"><template v-if=\"cell\"><span v-for=\"part in cell.parts\" :class=\"'t-' + part[0]\">{{ part[1] }}</span></template></td></tr></tbody></table><js-value v-for=\"(description, index) in display.descriptions\" :description=\"description\" :path=\"[display.logId, index]\"></js-value></div></div><div v-if=\"display.type === 'notice'\" class=\"console-row notice\">{{ display.message }}</div><div v-if=\"display.type === 'error'\" class=\"console-row log-error\"><span class=\"row-icon\"><icon name=\"consoleError\"></icon></span><div class=\"row-content\">{{ display.message }}</div></div></template></div></div><div class=\"divider\" ref=\"mainDivider\"></div><div id=\"editor\"><ace-editor v-model=\"jsCode\" language=\"javascript\" :theme=\"theme\" @key-combo=\"onEditorKeyCombo\" height=\"100%\"></ace-editor></div></div><div id=\"bottomNav\"><div class=\"brand\"><span class=\"logo\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span> <span class=\"runner-badge\" :title=\"runnerMode === 'window' ? 'Code runs directly in this page' : 'Code runs in a Web Worker (no DOM access)'\">{{ runnerMode === 'window' ? 'Window' : 'Worker' }}</span></div><div class=\"buttons\"><template v-for=\"bottomButton in bottomButtons\"><span v-if=\"bottomButton.isSeparator\" class=\"separator\"></span> <button v-else :class=\"bottomButton.className\" @click=\"bottomButton.callback.call(this, $event)\" :title=\"bottomButton.title\" :disabled=\"bottomButton.disableIf && bottomButton.disableIf.call(this)\"><icon :name=\"bottomButton.iconName\"></icon><span v-if=\"bottomButton.label\" class=\"label\">{{ bottomButton.label }}</span></button></template></div></div><div v-if=\"dialog\" class=\"dialog-backdrop\" @mousedown.self=\"closeDialog(false)\" @keydown.esc=\"closeDialog(false)\"><div class=\"dialog\" role=\"alertdialog\" aria-modal=\"true\" :aria-label=\"dialog.title\"><div class=\"dialog-title\">{{ dialog.title }}</div><div class=\"dialog-message\">{{ dialog.message }}</div><div class=\"dialog-buttons\"><button class=\"dialog-button\" @click=\"closeDialog(false)\">Cancel</button> <button class=\"dialog-button primary\" ref=\"dialogConfirmButton\" @click=\"closeDialog(true)\">{{ dialog.confirmText }}</button></div></div></div></div>";
|
|
13
|
+
/**
|
|
14
|
+
* The URL of this script which is used to remove this script's lines from the
|
|
15
|
+
* stack traces of errors in window mode.
|
|
16
|
+
*/
|
|
17
|
+
const OWN_URL = document.currentScript?.src ?? '';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The page's console methods before any console in window mode replaced them
|
|
21
|
+
* so that this script's own messages don't show up in those consoles. These
|
|
22
|
+
* are shared by every copy of this script that is loaded in the page.
|
|
23
|
+
*/
|
|
24
|
+
const ORIGINAL_CONSOLE = window[Symbol.for('yourjs-box.originalConsole')] ??= {...console};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The only functions that may be relayed to the viewer and to the runner.
|
|
28
|
+
* The runner executes the user's code so anything it sends must be limited
|
|
29
|
+
* to these calls.
|
|
30
|
+
*/
|
|
31
|
+
const RELAYABLE_FUNCS = {
|
|
32
|
+
viewer: ['appendLog', 'appendError', 'clearDisplays', 'onCodeRan', 'updateDescriptionFor'],
|
|
33
|
+
runner: ['clearLogs', 'reset', 'runCode', 'sendDescriptionFor'],
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Relays a message to its target as long as the function being called is
|
|
38
|
+
* allowed.
|
|
39
|
+
* @param {*} data
|
|
40
|
+
* @param {{[target: string]: {apply: (func: string, args: any[]) => void}}} targets
|
|
41
|
+
*/
|
|
42
|
+
function relayMessage(data, targets) {
|
|
43
|
+
const {target, func, args} = Object(data);
|
|
44
|
+
const targetObj = targets[target];
|
|
45
|
+
if (targetObj && RELAYABLE_FUNCS[target].includes(func) && Array.isArray(args)) {
|
|
46
|
+
targetObj.apply(func, args);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
ORIGINAL_CONSOLE.error('Unhandled message sent to main handler:', data);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function createRunner(e,t){const{mode:n}=t,r=t.ownUrl||("worker"===n?self.location.href:""),o={};let s=0,c=0,i=!0;for(const t of["clear","debug","error","info","log","table","warn"]){const n=console[t];"function"==typeof n&&(console[t]=function(...r){if("clear"===t)E(),e({target:"viewer",func:"clearDisplays",args:[!0]});else{const n="table"===t?f(r[0],r[1]):null;n&&(r=r.slice(0,1));const c=""+ ++s;o[c]=r.map((e=>({...g(e),value:e}))),e({target:"viewer",func:"appendLog",args:[{logId:c,key:t,descriptions:o[c].map(l(["value"])),table:n}]})}return n.apply(this,arguments)})}function u(t,n){e({target:"viewer",func:"appendError",args:[{message:(n?"Uncaught (in promise) ":"Uncaught ")+a(t?.stack??`${t?.message??t}`)}]})}function a(e){return`${e}`.split("\n").filter((e=>!(r&&/^\s*at\b/.test(e)&&e.includes(r)))).join("\n")}function l(e,t){if(!t)return t=>l(e,t);const n={...t};for(const t of e)delete n[t];return n}function f(e,t){if(null===e||"object"!=typeof e)return null;const n="Value",r=[];let o=!1;const s=Object.keys(e).map((t=>{const n=e[t],s={index:t,values:{},hasValue:!1};if(null===n||"object"!=typeof n&&"function"!=typeof n)o=!0,s.hasValue=!0,s.value=n;else for(const e of Object.keys(n))r.includes(e)||r.push(e),s.values[e]=n[e];return s})),c=Array.isArray(t)?t.map((e=>`${e}`)):r,i=o&&!Array.isArray(t);return{headers:["(index)",...c,...i?[n]:[]],rows:s.map((({index:e,values:t,hasValue:n,value:r})=>({index:e,cells:[...c.map((e=>Object.hasOwn(t,e)?g(t[e],2):null)),...i?[n?g(r,2):null]:[]]})))}}function p(e){if(null==e)return""+e;const t=typeof e;return"object"===t||"undefined"===t?Object.prototype.toString.call(e).slice(8,-1):t}function d(e,t){try{const t=Object.getPrototypeOf(e)?.constructor?.name;if(t&&"string"==typeof t)return t}catch(e){}return t}addEventListener("error",(e=>{u(void 0!==e.error?e.error:e.message)})),addEventListener("unhandledrejection",(e=>{u(e.reason,!0)}));const y=5,h=100;function g(e,t=0,n=!1){const r=p(e),o="function"!==r&&r===r.toLowerCase(),s=undefined;return{typeName:r,isPrimitive:o,parts:n?[["text","Object"!==r?r:d(e,r)]]:m(e,t,r)}}function m(e,t,n=p(e)){if("string"===n)return t?(t>1&&e.length>100&&(e=e.slice(0,99)+"…"),[["string",b(e)]]):[["text",e]];if("number"===n)return[["number",Object.is(e,-0)?"-0":""+e]];if("bigint"===n)return[["number",e+"n"]];if("boolean"===n)return[["number",""+e]];if("null"===n||"undefined"===n)return[["null",n]];if("symbol"===n)return[["symbol",e.toString()]];if("function"===n)return v(e,t);try{return x(e,t,n)}catch(e){return[["text",n]]}}function b(e){const t=JSON.stringify(e);return e.includes("'")?t:`'${t.slice(1,-1).replace(/\\"/g,'"')}'`}function v(e,t){let n="";try{n=Function.prototype.toString.call(e)}catch(e){}const r=/^class\b/.test(n);return t?t>1?[["function","ƒ"]]:[["function",r?`class ${e.name}`:`ƒ ${e.name}()`]]:[["text",r?n:n.replace(/^(async\s+)?function\b\s*/,"$1ƒ ")]]}function x(e,t,n){const r=d(e,n),o=/^Array(?:[^a-z]|$)|[^A-Z]Array$/.test(n);if("Date"===n)return[["text",isNaN(e)?"Invalid Date":Date.prototype.toString.call(e)]];if("RegExp"===n)return[["regexp",""+e]];if("undefined"!=typeof Node&&e instanceof Node)return e.nodeType===Node.ELEMENT_NODE?[["node",e.localName+(e.id?"#"+e.id:"")+[...e.classList].map((e=>"."+e)).join("")]]:e.nodeType===Node.TEXT_NODE?[["string",b(e.data)]]:[["node",e.nodeName]];if(e instanceof Error)return[["text",t?`${e.name}: ${e.message}`:a(e.stack??`${e}`)]];if(t>1)return o?[["text",`${r}(${e.length})`]]:"Map"===n||"Set"===n?[["text",`${r}(${e.size})`]]:[["text","Object"===r?"{…}":r]];const s=[],c=(e,t,n)=>{e.forEach(((e,t)=>{t&&s.push(["text",", "]),n(e)})),t>e.length&&s.push(["text",(e.length?", ":"")+"…"])};if(o){const t=e.length;s.push(["text","Array"===n&&"Array"===r?`(${t}) [`:`${r}(${t}) [`]),c(Array.from({length:Math.min(t,h)},((e,t)=>t)),t,(t=>s.push(...t in e?m(e[t],2):[["null","empty"]]))),s.push(["text","]"])}else if("Map"===n||"Set"===n){const t=[...e].slice(0,h);s.push(["text",`${r}(${e.size}) {`]),c(t,e.size,(e=>{"Map"===n?s.push(...m(e[0],2),["text"," => "],...m(e[1],2)):s.push(...m(e,2))})),s.push(["text","}"])}else{const t=Object.keys(e);"Object"!==r&&s.push(["text",r+" "]),"Number"===n||"String"===n||"Boolean"===n?s.push(["text","{"],...m(e.valueOf(),2),["text","}"]):(s.push(["text","{"]),c(t.slice(0,y),t.length,(t=>{const n=Object.getOwnPropertyDescriptor(e,t);s.push(["key",t],["text",": "],..."value"in n?m(n.value,2):[["text","(…)"]])})),s.push(["text","}"]))}return s}function j(e,t=e){const n=p(e),r=[],o=[],s=[],c=[];if(null!==e&&("object"==typeof e||"function"==typeof e)){let i=!1;try{if("Map"===n){for(const[t,n]of[...e])r.push([m(t,2).map((e=>e[1])).join(""),g(n,1)]),o.push({value:n});i=!0}else if("Array"!==n&&"function"!==n&&"function"==typeof e[Symbol.iterator]){let t=0;for(const n of e)r.push([""+t,g(n,1)]),o.push({value:n}),++t;i=!0}}catch(e){r.length=o.length=0}if(!i)for(const n of Object.getOwnPropertyNames(e))try{const s=Object.getOwnPropertyDescriptor(e,n),c="value"in s?s.value:Reflect.get(e,n,t);r.push([n,g(c,1),s.enumerable]),o.push({value:c})}catch(e){}const u=Object.getPrototypeOf(e);null!==u&&(s.push(["[[Prototype]]",g(u,1,!0)]),c.push({value:u,receiver:t}))}return{entries:r,protoEntries:s,$entries:o,$protoEntries:c}}function O(t){const r=`${t}\n//# sourceURL=snippet-${++c}.js`;if("worker"===n)try{$(r)}catch(e){u(e)}else{const e=document.createElement("script");e.textContent=r,document.head.appendChild(e),e.remove()}e({target:"viewer",func:"onCodeRan",args:[]})}function $(e){if(i)try{return void importScripts("data:text/javascript;charset=utf-8,"+encodeURIComponent(e))}catch(e){if("NetworkError"!==e?.name)throw e;i=!1}const t=URL.createObjectURL(new Blob([e],{type:"text/javascript"}));try{importScripts(t)}finally{URL.revokeObjectURL(t)}}function w(t){let n=o,r=0;for(let e of t)r&&r%2==0&&(e="$"+e),n=n?.[e],r++;if(!n)return;const s=j(n.value,"receiver"in n?n.receiver:n.value);Object.assign(n,s),e({target:"viewer",func:"updateDescriptionFor",args:[t,l(["$entries","$protoEntries"],s)]})}function E(){for(const e of Object.keys(o))delete o[e]}return{clearLogs:E,runCode:O,sendDescriptionFor:w}}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Runs the user's code in a Web Worker. The worker can be terminated which
|
|
57
|
+
* makes it possible to reset the console even if the code never finishes.
|
|
58
|
+
* @param {(message: any) => void} onMessage
|
|
59
|
+
*/
|
|
60
|
+
function createWorkerRunner(onMessage) {
|
|
61
|
+
const WORKER_SOURCE = '(function(){'
|
|
62
|
+
+ 'var runner = (' + createRunner + ')(function(m) { postMessage(m); }, {mode: "worker"});'
|
|
63
|
+
+ 'onmessage = function(e) {'
|
|
64
|
+
+ 'var d = e.data;'
|
|
65
|
+
+ 'if (d && Object.prototype.hasOwnProperty.call(runner, d.func) && Array.isArray(d.args)) {'
|
|
66
|
+
+ 'runner[d.func].apply(null, d.args);'
|
|
67
|
+
+ '}'
|
|
68
|
+
+ '};'
|
|
69
|
+
+ 'postMessage({target: "main", func: "ready"});'
|
|
70
|
+
+ '})();';
|
|
71
|
+
|
|
72
|
+
// A data URL gives the worker an opaque origin so that it cannot make
|
|
73
|
+
// requests with the page's cookies. Not all browsers support data URL
|
|
74
|
+
// workers so a blob URL is used as a fallback.
|
|
75
|
+
let canUseDataUrl = true;
|
|
76
|
+
let blobUrl;
|
|
77
|
+
let worker, isReady, pendingMessages;
|
|
78
|
+
|
|
79
|
+
function start() {
|
|
80
|
+
isReady = false;
|
|
81
|
+
pendingMessages = [];
|
|
82
|
+
const url = canUseDataUrl
|
|
83
|
+
? 'data:text/javascript;charset=utf-8,' + encodeURIComponent(WORKER_SOURCE)
|
|
84
|
+
: (blobUrl ??= URL.createObjectURL(new Blob([WORKER_SOURCE], {type: 'text/javascript'})));
|
|
85
|
+
try {
|
|
86
|
+
worker = new Worker(url);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
if (!canUseDataUrl) throw e;
|
|
90
|
+
canUseDataUrl = false;
|
|
91
|
+
return start();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const thisWorker = worker;
|
|
95
|
+
worker.onmessage = e => {
|
|
96
|
+
if (thisWorker !== worker) return;
|
|
97
|
+
if (e.data?.target === 'main' && e.data.func === 'ready') {
|
|
98
|
+
isReady = true;
|
|
99
|
+
for (const message of pendingMessages.splice(0)) worker.postMessage(message);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
onMessage(e.data);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
worker.onerror = e => {
|
|
106
|
+
// If the worker failed to start from a data URL try a blob URL.
|
|
107
|
+
if (thisWorker === worker && !isReady && canUseDataUrl) {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
canUseDataUrl = false;
|
|
110
|
+
const messages = pendingMessages;
|
|
111
|
+
worker.terminate();
|
|
112
|
+
start();
|
|
113
|
+
pendingMessages.push(...messages);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
start();
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
apply(func, args) {
|
|
122
|
+
if (func === 'reset') {
|
|
123
|
+
worker.terminate();
|
|
124
|
+
start();
|
|
125
|
+
}
|
|
126
|
+
else if (isReady) {
|
|
127
|
+
worker.postMessage({func, args});
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
pendingMessages.push({func, args});
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Runs the user's code directly in this window so that it has access to
|
|
138
|
+
* everything defined by the page.
|
|
139
|
+
* @param {(message: any) => void} onMessage
|
|
140
|
+
*/
|
|
141
|
+
function createWindowRunner(onMessage) {
|
|
142
|
+
const runner = createRunner(onMessage, {mode: 'window', ownUrl: OWN_URL});
|
|
143
|
+
return {
|
|
144
|
+
apply(func, args) {
|
|
145
|
+
// Anything the code defined stays defined so a reset can only forget
|
|
146
|
+
// the logged values.
|
|
147
|
+
if (func === 'reset') runner.clearLogs();
|
|
148
|
+
else runner[func](...args);
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Function executed when the script is included in a document.
|
|
155
|
+
* @param {HTMLScriptElement} script
|
|
156
|
+
* This is the current script but also the placeholder for where the
|
|
157
|
+
* console will be inserted into the DOM.
|
|
158
|
+
*/
|
|
159
|
+
function main(script) {
|
|
160
|
+
const dataset = JSON.parse(JSON.stringify(script.dataset));
|
|
161
|
+
const runnerMode = dataset.runner === 'window' ? 'window' : 'worker';
|
|
162
|
+
dataset.runner = runnerMode;
|
|
163
|
+
|
|
164
|
+
/** @type {ReturnType<createCallableFrame>} */
|
|
165
|
+
let callableViewerFrame;
|
|
166
|
+
const sendToViewer = data => relayMessage(data, {viewer: callableViewerFrame});
|
|
167
|
+
const runner = runnerMode === 'window'
|
|
168
|
+
? createWindowRunner(sendToViewer)
|
|
169
|
+
: createWorkerRunner(sendToViewer);
|
|
170
|
+
|
|
171
|
+
// The theme is determined up front so that the loading screen uses it.
|
|
172
|
+
const theme = /^(light|dark)$/.test(dataset.theme)
|
|
173
|
+
? dataset.theme
|
|
174
|
+
: matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
175
|
+
|
|
176
|
+
callableViewerFrame = createCallableFrame({
|
|
177
|
+
jsCode() {
|
|
178
|
+
let mountedApp;const Prism=window.Prism;delete window.Prism,Prism.plugins.autoloader.loadLanguages("javascript");const MIN_LOADING_TIME=1e3,NARROW_WIDTH=600;function getAutoDividerOrient(){return innerWidth<NARROW_WIDTH?"horizontal":"vertical"}function init(e,t){const i=t.hidePrefix??"",n=matchMedia("(prefers-color-scheme: dark)"),{visibleCode:r,hiddenGroups:s}=extractHiddenGroups(unindentMin(e),i),o=()=>s.map((e=>({...e})));mountedApp=Vue.createApp({data:()=>({displays:[],hidePrefix:i,hiddenGroups:o(),runnerMode:t.runner,runningCount:0,dialog:null,isDisplaysScrolledToBottom:!0,forcedTheme:/^(light|dark)$/.test(t.theme)?t.theme:null,prefersDark:n.matches,runCount:0,jsCode:r,isDividerOrientAuto:!/^(horizontal|vertical)$/.test(t.dividerOrient),dividerOrient:/^(horizontal|vertical)$/.test(t.dividerOrient)?t.dividerOrient:getAutoDividerOrient(),isMovingDivider:!1,dividerPct:"50%",dividerSize:"8px",tempDividerPct:null}),computed:{theme(){return this.forcedTheme??(this.prefersDark?"dark":"light")},bottomButtons(){const e=/Mac|iPhone|iPad/.test(navigator.platform);return[{iconName:"clear",title:"Clear console",callback(){this.clearConsole()}},{iconName:"refresh",title:"worker"===this.runnerMode?"Reset (also stops any code that is still running)":"Reset",callback(){this.resetConsole()}},{isSeparator:!0},{iconName:"horizontalView",title:"Show the editor below the console",callback(){this.setDividerOrient("horizontal")},showIf(){return"horizontal"!==this.dividerOrient}},{iconName:"verticalView",title:"Show the editor beside the console",callback(){this.setDividerOrient("vertical")},showIf(){return"vertical"!==this.dividerOrient}},{iconName:this.runningCount?"spinner":"play",label:"Run",className:"primary",title:`Run the next block of code (${e?"⌘":"Ctrl+"}Enter)`,callback(){this.runCode()},disableIf(){return!this.canRunCode}}].filter((e=>!e.showIf||e.showIf.call(this)))},canRunCode(){return this.jsCode.trim()},mainElemClassNames(){return["vertical"===this.dividerOrient?"col-orient":"row-orient",this.isMovingDivider?"is-moving-divider":""].join(" ")},mainElemStyles(){return{"--divider-size":this.dividerSize,"--editor-pct":this.dividerPct,"--temp-editor-pct":this.tempDividerPct}}},methods:{onEditorKeyCombo(e){(e.ctrlKey||e.metaKey)&&"Enter"===e.key&&this.canRunCode&&this.runCode()},runCode(){this.isDisplaysScrolledToBottom=!0;const[e,...t]=parseJSCodeGroups(this.jsCode);this.runGroup(e),this.jsCode=t.map((e=>e.allLines)).join("\n"),this.runCount++},runHiddenGroups(){const{hiddenGroups:e}=this;e.length&&e[0].runCount<=this.runCount&&this.runGroup(e.shift())},runGroup(e){const{header:t,isHidden:i}=parseGroupHeader(e.headerLines,this.hidePrefix);this.displays.push({type:"prism",header:t,isHidden:i,isCodeShown:!i,value:e.lines}),this.runningCount++,messageParent({target:"runner",func:"runCode",args:[e.lines]})},clearConsole(){this.displays=[],messageParent({target:"runner",func:"clearLogs",args:[]})},async resetConsole(){await this.confirm({title:"Reset the console?",message:"The output will be cleared and the editor will go back to the original code."+("window"===this.runnerMode?" Anything the code already defined on the page will stay defined.":""),confirmText:"Reset"})&&(this.displays=[],this.jsCode=r,this.hiddenGroups=o(),this.runCount=0,this.runningCount=0,messageParent({target:"runner",func:"reset",args:[]}),this.runHiddenGroups())},async copyToEditor(e){if(this.jsCode.trim()&&this.jsCode!==e){if(!await this.confirm({title:"Replace the code in the editor?",message:"The code that is currently in the editor will be replaced with a copy of the code you selected.",confirmText:"Replace"}))return}this.jsCode=e},confirm(e){return this.dialog?.resolve(!1),new Promise((t=>{this.dialog={...e,resolve:t},this.$nextTick((()=>this.$refs.dialogConfirmButton?.focus()))}))},closeDialog(e){const{dialog:t}=this;this.dialog=null,t?.resolve(e)},setDividerOrient(e){this.isDividerOrientAuto=!1,this.dividerOrient=e},onWindowResize(){this.isDividerOrientAuto&&(this.dividerOrient=getAutoDividerOrient())},onDisplaysScroll(){const{scrollTop:e,scrollHeight:t,clientHeight:i}=this.$refs.displaysScroller;this.isDisplaysScrolledToBottom=t-e-i<8},getEditorPct(e){const t=this.$refs.main.getBoundingClientRect(),{dividerOrient:i}=this,n=e.pageX-t.left,r=e.pageY-t.top,s=t.width,o=t.height;return 100-100*Math.min(Math.max(.2,"vertical"===i?n/s:r/o),.8)+"%"},onMainElemMouseDown(e){e.target===this.$refs.mainDivider&&(this.isMovingDivider=!0,this.tempDividerPct=this.getEditorPct(e))},onWindowMouseMove(e){this.isMovingDivider&&(this.tempDividerPct=this.getEditorPct(e))},onWindowMouseUp(e){this.isMovingDivider&&(this.isMovingDivider=!1,this.dividerPct=this.getEditorPct(e))},onWindowError(e){this.displays.push({type:"error",message:e.error?.stack??e.message??e.error?.message??`${e.error}`,line:e.lineno,column:e.colno})}},watch:{theme:{handler(e){document.documentElement.dataset.theme=e},immediate:!0},"displays.length"(){this.isDisplaysScrolledToBottom&&this.$nextTick((()=>{const e=this.$refs.displaysScroller;e.scrollTop=e.scrollHeight}))}},mounted(){addEventListener("mousemove",this.onWindowMouseMove),addEventListener("resize",this.onWindowResize),addEventListener("mouseup",this.onWindowMouseUp),addEventListener("error",this.onWindowError),n.addEventListener("change",(e=>this.prefersDark=e.matches)),this.runHiddenGroups(),setTimeout((()=>document.querySelector("#splash").classList.add("hidden")),Math.max(0,1e3-performance.now()))}}).component("ace-editor",getAceComponentProps()).component("prism",getPrismComponentProps()).component("icon",getIconComponentProps()).component("js-value",getJSValueComponentProps()).mount(document.querySelector("#vueApp"))}function getAceComponentProps(){return{data:()=>({annotations:[]}),props:["height","keybinding","language","modelValue","theme","width"],computed:{infoNotes(){return this.annotations.filter((({type:e})=>"info"===e))},warningNotes(){return this.annotations.filter((({type:e})=>"warning"===e))},errorNotes(){return this.annotations.filter((({type:e})=>"error"===e))},style(){return{height:null!=this.height?"number"==typeof this.height?this.height+"px":this.height:"150px",width:null!=this.width?"number"==typeof this.width?this.width+"px":this.width:"100%"}},modeSig(){return`ace/mode/${this.language??"text"}`},themeSig(){return`ace/theme/${`${this.theme??"light"}`.replace(/^light$|(^dark$)/i,((e,t)=>"cloud_editor"+(t?"_dark":"")))}`}},watch:{themeSig(e){this.editor.setTheme(e)},modelValue(e){e!==this.editor.getValue()&&this.editor.setValue(e,-1)}},async mounted(){const e=ace.edit(this.$refs.editor,{value:this.modelValue,mode:this.modeSig,theme:this.themeSig});this.editor=e,e.setKeyboardHandler("ace/keyboard/vscode"),e.commands.removeCommand("addLineAfter"),e.commands.removeCommand("addLineBefore"),e.on("change",(()=>this.$emit("update:modelValue",e.getValue()))),e.session.on("changeAnnotation",(()=>{const t=JSON.stringify(this.errorNotes),i=JSON.stringify(this.infoNotes),n=JSON.stringify(this.warningNotes);this.annotations=e.getSession().getAnnotations();const{errorNotes:r,infoNotes:s,warningNotes:o}=this;t!==JSON.stringify(r)&&this.$emit("changeErrorNotes",r),i!==JSON.stringify(s)&&this.$emit("changeInfoNotes",s),n!==JSON.stringify(o)&&this.$emit("changeWarningNotes",o)})),e.textInput.getElement().addEventListener("keydown",(e=>{if(16===e.keyCode||17===e.keyCode||18===e.keyCode||91===e.keyCode||92===e.keyCode||93===e.keyCode)return;(e.metaKey||e.ctrlKey||e.altKey||e.shiftKey&&(e.key??"").length>1)&&this.$emit("keyCombo",e)}))},template:'<div ref="editor" :style="style"></div>'}}function getPrismComponentProps(){return{data:()=>({annotations:[]}),props:["code","isDark","language","lineNumbers","matchBraces"],computed:{html(){},preStyle(){return{filter:!1!==(this.isDark??!1)?"none":"invert(1) hue-rotate(180deg) brightness(1.1)",margin:0,paddingTop:"0.5em",paddingBottom:"0.5em"}}},watch:{code(){this.redraw()},language(){this.redraw()},lineNumbers(){this.redraw()},matchBraces(){this.redraw()}},methods:{async redraw(){const e=this.$refs.pre,t=this.$refs.code;t.textContent=this.code,t.className="language-javascript",e.className="";for(const t of["lineNumbers","matchBraces"])if(null!=this[t]){const i=t.replace(/[A-Z]+/g,"-$&").toLowerCase();e.className+=" "+i}Prism.highlightElement(t)}},async mounted(){await this.redraw()},template:'<pre ref="pre" :style="preStyle"><code ref="code"></code></pre>'}}function getIconComponentProps(){return{props:["name"],computed:{svgCode(){const e={missing:'<svg viewBox="0 0 32 32"><circle cx="16" cy="22.5" r="1.5" fill="currentColor"/><path fill="currentColor" d="M17 19h-2v-4h2c1.103 0 2-.897 2-2s-.897-2-2-2h-2c-1.103 0-2 .897-2 2v.5h-2V13c0-2.206 1.794-4 4-4h2c2.206 0 4 1.794 4 4s-1.794 4-4 4z"/><path fill="currentColor" d="M29.391 14.527L17.473 2.609A2.078 2.078 0 0 0 16 2c-.533 0-1.067.203-1.473.609L2.609 14.527C2.203 14.933 2 15.466 2 16s.203 1.067.609 1.473L14.526 29.39c.407.407.941.61 1.474.61s1.067-.203 1.473-.609L29.39 17.474c.407-.407.61-.94.61-1.474s-.203-1.067-.609-1.473M16 28.036L3.965 16L16 3.964L28.036 16z"/></svg>',trash:'<svg viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M5.5 1a.5.5 0 0 0 0 1h4a.5.5 0 0 0 0-1zM3 3.5a.5.5 0 0 1 .5-.5h8a.5.5 0 0 1 0 1H11v8a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4h-.5a.5.5 0 0 1-.5-.5M5 4h5v8H5z" clip-rule="evenodd"/></svg>',play:'<svg viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M3.242 2.322a.5.5 0 0 1 .491-.014l9 4.75a.5.5 0 0 1 0 .884l-9 4.75A.5.5 0 0 1 3 12.25v-9.5a.5.5 0 0 1 .242-.428M4 3.579v7.842L11.429 7.5z" clip-rule="evenodd"/></svg>',refresh:'<svg viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M1.903 7.297c0 3.044 2.207 5.118 4.686 5.547a.521.521 0 1 1-.178 1.027C3.5 13.367.861 10.913.861 7.297c0-1.537.699-2.745 1.515-3.663c.585-.658 1.254-1.193 1.792-1.602H2.532a.5.5 0 0 1 0-1h3a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-1 0V2.686l-.001.002c-.572.43-1.27.957-1.875 1.638c-.715.804-1.253 1.776-1.253 2.97m11.108.406c0-3.012-2.16-5.073-4.607-5.533a.521.521 0 1 1 .192-1.024c2.874.54 5.457 2.98 5.457 6.557c0 1.537-.699 2.744-1.515 3.663c-.585.658-1.254 1.193-1.792 1.602h1.636a.5.5 0 1 1 0 1h-3a.5.5 0 0 1-.5-.5v-3a.5.5 0 1 1 1 0v1.845h.002c.571-.432 1.27-.958 1.874-1.64c.715-.803 1.253-1.775 1.253-2.97" clip-rule="evenodd"/></svg>',horizontalView:'<svg viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M1.5 2h12a.5.5 0 0 1 .5.5V7H1V2.5a.5.5 0 0 1 .5-.5M1 8v4.5a.5.5 0 0 0 .5.5h12a.5.5 0 0 0 .5-.5V8zM0 2.5A1.5 1.5 0 0 1 1.5 1h12A1.5 1.5 0 0 1 15 2.5v10a1.5 1.5 0 0 1-1.5 1.5h-12A1.5 1.5 0 0 1 0 12.5z" clip-rule="evenodd"/></svg>',verticalView:'<svg viewBox="0 0 15 15"><path fill="currentColor" fill-rule="evenodd" d="M8 2h5.5a.5.5 0 0 1 .5.5v10a.5.5 0 0 1-.5.5H8zM7 2H1.5a.5.5 0 0 0-.5.5v10a.5.5 0 0 0 .5.5H7zm-7 .5A1.5 1.5 0 0 1 1.5 1h12A1.5 1.5 0 0 1 15 2.5v10a1.5 1.5 0 0 1-1.5 1.5h-12A1.5 1.5 0 0 1 0 12.5z" clip-rule="evenodd"/></svg>',play:'<svg viewBox="0 0 16 16"><path fill="currentColor" d="M2 1v14l12-7z"/></svg>',error:'<svg viewBox="0 0 24 24"><path fill="currentColor" d="M11 15h2v2h-2zm0-8h2v6h-2zm1-5C6.47 2 2 6.5 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m0 18a8 8 0 0 1-8-8a8 8 0 0 1 8-8a8 8 0 0 1 8 8a8 8 0 0 1-8 8"/></svg>',clear:'<svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M3.75 12.25l8.5-8.5" stroke="currentColor" stroke-width="1.5"/></svg>',copyToEditor:'<svg viewBox="0 0 16 16"><path d="M6 3.5L2.5 7 6 10.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M3 7h6.5a4 4 0 0 1 4 4v1.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>',spinner:'<svg viewBox="0 0 16 16" class="spin"><path d="M8 1.75a6.25 6.25 0 1 1-6.25 6.25" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round"/></svg>',chevron:'<svg viewBox="0 0 16 16"><path d="M6 4l4 4-4 4" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',consoleError:'<svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="7" fill="currentColor"/><path d="M5.5 5.5l5 5m0-5l-5 5" stroke="#fff" stroke-width="1.75" stroke-linecap="round"/></svg>',consoleWarning:'<svg viewBox="0 0 16 16"><path d="M8 1.75L14.75 14H1.25z" fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M8 6v3.5" stroke="#202124" stroke-width="1.75" stroke-linecap="round"/><circle cx="8" cy="11.75" r="1" fill="#202124"/></svg>'};return(e[this.name]??e.missing).replace("<svg",'$& xmlns="http://www.w3.org/2000/svg" style="height: 1em; width: 1em; display: inline-block; transform: translateY(0.1em);"')}},template:'<span v-html="svgCode"></span>'}}function getJSValueComponentProps(){return{props:["description","path","name","isDimName"],data:()=>({isExpanded:!1,hasBeenExpanded:!1}),watch:{isExpanded(e){e&&!this.hasBeenExpanded&&(this.hasBeenExpanded=e)}},computed:{isPartialDescription(){return void 0===this.description.entries},isExpandable(){return!this.description.isPrimitive},isEntry(){return null!=this.name},classNames(){return["js-value",this.isEntry||this.isExpandable&&this.isExpanded?"d-block":"d-inline-block",this.isExpanded?"expanded":""]},entryGroups:()=>[{key:"entries",isDim:e=>!1===e[2]},{key:"protoEntries",isDim:()=>!0}]},methods:{toggleExpanded(){this.isExpandable&&(this.isExpanded=!this.isExpanded,this.isExpanded&&this.isPartialDescription&&messageParent({target:"runner",func:"sendDescriptionFor",args:[this.path]}))}},template:'\n <div :class="classNames">\n <div :class="[\'js-value-header\', isExpandable ? \'expandable\' : \'\']" @click="toggleExpanded"><span\n v-if="isExpandable || isEntry" :class="[\'arrow\', isExpandable ? \'expandable\' : \'\', isExpanded ? \'expanded\' : \'\']"></span><template\n v-if="isEntry"><span :class="[\'entry-key\', isDimName ? \'dim\' : \'\']">{{ name }}</span>: </template><span\n v-for="part in description.parts" :class="\'t-\' + part[0]">{{ part[1] }}</span></div>\n <div v-if="hasBeenExpanded" v-show="isExpanded" class="expansion">\n <div v-if="isPartialDescription" class="loading">Loading…</div>\n <template v-else>\n <template v-for="group in entryGroups">\n <js-value\n v-for="(entry, entryIndex) in description[group.key]"\n :name="entry[0]"\n :is-dim-name="group.isDim(entry)"\n :description="entry[1]"\n :path="path.concat([group.key, entryIndex])">\n </js-value>\n </template>\n </template>\n </div>\n </div>\n '}}function parseJSCodeGroups(e){return e.split(/\r\n|\r|\n/).reduce(((e,t)=>{const i=e[e.length-1],n=/^\/\/[^]*\\\\$/.test(t),r=n?t.replace(/^\/+\s*|\s*\\+$/g,""):t;return!e.length||n&&!i.canAppendHeader?e.push({headerLines:n?[r]:[],lines:n?[]:[t],allLines:[t],canAppendHeader:n}):n?(i.headerLines.push(r),i.allLines.push(t)):(i.canAppendHeader=!1,i.lines.push(t),i.allLines.push(t)),e}),[]).reduce(((e,t)=>(t.canAppendHeader||(delete t.canAppendHeader,t.headerLines=t.headerLines.join("\n").trimEnd(),t.lines=t.lines.join("\n"),t.allLines=t.allLines.join("\n"),e.push(t)),e)),[])}function parseGroupHeader(e,t){const i=!!t&&e.startsWith(t);return{header:i?e.slice(t.length).replace(/^\s*:?\s*/,""):e,isHidden:i}}function extractHiddenGroups(e,t){const i=[],n=[];for(const r of parseJSCodeGroups(e))parseGroupHeader(r.headerLines,t).isHidden?n.push({...r,runCount:i.length}):i.push(r);return{visibleCode:i.map((e=>e.allLines)).join("\n"),hiddenGroups:n}}function unindentMin(e,t){const i=(t=Object(t)).tabSize??4,n=t.trim??!0;if(e=e.replace(/\t/g," ".repeat(i)),!/(^|[\r\n])\S/.test(e)){const t=/(^|[\r\n])((?:(?!\r|\n)\s)+)(?=(\S)?)/g;let i=1/0;for(let n;n=t.exec(e);)n[3]&&(i=Math.min(i,n[2].length));e=e.replace(t,((e,t,n)=>t+n.slice(i)))}return n?e.replace(/^(\s*[\r\n]+)+|\s+$/g,""):e}function sanitizeDescription(e){return{typeName:`${(e=Object(e)).typeName}`,isPrimitive:!!e.isPrimitive,parts:Array.from(e.parts??[],(e=>[/^[a-z]+$/.test(Object(e)[0])?e[0]:"text",`${Object(e)[1]}`])),...sanitizeEntries(e)}}function sanitizeEntries(e){e=Object(e);const t={};for(const i of["entries","protoEntries"])Array.isArray(e[i])&&(t[i]=e[i].map((e=>[`${Object(e)[0]}`,sanitizeDescription(Object(e)[1]),!1!==Object(e)[2]])));return t}function appendLog({logId:e,key:t,descriptions:i,table:n}){t=`${t}`,mountedApp.displays.push({type:"log",key:t,name:`console.${t}`,descriptions:Array.from(i,sanitizeDescription),table:n?{headers:Array.from(n.headers,(e=>`${e}`)),rows:Array.from(n.rows,(e=>({index:`${e.index}`,cells:Array.from(e.cells,(e=>e&&sanitizeDescription(e)))})))}:null,logId:`${e}`})}function appendError({message:e,line:t,column:i}){mountedApp.displays.push({type:"error",message:`${e}`,line:+t,column:+i})}function updateDescriptionFor(e,t){const i=(e=Array.from(e)).shift(),n=mountedApp.displays.find((e=>"log"===e.type&&e.logId===i));let r=n?.descriptions[toIndex(e.shift())];for(let t=0;r&&t<e.length;t+=2){const i=e[t];if("entries"!==i&&"protoEntries"!==i)return;r=r[i]?.[toIndex(e[t+1])]?.[1]}r&&Object.assign(r,sanitizeEntries(t))}function onCodeRan(){mountedApp.runningCount=Math.max(0,mountedApp.runningCount-1),mountedApp.runHiddenGroups()}function clearDisplays(){mountedApp.displays=[{type:"notice",message:"Console was cleared"}]}function toIndex(e){return Number.isInteger(e)&&e>=0?e:void 0}
|
|
179
|
+
},
|
|
180
|
+
jsUrls: [
|
|
181
|
+
'https://unpkg.com/vue@3/dist/vue.global.prod.js',
|
|
182
|
+
'https://unpkg.com/ace-builds@1/src-noconflict/ace.js',
|
|
183
|
+
'https://unpkg.com/prismjs@1/components/prism-core.min.js',
|
|
184
|
+
'https://unpkg.com/prismjs@1/plugins/autoloader/prism-autoloader.min.js',
|
|
185
|
+
'https://unpkg.com/prismjs@1/plugins/match-braces/prism-match-braces.min.js',
|
|
186
|
+
],
|
|
187
|
+
cssUrls: [
|
|
188
|
+
'data:text/css,' + encodeURIComponent(VIEWER_IFRAME_CSS),
|
|
189
|
+
'https://unpkg.com/prism-themes@1/themes/prism-vsc-dark-plus.min.css',
|
|
190
|
+
'https://unpkg.com/prismjs@1/plugins/match-braces/prism-match-braces.min.css',
|
|
191
|
+
],
|
|
192
|
+
htmlAttributes: 'data-theme="' + theme + '"',
|
|
193
|
+
onMessage(message) {
|
|
194
|
+
relayMessage(message.data, {runner});
|
|
195
|
+
},
|
|
196
|
+
async onReady() {
|
|
197
|
+
this.call('init', script.textContent, dataset);
|
|
198
|
+
},
|
|
199
|
+
body: VIEWER_IFRAME_HTML,
|
|
200
|
+
style: {
|
|
201
|
+
width: '100%',
|
|
202
|
+
height: '100%',
|
|
203
|
+
border: 0,
|
|
204
|
+
display: 'block',
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
script.parentNode.insertBefore(callableViewerFrame.iframe, script);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// NOTE: This solution was intentionally written without using newer JS
|
|
212
|
+
// features to make the minified version even smaller.
|
|
213
|
+
var createCallableFrame = (function () {
|
|
214
|
+
var IFRAME_SCRIPT_MESSAGE_CODE = parseFunction(function() {
|
|
215
|
+
// NOTE: Referencing with window to ensure that local namespace will not
|
|
216
|
+
// interfere.
|
|
217
|
+
window.addEventListener('message', function(e) {
|
|
218
|
+
var data = e.data;
|
|
219
|
+
if (
|
|
220
|
+
e.source === window.parent
|
|
221
|
+
&& data && /^[A-Za-z_$][\w$]*$/.test(data.funcName)
|
|
222
|
+
&& Array.isArray(data.args)
|
|
223
|
+
) {
|
|
224
|
+
var func = eval(data.funcName);
|
|
225
|
+
if ('function' === typeof func) func.apply(e, data.args);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
function messageParent(message) {
|
|
230
|
+
window.parent.postMessage(message, '*');
|
|
231
|
+
}
|
|
232
|
+
}).body;
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Creates an IFRAME which allows for easier 2-way communication.
|
|
236
|
+
* @template {createCallableFrame_Return} R
|
|
237
|
+
* @param {Object} options
|
|
238
|
+
* @param {(string|Function)=} options.jsCode
|
|
239
|
+
* The JavaScript code that will be encapsulated and run within the IFRAME.
|
|
240
|
+
* The code can call `messageParent(message)` to send a message to
|
|
241
|
+
* `options.onMessage()`. The code can call `callParent(funcName, ...args)`
|
|
242
|
+
* to execute `options.functions[funcName](...args)`. The code can call
|
|
243
|
+
* `applyParent(funcName, args)` to execute
|
|
244
|
+
* `options.functions[funcName](...args)`.
|
|
245
|
+
* @param {{[funcName: string]: (this: R, ...args) => void}=} options.functions
|
|
246
|
+
* Functions that can be called by the IFRAME code to execute code on the
|
|
247
|
+
* parent level.
|
|
248
|
+
* @param {string[]=} options.jsUrls
|
|
249
|
+
* @param {string[]=} options.cssUrls
|
|
250
|
+
* @param {string=} options.head
|
|
251
|
+
* @param {string=} options.body
|
|
252
|
+
* @param {(CSSStyleDeclaration|string)=} options.style
|
|
253
|
+
* The HTML code that will be used to instantiate the page.
|
|
254
|
+
* @param {string=} options.htmlAttributes
|
|
255
|
+
* Attributes to add to the `<html>` element of the IFRAME.
|
|
256
|
+
* @param {((this: R, event: MessageEvent) => void)=} options.onMessage
|
|
257
|
+
* @param {((this: R, event: MessageEvent) => void)=} options.onReady
|
|
258
|
+
* @returns {R}
|
|
259
|
+
*/
|
|
260
|
+
function createCallableFrame(options) {
|
|
261
|
+
// Break some of the options out into their own variables.
|
|
262
|
+
var jsCode = options.jsCode;
|
|
263
|
+
var onMessage = options.onMessage;
|
|
264
|
+
var onReady = options.onReady;
|
|
265
|
+
var style = options.style;
|
|
266
|
+
|
|
267
|
+
// isReady indicates if the IFRAME is ready to have messages sent to it
|
|
268
|
+
// while READY_ID is used internally to confirm if the IFRAME is actually
|
|
269
|
+
// ready to receive function calls.
|
|
270
|
+
var isReady, READY_ID = Math.random() + '' + Math.random();
|
|
271
|
+
|
|
272
|
+
// The script code for the IFRAME.
|
|
273
|
+
var IFRAME_SCRIPT_CODE = [
|
|
274
|
+
'(function(){',
|
|
275
|
+
IFRAME_SCRIPT_MESSAGE_CODE,
|
|
276
|
+
'var applyParent, callParent;',
|
|
277
|
+
'(function(READY_ID){',
|
|
278
|
+
parseFunction(function() {
|
|
279
|
+
applyParent = function(funcName, args) {
|
|
280
|
+
window.parent.postMessage({funcName: funcName, args: args, id: READY_ID}, '*');
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
callParent = function(funcName) {
|
|
284
|
+
window.parent.postMessage(
|
|
285
|
+
{funcName: funcName, args: Array.prototype.slice.call(arguments, 1), id: READY_ID},
|
|
286
|
+
'*'
|
|
287
|
+
);
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
var interval = setInterval(function() {
|
|
291
|
+
if (/^(complete|interactive)$/.test(document.readyState)) {
|
|
292
|
+
clearInterval(interval);
|
|
293
|
+
messageParent(READY_ID);
|
|
294
|
+
}
|
|
295
|
+
}, 100);
|
|
296
|
+
}).body,
|
|
297
|
+
'})(' + JSON.stringify(READY_ID) + ');',
|
|
298
|
+
'function' !== typeof jsCode ? jsCode || '' : parseFunction(jsCode).body,
|
|
299
|
+
'})();',
|
|
300
|
+
].join('\n')
|
|
301
|
+
// Prevents the HTML parser from ending the inline script early.
|
|
302
|
+
.replace(/<(?=\/script|!--)/gi, '\\x3C');
|
|
303
|
+
|
|
304
|
+
// Creates the IFRAME and sets its source via srcdoc.
|
|
305
|
+
var IFRAME = document.createElement('iframe');
|
|
306
|
+
var HTML_CODE = [
|
|
307
|
+
'<!DOCTYPE html>',
|
|
308
|
+
'<html ' + (options.htmlAttributes || '') + '>',
|
|
309
|
+
'<head>',
|
|
310
|
+
options.head || '',
|
|
311
|
+
(options.cssUrls || []).map(function(cssUrl) {
|
|
312
|
+
return '<link href="' + cssUrl + '" rel="stylesheet">';
|
|
313
|
+
}).join('\n'),
|
|
314
|
+
'</head>',
|
|
315
|
+
'<body>',
|
|
316
|
+
options.body || '',
|
|
317
|
+
// The scripts are loaded after the body so that the body can be shown
|
|
318
|
+
// while they load.
|
|
319
|
+
(options.jsUrls || []).map(function(jsUrl) {
|
|
320
|
+
return '<script src="' + jsUrl + '"><\x2fscript>';
|
|
321
|
+
}).join('\n'),
|
|
322
|
+
'<script>' + IFRAME_SCRIPT_CODE + '<\x2fscript>',
|
|
323
|
+
'</body>',
|
|
324
|
+
'</html>'
|
|
325
|
+
].join('\n');
|
|
326
|
+
IFRAME.srcdoc = HTML_CODE;
|
|
327
|
+
|
|
328
|
+
// Set the style of the iframe.
|
|
329
|
+
if (style) {
|
|
330
|
+
if ('string' === typeof style) {
|
|
331
|
+
IFRAME.style.cssText = style;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
for (var styleKey in style) {
|
|
335
|
+
if (hasOwn(style, styleKey)) {
|
|
336
|
+
IFRAME.style[styleKey] = style[styleKey];
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Adds an event listener so that messages from the IFRAME will be captured.
|
|
343
|
+
addEventListener('message', function(e) {
|
|
344
|
+
if (e.source === IFRAME.contentWindow) {
|
|
345
|
+
var data = e.data;
|
|
346
|
+
var dataIsReadyId = READY_ID === data;
|
|
347
|
+
if (isReady && !dataIsReadyId) {
|
|
348
|
+
if (options.functions && data.id === READY_ID && hasOwn(options.functions, data.funcName)) {
|
|
349
|
+
options.functions[data.funcName].apply(callableFrame, data.args);
|
|
350
|
+
}
|
|
351
|
+
else if ('function' === typeof onMessage) {
|
|
352
|
+
onMessage.call(callableFrame, e);
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
console.warn('Message sent from callable frame but no listener was set up.', e);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
else if (dataIsReadyId) {
|
|
359
|
+
isReady = true;
|
|
360
|
+
if ('function' === typeof onReady) onReady.call(callableFrame, e);
|
|
361
|
+
// Sends anything that was queued before the IFRAME was ready.
|
|
362
|
+
while (queuedMessages.length) postToFrame(queuedMessages.shift());
|
|
363
|
+
}
|
|
364
|
+
else { // !isReady
|
|
365
|
+
console.warn('Message sent from callable frame prematurely:', e);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Returns an object which makes it possible to call functions and get
|
|
371
|
+
// access to the IFRAME.
|
|
372
|
+
var queuedMessages = [];
|
|
373
|
+
function postToFrame(message) {
|
|
374
|
+
if (isReady) IFRAME.contentWindow.postMessage(message, '*');
|
|
375
|
+
else queuedMessages.push(message);
|
|
376
|
+
}
|
|
377
|
+
var callableFrame = {
|
|
378
|
+
apply: function(funcName, args) {
|
|
379
|
+
postToFrame({funcName: funcName, args: args});
|
|
380
|
+
},
|
|
381
|
+
call: function(funcName) {
|
|
382
|
+
postToFrame({funcName: funcName, args: Array.prototype.slice.call(arguments, 1)});
|
|
383
|
+
},
|
|
384
|
+
iframe: IFRAME
|
|
385
|
+
};
|
|
386
|
+
return callableFrame;
|
|
387
|
+
};
|
|
388
|
+
/**
|
|
389
|
+
* @typedef {Object} createCallableFrame_Return
|
|
390
|
+
* @property {(funcName: string, args: any[]) => void} apply
|
|
391
|
+
* @property {(funcName: string, ...args: any[]) => void} call
|
|
392
|
+
* @property {HTMLIFrameElement} iframe
|
|
393
|
+
*/
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Determines if `obj` has its own property named `prop`.
|
|
397
|
+
* @type {(obj: any, prop: string|symbol) => boolean}
|
|
398
|
+
*/
|
|
399
|
+
var hasOwn = atob.call.bind({}.hasOwnProperty);
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Parses a user-defined function to determine if it is an arrow function, an
|
|
403
|
+
* async function a generator function and also gets the parameters as a string
|
|
404
|
+
* and the function body as a string.
|
|
405
|
+
* @param {Function} input
|
|
406
|
+
* @returns {{
|
|
407
|
+
* isArrow: boolean;
|
|
408
|
+
* isAsync: boolean;
|
|
409
|
+
* isGenerator: boolean;
|
|
410
|
+
* parameters: string;
|
|
411
|
+
* body: string;
|
|
412
|
+
* }}
|
|
413
|
+
*/
|
|
414
|
+
function parseFunction(input) {
|
|
415
|
+
// Get rid of all comments encountered before the function body.
|
|
416
|
+
var strFunc = input + '';
|
|
417
|
+
/** @type {RegExpExecArray?} */
|
|
418
|
+
var m;
|
|
419
|
+
while (m = /\/\*[^]*?\*\/|\/\/.*/g.exec(strFunc)) {
|
|
420
|
+
var STR_BEFORE = strFunc.slice(0, m.index);
|
|
421
|
+
if (/[\{"'`]/.test(STR_BEFORE)) break;
|
|
422
|
+
strFunc = STR_BEFORE + ' ' + strFunc.slice(m.index + m[0].length);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Determines if the function is an async function.
|
|
426
|
+
var IS_ASYNC = /^\s*async\b/.test(strFunc);
|
|
427
|
+
if (IS_ASYNC) strFunc = strFunc.replace('async', '');
|
|
428
|
+
|
|
429
|
+
// Determines if the function is a generator function.
|
|
430
|
+
var IS_GENERATOR = /^\s*(?:function\s*)?\*/.test(strFunc);
|
|
431
|
+
if (IS_GENERATOR) strFunc = strFunc.replace(/(?:\bfunction\s*)?\*/, '');
|
|
432
|
+
|
|
433
|
+
// Determines if the function is an arrow function.
|
|
434
|
+
var ARROW_MATCH = /\s*=>\s*/.exec(strFunc);
|
|
435
|
+
var INDEX_OF_FUNC_START = strFunc.search(/\)\s*\{/);
|
|
436
|
+
var IS_ARROW = ARROW_MATCH && (ARROW_MATCH.index < INDEX_OF_FUNC_START || INDEX_OF_FUNC_START < 0);
|
|
437
|
+
|
|
438
|
+
// Parses out the parameters and body as strings.
|
|
439
|
+
var parameters, body;
|
|
440
|
+
if (IS_ARROW) {
|
|
441
|
+
var STR_BEFORE = strFunc.slice(0, ARROW_MATCH.index);
|
|
442
|
+
var STR_AFTER = strFunc.slice(ARROW_MATCH.index + ARROW_MATCH[0].length);
|
|
443
|
+
parameters = (/\S+(?:\s*,\s*\S+)*/.exec(STR_BEFORE.replace(/[()]/g, ' ')) ?? [])[0];
|
|
444
|
+
body = !/^\{/.test(STR_AFTER)
|
|
445
|
+
? 'return ' + STR_AFTER
|
|
446
|
+
: STR_AFTER.replace(/^\{|\}\s*$/g, '');
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
parameters = (/\(([^\)]*)\)/.exec(strFunc) ?? [])[1];
|
|
450
|
+
body = (/\{([^]*)\}/.exec(strFunc) ?? [])[1];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Returns the parsed function.
|
|
454
|
+
return {
|
|
455
|
+
isAsync: IS_ASYNC,
|
|
456
|
+
isGenerator: IS_GENERATOR,
|
|
457
|
+
isArrow: IS_ARROW,
|
|
458
|
+
parameters,
|
|
459
|
+
body,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Make parseFunction() available.
|
|
464
|
+
createCallableFrame.parseFunction = parseFunction;
|
|
465
|
+
|
|
466
|
+
return createCallableFrame;
|
|
467
|
+
})();
|
|
468
|
+
|
|
469
|
+
main(document.currentScript);
|
|
470
|
+
})();
|