feat: use sandbox
diff --git a/build/webpack.config.js b/build/webpack.config.js
index 80dd631..97d692e 100644
--- a/build/webpack.config.js
+++ b/build/webpack.config.js
@@ -49,12 +49,13 @@
]
},
{
- test: /\.svg$/,
+ test: /\.(svg|html)$/,
use: [
{
loader: 'html-loader',
options: {
- minimize: true
+ // will be `true` in production
+ // minimize: true
}
}
]
diff --git a/package-lock.json b/package-lock.json
index 304d4f1..6ce6c9d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -49,6 +49,7 @@
"open": "^7.1.0",
"pixelmatch": "^5.2.1",
"pngjs": "^6.0.0",
+ "raw-loader": "^4.0.2",
"sass.js": "^0.11.1",
"sassjs-loader": "^2.0.0",
"seedrandom": "^3.0.5",
@@ -1584,9 +1585,9 @@
}
},
"node_modules/@types/json-schema": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
- "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"node_modules/@types/minimatch": {
@@ -4899,12 +4900,6 @@
"webpack": "^4.0.0 || ^5.0.0"
}
},
- "node_modules/html-loader/node_modules/@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"node_modules/html-loader/node_modules/json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
@@ -5899,12 +5894,6 @@
"webpack": "^4.4.0 || ^5.0.0"
}
},
- "node_modules/mini-css-extract-plugin/node_modules/@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"node_modules/mini-css-extract-plugin/node_modules/json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
@@ -7014,6 +7003,70 @@
"safe-buffer": "^5.1.0"
}
},
+ "node_modules/raw-loader": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+ "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+ "dev": true,
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/raw-loader/node_modules/json5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true,
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/raw-loader/node_modules/loader-utils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+ "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+ "dev": true,
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/raw-loader/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
"node_modules/rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -8000,12 +8053,6 @@
"webpack": "^4.0.0 || ^5.0.0"
}
},
- "node_modules/style-loader/node_modules/@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"node_modules/style-loader/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -8264,12 +8311,6 @@
"webpack": "^5.1.0"
}
},
- "node_modules/terser-webpack-plugin/node_modules/@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"node_modules/terser-webpack-plugin/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -9001,12 +9042,6 @@
"source-map": "~0.6.1"
}
},
- "node_modules/webpack/node_modules/@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"node_modules/webpack/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -10606,9 +10641,9 @@
}
},
"@types/json-schema": {
- "version": "7.0.5",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz",
- "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==",
+ "version": "7.0.11",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
+ "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==",
"dev": true
},
"@types/minimatch": {
@@ -13272,12 +13307,6 @@
"schema-utils": "^3.0.0"
},
"dependencies": {
- "@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
@@ -14042,12 +14071,6 @@
"webpack-sources": "^1.1.0"
},
"dependencies": {
- "@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"json5": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
@@ -14926,6 +14949,46 @@
"safe-buffer": "^5.1.0"
}
},
+ "raw-loader": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.2.tgz",
+ "integrity": "sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^3.0.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
+ "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz",
+ "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
+ "schema-utils": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz",
+ "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ }
+ }
+ }
+ },
"rc": {
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
@@ -15690,12 +15753,6 @@
"schema-utils": "^3.0.0"
},
"dependencies": {
- "@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -15901,12 +15958,6 @@
"terser": "^5.3.8"
},
"dependencies": {
- "@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -16265,12 +16316,6 @@
"webpack-sources": "^2.1.1"
},
"dependencies": {
- "@types/json-schema": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
- "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
- "dev": true
- },
"ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
diff --git a/package.json b/package.json
index 49e3069..6d04c91 100644
--- a/package.json
+++ b/package.json
@@ -45,6 +45,7 @@
"open": "^7.1.0",
"pixelmatch": "^5.2.1",
"pngjs": "^6.0.0",
+ "raw-loader": "^4.0.2",
"sass.js": "^0.11.1",
"sassjs-loader": "^2.0.0",
"seedrandom": "^3.0.5",
diff --git a/src/common/config.js b/src/common/config.js
index 919c39a..a2ea9cf 100644
--- a/src/common/config.js
+++ b/src/common/config.js
@@ -98,13 +98,11 @@
]);
const URL_PARAMS = {};
-(location.search || '')
- .slice(1)
- .split('&')
- .forEach(function (item) {
- const kv = item.split('=');
- URL_PARAMS[kv[0]] = kv[1];
- });
+(() =>
+ // Object.fromEntries(new URLSearchParams(location.search).entries())
+ new URLSearchParams(location.search).forEach(
+ (val, key) => (URL_PARAMS[key] = val)
+ ))();
export { URL_PARAMS };
@@ -112,9 +110,10 @@
export const CDN_ROOT = 'https://cdn.jsdelivr.net/npm/';
export const SCRIPT_URLS = {
- echartsMinJS: '/dist/echarts.min.js',
echartsDir: `${CDN_ROOT}echarts@{{version}}`,
echartsNightlyDir: `${CDN_ROOT}echarts-nightly@{{version}}`,
+ echartsJS: '/dist/echarts.js',
+ echartsMinJS: '/dist/echarts.min.js',
localEChartsMinJS: 'http://localhost/echarts/dist/echarts.js',
localEChartsDir: 'http://localhost/echarts',
@@ -127,5 +126,9 @@
monacoDir: `${CDN_ROOT}monaco-editor@0.27.0/min/vs`,
aceDir: `${CDN_ROOT}ace-builds@1.4.12/src-min-noconflict`,
- prettierDir: `${CDN_ROOT}prettier@2.3.2`
+ prettierDir: `${CDN_ROOT}prettier@2.3.2`,
+
+ bmapLibJS:
+ 'https://api.map.baidu.com/getscript?v=3.0&ak=KOmVjPVUAey1G2E8zNhPiuQ6QiEmAwZu',
+ echartsBMapMinJS: '/dist/extension/bmap.min.js'
};
diff --git a/src/common/helper.js b/src/common/helper.js
index 94f3e24..964ce3f 100644
--- a/src/common/helper.js
+++ b/src/common/helper.js
@@ -67,7 +67,7 @@
SCRIPT_URLS.prettierDir + '/standalone.js',
SCRIPT_URLS.prettierDir +
(store.typeCheck ? '/parser-typescript.js' : '/parser-babel.js')
- ]).then(([_, parser]) => {});
+ ]);
}
return Promise.resolve();
}
diff --git a/src/dep/showDebugDirtyRect.js b/src/dep/showDebugDirtyRect.js
index 5ee3204..34ece51 100644
--- a/src/dep/showDebugDirtyRect.js
+++ b/src/dep/showDebugDirtyRect.js
@@ -1,6 +1,6 @@
-var DebugRect = (function () {
+const DebugRect = (function () {
function DebugRect(style) {
- var dom = (this.dom = document.createElement('div'));
+ const dom = (this.dom = document.createElement('div'));
dom.className = 'ec-debug-dirty-rect';
style = Object.assign({}, style);
Object.assign(style, {
@@ -8,15 +8,15 @@
border: '1px solid #00f'
});
dom.style.cssText =
- '\nposition: absolute;\nopacity: 0;\ntransition: opacity 0.5s linear;\npointer-events: none;\n';
- for (var key in style) {
+ 'position:absolute;opacity:0;transition:opacity 0.5s linear;pointer-events:none;';
+ for (const key in style) {
if (style.hasOwnProperty(key)) {
dom.style[key] = style[key];
}
}
}
DebugRect.prototype.update = function (rect) {
- var domStyle = this.dom.style;
+ const domStyle = this.dom.style;
domStyle.width = rect.width + 'px';
domStyle.height = rect.height + 'px';
domStyle.left = rect.x + 'px';
@@ -26,7 +26,7 @@
this.dom.style.opacity = '0';
};
DebugRect.prototype.show = function () {
- var _this = this;
+ const _this = this;
clearTimeout(this._hideTimeout);
this.dom.style.opacity = '1';
this._hideTimeout = setTimeout(function () {
@@ -35,9 +35,10 @@
};
return DebugRect;
})();
-export default function (zr, opts) {
+
+function showDebugDirtyRect(zr, opts) {
opts = opts || {};
- var painter = zr.painter;
+ const painter = zr.painter;
if (!painter.getLayers) {
throw new Error('Debug dirty rect can only been used on canvas renderer.');
}
@@ -46,12 +47,12 @@
'Debug dirty rect can only been used on zrender inited with container.'
);
}
- var debugViewRoot = document.createElement('div');
- debugViewRoot.style.cssText =
- '\nposition:absolute;\nleft:0;\ntop:0;\nright:0;\nbottom:0;\npointer-events:none;\n';
+ const debugViewRoot = document.createElement('div');
debugViewRoot.className = 'ec-debug-dirty-rect-container';
- var debugRects = [];
- var dom = zr.dom;
+ debugViewRoot.style.cssText =
+ 'position:absolute;left:0;top:0;right:0;bottom:0;pointer-events:none;';
+ const debugRects = [];
+ const dom = zr.dom;
dom.appendChild(debugViewRoot);
var computedStyle = getComputedStyle(dom);
if (computedStyle.position === 'static') {
@@ -59,13 +60,13 @@
}
zr.on('rendered', function () {
if (painter.getLayers) {
- var idx_1 = 0;
+ let idx_1 = 0;
painter.eachBuiltinLayer(function (layer) {
if (!layer.debugGetPaintRects) {
return;
}
- var paintRects = layer.debugGetPaintRects();
- for (var i = 0; i < paintRects.length; i++) {
+ const paintRects = layer.debugGetPaintRects();
+ for (let i = 0; i < paintRects.length; i++) {
if (!debugRects[idx_1]) {
debugRects[idx_1] = new DebugRect(opts.style);
debugViewRoot.appendChild(debugRects[idx_1].dom);
@@ -75,7 +76,7 @@
idx_1++;
}
});
- for (var i = idx_1; i < debugRects.length; i++) {
+ for (let i = idx_1; i < debugRects.length; i++) {
debugRects[i].hide();
}
}
diff --git a/src/editor/CodeMonaco.vue b/src/editor/CodeMonaco.vue
index a7e910c..80275bf 100644
--- a/src/editor/CodeMonaco.vue
+++ b/src/editor/CodeMonaco.vue
@@ -6,7 +6,6 @@
import { loadScriptsAsync } from '../common/helper';
import { store } from '../common/store';
import { SCRIPT_URLS, URL_PARAMS } from '../common/config';
-import { ensureECharts } from './Preview.vue';
function loadTypes() {
return fetch(
@@ -88,31 +87,26 @@
}
function ensureMonacoAndTsTransformer() {
- function loadMonaco() {
- if (typeof monaco === 'undefined') {
- return loadScriptsAsync([
- SCRIPT_URLS.monacoDir + '/loader.js',
- // Prebuilt TS transformer with surcrase
- store.cdnRoot + '/js/example-transform-ts-bundle.js'
- ]).then(function () {
- window.require.config({ paths: { vs: SCRIPT_URLS.monacoDir } });
- return new Promise((resolve) => {
- window.require(['vs/editor/editor.main'], function () {
- loadTypes().then(() => {
- // Disable AMD. Which will break other libs.
- // FIXME
- window.define.amd = null;
- resolve();
- });
+ if (typeof monaco === 'undefined') {
+ return loadScriptsAsync([
+ SCRIPT_URLS.monacoDir + '/loader.js',
+ // Prebuilt TS transformer with surcrase
+ store.cdnRoot + '/js/example-transform-ts-bundle.js'
+ ]).then(function () {
+ window.require.config({ paths: { vs: SCRIPT_URLS.monacoDir } });
+ return new Promise((resolve) => {
+ window.require(['vs/editor/editor.main'], function () {
+ loadTypes().then(() => {
+ // Disable AMD. Which will break other libs.
+ // FIXME
+ window.define.amd = null;
+ resolve();
});
});
});
- }
- return Promise.resolve();
+ });
}
-
- // Must load echarts before monaco. Or the AMD loader will affect loading of echarts.
- return ensureECharts().then(loadMonaco);
+ return Promise.resolve();
}
export default {
diff --git a/src/editor/Editor.vue b/src/editor/Editor.vue
index cf54828..516a50c 100644
--- a/src/editor/Editor.vue
+++ b/src/editor/Editor.vue
@@ -81,12 +81,12 @@
<CodeMonaco
v-if="shared.typeCheck"
id="code-panel"
- :initialCode="shared.initialCode"
+ :initialCode="initialCode"
></CodeMonaco>
<CodeAce
v-else
id="code-panel"
- :initialCode="shared.initialCode"
+ :initialCode="initialCode"
></CodeAce>
</el-main>
</el-container>
@@ -226,7 +226,7 @@
} else {
loadExampleCode().then((code) => {
// Only set the code in editor. editor will sync to the store.
- store.initialCode = parseSourceCode(code);
+ store.initialCode = this.initialCode = parseSourceCode(code);
});
window.addEventListener('mousemove', (e) => {
@@ -339,7 +339,7 @@
},
changeLang(lang) {
if ((URL_PARAMS.lang || 'js').toLowerCase() !== lang) {
- if (!store.initialCode || store.sourceCode === store.initialCode) {
+ if (!this.initialCode || store.sourceCode === this.initialCode) {
gotoURL(
Object.assign({}, URL_PARAMS, {
lang
@@ -364,7 +364,7 @@
},
format() {
formatCode(store.sourceCode).then((code) => {
- store.initialCode = code;
+ store.initialCode = this.initialCode = code;
});
}
},
diff --git a/src/editor/Preview.vue b/src/editor/Preview.vue
index f56b500..398c77f 100644
--- a/src/editor/Preview.vue
+++ b/src/editor/Preview.vue
@@ -4,10 +4,9 @@
v-loading="loading"
class="right-panel"
id="chart-panel"
+ ref="chartPanel"
:style="{ background: backgroundColor }"
- >
- <div class="chart-container"></div>
- </div>
+ ></div>
<div id="tool-panel">
<div class="left-panel">
<el-switch
@@ -90,7 +89,7 @@
</el-option>
</el-select>
<el-checkbox
- v-if="!shared.isMobile"
+ v-if="inEditor && !shared.isMobile"
v-model="nightly"
class="use-nightly"
>Nightly</el-checkbox
@@ -109,11 +108,7 @@
<div id="preview-status">
<div class="left">
<template v-if="inEditor && !shared.isMobile">
- <el-button
- icon="el-icon-download"
- size="mini"
- @click="downloadExample"
- >
+ <el-button icon="el-icon-download" size="mini" @click="download">
{{ $t('editor.download') }}
</el-button>
<el-button
@@ -153,10 +148,9 @@
updateRunHash
} from '../common/store';
import { SCRIPT_URLS, URL_PARAMS } from '../common/config';
-import { loadScriptsAsync, compressStr } from '../common/helper';
+import { compressStr } from '../common/helper';
import { createSandbox } from './sandbox';
import debounce from 'lodash/debounce';
-import { addListener } from 'resize-detector';
import { download } from './downloadExample';
import { gotoURL } from '../common/route';
import { gt } from 'semver';
@@ -164,47 +158,25 @@
const example = getExampleConfig();
const isGL = isGLExample();
-function addDecalIfNecessary(option) {
- if (store.enableDecal) {
- option.aria = option.aria || {};
- option.aria.decal = option.aria.decal || {};
- option.aria.decal.show = true;
- option.aria.show = option.aria.enabled = true;
- }
-}
+function getScripts(nightly) {
+ const echartsDir = SCRIPT_URLS[
+ nightly ? 'echartsNightlyDir' : 'echartsDir'
+ ].replace('{{version}}', store.echartsVersion);
+ const hasBmap = example && example.tags.indexOf('bmap') >= 0;
-export function ensureECharts(nightly) {
- if (typeof echarts === 'undefined') {
- const hasBmap = example && example.tags.indexOf('bmap') >= 0;
- const echartsDir = SCRIPT_URLS[
- nightly ? 'echartsNightlyDir' : 'echartsDir'
- ].replace('{{version}}', store.echartsVersion);
-
- // Code from https://api.map.baidu.com/api?v=2.0&ak=KOmVjPVUAey1G2E8zNhPiuQ6QiEmAwZu
- if (hasBmap) {
- window.HOST_TYPE = '2';
- window.BMap_loadScriptTime = new Date().getTime();
- }
-
- return loadScriptsAsync([
- SCRIPT_URLS.datGUIMinJS,
- 'local' in URL_PARAMS
- ? SCRIPT_URLS.localEChartsMinJS
- : echartsDir + SCRIPT_URLS.echartsMinJS,
- SCRIPT_URLS.echartsWorldMapJS,
- SCRIPT_URLS.echartsStatMinJS,
- ...(URL_PARAMS.gl ? [SCRIPT_URLS.echartsGLMinJS] : []),
- ...(hasBmap
- ? [
- 'https://api.map.baidu.com/getscript?v=3.0&ak=KOmVjPVUAey1G2E8zNhPiuQ6QiEmAwZu&services=&t=20200327103013',
- echartsDir + '/dist/extension/bmap.min.js'
- ]
- : [])
- ]).then(() => {
- echarts.registerPreprocessor(addDecalIfNecessary);
- });
- }
- return Promise.resolve();
+ return [
+ 'local' in URL_PARAMS
+ ? SCRIPT_URLS.localEChartsMinJS
+ : echartsDir +
+ SCRIPT_URLS['dev' in URL_PARAMS ? 'echartsJS' : 'echartsMinJS'],
+ ...(URL_PARAMS.gl ? [SCRIPT_URLS.echartsGLMinJS] : []),
+ ...(hasBmap
+ ? [SCRIPT_URLS.bmapLibJS, echartsDir + SCRIPT_URLS.echartsBMapMinJS]
+ : []),
+ SCRIPT_URLS.echartsStatMinJS,
+ SCRIPT_URLS.echartsWorldMapJS,
+ SCRIPT_URLS.datGUIMinJS
+ ].map((url) => ({ src: url }));
}
function log(text, type) {
@@ -215,60 +187,78 @@
store.editorStatus.type = type;
}
-function run() {
- if (typeof echarts === 'undefined') {
+function run(recreateInstance) {
+ if (!store.runCode) {
return;
}
- if (!this.sandbox) {
- this.sandbox = createSandbox((chart) => {
- const option = chart.getOption();
- if (
- typeof option.backgroundColor === 'string' &&
- option.backgroundColor !== 'transparent'
- ) {
- this.backgroundColor = option.backgroundColor;
- } else {
- this.backgroundColor = '#fff';
- }
- });
- }
- try {
- const updateTime = this.sandbox.run(
- this.$el.querySelector('.chart-container'),
- store
- );
+ const runCode = () => {
+ console.log('runCode');
- log(this.$t('editor.chartOK') + updateTime + 'ms');
-
- // Find the appropriate throttle time
- const debounceTime = 500;
- const debounceTimeQuantities = [0, 500, 2000, 5000, 10000];
- for (let i = debounceTimeQuantities.length - 1; i >= 0; i--) {
- const quantity = debounceTimeQuantities[i];
- const preferredDebounceTime = debounceTimeQuantities[i + 1] || 1000000;
- if (
- updateTime >= quantity &&
- this.debouncedTime !== preferredDebounceTime
- ) {
- this.debouncedRun = debounce(run, preferredDebounceTime, {
- trailing: true
- });
- this.debouncedTime = preferredDebounceTime;
- break;
- }
- }
+ this.sandbox.run(store, recreateInstance);
// Update run hash to let others known chart has been changed.
updateRunHash();
- } catch (e) {
- log(this.$t('editor.errorInEditor'), 'error');
- console.error(e);
+ };
+
+ if (!this.sandbox) {
+ this.loading = true;
+ this.sandbox = createSandbox(
+ this.$refs.chartPanel,
+ getScripts(this.nightly),
+ () => {
+ runCode();
+ this.loading = false;
+ },
+ () => {
+ // TODO show error hints
+ this.loading = false;
+ },
+ () => {
+ log(this.$t('editor.errorInEditor'), 'error');
+ },
+ (option, updateTime) => {
+ if (
+ typeof option.backgroundColor === 'string' &&
+ option.backgroundColor !== 'transparent'
+ ) {
+ this.backgroundColor = option.backgroundColor;
+ } else {
+ this.backgroundColor = '#fff';
+ }
+
+ log(this.$t('editor.chartOK') + updateTime.toFixed(2) + 'ms');
+
+ // Find the appropriate throttle time
+ const debounceTimeQuantities = [0, 500, 2000, 5000, 10000];
+ for (let i = debounceTimeQuantities.length - 1; i >= 0; i--) {
+ const quantity = debounceTimeQuantities[i];
+ const preferredDebounceTime =
+ debounceTimeQuantities[i + 1] || 1000000;
+ if (
+ updateTime >= quantity &&
+ this.debouncedTime !== preferredDebounceTime
+ ) {
+ this.debouncedRun = debounce(run, preferredDebounceTime, {
+ trailing: true
+ });
+ this.debouncedTime = preferredDebounceTime;
+ break;
+ }
+ }
+ }
+ );
+ } else {
+ runCode();
}
}
export default {
- props: ['inEditor'],
+ props: {
+ inEditor: {
+ type: Boolean
+ }
+ },
data() {
return {
@@ -276,7 +266,7 @@
debouncedTime: undefined,
backgroundColor: '',
autoRun: true,
- loading: false,
+ loading: true,
isGL,
@@ -287,13 +277,7 @@
},
mounted() {
- this.loadECharts();
-
- addListener(this.$el, () => {
- if (this.sandbox) {
- this.sandbox.resize();
- }
- });
+ this.run();
this.fetchVersionList();
},
@@ -357,52 +341,37 @@
run,
// debouncedRun will be created at first run
// debouncedRun: null,
- loadECharts() {
- this.loading = true;
- ensureECharts(this.nightly).then(() => {
- this.loading = false;
- if (store.runCode) {
- this.run();
- }
- });
- },
refreshAll() {
- this.dispose();
- this.run();
+ this.run(true);
},
dispose() {
if (this.sandbox) {
this.sandbox.dispose();
+ this.sandbox = null;
}
},
- downloadExample() {
- download();
- },
+ download,
screenshot() {
- if (this.sandbox) {
- const url = this.sandbox.getDataURL();
- const $a = document.createElement('a');
- $a.download =
- URL_PARAMS.c + '.' + (store.renderer === 'svg' ? 'svg' : 'png');
- $a.target = '_blank';
- $a.href = url;
- const evt = new MouseEvent('click', {
- bubbles: true,
- cancelable: false
- });
- $a.dispatchEvent(evt);
- }
+ this.sandbox &&
+ this.sandbox.screenshot(
+ (URL_PARAMS.c || Date.now()) +
+ '.' +
+ (store.renderer === 'svg' ? 'svg' : 'png')
+ );
},
share() {
- let shareURL = location.href;
+ let shareURL = new URL(location.href);
if (store.initialCode !== store.sourceCode) {
- shareURL += '&code=' + compressStr(store.sourceCode);
+ shareURL.searchParams.set('code', compressStr(store.sourceCode));
}
navigator.clipboard
- .writeText(shareURL)
+ .writeText(shareURL.toString())
.then(() => this.$message.success(this.$t('editor.share.success')))
// PENDING
- .catch(() => window.open(shareURL, '_blank'));
+ .catch((e) => {
+ console.error('failed to write share url to the clipboard', e);
+ window.open(shareURL, '_blank');
+ });
},
getOption() {
return this.sandbox && this.sandbox.getOption();
@@ -495,25 +464,7 @@
border-radius: 5px;
background: #fff;
overflow: hidden;
-
padding: 10px;
-
- .ec-debug-dirty-rect-container {
- left: 10px !important;
- top: 10px !important;
- right: 10px !important;
- bottom: 10px !important;
-
- .ec-debug-dirty-rect {
- background-color: rgba(255, 0, 0, 0.2) !important;
- border: 1px solid red !important;
- }
- }
-
- .chart-container {
- position: relative;
- height: 100%;
- }
}
.render-config-container {
diff --git a/src/editor/sandbox.js b/src/editor/sandbox.js
deleted file mode 100644
index 632fe74..0000000
--- a/src/editor/sandbox.js
+++ /dev/null
@@ -1,216 +0,0 @@
-import showDebugDirtyRect from '../dep/showDebugDirtyRect';
-import seedrandom from 'seedrandom';
-
-export function createSandbox(optionUpdated) {
- let appEnv = {};
- let gui;
-
- let _intervalIdList = [];
- let _timeoutIdList = [];
-
- const _oldSetTimeout = window.setTimeout;
- const _oldSetInterval = window.setInterval;
-
- function setTimeout(func, delay) {
- var id = _oldSetTimeout(func, delay);
- _timeoutIdList.push(id);
- return id;
- }
- function setInterval(func, gap) {
- var id = _oldSetInterval(func, gap);
- _intervalIdList.push(id);
- return id;
- }
- function _clearTimeTickers() {
- for (var i = 0; i < _intervalIdList.length; i++) {
- clearInterval(_intervalIdList[i]);
- }
- for (var i = 0; i < _timeoutIdList.length; i++) {
- clearTimeout(_timeoutIdList[i]);
- }
- _intervalIdList = [];
- _timeoutIdList = [];
- }
- const _events = [];
- function _wrapOnMethods(chart) {
- const oldOn = chart.on;
- const oldSetOption = chart.setOption;
- chart.on = function (eventName) {
- const res = oldOn.apply(chart, arguments);
- _events.push(eventName);
- return res;
- };
- chart.setOption = function () {
- const res = oldSetOption.apply(this, arguments);
- optionUpdated && optionUpdated(chart);
- return res;
- };
- }
-
- function _clearChartEvents(chart) {
- _events.forEach(function (eventName) {
- if (chart) {
- chart.off(eventName);
- }
- });
-
- _events.length = 0;
- }
-
- let chartInstance;
-
- return {
- resize() {
- if (chartInstance) {
- chartInstance.resize();
- }
- },
-
- dispose() {
- if (chartInstance) {
- chartInstance.dispose();
- chartInstance = null;
- }
- },
-
- getDataURL() {
- return chartInstance.getDataURL({
- pixelRatio: 2,
- excludeComponents: ['toolbox']
- });
- },
-
- getOption() {
- return chartInstance.getOption();
- },
-
- run(el, store) {
- if (!chartInstance) {
- chartInstance = echarts.init(el, store.darkMode ? 'dark' : '', {
- renderer: store.renderer,
- useDirtyRect: store.useDirtyRect
- });
- if (store.useDirtyRect && store.renderer === 'canvas') {
- try {
- showDebugDirtyRect(chartInstance.getZr(), {
- autoHideDelay: 500
- });
- } catch (e) {
- console.error(e);
- }
- }
- _wrapOnMethods(chartInstance);
- }
-
- // if (this.hasEditorError()) {
- // log(this.$t('editor.errorInEditor'), 'error');
- // return;
- // }
-
- // TODO Scope the variables in component.
- _clearTimeTickers();
- _clearChartEvents(chartInstance);
- // Reset
- appEnv.config = null;
-
- // run the code
-
- const compiledCode = store.runCode
- // Replace random method
- .replace(/Math.random\(\)/g, '__ECHARTS_EXAMPLE_RANDOM__()');
- const echartsExampleRandom = seedrandom(store.randomSeed);
-
- const func = new Function(
- 'myChart',
- 'app',
- 'setTimeout',
- 'setInterval',
- 'ROOT_PATH',
- '__ECHARTS_EXAMPLE_RANDOM__',
- 'var option;\n' + compiledCode + '\nreturn option;'
- );
- const option = func(
- chartInstance,
- appEnv,
- setTimeout,
- setInterval,
- store.cdnRoot,
- echartsExampleRandom
- );
- let updateTime = 0;
-
- if (option && typeof option === 'object') {
- const startTime = +new Date();
- chartInstance.setOption(option, true);
- const endTime = +new Date();
- updateTime = endTime - startTime;
- }
-
- if (gui) {
- $(gui.domElement).remove();
- gui.destroy();
- gui = null;
- }
-
- if (appEnv.config) {
- gui = new dat.GUI({
- autoPlace: false
- });
- $(gui.domElement).css({
- position: 'absolute',
- right: 5,
- top: 0,
- zIndex: 1000
- });
- $('.right-container').append(gui.domElement);
-
- var configParameters = appEnv.configParameters || {};
- for (var name in appEnv.config) {
- var value = appEnv.config[name];
- if (name !== 'onChange' && name !== 'onFinishChange') {
- var isColor = false;
- // var value = obj;
- var controller = null;
- if (configParameters[name]) {
- if (configParameters[name].options) {
- controller = gui.add(
- appEnv.config,
- name,
- configParameters[name].options
- );
- } else if (configParameters[name].min != null) {
- controller = gui.add(
- appEnv.config,
- name,
- configParameters[name].min,
- configParameters[name].max
- );
- }
- }
- if (typeof value === 'string') {
- try {
- var colorArr = echarts.color.parse(value);
- isColor = !!colorArr;
- if (isColor) {
- value = echarts.color.stringify(colorArr, 'rgba');
- }
- } catch (e) {}
- }
- if (!controller) {
- controller = gui[isColor ? 'addColor' : 'add'](
- appEnv.config,
- name
- );
- }
- appEnv.config.onChange &&
- controller.onChange(appEnv.config.onChange);
- appEnv.config.onFinishChange &&
- controller.onFinishChange(appEnv.config.onFinishChange);
- }
- }
- }
-
- return updateTime;
- }
- };
-}
diff --git a/src/editor/sandbox/estraverse.browser b/src/editor/sandbox/estraverse.browser
new file mode 100644
index 0000000..21a92e2
--- /dev/null
+++ b/src/editor/sandbox/estraverse.browser
@@ -0,0 +1 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).estraverse=e()}}((function(){return function e(t,n,r){function i(a,s){if(!n[a]){if(!t[a]){var l="function"==typeof require&&require;if(!s&&l)return l(a,!0);if(o)return o(a,!0);var p=new Error("Cannot find module '"+a+"'");throw p.code="MODULE_NOT_FOUND",p}var u=n[a]={exports:{}};t[a][0].call(u.exports,(function(e){return i(t[a][1][e]||e)}),u,u.exports,e,t,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a<r.length;a++)i(r[a]);return i}({1:[function(e,t,n){!function e(t){var n,r,i,o,a,s;function l(e){var t,n,r={};for(t in e)e.hasOwnProperty(t)&&(n=e[t],r[t]="object"==typeof n&&null!==n?l(n):n);return r}function p(e,t){this.parent=e,this.key=t}function u(e,t,n,r){this.node=e,this.path=t,this.wrap=n,this.ref=r}function c(){}function f(e){return null!=e&&("object"==typeof e&&"string"==typeof e.type)}function h(e,t){return(e===n.ObjectExpression||e===n.ObjectPattern)&&"properties"===t}function m(e,t){for(var n=e.length-1;n>=0;--n)if(e[n].node===t)return!0;return!1}function d(e,t){return(new c).traverse(e,t)}function y(e,t){var n;return n=function(e,t){var n,r,i,o;for(r=e.length,i=0;r;)t(e[o=i+(n=r>>>1)])?r=n:(i=o+1,r-=n+1);return i}(t,(function(t){return t.range[0]>e.range[0]})),e.extendedRange=[e.range[0],e.range[1]],n!==t.length&&(e.extendedRange[1]=t[n].range[0]),(n-=1)>=0&&(e.extendedRange[0]=t[n].range[1]),e}return n={AssignmentExpression:"AssignmentExpression",AssignmentPattern:"AssignmentPattern",ArrayExpression:"ArrayExpression",ArrayPattern:"ArrayPattern",ArrowFunctionExpression:"ArrowFunctionExpression",AwaitExpression:"AwaitExpression",BlockStatement:"BlockStatement",BinaryExpression:"BinaryExpression",BreakStatement:"BreakStatement",CallExpression:"CallExpression",CatchClause:"CatchClause",ChainExpression:"ChainExpression",ClassBody:"ClassBody",ClassDeclaration:"ClassDeclaration",ClassExpression:"ClassExpression",ComprehensionBlock:"ComprehensionBlock",ComprehensionExpression:"ComprehensionExpression",ConditionalExpression:"ConditionalExpression",ContinueStatement:"ContinueStatement",DebuggerStatement:"DebuggerStatement",DirectiveStatement:"DirectiveStatement",DoWhileStatement:"DoWhileStatement",EmptyStatement:"EmptyStatement",ExportAllDeclaration:"ExportAllDeclaration",ExportDefaultDeclaration:"ExportDefaultDeclaration",ExportNamedDeclaration:"ExportNamedDeclaration",ExportSpecifier:"ExportSpecifier",ExpressionStatement:"ExpressionStatement",ForStatement:"ForStatement",ForInStatement:"ForInStatement",ForOfStatement:"ForOfStatement",FunctionDeclaration:"FunctionDeclaration",FunctionExpression:"FunctionExpression",GeneratorExpression:"GeneratorExpression",Identifier:"Identifier",IfStatement:"IfStatement",ImportExpression:"ImportExpression",ImportDeclaration:"ImportDeclaration",ImportDefaultSpecifier:"ImportDefaultSpecifier",ImportNamespaceSpecifier:"ImportNamespaceSpecifier",ImportSpecifier:"ImportSpecifier",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",MetaProperty:"MetaProperty",MethodDefinition:"MethodDefinition",ModuleSpecifier:"ModuleSpecifier",NewExpression:"NewExpression",ObjectExpression:"ObjectExpression",ObjectPattern:"ObjectPattern",PrivateIdentifier:"PrivateIdentifier",Program:"Program",Property:"Property",PropertyDefinition:"PropertyDefinition",RestElement:"RestElement",ReturnStatement:"ReturnStatement",SequenceExpression:"SequenceExpression",SpreadElement:"SpreadElement",Super:"Super",SwitchStatement:"SwitchStatement",SwitchCase:"SwitchCase",TaggedTemplateExpression:"TaggedTemplateExpression",TemplateElement:"TemplateElement",TemplateLiteral:"TemplateLiteral",ThisExpression:"ThisExpression",ThrowStatement:"ThrowStatement",TryStatement:"TryStatement",UnaryExpression:"UnaryExpression",UpdateExpression:"UpdateExpression",VariableDeclaration:"VariableDeclaration",VariableDeclarator:"VariableDeclarator",WhileStatement:"WhileStatement",WithStatement:"WithStatement",YieldExpression:"YieldExpression"},i={AssignmentExpression:["left","right"],AssignmentPattern:["left","right"],ArrayExpression:["elements"],ArrayPattern:["elements"],ArrowFunctionExpression:["params","body"],AwaitExpression:["argument"],BlockStatement:["body"],BinaryExpression:["left","right"],BreakStatement:["label"],CallExpression:["callee","arguments"],CatchClause:["param","body"],ChainExpression:["expression"],ClassBody:["body"],ClassDeclaration:["id","superClass","body"],ClassExpression:["id","superClass","body"],ComprehensionBlock:["left","right"],ComprehensionExpression:["blocks","filter","body"],ConditionalExpression:["test","consequent","alternate"],ContinueStatement:["label"],DebuggerStatement:[],DirectiveStatement:[],DoWhileStatement:["body","test"],EmptyStatement:[],ExportAllDeclaration:["source"],ExportDefaultDeclaration:["declaration"],ExportNamedDeclaration:["declaration","specifiers","source"],ExportSpecifier:["exported","local"],ExpressionStatement:["expression"],ForStatement:["init","test","update","body"],ForInStatement:["left","right","body"],ForOfStatement:["left","right","body"],FunctionDeclaration:["id","params","body"],FunctionExpression:["id","params","body"],GeneratorExpression:["blocks","filter","body"],Identifier:[],IfStatement:["test","consequent","alternate"],ImportExpression:["source"],ImportDeclaration:["specifiers","source"],ImportDefaultSpecifier:["local"],ImportNamespaceSpecifier:["local"],ImportSpecifier:["imported","local"],Literal:[],LabeledStatement:["label","body"],LogicalExpression:["left","right"],MemberExpression:["object","property"],MetaProperty:["meta","property"],MethodDefinition:["key","value"],ModuleSpecifier:[],NewExpression:["callee","arguments"],ObjectExpression:["properties"],ObjectPattern:["properties"],PrivateIdentifier:[],Program:["body"],Property:["key","value"],PropertyDefinition:["key","value"],RestElement:["argument"],ReturnStatement:["argument"],SequenceExpression:["expressions"],SpreadElement:["argument"],Super:[],SwitchStatement:["discriminant","cases"],SwitchCase:["test","consequent"],TaggedTemplateExpression:["tag","quasi"],TemplateElement:[],TemplateLiteral:["quasis","expressions"],ThisExpression:[],ThrowStatement:["argument"],TryStatement:["block","handler","finalizer"],UnaryExpression:["argument"],UpdateExpression:["argument"],VariableDeclaration:["declarations"],VariableDeclarator:["id","init"],WhileStatement:["test","body"],WithStatement:["object","body"],YieldExpression:["argument"]},r={Break:o={},Skip:a={},Remove:s={}},p.prototype.replace=function(e){this.parent[this.key]=e},p.prototype.remove=function(){return Array.isArray(this.parent)?(this.parent.splice(this.key,1),!0):(this.replace(null),!1)},c.prototype.path=function(){var e,t,n,r,i;function o(e,t){if(Array.isArray(t))for(n=0,r=t.length;n<r;++n)e.push(t[n]);else e.push(t)}if(!this.__current.path)return null;for(i=[],e=2,t=this.__leavelist.length;e<t;++e)o(i,this.__leavelist[e].path);return o(i,this.__current.path),i},c.prototype.type=function(){return this.current().type||this.__current.wrap},c.prototype.parents=function(){var e,t,n;for(n=[],e=1,t=this.__leavelist.length;e<t;++e)n.push(this.__leavelist[e].node);return n},c.prototype.current=function(){return this.__current.node},c.prototype.__execute=function(e,t){var n,r;return r=void 0,n=this.__current,this.__current=t,this.__state=null,e&&(r=e.call(this,t.node,this.__leavelist[this.__leavelist.length-1].node)),this.__current=n,r},c.prototype.notify=function(e){this.__state=e},c.prototype.skip=function(){this.notify(a)},c.prototype.break=function(){this.notify(o)},c.prototype.remove=function(){this.notify(s)},c.prototype.__initialize=function(e,t){this.visitor=t,this.root=e,this.__worklist=[],this.__leavelist=[],this.__current=null,this.__state=null,this.__fallback=null,"iteration"===t.fallback?this.__fallback=Object.keys:"function"==typeof t.fallback&&(this.__fallback=t.fallback),this.__keys=i,t.keys&&(this.__keys=Object.assign(Object.create(this.__keys),t.keys))},c.prototype.traverse=function(e,t){var n,r,i,s,l,p,c,d,y,x,_,E;for(this.__initialize(e,t),E={},n=this.__worklist,r=this.__leavelist,n.push(new u(e,null,null,null)),r.push(new u(null,null,null,null));n.length;)if((i=n.pop())!==E){if(i.node){if(p=this.__execute(t.enter,i),this.__state===o||p===o)return;if(n.push(E),r.push(i),this.__state===a||p===a)continue;if(l=(s=i.node).type||i.wrap,!(x=this.__keys[l])){if(!this.__fallback)throw new Error("Unknown node type "+l+".");x=this.__fallback(s)}for(d=x.length;(d-=1)>=0;)if(_=s[c=x[d]])if(Array.isArray(_)){for(y=_.length;(y-=1)>=0;)if(_[y]&&!m(r,_[y])){if(h(l,x[d]))i=new u(_[y],[c,y],"Property",null);else{if(!f(_[y]))continue;i=new u(_[y],[c,y],null,null)}n.push(i)}}else if(f(_)){if(m(r,_))continue;n.push(new u(_,c,null,null))}}}else if(i=r.pop(),p=this.__execute(t.leave,i),this.__state===o||p===o)return},c.prototype.replace=function(e,t){var n,r,i,l,c,m,d,y,x,_,E,g,S;function b(e){var t,r,i,o;if(e.ref.remove())for(r=e.ref.key,o=e.ref.parent,t=n.length;t--;)if((i=n[t]).ref&&i.ref.parent===o){if(i.ref.key<r)break;--i.ref.key}}for(this.__initialize(e,t),E={},n=this.__worklist,r=this.__leavelist,m=new u(e,null,null,new p(g={root:e},"root")),n.push(m),r.push(m);n.length;)if((m=n.pop())!==E){if(void 0!==(c=this.__execute(t.enter,m))&&c!==o&&c!==a&&c!==s&&(m.ref.replace(c),m.node=c),this.__state!==s&&c!==s||(b(m),m.node=null),this.__state===o||c===o)return g.root;if((i=m.node)&&(n.push(E),r.push(m),this.__state!==a&&c!==a)){if(l=i.type||m.wrap,!(x=this.__keys[l])){if(!this.__fallback)throw new Error("Unknown node type "+l+".");x=this.__fallback(i)}for(d=x.length;(d-=1)>=0;)if(_=i[S=x[d]])if(Array.isArray(_)){for(y=_.length;(y-=1)>=0;)if(_[y]){if(h(l,x[d]))m=new u(_[y],[S,y],"Property",new p(_,y));else{if(!f(_[y]))continue;m=new u(_[y],[S,y],null,new p(_,y))}n.push(m)}}else f(_)&&n.push(new u(_,S,null,new p(i,S)))}}else if(m=r.pop(),void 0!==(c=this.__execute(t.leave,m))&&c!==o&&c!==a&&c!==s&&m.ref.replace(c),this.__state!==s&&c!==s||b(m),this.__state===o||c===o)return g.root;return g.root},t.Syntax=n,t.traverse=d,t.replace=function(e,t){return(new c).replace(e,t)},t.attachComments=function(e,t,n){var i,o,a,s,p=[];if(!e.range)throw new Error("attachComments needs range information");if(!n.length){if(t.length){for(a=0,o=t.length;a<o;a+=1)(i=l(t[a])).extendedRange=[0,e.range[0]],p.push(i);e.leadingComments=p}return e}for(a=0,o=t.length;a<o;a+=1)p.push(y(l(t[a]),n));return s=0,d(e,{enter:function(e){for(var t;s<p.length&&!((t=p[s]).extendedRange[1]>e.range[0]);)t.extendedRange[1]===e.range[0]?(e.leadingComments||(e.leadingComments=[]),e.leadingComments.push(t),p.splice(s,1)):s+=1;return s===p.length?r.Break:p[s].extendedRange[0]>e.range[1]?r.Skip:void 0}}),s=0,d(e,{leave:function(e){for(var t;s<p.length&&(t=p[s],!(e.range[1]<t.extendedRange[0]));)e.range[1]===t.extendedRange[0]?(e.trailingComments||(e.trailingComments=[]),e.trailingComments.push(t),p.splice(s,1)):s+=1;return s===p.length?r.Break:p[s].extendedRange[0]>e.range[1]?r.Skip:void 0}}),e},t.VisitorKeys=i,t.VisitorOption=r,t.Controller=c,t.cloneEnvironment=function(){return e({})},t}(n)},{}]},{},[1])(1)}));
\ No newline at end of file
diff --git a/src/editor/sandbox/handleLoop.js b/src/editor/sandbox/handleLoop.js
new file mode 100644
index 0000000..8c4013e
--- /dev/null
+++ b/src/editor/sandbox/handleLoop.js
@@ -0,0 +1,86 @@
+/**
+ * This function is used to prevent the page from getting stuck
+ * for the infinite loop in the user code.
+ *
+ * @param {string} code the source code
+ */
+export default function handleLoop(code) {
+ let AST;
+ try {
+ AST = acorn.parse(code, {
+ ecmaVersion: 'latest',
+ sourceType: 'script'
+ });
+ } catch (e) {
+ console.error('failed to parse code', e);
+ return code;
+ }
+
+ /**
+ * Temporarily store the range of positions where the code needs to be inserted
+ */
+ const fragments = [];
+ /**
+ * loopID is used to mark the loop
+ */
+ let loopID = 1;
+ /**
+ * Mark the code that needs to be inserted when looping
+ */
+ const insertCode = {
+ setMonitor: 'LoopController.loopMonitor(%d);',
+ delMonitor: ';LoopController.delLoop(%d);'
+ };
+
+ /**
+ * Traverse the AST to find the loop position
+ */
+ estraverse.traverse(AST, {
+ enter(node) {
+ switch (node.type) {
+ case 'WhileStatement':
+ case 'DoWhileStatement':
+ case 'ForStatement':
+ case 'ForInStatement':
+ case 'ForOfStatement':
+ /**
+ * Gets the head and tail of the loop body
+ */
+ let { start, end } = node.body;
+ start++;
+ let pre = insertCode.setMonitor.replace('%d', loopID);
+ let aft = '';
+ /**
+ * If the body of the loop is not enveloped by {} and is indented, we need to manually add {}
+ */
+ if (node.body.type !== 'BlockStatement') {
+ pre = '{' + pre;
+ aft = '}';
+ --start;
+ }
+ fragments.push({ pos: start, str: pre });
+ fragments.push({ pos: end, str: aft });
+ fragments.push({
+ pos: node.end,
+ str: insertCode.delMonitor.replace('%d', loopID)
+ });
+ ++loopID;
+ break;
+ default:
+ break;
+ }
+ }
+ });
+
+ /**
+ * Insert code to corresponding position
+ */
+ fragments
+ .sort((a, b) => b.pos - a.pos)
+ .forEach((fragment) => {
+ code =
+ code.slice(0, fragment.pos) + fragment.str + code.slice(fragment.pos);
+ });
+
+ return code;
+}
diff --git a/src/editor/sandbox/index.js b/src/editor/sandbox/index.js
new file mode 100644
index 0000000..6d5a257
--- /dev/null
+++ b/src/editor/sandbox/index.js
@@ -0,0 +1,101 @@
+import srcdoc from './srcdoc.html';
+import handleLoop from './handleLoop';
+import setup from './setup';
+import loopController from 'raw-loader!./loopController';
+import showDebugDirtyRect from 'raw-loader!../../dep/showDebugDirtyRect';
+import estraverse from 'raw-loader!./estraverse.browser';
+
+export function createSandbox(
+ container,
+ scripts,
+ onload,
+ onerror,
+ onCodeError,
+ onOptionUpdated
+) {
+ scripts = scripts || [];
+ scripts.push(
+ { content: estraverse },
+ { content: loopController },
+ {
+ content: `
+ (function(){
+ ${handleLoop}
+ ${showDebugDirtyRect}
+ ${setup}
+ setup()
+ })()
+ `
+ }
+ );
+
+ const sandbox = document.createElement('iframe');
+ sandbox.setAttribute(
+ 'sandbox',
+ [
+ 'allow-forms',
+ 'allow-modals',
+ 'allow-pointer-lock',
+ 'allow-popups',
+ 'allow-same-origin',
+ 'allow-scripts',
+ 'allow-top-navigation-by-user-activation',
+ 'allow-downloads'
+ ].join(' ')
+ );
+ sandbox.style.cssText = 'width:100%;height:100%;border:none;background:none';
+ sandbox.srcdoc = srcdoc.replace(
+ '<!--SCRIPTS-->',
+ scripts
+ .map((script) =>
+ script.content
+ ? `<script>${script.content}</script>`
+ : `<script src="${script.src}"></script>`
+ )
+ .join('')
+ );
+ sandbox.onload = onload;
+ sandbox.onerror = onerror;
+ container.appendChild(sandbox);
+
+ function hanldeMessage(e) {
+ const evt = e.data.evt;
+ console.log('event from sandbox', evt);
+ switch (evt) {
+ case 'optionUpdated':
+ onOptionUpdated(e.data.option, e.data.updateTime);
+ break;
+ // case 'error':
+ // case 'unhandledRejection':
+ // onerror();
+ // break;
+ case 'codeError':
+ onCodeError();
+ break;
+ default:
+ break;
+ }
+ }
+
+ function sendMessage(action, argumentMap) {
+ sandbox.contentWindow.postMessage({ action, ...argumentMap }, '*');
+ }
+
+ window.addEventListener('message', hanldeMessage, false);
+
+ return {
+ dispose() {
+ sendMessage('dispose');
+ window.removeEventListener('message', hanldeMessage);
+ },
+ run(store, recreateInstance) {
+ sendMessage('run', { store, recreateInstance });
+ },
+ screenshot(filename) {
+ sendMessage('screenshot', { filename });
+ },
+ getOption() {
+ sendMessage('getOption');
+ }
+ };
+}
diff --git a/src/editor/sandbox/loopController.js b/src/editor/sandbox/loopController.js
new file mode 100644
index 0000000..96ce95f
--- /dev/null
+++ b/src/editor/sandbox/loopController.js
@@ -0,0 +1,60 @@
+const LoopController = {
+ _config: {
+ maxExecTimePerLoop: 3e3,
+ maxLoopCount: 1e6
+ },
+ loopMap: new Map(),
+ initLoop(loopID) {
+ this.setLoop(loopID, {
+ isInit: true,
+ totalExecTime: 0,
+ startTime: Date.now(),
+ count: 0
+ });
+ },
+ getLoop(loopID) {
+ return this.loopMap.get(loopID);
+ },
+ setLoop(loopID, loop) {
+ this.loopMap.set(loopID, loop);
+ },
+ delLoop(loopID) {
+ this.loopMap.delete(loopID);
+ },
+ clearLoops() {
+ this.loopMap.clear();
+ },
+ exitLoop(loopID) {
+ this.delLoop(loopID);
+ },
+ calcLoop(loopID) {
+ if (this.loopMap.has(loopID)) {
+ let { isInit, totalExecTime, startTime, count } = this.getLoop(loopID);
+ if (isInit) {
+ totalExecTime = Date.now() - startTime;
+ count++;
+ this.setLoop(loopID, {
+ isInit,
+ totalExecTime,
+ startTime,
+ count
+ });
+ } else {
+ this.initLoop(loopID);
+ }
+ } else {
+ this.initLoop(loopID);
+ }
+ },
+ loopMonitor(loopID) {
+ this.calcLoop(loopID);
+ const loop = this.getLoop(loopID);
+ const { maxExecTimePerLoop, maxLoopCount } = this._config;
+ if (loop.totalExecTime > maxExecTimePerLoop && loop.count > maxLoopCount) {
+ this.clearLoops();
+ throw new Error(
+ 'The loop executes so many times that ECharts has to exit the loop in case the page gets stuck'
+ );
+ }
+ }
+};
diff --git a/src/editor/sandbox/setup.js b/src/editor/sandbox/setup.js
new file mode 100644
index 0000000..54880ed
--- /dev/null
+++ b/src/editor/sandbox/setup.js
@@ -0,0 +1,248 @@
+export default function setup() {
+ const sendMessage = function (payload) {
+ console.log('sendMessage', payload);
+ parent.postMessage(payload, '*');
+ };
+
+ const chartStyleEl = document.head.querySelector('#chart-styles');
+
+ const intervalIdList = [];
+ const timeoutIdList = [];
+
+ const nativeSetTimeout = window.setTimeout;
+ const nativeSetInterval = window.setInterval;
+
+ function setTimeout(func, delay) {
+ const id = nativeSetTimeout(func, delay);
+ timeoutIdList.push(id);
+ return id;
+ }
+
+ function setInterval(func, interval) {
+ const id = nativeSetInterval(func, interval);
+ intervalIdList.push(id);
+ return id;
+ }
+
+ function clearTimers() {
+ intervalIdList.forEach(clearInterval);
+ timeoutIdList.forEach(clearTimeout);
+ intervalIdList.length = 0;
+ timeoutIdList.length = 0;
+ }
+
+ const chartEvents = [];
+
+ function wrapChartMethods(chart) {
+ const nativeOn = chart.on;
+ const nativeSetOption = chart.setOption;
+
+ chart.on = function (eventName) {
+ const res = nativeOn.apply(chart, arguments);
+ chartEvents.push(eventName);
+ return res;
+ };
+
+ chart.setOption = function () {
+ const startTime = performance.now();
+ const res = nativeSetOption.apply(this, arguments);
+ const endTime = performance.now();
+ sendMessage({
+ evt: 'optionUpdated',
+ option: chart.getOption(),
+ updateTime: endTime - startTime
+ });
+ return res;
+ };
+ }
+
+ function clearChartEvents(chart) {
+ chart && chartEvents.forEach(chart.off);
+ chartEvents.length = 0;
+ }
+
+ let appStore;
+ let chartInstance;
+ let appEnv = {};
+ let gui;
+
+ const api = {
+ dispose() {
+ if (chartInstance) {
+ chartInstance.dispose();
+ chartInstance = null;
+ appStore = null;
+ }
+ },
+
+ screenshot({ filename }) {
+ console.log('screenshot');
+ const dataURL = chartInstance.getDataURL({
+ excludeComponents: ['toolbox']
+ });
+ const $a = document.createElement('a');
+ $a.download = filename;
+ $a.target = '_blank';
+ $a.href = dataURL;
+ $a.click();
+ },
+
+ getOption() {
+ return chartInstance.getOption();
+ },
+
+ run({ store, recreateInstance }) {
+ if (!chartInstance || recreateInstance) {
+ this.dispose();
+ chartInstance = echarts.init(
+ document.getElementById('chart-container'),
+ store.darkMode ? 'dark' : '',
+ {
+ renderer: store.renderer,
+ useDirtyRect: store.useDirtyRect
+ }
+ );
+ if (store.useDirtyRect && store.renderer === 'canvas') {
+ try {
+ showDebugDirtyRect(chartInstance.getZr(), {
+ autoHideDelay: 500
+ });
+ } catch (e) {
+ console.error('failed to show debug dirty rect', e);
+ }
+ }
+ window.addEventListener('resize', chartInstance.resize);
+ wrapChartMethods(chartInstance);
+ }
+
+ // TODO Scope the variables in component.
+ clearTimers();
+ clearChartEvents(chartInstance);
+ // Reset
+ appEnv.config = null;
+ appStore = store;
+
+ try {
+ // run the code
+ const compiledCode = store.runCode
+ // Replace random method
+ .replace(/Math.random\(\)/g, '__ECHARTS_EXAMPLE_RANDOM__()');
+ const echartsExampleRandom = Math.seedrandom(store.randomSeed);
+
+ const func = new Function(
+ 'myChart',
+ 'app',
+ 'setTimeout',
+ 'setInterval',
+ 'ROOT_PATH',
+ '__ECHARTS_EXAMPLE_RANDOM__',
+ 'top',
+ 'parent',
+ // PENDING: create a single panel for CSS code?
+ 'var css, option;\n' +
+ handleLoop(compiledCode) +
+ '\nreturn [option, css];'
+ );
+
+ const res = func(
+ chartInstance,
+ appEnv,
+ setTimeout,
+ setInterval,
+ store.cdnRoot,
+ // prevent someone from trying to close the parent window via top/parent.close()
+ // or any other unexpected and dangerous behaviors
+ void 0,
+ void 0,
+ echartsExampleRandom
+ );
+ chartStyleEl.textContent = res[1] || '';
+
+ const option = res[0];
+ echarts.util.isObject(option) && chartInstance.setOption(option, true);
+ } catch (e) {
+ console.error('failed to run code', e);
+ sendMessage({ evt: 'codeError' });
+ }
+
+ if (gui) {
+ $(gui.domElement).remove();
+ gui.destroy();
+ gui = null;
+ }
+
+ if (appEnv.config) {
+ gui = new dat.GUI({ autoPlace: false });
+ $(gui.domElement).css({
+ position: 'absolute',
+ right: 5,
+ top: 0,
+ zIndex: 1000
+ });
+ document.body.append(gui.domElement);
+
+ const configParams = appEnv.configParams || {};
+ const config = appEnv.config;
+ for (const name in config) {
+ const value = config[name];
+ if (name !== 'onChange' && name !== 'onFinishChange') {
+ let isColor;
+ let controller;
+ const configVal = configParams[name];
+ if (configVal) {
+ if (configVal.options) {
+ controller = gui.add(config, name, configVal.options);
+ } else if (configVal.min != null) {
+ controller = gui.add(
+ config,
+ name,
+ configVal.min,
+ configVal.max
+ );
+ }
+ }
+ if (typeof value === 'string') {
+ try {
+ const colorArr = echarts.color.parse(value);
+ if ((isColor = !!colorArr)) {
+ value = echarts.color.stringify(colorArr, 'rgba');
+ }
+ } catch (e) {}
+ }
+ if (!controller) {
+ controller = gui[isColor ? 'addColor' : 'add'](config, name);
+ }
+ config.onChange && controller.onChange(config.onChange);
+ config.onFinishChange &&
+ controller.onFinishChange(config.onFinishChange);
+ }
+ }
+ }
+ }
+ };
+
+ echarts.registerPreprocessor(function (option) {
+ if (appStore.enableDecal) {
+ option.aria = option.aria || {};
+ option.aria.decal = option.aria.decal || {};
+ option.aria.decal.show = true;
+ option.aria.show = option.aria.enabled = true;
+ }
+ });
+
+ function handleMessage(ev) {
+ console.log('handle message in sandbox', ev);
+ // const { action, ...args } = ev.data;
+ const action = ev.data.action;
+ delete ev.data.action;
+ typeof api[action] === 'function' && api[action].apply(api, [ev.data]);
+ }
+
+ window.addEventListener('message', handleMessage, false);
+ window.addEventListener('error', function () {
+ sendMessage({ evt: 'error' });
+ });
+ window.addEventListener('unhandledrejection', function () {
+ sendMessage({ evt: 'unhandledRejection' });
+ });
+}
diff --git a/src/editor/sandbox/srcdoc.html b/src/editor/sandbox/srcdoc.html
new file mode 100644
index 0000000..c5d8851
--- /dev/null
+++ b/src/editor/sandbox/srcdoc.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ * {
+ margin: 0;
+ padding: 0;
+ }
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
+ Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+ }
+ #chart-container {
+ position: relative;
+ height: 100vh;
+ overflow: hidden;
+ }
+ .ec-debug-dirty-rect {
+ background-color: rgba(255, 0, 0, 0.2) !important;
+ border: 1px solid red !important;
+ box-sizing: border-box;
+ }
+ </style>
+ <style id="chart-styles"></style>
+ </head>
+ <body>
+ <div id="chart-container"></div>
+ <script src="https://cdn.jsdelivr.net/npm/jquery"></script>
+ <script src="https://cdn.jsdelivr.net/npm/seedrandom@3.0.5/seedrandom.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/acorn@8.7.1/dist/acorn.js"></script>
+ <!--SCRIPTS-->
+ </body>
+</html>