yourjs-box 1.1.2 → 1.3.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/README.md +83 -11
- package/dist/yourjs-box.full.js +1812 -1139
- package/dist/yourjs-box.js +233 -51
- package/dist/yourjs-box.min.js +5 -4
- package/package.json +3 -1
package/dist/yourjs-box.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
/*! yourjs-box v1.
|
|
1
|
+
/*! yourjs-box v1.3.0 | (c) 2023-present Christopher West | MIT License | https://github.com/westc/yourjs-box */
|
|
2
2
|
(() => {
|
|
3
3
|
/**
|
|
4
4
|
* Viewer IFRAME's CSS code
|
|
5
5
|
* @type {string}
|
|
6
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}#bottomNav button.logo-button{margin-left:-4px;padding:0 4px}.about-dialog{display:flex;flex-direction:column;max-height:calc(100% - 24px);padding:0;width:min(620px,calc(100% - 24px))}.about-dialog.is-export{height:min(560px,calc(100% - 24px))}.about-header{align-items:center;border-bottom:1px solid var(--toolbar-border);display:flex;flex:0 0 auto;padding:0 8px 0 12px}.tabs{display:flex;flex-grow:1;gap:4px}.tab{background:0 0;border:0;border-bottom:2px solid transparent;color:var(--muted-text);cursor:pointer;font:inherit;font-weight:600;padding:10px 8px 8px}.tab:hover{color:var(--console-text)}.tab.active{border-bottom-color:var(--accent);color:var(--console-text)}.close-button{align-items:center;background:0 0;border:0;border-radius:4px;color:var(--muted-text);cursor:pointer;display:inline-flex;font-size:16px;height:28px;justify-content:center;width:28px}.close-button:hover{background-color:var(--button-hover);color:var(--console-text)}.close-button:focus-visible,.segmented>button:focus-visible,.tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.about-body{flex:1 1 auto;min-height:0;overflow:auto;padding:16px}.about-title{align-items:center;display:flex;gap:12px}.logo-medium>.logo-mark{border-radius:7px;font-size:16px;height:40px;padding:0 4px 4px 0;width:40px}.logo-medium>.logo-text{display:none}.about-name{font-size:17px;font-weight:700}.about-aka{color:var(--muted-text);font-size:13px;font-weight:400;margin-left:4px}.about-version{color:var(--muted-text);font-size:12px}.about-description{line-height:1.5;margin:12px 0}.about-links{display:flex;flex-wrap:wrap;gap:6px 16px}.about-links a{color:var(--accent);font-weight:600;text-decoration:none}.about-links a:hover{text-decoration:underline}.about-body h3{color:var(--muted-text);font-size:11px;letter-spacing:.05em;margin:18px 0 6px;text-transform:uppercase}.about-details{display:grid;gap:4px 16px;grid-template-columns:max-content 1fr;margin:0}.about-details dt{color:var(--muted-text)}.about-details dd{margin:0}.about-details kbd{background-color:var(--toolbar-bg);border:1px solid var(--toolbar-border);border-bottom-width:2px;border-radius:4px;font-family:inherit;font-size:11px;padding:0 4px}.about-footer{border-top:1px solid var(--toolbar-border);color:var(--muted-text);font-size:12px;margin-top:18px;padding-top:12px}.export-body{display:flex;flex-direction:column;gap:10px}.export-options{display:flex;flex-wrap:wrap;gap:8px;justify-content:space-between}.segmented{border:1px solid var(--toolbar-border);border-radius:6px;display:inline-flex;overflow:hidden}.segmented>button{background:0 0;border:0;color:var(--muted-text);cursor:pointer;font:inherit;font-size:12px;font-weight:600;padding:5px 10px}.segmented>button+button{border-left:1px solid var(--toolbar-border)}.segmented>button:hover{background-color:var(--button-hover)}.segmented>button.active{background-color:var(--accent);color:var(--accent-text)}.export-note{color:var(--muted-text);font-size:12px}.export-preview{border:1px solid var(--toolbar-border);border-radius:6px;flex:1 1 auto;min-height:120px;overflow:hidden}.export-actions{display:flex;gap:8px;justify-content:flex-end}@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}";
|
|
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}#bottomNav button.logo-button{margin-left:-4px;padding:0 4px}.about-dialog{display:flex;flex-direction:column;max-height:calc(100% - 24px);padding:0;width:min(620px,calc(100% - 24px))}.about-dialog.is-export{height:min(560px,calc(100% - 24px))}.about-header{align-items:center;border-bottom:1px solid var(--toolbar-border);display:flex;flex:0 0 auto;padding:0 8px 0 12px}.tabs{display:flex;flex-grow:1;gap:4px}.tab{background:0 0;border:0;border-bottom:2px solid transparent;color:var(--muted-text);cursor:pointer;font:inherit;font-weight:600;padding:10px 8px 8px}.tab:hover{color:var(--console-text)}.tab.active{border-bottom-color:var(--accent);color:var(--console-text)}.close-button{align-items:center;background:0 0;border:0;border-radius:4px;color:var(--muted-text);cursor:pointer;display:inline-flex;font-size:16px;height:28px;justify-content:center;width:28px}.close-button:hover{background-color:var(--button-hover);color:var(--console-text)}.close-button:focus-visible,.segmented>button:focus-visible,.tab:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.about-body{flex:1 1 auto;min-height:0;overflow:auto;padding:16px}.about-title{align-items:center;display:flex;gap:12px}.logo-medium>.logo-mark{border-radius:7px;font-size:16px;height:40px;padding:0 4px 4px 0;width:40px}.logo-medium>.logo-text{display:none}.about-name{font-size:17px;font-weight:700}.about-aka{color:var(--muted-text);font-size:13px;font-weight:400;margin-left:4px}.about-version{color:var(--muted-text);font-size:12px}.about-description{line-height:1.5;margin:12px 0}.about-links{display:flex;flex-wrap:wrap;gap:6px 16px}.about-links a{color:var(--accent);font-weight:600;text-decoration:none}.about-links a:hover{text-decoration:underline}.about-body h3{color:var(--muted-text);font-size:11px;letter-spacing:.05em;margin:18px 0 6px;text-transform:uppercase}.about-details{display:grid;gap:4px 16px;grid-template-columns:max-content 1fr;margin:0}.about-details dt{color:var(--muted-text)}.about-details dd{margin:0}.about-details kbd{background-color:var(--toolbar-bg);border:1px solid var(--toolbar-border);border-bottom-width:2px;border-radius:4px;font-family:inherit;font-size:11px;padding:0 4px}.about-footer{border-top:1px solid var(--toolbar-border);color:var(--muted-text);font-size:12px;margin-top:18px;padding-top:12px}.export-body{display:flex;flex-direction:column;gap:10px}.export-options{display:flex;flex-wrap:wrap;gap:8px;justify-content:space-between}.segmented{border:1px solid var(--toolbar-border);border-radius:6px;display:inline-flex;overflow:hidden}.segmented>button{background:0 0;border:0;color:var(--muted-text);cursor:pointer;font:inherit;font-size:12px;font-weight:600;padding:5px 10px}.segmented>button+button{border-left:1px solid var(--toolbar-border)}.segmented>button:hover{background-color:var(--button-hover)}.segmented>button.active{background-color:var(--accent);color:var(--accent-text)}.export-note{color:var(--muted-text);font-size:12px}.export-preview{border:1px solid var(--toolbar-border);border-radius:6px;flex:1 1 auto;min-height:120px;overflow:hidden}.export-actions{display:flex;gap:8px;justify-content:flex-end}.more-menu{background-color:var(--dialog-bg);border:1px solid var(--toolbar-border);border-radius:8px;bottom:38px;box-shadow:0 8px 28px rgb(0 0 0 / .25);color:var(--console-text);font-size:13px;max-height:calc(100% - 48px);min-width:236px;overflow:auto;padding:4px;position:absolute;right:6px;z-index:800}.menu-item{align-items:center;background:0 0;border:0;border-radius:5px;color:inherit;cursor:pointer;display:flex;font:inherit;gap:8px;padding:6px 8px;text-align:left;width:100%}.menu-item:focus-visible,.menu-item:hover:not(:disabled){background-color:var(--button-hover);outline:0}.menu-item:disabled{cursor:not-allowed;opacity:.4}.menu-icon{color:var(--muted-text);display:inline-flex;font-size:14px;justify-content:center;width:16px}.menu-icon svg{transform:none!important}.menu-row{align-items:center;display:flex;gap:2px;padding:2px 2px 2px 8px}.menu-row-label{flex-grow:1}.menu-step{font-weight:600;justify-content:center;width:auto}.menu-percent{color:var(--muted-text);font-variant-numeric:tabular-nums;font-weight:400;min-width:52px}.menu-separator{background-color:var(--toolbar-border);height:1px;margin:4px 0}.popped-out{align-items:center;background-color:var(--console-bg);color:var(--console-text);display:flex;inset:0;justify-content:center;position:fixed;z-index:950}.popped-out-content{align-items:center;display:flex;flex-direction:column;gap:8px;padding:16px;text-align:center}.popped-out-title{font-size:15px;font-weight:600;margin-top:8px}.popped-out-text{color:var(--muted-text);font-size:13px}@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:calc(12px * var(--text-scale, 1));line-height:calc(16px * var(--text-scale, 1))}.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)}.console-row{padding-left:calc(24px + var(--depth,0) * 16px)}.console-row>.row-icon{left:calc(6px + var(--depth,0) * 16px)}.group-header{cursor:pointer;font-weight:700}.group-header>.row-icon.arrow{top:5px}.log-assert{background-color:var(--error-bg);border-bottom-color:var(--error-border);border-top:1px solid var(--error-border);color:var(--error-text);margin-top:-1px}.log-assert>.row-icon{color:var(--error-icon)}.log-result>.row-icon{color:var(--chevron)}.trace-stack{color:var(--null);padding-left:1ch}.splash-message{color:var(--muted-text);font-size:13px;max-width:280px;text-align:center}.splash-slow{animation:show-after-delay 0s 10s both}@keyframes show-after-delay{from{visibility:hidden}to{visibility:visible}}#splash.failed .splash-progress,#splash.failed .splash-slow,.splash-error{display:none}#splash.failed .splash-error{display:block}.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:calc(12px * var(--text-scale, 1))!important;line-height:calc(16px * var(--text-scale, 1))!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
8
|
/**
|
|
9
9
|
* Information about this package (eg. its version).
|
|
10
10
|
* @type {{name: string, version: string, homepage: string, repoUrl: string, bugsUrl: string}}
|
|
11
11
|
*/
|
|
12
|
-
const PACKAGE_INFO = {"name":"yourjs-box","version":"1.
|
|
12
|
+
const PACKAGE_INFO = {"name":"yourjs-box","version":"1.3.0","homepage":"https://westc.github.io/yourjs-box/","repoUrl":"https://github.com/westc/yourjs-box","bugsUrl":"https://github.com/westc/yourjs-box/issues"};
|
|
13
13
|
/**
|
|
14
14
|
* Viewer IFRAME's HTML code
|
|
15
15
|
* @type {string}
|
|
16
16
|
*/
|
|
17
|
-
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\"><button class=\"logo-button\" title=\"About JS Box\" @click=\"openAbout('about')\"><span class=\"logo\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span></button> <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=\"isAboutOpen\" class=\"dialog-backdrop\" @mousedown.self=\"closeAbout\" @keydown.esc=\"closeAbout\"><div :class=\"['dialog', 'about-dialog', aboutTab === 'html' ? 'is-export' : '']\" role=\"dialog\" aria-modal=\"true\" aria-label=\"About JS Box\"><div class=\"about-header\"><div class=\"tabs\" role=\"tablist\"><button role=\"tab\" :aria-selected=\"aboutTab === 'about'\" :class=\"['tab', aboutTab === 'about' ? 'active' : '']\" @click=\"aboutTab = 'about'\">About</button> <button role=\"tab\" :aria-selected=\"aboutTab === 'html'\" :class=\"['tab', aboutTab === 'html' ? 'active' : '']\" @click=\"aboutTab = 'html'\">Copy as HTML</button></div><button class=\"close-button\" ref=\"aboutCloseButton\" title=\"Close\" @click=\"closeAbout\"><icon name=\"close\"></icon></button></div><div v-if=\"aboutTab === 'about'\" class=\"about-body\"><div class=\"about-title\"><span class=\"logo logo-medium\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span><div><div class=\"about-name\">YourJS Box <span class=\"about-aka\">aka JS Box</span></div><div class=\"about-version\">Version {{ packageInfo.version }}</div></div></div><p class=\"about-description\">An interactive JavaScript console that can be embedded in any web page with a single script tag.</p><div class=\"about-links\"><a :href=\"packageInfo.homepage\" target=\"_blank\" rel=\"noopener\">Website</a> <a :href=\"packageInfo.repoUrl\" target=\"_blank\" rel=\"noopener\">GitHub</a> <a :href=\"packageInfo.repoUrl + '#readme'\" target=\"_blank\" rel=\"noopener\">Documentation</a> <a :href=\"packageInfo.bugsUrl\" target=\"_blank\" rel=\"noopener\">Report an issue</a></div><h3>This console</h3><dl class=\"about-details\"><dt>Code runs in</dt><dd>{{ runnerDescription }}</dd><dt>Theme</dt><dd>{{ themeDescription }}</dd><dt>Layout</dt><dd>{{ layoutDescription }}</dd></dl><h3>Keyboard shortcuts</h3><dl class=\"about-details\"><dt><kbd>{{ modKey }}</kbd> + <kbd>Enter</kbd></dt><dd>Run the next block of code</dd><dt><kbd>Esc</kbd></dt><dd>Close this window</dd></dl><div class=\"about-footer\">MIT License © 2023-present Christopher West · Built with Vue, Ace and Prism</div></div><div v-else class=\"about-body export-body\"><div class=\"export-options\"><div class=\"segmented\" role=\"radiogroup\" aria-label=\"Code\"><button v-for=\"option in exportCodeOptions\" role=\"radio\" :aria-checked=\"exportCode === option.value\" :class=\"exportCode === option.value ? 'active' : ''\" @click=\"exportCode = option.value\">{{ option.label }}</button></div><div class=\"segmented\" role=\"radiogroup\" aria-label=\"Format\"><button v-for=\"option in exportFormatOptions\" role=\"radio\" :aria-checked=\"exportFormat === option.value\" :class=\"exportFormat === option.value ? 'active' : ''\" @click=\"exportFormat = option.value\">{{ option.label }}</button></div></div><div class=\"export-note\">{{ exportNote }}</div><div class=\"export-preview\"><ace-editor :model-value=\"exportHtml\" language=\"html\" :theme=\"theme\" height=\"100%\" :read-only=\"true\"></ace-editor></div><div class=\"export-actions\"><button class=\"dialog-button\" @click=\"downloadExport\">Download page</button> <button class=\"dialog-button primary\" @click=\"copyExport\">{{ copyLabel }}</button></div></div></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>";
|
|
17
|
+
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 class=\"splash-message splash-slow\">Still loading…</div><div class=\"splash-message splash-error\">JS Box couldn’t load. Please check your connection and reload the page.</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' && !isInCollapsedGroup(display)\" :class=\"['console-row', 'log-' + display.key, display.groupId ? 'group-header' : '']\" :style=\"{'--depth': display.groupIds.length}\" :title=\"display.name\" @click=\"onLogRowClick(display, $event)\"><span v-if=\"display.key === 'error' || display.key === 'assert'\" class=\"row-icon\"><icon name=\"consoleError\"></icon></span><span v-else-if=\"display.key === 'warn'\" class=\"row-icon\"><icon name=\"consoleWarning\"></icon></span><span v-else-if=\"display.key === 'result'\" class=\"row-icon\"><icon name=\"result\"></icon></span><span v-else-if=\"display.groupId\" :class=\"['row-icon', 'arrow', 'expandable', display.isCollapsed ? '' : 'expanded']\"></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 v-if=\"display.stack\" class=\"trace-stack\">{{ display.stack }}</div></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\" :font-size=\"Math.round(12 * textScale)\" @key-combo=\"onEditorKeyCombo\" height=\"100%\"></ace-editor></div></div><div id=\"bottomNav\"><div class=\"brand\"><button class=\"logo-button\" title=\"About JS Box\" @click=\"openAbout('about')\"><span class=\"logo\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span></button> <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=\"isMenuOpen\" class=\"more-menu\" role=\"menu\" aria-label=\"More\" ref=\"moreMenu\" @keydown=\"onMenuKeyDown\"><div class=\"menu-row\" role=\"group\" aria-label=\"Text size\"><span class=\"menu-row-label\">Text size</span> <button class=\"menu-item menu-step\" role=\"menuitem\" title=\"Smaller text\" aria-label=\"Smaller text\" :disabled=\"!canShrinkText\" @click=\"changeTextScale(-1)\">A−</button> <button class=\"menu-item menu-step menu-percent\" role=\"menuitem\" title=\"Reset the text size\" aria-label=\"Reset the text size\" @click=\"setTextScale(1)\">{{ Math.round(textScale * 100) }}%</button> <button class=\"menu-item menu-step\" role=\"menuitem\" title=\"Bigger text\" aria-label=\"Bigger text\" :disabled=\"!canGrowText\" @click=\"changeTextScale(1)\">A+</button></div><div class=\"menu-separator\"></div><button class=\"menu-item\" role=\"menuitemradio\" :aria-checked=\"dividerOrient === 'vertical'\" @click=\"setDividerOrient('vertical'); closeMenu(true)\"><span class=\"menu-icon\"><icon v-if=\"dividerOrient === 'vertical'\" name=\"check\"></icon></span>Editor beside the console</button> <button class=\"menu-item\" role=\"menuitemradio\" :aria-checked=\"dividerOrient === 'horizontal'\" @click=\"setDividerOrient('horizontal'); closeMenu(true)\"><span class=\"menu-icon\"><icon v-if=\"dividerOrient === 'horizontal'\" name=\"check\"></icon></span>Editor below the console</button><div class=\"menu-separator\"></div><button v-if=\"isPopOut\" class=\"menu-item\" role=\"menuitem\" @click=\"closeMenu(); bringBack()\"><span class=\"menu-icon\"><icon name=\"popIn\"></icon></span>Bring back into the page</button> <button v-else class=\"menu-item\" role=\"menuitem\" @click=\"popOut()\"><span class=\"menu-icon\"><icon name=\"popOut\"></icon></span>Pop out into a window</button> <button class=\"menu-item\" role=\"menuitem\" @click=\"closeMenu(); openAbout('html')\"><span class=\"menu-icon\"><icon name=\"code\"></icon></span>Copy as HTML…</button> <button class=\"menu-item\" role=\"menuitem\" @click=\"closeMenu(); resetConsole()\"><span class=\"menu-icon\"><icon name=\"refresh\"></icon></span>Reset…</button><div class=\"menu-separator\"></div><button class=\"menu-item\" role=\"menuitem\" @click=\"closeMenu(); openAbout('about')\"><span class=\"menu-icon\"><icon name=\"info\"></icon></span>About JS Box</button></div><div v-if=\"isPoppedOut\" class=\"popped-out\"><div class=\"popped-out-content\"><span class=\"logo logo-medium\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span><div class=\"popped-out-title\">This console is open in a separate window</div><div class=\"popped-out-text\">Its code still runs in this page.</div><div class=\"dialog-buttons\"><button class=\"dialog-button\" @click=\"focusPopOut\">Show the window</button> <button class=\"dialog-button primary\" @click=\"requestPopIn\">Bring it back</button></div></div></div><div v-if=\"isAboutOpen\" class=\"dialog-backdrop\" @mousedown.self=\"closeAbout\" @keydown.esc=\"closeAbout\"><div :class=\"['dialog', 'about-dialog', aboutTab === 'html' ? 'is-export' : '']\" role=\"dialog\" aria-modal=\"true\" aria-label=\"About JS Box\"><div class=\"about-header\"><div class=\"tabs\" role=\"tablist\"><button role=\"tab\" :aria-selected=\"aboutTab === 'about'\" :class=\"['tab', aboutTab === 'about' ? 'active' : '']\" @click=\"aboutTab = 'about'\">About</button> <button role=\"tab\" :aria-selected=\"aboutTab === 'html'\" :class=\"['tab', aboutTab === 'html' ? 'active' : '']\" @click=\"aboutTab = 'html'\">Copy as HTML</button></div><button class=\"close-button\" ref=\"aboutCloseButton\" title=\"Close\" @click=\"closeAbout\"><icon name=\"close\"></icon></button></div><div v-if=\"aboutTab === 'about'\" class=\"about-body\"><div class=\"about-title\"><span class=\"logo logo-medium\"><span class=\"logo-mark\">JS</span><span class=\"logo-text\">Box</span></span><div><div class=\"about-name\">YourJS Box <span class=\"about-aka\">aka JS Box</span></div><div class=\"about-version\">Version {{ packageInfo.version }}</div></div></div><p class=\"about-description\">An interactive JavaScript console that can be embedded in any web page with a single script tag.</p><div class=\"about-links\"><a :href=\"packageInfo.homepage\" target=\"_blank\" rel=\"noopener\">Website</a> <a :href=\"packageInfo.repoUrl\" target=\"_blank\" rel=\"noopener\">GitHub</a> <a :href=\"packageInfo.repoUrl + '#readme'\" target=\"_blank\" rel=\"noopener\">Documentation</a> <a :href=\"packageInfo.bugsUrl\" target=\"_blank\" rel=\"noopener\">Report an issue</a></div><h3>This console</h3><dl class=\"about-details\"><dt>Code runs in</dt><dd>{{ runnerDescription }}</dd><dt>Theme</dt><dd>{{ themeDescription }}</dd><dt>Code blocks run as</dt><dd>{{ blockTypeDescription }}</dd><dt>Results</dt><dd>{{ showResults ? 'The value of the last expression is shown' : 'Not shown' }}</dd><dt>Layout</dt><dd>{{ layoutDescription }}</dd></dl><h3>Keyboard shortcuts</h3><dl class=\"about-details\"><dt><kbd>{{ modKey }}</kbd> + <kbd>Enter</kbd></dt><dd>Run the next block of code</dd><dt><kbd>Esc</kbd></dt><dd>Close this window, the menu or full screen</dd></dl><div class=\"about-footer\">MIT License © 2023-present Christopher West · Built with Vue {{ libraryVersions.vue }}, Ace {{ libraryVersions['ace-builds'] }}, Prism {{ libraryVersions.prismjs }} and Acorn {{ libraryVersions.acorn }}</div></div><div v-else class=\"about-body export-body\"><div class=\"export-options\"><div class=\"segmented\" role=\"radiogroup\" aria-label=\"Code\"><button v-for=\"option in exportCodeOptions\" role=\"radio\" :aria-checked=\"exportCode === option.value\" :class=\"exportCode === option.value ? 'active' : ''\" @click=\"exportCode = option.value\">{{ option.label }}</button></div><div class=\"segmented\" role=\"radiogroup\" aria-label=\"Format\"><button v-for=\"option in exportFormatOptions\" role=\"radio\" :aria-checked=\"exportFormat === option.value\" :class=\"exportFormat === option.value ? 'active' : ''\" @click=\"exportFormat = option.value\">{{ option.label }}</button></div></div><div class=\"export-note\">{{ exportNote }}</div><div class=\"export-preview\"><ace-editor :model-value=\"exportHtml\" language=\"html\" :theme=\"theme\" height=\"100%\" :read-only=\"true\"></ace-editor></div><div class=\"export-actions\"><button class=\"dialog-button\" @click=\"downloadExport\">Download page</button> <button class=\"dialog-button primary\" @click=\"copyExport\">{{ copyLabel }}</button></div></div></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>";
|
|
18
18
|
/**
|
|
19
19
|
* The URL of this script which is used to remove this script's lines from the
|
|
20
20
|
* stack traces of errors in window mode.
|
|
@@ -28,6 +28,40 @@
|
|
|
28
28
|
*/
|
|
29
29
|
const ORIGINAL_CONSOLE = window[Symbol.for('yourjs-box.originalConsole')] ??= {...console};
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* The libraries that the viewer loads along with the exact versions that it
|
|
33
|
+
* was tested with.
|
|
34
|
+
*/
|
|
35
|
+
const LIBRARY_VERSIONS = {
|
|
36
|
+
'ace-builds': '1.44.0',
|
|
37
|
+
'acorn': '8.18.0',
|
|
38
|
+
'prism-themes': '1.9.0',
|
|
39
|
+
'prismjs': '1.30.0',
|
|
40
|
+
'vue': '3.5.43',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Where the libraries are loaded from unless data-libraries-url is given.
|
|
45
|
+
* `{name}` and `{version}` are replaced with each library's name and version.
|
|
46
|
+
*/
|
|
47
|
+
const DEFAULT_LIBRARIES_URL = 'https://unpkg.com/{name}@{version}/';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} librariesUrl
|
|
51
|
+
* The URL template (see DEFAULT_LIBRARIES_URL). Relative URLs are relative
|
|
52
|
+
* to the page.
|
|
53
|
+
* @param {keyof LIBRARY_VERSIONS} name
|
|
54
|
+
* @param {string} path
|
|
55
|
+
* The path of the file within the library's package.
|
|
56
|
+
* @returns {string}
|
|
57
|
+
*/
|
|
58
|
+
function getLibraryFileUrl(librariesUrl, name, path) {
|
|
59
|
+
const baseUrl = librariesUrl
|
|
60
|
+
.replace(/\{(name|version)\}/g, (_, key) => key === 'name' ? name : LIBRARY_VERSIONS[name])
|
|
61
|
+
.replace(/\/?$/, '/');
|
|
62
|
+
return new URL(baseUrl + path, document.baseURI).href;
|
|
63
|
+
}
|
|
64
|
+
|
|
31
65
|
/**
|
|
32
66
|
* The only functions that may be relayed to the viewer and to the runner.
|
|
33
67
|
* The runner executes the user's code so anything it sends must be limited
|
|
@@ -36,6 +70,8 @@
|
|
|
36
70
|
const RELAYABLE_FUNCS = {
|
|
37
71
|
viewer: ['appendLog', 'appendError', 'clearDisplays', 'onCodeRan', 'updateDescriptionFor'],
|
|
38
72
|
runner: ['clearLogs', 'reset', 'runCode', 'sendDescriptionFor'],
|
|
73
|
+
// Things that the viewer (but never the runner) can ask this script to do.
|
|
74
|
+
host: ['focusPopOut', 'popIn', 'popOut', 'requestPopIn', 'setMaximized'],
|
|
39
75
|
};
|
|
40
76
|
|
|
41
77
|
/**
|
|
@@ -55,7 +91,7 @@
|
|
|
55
91
|
}
|
|
56
92
|
}
|
|
57
93
|
|
|
58
|
-
function createRunner(e,t){const{mode:n}=t,r=t.ownUrl||("worker"===n?self.location.href:""),o={};let s=0,
|
|
94
|
+
function createRunner(e,t){const{mode:n}=t,r=t.ownUrl||("worker"===n?self.location.href:""),o={};let s=0,i=0,c=!0,a;const l={},u={},f={};let p=[],d=0;const g=`yourjs-box.result.${Math.random().toString(36).slice(2)}`;globalThis[Symbol.for(g)]=y;for(const e of["assert","clear","count","countReset","debug","dir","dirxml","error","group","groupCollapsed","groupEnd","info","log","table","time","timeEnd","timeLog","trace","warn"]){const t=console[e];"function"==typeof t&&(console[e]=function(...n){return h(e,n),t.apply(this,arguments)})}function h(t,n){const r=n.length&&void 0!==n[0]?`${n[0]}`:"default";if("clear"===t)U(),p=[],e({target:"viewer",func:"clearDisplays",args:[!0]});else if("assert"===t){if(n[0])return;const e=n.slice(1);m("assert",e.length?"string"==typeof e[0]?["Assertion failed: "+e[0],...e.slice(1)]:["Assertion failed:",...e]:["Assertion failed: console.assert"])}else if("count"===t)u[r]=(u[r]||0)+1,m("count",[`${r}: ${u[r]}`]);else if("countReset"===t)Object.hasOwn(u,r)?u[r]=0:m("warn",[`Count for '${r}' does not exist`]);else if("time"===t)Object.hasOwn(f,r)?m("warn",[`Timer '${r}' already exists`]):f[r]=performance.now();else if("timeLog"===t||"timeEnd"===t){if(!Object.hasOwn(f,r))return void m("warn",[`Timer '${r}' does not exist`]);const e=performance.now()-f[r];"timeEnd"===t&&delete f[r],m(t,[`${r}: ${+e.toFixed(3)} ms`,..."timeLog"===t?n.slice(1):[]])}else if("trace"===t){const e=$((new Error).stack??"").split("\n").slice(1).join("\n");m("trace",n.length?n:["console.trace"],{stack:e})}else if("group"===t||"groupCollapsed"===t){const e=""+ ++d;m(t,n.length?n:["console.group"],{groupId:e,isCollapsed:"groupCollapsed"===t}),p.push(e)}else if("groupEnd"===t)p.pop();else{const e="table"===t?x(n[0],n[1]):null;m(t,e?n.slice(0,1):n,{table:e})}}function m(t,n,r,i=0){const c=""+ ++s;o[c]=n.map((e=>({...N(e,i),value:e}))),e({target:"viewer",func:"appendLog",args:[{logId:c,key:t,descriptions:o[c].map(v(["value"])),groupIds:p.slice(),...r}]})}function y(e){void 0!==e&&m("result",[e],{},"string"==typeof e?1:0)}function b(t,n){const r="classic"===a&&"SyntaxError"===t?.name&&/\bawait\b/.test(t.message)?'\n(To use top-level await, add data-block-type="module" to the script tag.)':"";e({target:"viewer",func:"appendError",args:[{message:(n?"Uncaught (in promise) ":"Uncaught ")+$(t?.stack??`${t?.message??t}`)+r}]})}function $(e){return`${e}`.split("\n").filter((e=>!(r&&/^\s*at\b/.test(e)&&e.includes(r)))).join("\n").replace(/(snippet-\d+\.js):(\d+):(\d+)/g,((e,t,n,r)=>{const o=l[t];return o&&+n===o.line&&+r>o.column?`${t}:${n}:${Math.max(o.column,r-o.shift)}`:e}))}function v(e,t){if(!t)return t=>v(e,t);const n={...t};for(const t of e)delete n[t];return n}function x(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})),i=Array.isArray(t)?t.map((e=>`${e}`)):r,c=o&&!Array.isArray(t);return{headers:["(index)",...i,...c?[n]:[]],rows:s.map((({index:e,values:t,hasValue:n,value:r})=>({index:e,cells:[...i.map((e=>Object.hasOwn(t,e)?N(t[e],2):null)),...c?[n?N(r,2):null]:[]]})))}}function j(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 O(e,t){try{const t=Object.getPrototypeOf(e)?.constructor?.name;if(t&&"string"==typeof t)return t}catch(e){}return t}addEventListener("error",(e=>{b(void 0!==e.error?e.error:e.message)})),addEventListener("unhandledrejection",(e=>{b(e.reason,!0)}));const w=5,E=100;function N(e,t=0,n=!1){const r=j(e),o="function"!==r&&r===r.toLowerCase(),s=undefined;return{typeName:r,isPrimitive:o,parts:n?[["text","Object"!==r?r:O(e,r)]]:k(e,t,r)}}function k(e,t,n=j(e)){if("string"===n)return t?(t>1&&e.length>100&&(e=e.slice(0,99)+"\u2026"),[["string",A(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 S(e,t);try{return L(e,t,n)}catch(e){return[["text",n]]}}function A(e){const t=JSON.stringify(e);return e.includes("'")?t:`'${t.slice(1,-1).replace(/\\"/g,'"')}'`}function S(e,t){let n="";try{n=Function.prototype.toString.call(e)}catch(e){}const r=/^class\b/.test(n);return t?t>1?[["function","\u0192"]]:[["function",r?`class ${e.name}`:`\u0192 ${e.name}()`]]:[["text",r?n:n.replace(/^(async\s+)?function\b\s*/,"$1\u0192 ")]]}function L(e,t,n){const r=O(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("Promise"===n)return[["text",`${r} {\u2026}`]];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",A(e.data)]]:[["node",e.nodeName]];if(e instanceof Error)return[["text",t?`${e.name}: ${e.message}`:$(e.stack??`${e}`)]];if(t>1)return o?[["text",`${r}(${e.length})`]]:"Map"===n||"Set"===n?[["text",`${r}(${e.size})`]]:[["text","Object"===r?"{\u2026}":r]];const s=[],i=(e,t,n)=>{e.forEach(((e,t)=>{t&&s.push(["text",", "]),n(e)})),t>e.length&&s.push(["text",(e.length?", ":"")+"\u2026"])};if(o){const t=e.length;s.push(["text","Array"===n&&"Array"===r?`(${t}) [`:`${r}(${t}) [`]),i(Array.from({length:Math.min(t,E)},((e,t)=>t)),t,(t=>s.push(...t in e?k(e[t],2):[["null","empty"]]))),s.push(["text","]"])}else if("Map"===n||"Set"===n){const t=[...e].slice(0,E);s.push(["text",`${r}(${e.size}) {`]),i(t,e.size,(e=>{"Map"===n?s.push(...k(e[0],2),["text"," => "],...k(e[1],2)):s.push(...k(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","{"],...k(e.valueOf(),2),["text","}"]):(s.push(["text","{"]),i(t.slice(0,w),t.length,(t=>{const n=Object.getOwnPropertyDescriptor(e,t);s.push(["key",t],["text",": "],..."value"in n?k(n.value,2):[["text","(\u2026)"]])})),s.push(["text","}"]))}return s}function R(e,t=e){const n=j(e),r=[],o=[],s=[],i=[];if(null!==e&&("object"==typeof e||"function"==typeof e)){let c=!1;try{if("Map"===n){for(const[t,n]of[...e])r.push([k(t,2).map((e=>e[1])).join(""),N(n,1)]),o.push({value:n});c=!0}else if("Array"!==n&&"function"!==n&&"function"==typeof e[Symbol.iterator]){let t=0;for(const n of e)r.push([""+t,N(n,1)]),o.push({value:n}),++t;c=!0}}catch(e){r.length=o.length=0}if(!c)for(const n of Object.getOwnPropertyNames(e))try{const s=Object.getOwnPropertyDescriptor(e,n),i="value"in s?s.value:Reflect.get(e,n,t);r.push([n,N(i,1),s.enumerable]),o.push({value:i})}catch(e){}const a=Object.getPrototypeOf(e);null!==a&&(s.push(["[[Prototype]]",N(a,1,!0)]),i.push({value:a,receiver:t}))}return{entries:r,protoEntries:s,$entries:o,$protoEntries:i}}async function C(t,r){const{blockType:o,resultRange:s}=Object(r),c=`snippet-${++i}.js`;if(Array.isArray(s)){const[e,n]=s,r=`globalThis[Symbol.for(${JSON.stringify(g)})]((`,o=t.slice(0,e).split("\n");l[c]={line:o.length,column:o[o.length-1].length+1,shift:r.length},t=t.slice(0,e)+r+t.slice(e,n)+"))"+t.slice(n)}const u=`${t}\n//# sourceURL=${c}`;a="module"===o?"module":"classic";try{if("module"===o)await import("data:text/javascript;charset=utf-8,"+encodeURIComponent(u));else if("worker"===n)T(u);else{const e=document.createElement("script");e.textContent=u,document.head.appendChild(e),e.remove()}}catch(e){b(e)}a=void 0,e({target:"viewer",func:"onCodeRan",args:[]})}function T(e){if(c)try{return void importScripts("data:text/javascript;charset=utf-8,"+encodeURIComponent(e))}catch(e){if("NetworkError"!==e?.name)throw e;c=!1}const t=URL.createObjectURL(new Blob([e],{type:"text/javascript"}));try{importScripts(t)}finally{URL.revokeObjectURL(t)}}function D(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=R(n.value,"receiver"in n?n.receiver:n.value);Object.assign(n,s),e({target:"viewer",func:"updateDescriptionFor",args:[t,v(["$entries","$protoEntries"],s)]})}function U(){for(const e of Object.keys(o))delete o[e];p=[]}return{clearLogs:U,runCode:C,sendDescriptionFor:D}}
|
|
59
95
|
|
|
60
96
|
/**
|
|
61
97
|
* Runs the user's code in a Web Worker. The worker can be terminated which
|
|
@@ -164,10 +200,21 @@
|
|
|
164
200
|
function main(script) {
|
|
165
201
|
const dataset = JSON.parse(JSON.stringify(script.dataset));
|
|
166
202
|
const runnerMode = dataset.runner === 'window' ? 'window' : 'worker';
|
|
203
|
+
const blockType = dataset.blockType === 'module' ? 'module' : 'classic';
|
|
204
|
+
const showResults = dataset.showResults !== 'false';
|
|
205
|
+
const libraryUrl = getLibraryFileUrl.bind(null, dataset.librariesUrl || DEFAULT_LIBRARIES_URL);
|
|
167
206
|
|
|
207
|
+
// The viewer in the page and, when the console is popped out, the viewer
|
|
208
|
+
// in the pop-out window. Messages from the runner go to the active one.
|
|
168
209
|
/** @type {ReturnType<createCallableFrame>} */
|
|
169
|
-
let
|
|
170
|
-
|
|
210
|
+
let inlineViewer;
|
|
211
|
+
/** @type {ReturnType<createCallableFrame>?} */
|
|
212
|
+
let popOutViewer = null;
|
|
213
|
+
/** @type {Window?} */
|
|
214
|
+
let popOutWindow = null;
|
|
215
|
+
let popOutWatcher;
|
|
216
|
+
let activeViewer;
|
|
217
|
+
const sendToViewer = data => relayMessage(data, {viewer: activeViewer});
|
|
171
218
|
const runner = runnerMode === 'window'
|
|
172
219
|
? createWindowRunner(sendToViewer)
|
|
173
220
|
: createWorkerRunner(sendToViewer);
|
|
@@ -177,53 +224,163 @@
|
|
|
177
224
|
? dataset.theme
|
|
178
225
|
: matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
179
226
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
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,i){const n=t.hidePrefix??"",r=matchMedia("(prefers-color-scheme: dark)"),s=unindentMin(e),{visibleCode:o,hiddenGroups:a}=extractHiddenGroups(s,n),l=/Mac|iPhone|iPad/.test(navigator.platform),d=()=>a.map((e=>({...e})));mountedApp=Vue.createApp({data:()=>({displays:[],hidePrefix:n,hiddenGroups:d(),runnerMode:i.runnerMode,packageInfo:i.packageInfo,runHistory:[],isAboutOpen:!1,aboutTab:"about",exportCode:"current",exportFormat:"snippet",copyLabel:"Copy",runningCount:0,dialog:null,isDisplaysScrolledToBottom:!0,forcedTheme:/^(light|dark)$/.test(t.theme)?t.theme:null,prefersDark:r.matches,runCount:0,jsCode:o,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")},modKey:()=>l?"⌘":"Ctrl",runnerDescription(){return"window"===this.runnerMode?"This page (it can use the page's globals and DOM)":"A Web Worker (isolated from the page, no DOM)"},themeDescription(){return`${"dark"===this.theme?"Dark":"Light"} (${this.forcedTheme?"set by data-theme":"follows your system"})`},layoutDescription(){return("vertical"===this.dividerOrient?"Editor beside the console":"Editor below the console")+(this.isDividerOrientAuto?" (automatic)":"")},exportCodeOptions:()=>[{value:"current",label:"Current code",note:"The code that already ran followed by the code in the editor."},{value:"original",label:"Original code",note:"The code this console started with."},{value:"blank",label:"Blank",note:"An empty console with the same settings."}],exportFormatOptions:()=>[{value:"snippet",label:"Embed snippet"},{value:"page",label:"Full page"}],exportNote(){return this.exportCodeOptions.find((e=>e.value===this.exportCode)).note},exportJsCode(){if("original"===this.exportCode)return s;if("blank"===this.exportCode)return"";const e=this.runHistory.slice(),t=this.hiddenGroups.slice();let i=this.runCount;const n=()=>{for(;t.length&&t[0].runCount<=i;)e.push(t.shift().allLines)};n();for(const t of parseJSCodeGroups(this.jsCode))t.allLines.trim()&&(e.push(t.allLines),i++,n());return e.push(...t.map((e=>e.allLines))),joinCodeBlocks(e)},exportHtml(){return buildConsoleHtml({code:this.exportJsCode,dataset:t,packageInfo:this.packageInfo,isFullPage:"page"===this.exportFormat})},bottomButtons(){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 (${l?"⌘":"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++,this.runHistory.push(e.allLines),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=o,this.hiddenGroups=d(),this.runCount=0,this.runningCount=0,this.runHistory=[],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()))}))},openAbout(e){this.aboutTab=e,this.isAboutOpen=!0,this.$nextTick((()=>this.$refs.aboutCloseButton?.focus()))},closeAbout(){this.isAboutOpen=!1},async copyExport(){const e=this.exportHtml;try{await navigator.clipboard.writeText(e)}catch(t){const i=Object.assign(document.createElement("textarea"),{value:e});document.body.appendChild(i),i.select(),document.execCommand("copy"),i.remove()}this.copyLabel="Copied!",clearTimeout(this.copyLabelTimeout),this.copyLabelTimeout=setTimeout((()=>this.copyLabel="Copy"),1600)},downloadExport(){const e=buildConsoleHtml({code:this.exportJsCode,dataset:t,packageInfo:this.packageInfo,isFullPage:!0}),i=URL.createObjectURL(new Blob([e],{type:"text/html"}));Object.assign(document.createElement("a"),{href:i,download:"js-box.html"}).click(),setTimeout((()=>URL.revokeObjectURL(i)),1e3)},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),r.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","readOnly","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,this.readOnly&&(e.setReadOnly(!0),e.setHighlightActiveLine(!1),e.session.setUseWorker(!1)),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>',close:'<svg viewBox="0 0 16 16"><path d="M4 4l8 8m0-8l-8 8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></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 joinCodeBlocks(e){return e.map((e=>e.replace(/^(\s*[\r\n])+|\s+$/g,""))).filter(Boolean).map(((e,t)=>{const i=e.split(/\r\n|\r|\n/)[0];return t&&!/^\/\/[^]*\\\\$/.test(i)?`// \\\\\n${e}`:e})).join("\n\n")}function buildConsoleHtml({code:e,dataset:t,packageInfo:i,isFullPage:n}){const{name:r,version:s}=i,o=`https://cdn.jsdelivr.net/npm/${r}@${s.split(".")[0]}/dist/${r}.min.js`,a=Object.entries(t).map((([e,t])=>` data-${e.replace(/[A-Z]/g,(e=>"-"+e.toLowerCase()))}="${escapeHtmlAttribute(t)}"`)).join(""),l=n?" ":" ",d=e.replace(/<\/(script)/gi,"<\\/$1").replace(/<!--/g,"<\\!--"),c=(d.trim()?[`<script src="${o}"${a}>`,...d.split("\n").map((e=>e.trim()?" "+e:"")),"<\/script>"]:[`<script src="${o}"${a}><\/script>`]).map((e=>e?l+e:e)).join("\n");return n?["<!DOCTYPE html>",'<html lang="en">'," <head>",' <meta charset="utf-8">',' <meta name="viewport" content="width=device-width, initial-scale=1">'," <title>JS Box</title>"," <style>"," html, body { height: 100%; margin: 0; }"," </style>"," </head>"," <body>",c," </body>","</html>",""].join("\n"):`<div style="height: 400px;">\n${c}\n</div>\n`}function escapeHtmlAttribute(e){return`${e}`.replace(/[&"<>]/g,(e=>`&#${e.charCodeAt(0)};`))}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}
|
|
227
|
+
// What the viewer can ask this script to do (see RELAYABLE_FUNCS.host).
|
|
228
|
+
const hostFuncs = {
|
|
229
|
+
/**
|
|
230
|
+
* Makes the IFRAME fill the window (used if full screen isn't allowed).
|
|
231
|
+
* @param {boolean} isMaximized
|
|
232
|
+
*/
|
|
233
|
+
setMaximized(isMaximized) {
|
|
234
|
+
const {style} = inlineViewer.iframe;
|
|
235
|
+
if (isMaximized) {
|
|
236
|
+
inlineViewer.normalCssText ??= style.cssText;
|
|
237
|
+
Object.assign(style, {position: 'fixed', inset: '0', width: '100%', height: '100%', zIndex: '2147483647'});
|
|
238
|
+
}
|
|
239
|
+
else if (inlineViewer.normalCssText != null) {
|
|
240
|
+
style.cssText = inlineViewer.normalCssText;
|
|
241
|
+
inlineViewer.normalCssText = null;
|
|
242
|
+
}
|
|
183
243
|
},
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Opens the console in a separate window. The code still runs in this
|
|
246
|
+
* page.
|
|
247
|
+
* @param {*} state
|
|
248
|
+
* The state of the viewer (eg. what was logged) to show in the window.
|
|
249
|
+
*/
|
|
250
|
+
popOut(state) {
|
|
251
|
+
if (popOutWindow) {
|
|
252
|
+
popOutWindow.focus();
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
const rect = inlineViewer.iframe.getBoundingClientRect();
|
|
256
|
+
const width = Math.max(820, Math.round(rect.width));
|
|
257
|
+
const height = Math.max(560, Math.round(rect.height));
|
|
258
|
+
const newWindow = open('', '_blank', `popup,width=${width},height=${height}`);
|
|
259
|
+
if (!newWindow) {
|
|
260
|
+
inlineViewer.call('onPopOutFailed');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
popOutWindow = newWindow;
|
|
264
|
+
popOutViewer = createViewer(newWindow, state);
|
|
265
|
+
// Lets the window send its state back even while it is closing (a
|
|
266
|
+
// message sent then can't be traced back to the window). The name
|
|
267
|
+
// must match POP_IN_FUNCTION_NAME in the viewer.
|
|
268
|
+
newWindow.yourjsBoxPopIn = state => newWindow === popOutWindow && hostFuncs.popIn(state);
|
|
269
|
+
activeViewer = popOutViewer;
|
|
270
|
+
inlineViewer.call('setPoppedOut', true);
|
|
271
|
+
|
|
272
|
+
// If the window is closed without sending its state (which it does
|
|
273
|
+
// when it is closed) bring the console back as it was.
|
|
274
|
+
popOutWatcher = setInterval(() => {
|
|
275
|
+
if (newWindow.closed) setTimeout(() => newWindow === popOutWindow && hostFuncs.popIn(null), 300);
|
|
276
|
+
}, 500);
|
|
277
|
+
},
|
|
278
|
+
/**
|
|
279
|
+
* Brings the console back into the page.
|
|
280
|
+
* @param {*} state
|
|
281
|
+
* The state of the pop-out window's viewer or `null` to keep the state
|
|
282
|
+
* from when the console was popped out.
|
|
283
|
+
*/
|
|
284
|
+
popIn(state) {
|
|
285
|
+
const oldWindow = popOutWindow;
|
|
286
|
+
if (!oldWindow) return;
|
|
287
|
+
clearInterval(popOutWatcher);
|
|
288
|
+
popOutWindow = null;
|
|
289
|
+
popOutViewer.dispose();
|
|
290
|
+
popOutViewer = null;
|
|
291
|
+
activeViewer = inlineViewer;
|
|
292
|
+
inlineViewer.call('setPoppedOut', false, state);
|
|
293
|
+
if (!oldWindow.closed) oldWindow.close();
|
|
199
294
|
},
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
this.call('init', script.textContent, dataset, {runnerMode, packageInfo: PACKAGE_INFO});
|
|
295
|
+
/** Asks the pop-out window to send its state back to the page. */
|
|
296
|
+
requestPopIn() {
|
|
297
|
+
if (popOutViewer) popOutViewer.call('popIn');
|
|
204
298
|
},
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
width: '100%',
|
|
208
|
-
height: '100%',
|
|
209
|
-
border: 0,
|
|
210
|
-
display: 'block',
|
|
299
|
+
focusPopOut() {
|
|
300
|
+
popOutWindow?.focus();
|
|
211
301
|
},
|
|
212
|
-
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// The pop-out window can't work without this page so it is closed too.
|
|
305
|
+
addEventListener('pagehide', () => popOutWindow?.close());
|
|
213
306
|
|
|
214
|
-
|
|
307
|
+
/**
|
|
308
|
+
* @param {Window=} targetWindow
|
|
309
|
+
* If given the viewer is written into this window (a pop-out window).
|
|
310
|
+
* @param {*=} state
|
|
311
|
+
* The state to restore in the pop-out window.
|
|
312
|
+
* @returns {ReturnType<createCallableFrame>}
|
|
313
|
+
*/
|
|
314
|
+
function createViewer(targetWindow, state) {
|
|
315
|
+
return createCallableFrame({
|
|
316
|
+
jsCode() {
|
|
317
|
+
let mountedApp;const IS_MISSING_LIBRARIES=!window.Vue||!window.ace||!window.Prism;IS_MISSING_LIBRARIES&&document.querySelector("#splash").classList.add("failed");const Prism=window.Prism;delete window.Prism,Prism?.plugins.autoloader.loadLanguages("javascript");const MIN_LOADING_TIME=1e3,POP_IN_FUNCTION_NAME="yourjsBoxPopIn",TEXT_SCALES=[.75,.9,1,1.15,1.3,1.5,1.75,2],TEXT_SCALE_STORAGE_KEY="yourjs-box.textScale";function loadTextScale(){try{const e=+localStorage.getItem(TEXT_SCALE_STORAGE_KEY);return TEXT_SCALES.includes(e)?e:1}catch(e){return 1}}const NARROW_WIDTH=600;function getAutoDividerOrient(){return innerWidth<NARROW_WIDTH?"horizontal":"vertical"}function init(e,t,i){if(IS_MISSING_LIBRARIES)return;const s=t.hidePrefix??"",n=matchMedia("(prefers-color-scheme: dark)"),o=unindentMin(e),{visibleCode:r,hiddenGroups:a}=extractHiddenGroups(o,s),l=/Mac|iPhone|iPad/.test(navigator.platform),d=()=>a.map((e=>({...e})));mountedApp=Vue.createApp({data:()=>({displays:[],hidePrefix:s,hiddenGroups:d(),runnerMode:i.runnerMode,blockType:i.blockType,showResults:i.showResults,packageInfo:i.packageInfo,libraryVersions:i.libraryVersions,runHistory:[],isAboutOpen:!1,aboutTab:"about",exportCode:"current",exportFormat:"snippet",copyLabel:"Copy",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,isMenuOpen:!1,textScale:loadTextScale(),isFullscreen:!1,isMaximized:!1,isPopOut:i.isPopOut,isPoppedOut:!1,...i.popOutState??{}}),computed:{theme(){return this.forcedTheme??(this.prefersDark?"dark":"light")},modKey:()=>l?"\u2318":"Ctrl",runnerDescription(){return"window"===this.runnerMode?"This page (it can use the page's globals and DOM)":"A Web Worker (isolated from the page, no DOM)"},themeDescription(){return`${"dark"===this.theme?"Dark":"Light"} (${this.forcedTheme?"set by data-theme":"follows your system"})`},blockTypeDescription(){return"module"===this.blockType?"Modules (top-level await and imports work, declarations stay in each block)":"Classic scripts (top-level declarations are shared between blocks)"},collapsedGroupIds(){return new Set(this.displays.filter((e=>e.groupId&&e.isCollapsed)).map((e=>e.groupId)))},layoutDescription(){return("vertical"===this.dividerOrient?"Editor beside the console":"Editor below the console")+(this.isDividerOrientAuto?" (automatic)":"")},exportCodeOptions:()=>[{value:"current",label:"Current code",note:"The code that already ran followed by the code in the editor."},{value:"original",label:"Original code",note:"The code this console started with."},{value:"blank",label:"Blank",note:"An empty console with the same settings."}],exportFormatOptions:()=>[{value:"snippet",label:"Embed snippet"},{value:"page",label:"Full page"}],exportNote(){return this.exportCodeOptions.find((e=>e.value===this.exportCode)).note},exportJsCode(){if("original"===this.exportCode)return o;if("blank"===this.exportCode)return"";const e=this.runHistory.slice(),t=this.hiddenGroups.slice();let i=this.runCount;const s=()=>{for(;t.length&&t[0].runCount<=i;)e.push(t.shift().allLines)};s();for(const t of parseJSCodeGroups(this.jsCode))t.allLines.trim()&&(e.push(t.allLines),i++,s());return e.push(...t.map((e=>e.allLines))),joinCodeBlocks(e)},exportDataset(){const{librariesUrl:e,...i}=t;return i},exportHtml(){return buildConsoleHtml({code:this.exportJsCode,dataset:this.exportDataset,packageInfo:this.packageInfo,isFullPage:"page"===this.exportFormat})},bottomButtons(){const e=this.isFullscreen||this.isMaximized;return[{iconName:"clear",title:"Clear console",callback(){this.clearConsole()}},{iconName:e?"exitFullscreen":"fullscreen",title:e?"Exit full screen":"Full screen",callback(){this.toggleFullscreen()}},{iconName:"more",title:"More",className:"more-button",callback(){this.toggleMenu()}},{iconName:this.runningCount?"spinner":"play",label:"Run",className:"primary",title:`Run the next block of code (${l?"\u2318":"Ctrl+"}Enter)`,callback(){this.runCode()},disableIf(){return!this.canRunCode}}]},canShrinkText(){return this.textScale>TEXT_SCALES[0]},canGrowText(){return this.textScale<TEXT_SCALES[TEXT_SCALES.length-1]},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++,this.runHistory.push(e.allLines),messageParent({target:"runner",func:"runCode",args:[e.lines,{blockType:this.blockType,resultRange:this.showResults&&!i?findLastExpression(e.lines,this.blockType):null}]})},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=d(),this.runCount=0,this.runningCount=0,this.runHistory=[],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()))}))},openAbout(e){this.aboutTab=e,this.isAboutOpen=!0,this.$nextTick((()=>this.$refs.aboutCloseButton?.focus()))},closeAbout(){this.isAboutOpen=!1},async copyExport(){const e=this.exportHtml;try{await navigator.clipboard.writeText(e)}catch(t){const i=Object.assign(document.createElement("textarea"),{value:e});document.body.appendChild(i),i.select(),document.execCommand("copy"),i.remove()}this.copyLabel="Copied!",clearTimeout(this.copyLabelTimeout),this.copyLabelTimeout=setTimeout((()=>this.copyLabel="Copy"),1600)},downloadExport(){const e=buildConsoleHtml({code:this.exportJsCode,dataset:this.exportDataset,packageInfo:this.packageInfo,isFullPage:!0}),t=URL.createObjectURL(new Blob([e],{type:"text/html"}));Object.assign(document.createElement("a"),{href:t,download:"js-box.html"}).click(),setTimeout((()=>URL.revokeObjectURL(t)),1e3)},onLogRowClick(e,t){e.groupId&&!t.target.closest(".js-value-header.expandable")&&(e.isCollapsed=!e.isCollapsed)},isInCollapsedGroup(e){const{collapsedGroupIds:t}=this;return!!t.size&&!!e.groupIds?.some((e=>t.has(e)))},getState(){return JSON.parse(JSON.stringify({displays:this.displays,jsCode:this.jsCode,runHistory:this.runHistory,hiddenGroups:this.hiddenGroups,runCount:this.runCount,runningCount:this.runningCount,dividerOrient:this.dividerOrient,isDividerOrientAuto:this.isDividerOrientAuto,dividerPct:this.dividerPct}))},restoreState(e){Object.assign(this,e),this.isDisplaysScrolledToBottom=!0},toggleMenu(){this.isMenuOpen?this.closeMenu():(this.isMenuOpen=!0,this.$nextTick((()=>this.$refs.moreMenu?.querySelector(".menu-item:not(:disabled)")?.focus())))},closeMenu(e){this.isMenuOpen=!1,e&&document.querySelector("#bottomNav .more-button")?.focus()},onMenuKeyDown(e){if("Escape"===e.key)e.stopPropagation(),this.closeMenu(!0);else if("ArrowDown"===e.key||"ArrowUp"===e.key){e.preventDefault();const t=[...this.$refs.moreMenu.querySelectorAll(".menu-item:not(:disabled)")],i=t.indexOf(document.activeElement),s=t[(i+("ArrowDown"===e.key?1:t.length-1))%t.length];s?.focus()}},changeTextScale(e){const t=TEXT_SCALES.indexOf(this.textScale)+e;t>=0&&t<TEXT_SCALES.length&&this.setTextScale(TEXT_SCALES[t])},setTextScale(e){this.textScale=e;try{localStorage.setItem(TEXT_SCALE_STORAGE_KEY,`${e}`)}catch(e){}},async toggleFullscreen(){if(this.isMaximized)this.setMaximized(!1);else if(document.fullscreenElement)await document.exitFullscreen();else try{await document.documentElement.requestFullscreen()}catch(e){this.isPopOut||this.setMaximized(!0)}},setMaximized(e){this.isMaximized=e,messageParent({target:"host",func:"setMaximized",args:[e]})},popOut(){this.closeMenu(),this.isMaximized&&this.setMaximized(!1),messageParent({target:"host",func:"popOut",args:[this.getState()]})},bringBack(){this.hasSentState=!0;const e=window.yourjsBoxPopIn;"function"==typeof e?e(this.getState()):messageParent({target:"host",func:"popIn",args:[this.getState()]})},focusPopOut(){messageParent({target:"host",func:"focusPopOut",args:[]})},requestPopIn(){messageParent({target:"host",func:"requestPopIn",args:[]})},onWindowMouseDown(e){this.isMenuOpen&&!e.target.closest(".more-menu, .more-button")&&this.closeMenu()},onWindowKeyDown(e){"Escape"!==e.key||!this.isMaximized||this.isMenuOpen||this.dialog||this.isAboutOpen||this.setMaximized(!1)},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,s=e.pageX-t.left,n=e.pageY-t.top,o=t.width,r=t.height;return 100-100*Math.min(Math.max(.2,"vertical"===i?s/o:n/r),.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:{textScale:{handler(e){document.documentElement.style.setProperty("--text-scale",e)},immediate:!0},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("mousedown",this.onWindowMouseDown),addEventListener("keydown",this.onWindowKeyDown),addEventListener("resize",this.onWindowResize),addEventListener("mouseup",this.onWindowMouseUp),addEventListener("error",this.onWindowError),n.addEventListener("change",(e=>this.prefersDark=e.matches)),document.addEventListener("fullscreenchange",(()=>this.isFullscreen=!!document.fullscreenElement)),this.isPopOut&&addEventListener("pagehide",(()=>this.hasSentState||this.bringBack())),this.runningCount||this.runHiddenGroups(),setTimeout((()=>document.querySelector("#splash").classList.add("hidden")),Math.max(0,(this.isPopOut?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:["fontSize","height","keybinding","language","modelValue","readOnly","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)},fontSize(e){e&&this.editor.setFontSize(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,this.fontSize&&e.setFontSize(this.fontSize),this.readOnly&&(e.setReadOnly(!0),e.setHighlightActiveLine(!1),e.session.setUseWorker(!1)),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),s=JSON.stringify(this.warningNotes);this.annotations=e.getSession().getAnnotations();const{errorNotes:n,infoNotes:o,warningNotes:r}=this;t!==JSON.stringify(n)&&this.$emit("changeErrorNotes",n),i!==JSON.stringify(o)&&this.$emit("changeInfoNotes",o),s!==JSON.stringify(r)&&this.$emit("changeWarningNotes",r)})),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>',fullscreen:'<svg viewBox="0 0 16 16"><path d="M2.5 6V2.5H6M10 2.5h3.5V6M13.5 10v3.5H10M6 13.5H2.5V10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',exitFullscreen:'<svg viewBox="0 0 16 16"><path d="M6 2.5V6H2.5M13.5 6H10V2.5M10 13.5V10h3.5M2.5 10H6v3.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',more:'<svg viewBox="0 0 16 16"><circle cx="3.5" cy="8" r="1.4" fill="currentColor"/><circle cx="8" cy="8" r="1.4" fill="currentColor"/><circle cx="12.5" cy="8" r="1.4" fill="currentColor"/></svg>',check:'<svg viewBox="0 0 16 16"><path d="M3.5 8.5l3 3 6-7" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>',popOut:'<svg viewBox="0 0 16 16"><path d="M9.5 2.5h4v4M13.5 2.5L8 8M12 9.5v3a1 1 0 0 1-1 1H3.5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h3" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',popIn:'<svg viewBox="0 0 16 16"><path d="M12 8.5H8V4.5M8 8.5l5.5-5.5M12 10.5v2a1 1 0 0 1-1 1H3.5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h2" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',code:'<svg viewBox="0 0 16 16"><path d="M5.5 4.5L2 8l3.5 3.5M10.5 4.5L14 8l-3.5 3.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>',info:'<svg viewBox="0 0 16 16"><circle cx="8" cy="8" r="6.25" fill="none" stroke="currentColor" stroke-width="1.5"/><path d="M8 7.25v4" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/><circle cx="8" cy="5" r="1" fill="currentColor"/></svg>',result:'<svg viewBox="0 0 16 16"><path d="M9 4.5L5.5 8 9 11.5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><circle cx="12" cy="8" r="1.1" fill="currentColor"/></svg>',close:'<svg viewBox="0 0 16 16"><path d="M4 4l8 8m0-8l-8 8" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></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],s=/^\/\/[^]*\\\\$/.test(t),n=s?t.replace(/^\/+\s*|\s*\\+$/g,""):t;return!e.length||s&&!i.canAppendHeader?e.push({headerLines:s?[n]:[],lines:s?[]:[t],allLines:[t],canAppendHeader:s}):s?(i.headerLines.push(n),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=[],s=[];for(const n of parseJSCodeGroups(e))parseGroupHeader(n.headerLines,t).isHidden?s.push({...n,runCount:i.length}):i.push(n);return{visibleCode:i.map((e=>e.allLines)).join("\n"),hiddenGroups:s}}function findLastExpression(e,t){if(!window.acorn)return null;try{const{body:i}=acorn.parse(e,{ecmaVersion:"latest",sourceType:"module"===t?"module":"script",allowHashBang:!0}),s=i[i.length-1];if("ExpressionStatement"===s?.type&&!s.directive)return[s.expression.start,s.expression.end]}catch(e){}return null}function joinCodeBlocks(e){return e.map((e=>e.replace(/^(\s*[\r\n])+|\s+$/g,""))).filter(Boolean).map(((e,t)=>{const i=e.split(/\r\n|\r|\n/)[0];return t&&!/^\/\/[^]*\\\\$/.test(i)?`// \\\\\n${e}`:e})).join("\n\n")}function buildConsoleHtml({code:e,dataset:t,packageInfo:i,isFullPage:s}){const{name:n,version:o}=i,r=`https://cdn.jsdelivr.net/npm/${n}@${o.split(".")[0]}/dist/${n}.min.js`,a=Object.entries(t).map((([e,t])=>` data-${e.replace(/[A-Z]/g,(e=>"-"+e.toLowerCase()))}="${escapeHtmlAttribute(t)}"`)).join(""),l=s?" ":" ",d=e.replace(/<\/(script)/gi,"<\\/$1").replace(/<!--/g,"<\\!--"),c=(d.trim()?[`<script src="${r}"${a}>`,...d.split("\n").map((e=>e.trim()?" "+e:"")),"<\/script>"]:[`<script src="${r}"${a}><\/script>`]).map((e=>e?l+e:e)).join("\n");return s?["<!DOCTYPE html>",'<html lang="en">'," <head>",' <meta charset="utf-8">',' <meta name="viewport" content="width=device-width, initial-scale=1">'," <title>JS Box</title>"," <style>"," html, body { height: 100%; margin: 0; }"," </style>"," </head>"," <body>",c," </body>","</html>",""].join("\n"):`<div style="height: 400px;">\n${c}\n</div>\n`}function escapeHtmlAttribute(e){return`${e}`.replace(/[&"<>]/g,(e=>`&#${e.charCodeAt(0)};`))}function unindentMin(e,t){const i=(t=Object(t)).tabSize??4,s=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 s;s=t.exec(e);)s[3]&&(i=Math.min(i,s[2].length));e=e.replace(t,((e,t,s)=>t+s.slice(i)))}return s?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:s,groupIds:n,groupId:o,isCollapsed:r,stack:a}){t=`${t}`,mountedApp.displays.push({type:"log",key:t,name:"result"===t?"Result":`console.${t}`,descriptions:Array.from(i,sanitizeDescription),table:s?{headers:Array.from(s.headers,(e=>`${e}`)),rows:Array.from(s.rows,(e=>({index:`${e.index}`,cells:Array.from(e.cells,(e=>e&&sanitizeDescription(e)))})))}:null,groupIds:Array.from(n??[],(e=>`${e}`)),groupId:null!=o?`${o}`:null,isCollapsed:!!r,stack:null!=a?`${a}`: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(),s=mountedApp.displays.find((e=>"log"===e.type&&e.logId===i));let n=s?.descriptions[toIndex(e.shift())];for(let t=0;n&&t<e.length;t+=2){const i=e[t];if("entries"!==i&&"protoEntries"!==i)return;n=n[i]?.[toIndex(e[t+1])]?.[1]}n&&Object.assign(n,sanitizeEntries(t))}function onCodeRan(){mountedApp.runningCount=Math.max(0,mountedApp.runningCount-1),mountedApp.runHiddenGroups()}function setPoppedOut(e,t){mountedApp.isPoppedOut=e,t&&mountedApp.restoreState(t)}function popIn(){mountedApp.bringBack()}function onPopOutFailed(){mountedApp.displays.push({type:"notice",message:"The pop-out window was blocked by the browser. Allow pop-ups for this site and try again."})}function clearDisplays(){mountedApp.displays=[{type:"notice",message:"Console was cleared"}]}function toIndex(e){return Number.isInteger(e)&&e>=0?e:void 0}
|
|
318
|
+
},
|
|
319
|
+
// Exact versions are used so that a new release of a library can never
|
|
320
|
+
// change how an existing version of this console works. Ace and Prism
|
|
321
|
+
// load other files (eg. language modes) from next to these files.
|
|
322
|
+
jsUrls: [
|
|
323
|
+
libraryUrl('vue', 'dist/vue.global.prod.js'),
|
|
324
|
+
libraryUrl('ace-builds', 'src-noconflict/ace.js'),
|
|
325
|
+
libraryUrl('prismjs', 'components/prism-core.min.js'),
|
|
326
|
+
libraryUrl('prismjs', 'plugins/autoloader/prism-autoloader.min.js'),
|
|
327
|
+
libraryUrl('prismjs', 'plugins/match-braces/prism-match-braces.min.js'),
|
|
328
|
+
// Used to find the last expression in each block of code so that its
|
|
329
|
+
// value can be shown.
|
|
330
|
+
...(showResults ? [libraryUrl('acorn', 'dist/acorn.js')] : []),
|
|
331
|
+
],
|
|
332
|
+
cssUrls: [
|
|
333
|
+
'data:text/css,' + encodeURIComponent(VIEWER_IFRAME_CSS),
|
|
334
|
+
libraryUrl('prism-themes', 'themes/prism-vsc-dark-plus.min.css'),
|
|
335
|
+
libraryUrl('prismjs', 'plugins/match-braces/prism-match-braces.min.css'),
|
|
336
|
+
],
|
|
337
|
+
head: targetWindow ? '<meta charset="utf-8"><title>JS Box</title>' : '',
|
|
338
|
+
htmlAttributes: 'data-theme="' + theme + '"',
|
|
339
|
+
targetWindow,
|
|
340
|
+
onMessage(message) {
|
|
341
|
+
relayMessage(message.data, {runner, host: {apply: (func, args) => hostFuncs[func](...args)}});
|
|
342
|
+
},
|
|
343
|
+
async onReady() {
|
|
344
|
+
// The dataset is passed as is so that the viewer knows which options
|
|
345
|
+
// were actually specified (eg. when copying the console as HTML).
|
|
346
|
+
this.call('init', script.textContent, dataset, {
|
|
347
|
+
runnerMode,
|
|
348
|
+
blockType,
|
|
349
|
+
showResults,
|
|
350
|
+
packageInfo: PACKAGE_INFO,
|
|
351
|
+
libraryVersions: LIBRARY_VERSIONS,
|
|
352
|
+
isPopOut: !!targetWindow,
|
|
353
|
+
popOutState: state ?? null,
|
|
354
|
+
});
|
|
355
|
+
},
|
|
356
|
+
body: VIEWER_IFRAME_HTML,
|
|
357
|
+
style: {
|
|
358
|
+
width: '100%',
|
|
359
|
+
height: '100%',
|
|
360
|
+
border: 0,
|
|
361
|
+
display: 'block',
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
inlineViewer = activeViewer = createViewer();
|
|
367
|
+
script.parentNode.insertBefore(inlineViewer.iframe, script);
|
|
215
368
|
}
|
|
216
369
|
|
|
217
370
|
// NOTE: This solution was intentionally written without using newer JS
|
|
218
371
|
// features to make the minified version even smaller.
|
|
219
372
|
var createCallableFrame = (function () {
|
|
220
373
|
var IFRAME_SCRIPT_MESSAGE_CODE = parseFunction(function() {
|
|
374
|
+
// The window that created this one: the opener if this is a pop-out
|
|
375
|
+
// window or else the parent of the IFRAME.
|
|
376
|
+
var HOST_WINDOW = window.opener || window.parent;
|
|
377
|
+
|
|
221
378
|
// NOTE: Referencing with window to ensure that local namespace will not
|
|
222
379
|
// interfere.
|
|
223
380
|
window.addEventListener('message', function(e) {
|
|
224
381
|
var data = e.data;
|
|
225
382
|
if (
|
|
226
|
-
e.source ===
|
|
383
|
+
e.source === HOST_WINDOW
|
|
227
384
|
&& data && /^[A-Za-z_$][\w$]*$/.test(data.funcName)
|
|
228
385
|
&& Array.isArray(data.args)
|
|
229
386
|
) {
|
|
@@ -233,7 +390,7 @@
|
|
|
233
390
|
});
|
|
234
391
|
|
|
235
392
|
function messageParent(message) {
|
|
236
|
-
|
|
393
|
+
HOST_WINDOW.postMessage(message, '*');
|
|
237
394
|
}
|
|
238
395
|
}).body;
|
|
239
396
|
|
|
@@ -259,6 +416,9 @@
|
|
|
259
416
|
* The HTML code that will be used to instantiate the page.
|
|
260
417
|
* @param {string=} options.htmlAttributes
|
|
261
418
|
* Attributes to add to the `<html>` element of the IFRAME.
|
|
419
|
+
* @param {Window=} options.targetWindow
|
|
420
|
+
* If given the page is written into this (same origin) window (eg. a
|
|
421
|
+
* pop-out window) instead of into a new IFRAME.
|
|
262
422
|
* @param {((this: R, event: MessageEvent) => void)=} options.onMessage
|
|
263
423
|
* @param {((this: R, event: MessageEvent) => void)=} options.onReady
|
|
264
424
|
* @returns {R}
|
|
@@ -283,11 +443,11 @@
|
|
|
283
443
|
'(function(READY_ID){',
|
|
284
444
|
parseFunction(function() {
|
|
285
445
|
applyParent = function(funcName, args) {
|
|
286
|
-
|
|
446
|
+
HOST_WINDOW.postMessage({funcName: funcName, args: args, id: READY_ID}, '*');
|
|
287
447
|
};
|
|
288
448
|
|
|
289
449
|
callParent = function(funcName) {
|
|
290
|
-
|
|
450
|
+
HOST_WINDOW.postMessage(
|
|
291
451
|
{funcName: funcName, args: Array.prototype.slice.call(arguments, 1), id: READY_ID},
|
|
292
452
|
'*'
|
|
293
453
|
);
|
|
@@ -307,8 +467,12 @@
|
|
|
307
467
|
// Prevents the HTML parser from ending the inline script early.
|
|
308
468
|
.replace(/<(?=\/script|!--)/gi, '\\x3C');
|
|
309
469
|
|
|
310
|
-
|
|
311
|
-
var IFRAME = document.createElement('iframe');
|
|
470
|
+
var TARGET_WINDOW = options.targetWindow;
|
|
471
|
+
var IFRAME = TARGET_WINDOW ? null : document.createElement('iframe');
|
|
472
|
+
function getFrameWindow() {
|
|
473
|
+
return TARGET_WINDOW || IFRAME.contentWindow;
|
|
474
|
+
}
|
|
475
|
+
|
|
312
476
|
var HTML_CODE = [
|
|
313
477
|
'<!DOCTYPE html>',
|
|
314
478
|
'<html ' + (options.htmlAttributes || '') + '>',
|
|
@@ -329,10 +493,20 @@
|
|
|
329
493
|
'</body>',
|
|
330
494
|
'</html>'
|
|
331
495
|
].join('\n');
|
|
332
|
-
IFRAME.
|
|
496
|
+
// Writes the page into the target window or sets the IFRAME's source.
|
|
497
|
+
if (TARGET_WINDOW) {
|
|
498
|
+
TARGET_WINDOW.document.open();
|
|
499
|
+
TARGET_WINDOW.document.write(HTML_CODE);
|
|
500
|
+
TARGET_WINDOW.document.close();
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
IFRAME.srcdoc = HTML_CODE;
|
|
504
|
+
// Allows the console to go full screen.
|
|
505
|
+
IFRAME.setAttribute('allow', 'fullscreen');
|
|
506
|
+
}
|
|
333
507
|
|
|
334
508
|
// Set the style of the iframe.
|
|
335
|
-
if (style) {
|
|
509
|
+
if (style && IFRAME) {
|
|
336
510
|
if ('string' === typeof style) {
|
|
337
511
|
IFRAME.style.cssText = style;
|
|
338
512
|
}
|
|
@@ -346,8 +520,8 @@
|
|
|
346
520
|
}
|
|
347
521
|
|
|
348
522
|
// Adds an event listener so that messages from the IFRAME will be captured.
|
|
349
|
-
|
|
350
|
-
if (e.source ===
|
|
523
|
+
function onWindowMessage(e) {
|
|
524
|
+
if (e.source === getFrameWindow()) {
|
|
351
525
|
var data = e.data;
|
|
352
526
|
var dataIsReadyId = READY_ID === data;
|
|
353
527
|
if (isReady && !dataIsReadyId) {
|
|
@@ -371,13 +545,14 @@
|
|
|
371
545
|
console.warn('Message sent from callable frame prematurely:', e);
|
|
372
546
|
}
|
|
373
547
|
}
|
|
374
|
-
}
|
|
548
|
+
}
|
|
549
|
+
addEventListener('message', onWindowMessage);
|
|
375
550
|
|
|
376
551
|
// Returns an object which makes it possible to call functions and get
|
|
377
552
|
// access to the IFRAME.
|
|
378
553
|
var queuedMessages = [];
|
|
379
554
|
function postToFrame(message) {
|
|
380
|
-
if (isReady)
|
|
555
|
+
if (isReady) getFrameWindow().postMessage(message, '*');
|
|
381
556
|
else queuedMessages.push(message);
|
|
382
557
|
}
|
|
383
558
|
var callableFrame = {
|
|
@@ -387,7 +562,12 @@
|
|
|
387
562
|
call: function(funcName) {
|
|
388
563
|
postToFrame({funcName: funcName, args: Array.prototype.slice.call(arguments, 1)});
|
|
389
564
|
},
|
|
390
|
-
iframe: IFRAME
|
|
565
|
+
iframe: IFRAME,
|
|
566
|
+
window: TARGET_WINDOW,
|
|
567
|
+
// Stops listening for messages from the frame.
|
|
568
|
+
dispose: function() {
|
|
569
|
+
removeEventListener('message', onWindowMessage);
|
|
570
|
+
}
|
|
391
571
|
};
|
|
392
572
|
return callableFrame;
|
|
393
573
|
};
|
|
@@ -395,7 +575,9 @@
|
|
|
395
575
|
* @typedef {Object} createCallableFrame_Return
|
|
396
576
|
* @property {(funcName: string, args: any[]) => void} apply
|
|
397
577
|
* @property {(funcName: string, ...args: any[]) => void} call
|
|
398
|
-
* @property {HTMLIFrameElement} iframe
|
|
578
|
+
* @property {HTMLIFrameElement?} iframe
|
|
579
|
+
* @property {Window=} window
|
|
580
|
+
* @property {() => void} dispose
|
|
399
581
|
*/
|
|
400
582
|
|
|
401
583
|
/**
|