diff --git a/CTFd/themes/admin/assets/css/admin.scss b/CTFd/themes/admin/assets/css/admin.scss
index 0f28049d..e1f1acf7 100644
--- a/CTFd/themes/admin/assets/css/admin.scss
+++ b/CTFd/themes/admin/assets/css/admin.scss
@@ -1,7 +1,7 @@
@import "includes/sticky-footer.css";
#score-graph {
- height: 450px;
+ min-height: 400px;
display: block;
clear: both;
}
@@ -12,17 +12,22 @@
}
#keys-pie-graph {
- height: 400px;
+ min-height: 400px;
display: block;
}
#categories-pie-graph {
- height: 400px;
+ min-height: 400px;
display: block;
}
#solve-percentages-graph {
- height: 400px;
+ min-height: 400px;
+ display: block;
+}
+
+#score-distribution-graph {
+ min-height: 400px;
display: block;
}
diff --git a/CTFd/themes/admin/assets/js/pages/statistics.js b/CTFd/themes/admin/assets/js/pages/statistics.js
index f93478d0..7bc354a6 100644
--- a/CTFd/themes/admin/assets/js/pages/statistics.js
+++ b/CTFd/themes/admin/assets/js/pages/statistics.js
@@ -2,29 +2,16 @@ import "./main";
import "core/utils";
import CTFd from "core/CTFd";
import $ from "jquery";
-import Plotly from "plotly.js-basic-dist";
-// eslint-disable-next-line no-unused-vars
-import { createGraph, updateGraph } from "core/graphs";
+import echarts from "echarts/dist/echarts-en.common";
+import { colorHash } from "core/utils";
const graph_configs = {
"#solves-graph": {
- layout: annotations => ({
- title: "Solve Counts",
- annotations: annotations,
- xaxis: {
- title: "Challenge Name"
- },
- yaxis: {
- title: "Amount of Solves"
- }
- }),
- fn: () => "CTFd_solves_" + new Date().toISOString().slice(0, 19),
data: () => CTFd.api.get_challenge_solve_statistics(),
format: response => {
const data = response.data;
const chals = [];
const counts = [];
- const annotations = [];
const solves = {};
for (let c = 0; c < data.length; c++) {
solves[data[c]["id"]] = {
@@ -40,63 +27,155 @@ const graph_configs = {
$.each(solves_order, function(key, value) {
chals.push(solves[value].name);
counts.push(solves[value].solves);
- const result = {
- x: solves[value].name,
- y: solves[value].solves,
- text: solves[value].solves,
- xanchor: "center",
- yanchor: "bottom",
- showarrow: false
- };
- annotations.push(result);
});
- return [
- {
- type: "bar",
- x: chals,
- y: counts,
- text: counts,
- orientation: "v"
+ const option = {
+ title: {
+ left: "center",
+ text: "Solve Counts"
},
- annotations
- ];
+ tooltip: {
+ trigger: "item"
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ mark: { show: true },
+ dataView: { show: true, readOnly: false },
+ magicType: { show: true, type: ["line", "bar"] },
+ restore: { show: true },
+ saveAsImage: { show: true }
+ }
+ },
+ xAxis: {
+ name: "Solve Count",
+ nameLocation: "middle",
+ type: "value"
+ },
+ yAxis: {
+ name: "Challenge Name",
+ nameLocation: "middle",
+ nameGap: 60,
+ type: "category",
+ data: chals,
+ axisLabel: {
+ interval: 0,
+ rotate: 0 //If the label names are too long you can manage this by rotating the label.
+ }
+ },
+ dataZoom: [
+ {
+ id: "dataZoomY",
+ type: "slider",
+ yAxisIndex: [0],
+ filterMode: "empty"
+ }
+ ],
+ series: [
+ {
+ itemStyle: { normal: { color: "#1f76b4" } },
+ data: counts,
+ type: "bar"
+ }
+ ]
+ };
+
+ return option;
}
},
"#keys-pie-graph": {
- layout: () => ({
- title: "Submission Percentages"
- }),
- fn: () => "CTFd_submissions_" + new Date().toISOString().slice(0, 19),
data: () => CTFd.api.get_submission_property_counts({ column: "type" }),
format: response => {
const data = response.data;
const solves = data["correct"];
const fails = data["incorrect"];
- return [
- {
- values: [solves, fails],
- labels: ["Correct", "Incorrect"],
- marker: {
- colors: ["rgb(0, 209, 64)", "rgb(207, 38, 0)"]
- },
- text: ["Solves", "Fails"],
- hole: 0.4,
- type: "pie"
+ let option = {
+ title: {
+ left: "center",
+ text: "Submission Percentages"
},
- null
- ];
+ tooltip: {
+ trigger: "item"
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ dataView: { show: true, readOnly: false },
+ saveAsImage: {}
+ }
+ },
+ legend: {
+ orient: "horizontal",
+ bottom: 0,
+ data: ["Fails", "Solves"]
+ },
+ series: [
+ {
+ name: "Submission Percentages",
+ type: "pie",
+ radius: ["30%", "50%"],
+ avoidLabelOverlap: false,
+ label: {
+ show: false,
+ position: "center"
+ },
+ itemStyle: {
+ normal: {
+ label: {
+ show: true,
+ formatter: function(data) {
+ return `${data.name} - ${data.value} (${data.percent}%)`;
+ }
+ },
+ labelLine: {
+ show: true
+ }
+ },
+ emphasis: {
+ label: {
+ show: true,
+ position: "center",
+ textStyle: {
+ fontSize: "14",
+ fontWeight: "normal"
+ }
+ }
+ }
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: "30",
+ fontWeight: "bold"
+ }
+ },
+ labelLine: {
+ show: false
+ },
+ data: [
+ {
+ value: fails,
+ name: "Fails",
+ itemStyle: { color: "rgb(207, 38, 0)" }
+ },
+ {
+ value: solves,
+ name: "Solves",
+ itemStyle: { color: "rgb(0, 209, 64)" }
+ }
+ ]
+ }
+ ]
+ };
+
+ return option;
}
},
"#categories-pie-graph": {
- layout: () => ({
- title: "Category Breakdown"
- }),
data: () => CTFd.api.get_challenge_property_counts({ column: "category" }),
- fn: () => "CTFd_categories_" + new Date().toISOString().slice(0, 19),
format: response => {
const data = response.data;
@@ -115,15 +194,84 @@ const graph_configs = {
count.push(data[i].count);
}
- return [
- {
- values: count,
- labels: categories,
- hole: 0.4,
- type: "pie"
+ let option = {
+ title: {
+ left: "center",
+ text: "Category Breakdown"
},
- null
- ];
+ tooltip: {
+ trigger: "item"
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ dataView: { show: true, readOnly: false },
+ saveAsImage: {}
+ }
+ },
+ legend: {
+ orient: "horizontal",
+ bottom: 0,
+ data: []
+ },
+ series: [
+ {
+ name: "Category Breakdown",
+ type: "pie",
+ radius: ["30%", "50%"],
+ avoidLabelOverlap: false,
+ label: {
+ show: false,
+ position: "center"
+ },
+ itemStyle: {
+ normal: {
+ label: {
+ show: true,
+ formatter: function(data) {
+ return `${data.name} - ${data.value} (${data.percent}%)`;
+ }
+ },
+ labelLine: {
+ show: true
+ }
+ },
+ emphasis: {
+ label: {
+ show: true,
+ position: "center",
+ textStyle: {
+ fontSize: "14",
+ fontWeight: "normal"
+ }
+ }
+ }
+ },
+ emphasis: {
+ label: {
+ show: true,
+ fontSize: "30",
+ fontWeight: "bold"
+ }
+ },
+ labelLine: {
+ show: false
+ },
+ data: []
+ }
+ ]
+ };
+
+ categories.forEach((category, index) => {
+ option.legend.data.push(category);
+ option.series[0].data.push({
+ value: count[index],
+ name: category,
+ itemStyle: { color: colorHash(category) }
+ });
+ });
+
+ return option;
}
},
@@ -143,8 +291,6 @@ const graph_configs = {
annotations: annotations
}),
data: () => CTFd.api.get_challenge_solve_percentages(),
- fn: () =>
- "CTFd_challenge_percentages_" + new Date().toISOString().slice(0, 19),
format: response => {
const data = response.data;
@@ -168,15 +314,61 @@ const graph_configs = {
annotations.push(result);
}
- return [
- {
- type: "bar",
- x: names,
- y: percents,
- orientation: "v"
+ const option = {
+ title: {
+ left: "center",
+ text: "Solve Percentages per Challenge"
},
- annotations
- ];
+ tooltip: {
+ trigger: "item",
+ formatter: function(data) {
+ return `${data.name} - ${(Math.round(data.value * 10) / 10).toFixed(
+ 1
+ )}%`;
+ }
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ mark: { show: true },
+ dataView: { show: true, readOnly: false },
+ magicType: { show: true, type: ["line", "bar"] },
+ restore: { show: true },
+ saveAsImage: { show: true }
+ }
+ },
+ xAxis: {
+ name: "Challenge Name",
+ nameGap: 40,
+ nameLocation: "middle",
+ type: "category",
+ data: names,
+ axisLabel: {
+ interval: 0,
+ rotate: 50
+ }
+ },
+ yAxis: {
+ name: "Percentage of {0} (%)".format(
+ CTFd.config.userMode.charAt(0).toUpperCase() +
+ CTFd.config.userMode.slice(1)
+ ),
+ nameGap: 50,
+ nameLocation: "middle",
+ type: "value",
+ min: 0,
+ max: 100
+ },
+ series: [
+ {
+ itemStyle: { normal: { color: "#1f76b4" } },
+ data: percents,
+ type: "bar"
+ }
+ ]
+ };
+
+ return option;
}
},
@@ -202,8 +394,6 @@ const graph_configs = {
) {
return response.json();
}),
- fn: () =>
- "CTFd_score_distribution_" + new Date().toISOString().slice(0, 19),
format: response => {
const data = response.data.brackets;
const keys = [];
@@ -222,36 +412,73 @@ const graph_configs = {
start = key;
});
- return [
- {
- type: "bar",
- x: brackets,
- y: sizes,
- orientation: "v"
- }
- ];
+ const option = {
+ title: {
+ left: "center",
+ text: "Score Distribution"
+ },
+ tooltip: {
+ trigger: "item"
+ },
+ toolbox: {
+ show: true,
+ feature: {
+ mark: { show: true },
+ dataView: { show: true, readOnly: false },
+ magicType: { show: true, type: ["line", "bar"] },
+ restore: { show: true },
+ saveAsImage: { show: true }
+ }
+ },
+ xAxis: {
+ name: "Score Bracket",
+ nameGap: 40,
+ nameLocation: "middle",
+ type: "category",
+ data: brackets
+ },
+ yAxis: {
+ name: "Number of {0}".format(
+ CTFd.config.userMode.charAt(0).toUpperCase() +
+ CTFd.config.userMode.slice(1)
+ ),
+ nameGap: 50,
+ nameLocation: "middle",
+ type: "value"
+ },
+ series: [
+ {
+ itemStyle: { normal: { color: "#1f76b4" } },
+ data: sizes,
+ type: "bar"
+ }
+ ]
+ };
+
+ return option;
}
}
};
-const config = {
- displaylogo: false,
- responsive: true
-};
-
const createGraphs = () => {
for (let key in graph_configs) {
const cfg = graph_configs[key];
const $elem = $(key);
$elem.empty();
- $elem[0].fn = cfg.fn();
+
+ let chart = echarts.init(document.querySelector(key));
cfg
.data()
.then(cfg.format)
- .then(([data, annotations]) => {
- Plotly.newPlot($elem[0], [data], cfg.layout(annotations), config);
+ .then(option => {
+ chart.setOption(option);
+ $(window).on("resize", function() {
+ if (chart != null && chart != undefined) {
+ chart.resize();
+ }
+ });
});
}
};
@@ -259,13 +486,12 @@ const createGraphs = () => {
function updateGraphs() {
for (let key in graph_configs) {
const cfg = graph_configs[key];
- const $elem = $(key);
+ let chart = echarts.init(document.querySelector(key));
cfg
.data()
.then(cfg.format)
- .then(([data, annotations]) => {
- // FIXME: Pass annotations
- Plotly.react($elem[0], [data], cfg.layout(annotations), config);
+ .then(option => {
+ chart.setOption(option);
});
}
}
diff --git a/CTFd/themes/admin/static/css/admin.dev.css b/CTFd/themes/admin/static/css/admin.dev.css
index 41032e06..e246e895 100644
--- a/CTFd/themes/admin/static/css/admin.dev.css
+++ b/CTFd/themes/admin/static/css/admin.dev.css
@@ -1,4 +1,4 @@
html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal !important;z-index:-20}
-#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{height:400px;display:block}#categories-pie-graph{height:400px;display:block}#solve-percentages-graph{height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}[data-href]{cursor:pointer}.sort-col{cursor:pointer}input[type="checkbox"]{cursor:pointer}
+#score-graph{min-height:400px;display:block;clear:both}#solves-graph{display:block;height:350px}#keys-pie-graph{min-height:400px;display:block}#categories-pie-graph{min-height:400px;display:block}#solve-percentages-graph{min-height:400px;display:block}#score-distribution-graph{min-height:400px;display:block}.no-decoration{color:inherit !important;text-decoration:none !important}.no-decoration:hover{color:inherit !important;text-decoration:none !important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:"Avenir Next", "Helvetica Neue", Helvetica, Arial, sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,0.1) !important}[data-href]{cursor:pointer}.sort-col{cursor:pointer}input[type="checkbox"]{cursor:pointer}
diff --git a/CTFd/themes/admin/static/css/admin.min.css b/CTFd/themes/admin/static/css/admin.min.css
index d58df70a..8d4e944b 100644
--- a/CTFd/themes/admin/static/css/admin.min.css
+++ b/CTFd/themes/admin/static/css/admin.min.css
@@ -1 +1 @@
-html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal!important;z-index:-20}#score-graph{height:450px;display:block;clear:both}#solves-graph{display:block;height:350px}#categories-pie-graph,#keys-pie-graph,#solve-percentages-graph{height:400px;display:block}.no-decoration,.no-decoration:hover{color:inherit!important;text-decoration:none!important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:Avenir Next,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,.1)!important}.sort-col,[data-href],input[type=checkbox]{cursor:pointer}
\ No newline at end of file
+html{position:relative;min-height:100%}body{margin-bottom:60px}.footer{position:absolute;bottom:1px;width:100%;height:60px;line-height:normal!important;z-index:-20}#score-graph{min-height:400px;display:block;clear:both}#solves-graph{display:block;height:350px}#categories-pie-graph,#keys-pie-graph,#score-distribution-graph,#solve-percentages-graph{min-height:400px;display:block}.no-decoration,.no-decoration:hover{color:inherit!important;text-decoration:none!important}.table td,.table th{vertical-align:inherit}pre{white-space:pre-wrap;margin:0;padding:0}.form-control{position:relative;display:block;border-radius:0;font-weight:400;font-family:Avenir Next,Helvetica Neue,Helvetica,Arial,sans-serif;-webkit-appearance:none}tbody tr:hover{background-color:rgba(0,0,0,.1)!important}.sort-col,[data-href],input[type=checkbox]{cursor:pointer}
\ No newline at end of file
diff --git a/CTFd/themes/admin/static/js/core.dev.js b/CTFd/themes/admin/static/js/core.dev.js
index 2d387b9a..8879cf7f 100644
--- a/CTFd/themes/admin/static/js/core.dev.js
+++ b/CTFd/themes/admin/static/js/core.dev.js
@@ -128,7 +128,7 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n
/***/ (function(module, exports, __webpack_require__) {
;
-eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.WindowController = WindowController;\nexports.colorHash = colorHash;\nexports.htmlEntities = htmlEntities;\nexports.cumulativeSum = cumulativeSum;\nexports.init_notification_counter = init_notification_counter;\nexports.set_notification_counter = set_notification_counter;\nexports.inc_notification_counter = inc_notification_counter;\nexports.dec_notification_counter = dec_notification_counter;\nexports.clear_notification_counter = clear_notification_counter;\nexports.copyToClipboard = copyToClipboard;\nexports.makeSortableTables = makeSortableTables;\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_jquery.default.fn.serializeJSON = function (omit_nulls) {\n var params = {};\n var form = (0, _jquery.default)(this);\n var values = form.serializeArray();\n values = values.concat(form.find(\"input[type=checkbox]:checked\").map(function () {\n return {\n name: this.name,\n value: true\n };\n }).get());\n values = values.concat(form.find(\"input[type=checkbox]:not(:checked)\").map(function () {\n return {\n name: this.name,\n value: false\n };\n }).get());\n values.map(function (x) {\n if (omit_nulls) {\n if (x.value !== null && x.value !== \"\") {\n params[x.name] = x.value;\n } else {\n var input = form.find(\":input[name=\".concat(x.name, \"]\"));\n\n if (input.data(\"initial\") !== input.val()) {\n params[x.name] = x.value;\n }\n }\n } else {\n params[x.name] = x.value;\n }\n });\n return params;\n}; //http://stackoverflow.com/a/2648463 - wizardry!\n\n\nString.prototype.format = String.prototype.f = function () {\n var s = this,\n i = arguments.length;\n\n while (i--) {\n s = s.replace(new RegExp(\"\\\\{\" + i + \"\\\\}\", \"gm\"), arguments[i]);\n }\n\n return s;\n}; //http://stackoverflow.com/a/7616484\n\n\nString.prototype.hashCode = function () {\n var hash = 0,\n i,\n chr,\n len;\n if (this.length == 0) return hash;\n\n for (i = 0, len = this.length; i < len; i++) {\n chr = this.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n\n return hash;\n}; // https://gist.github.com/neilj/4146038\n// https://fastmail.blog/2012/11/26/inter-tab-communication-using-local-storage/\n\n\nfunction WindowController() {\n this.id = Math.random();\n this.isMaster = false;\n this.others = {};\n window.addEventListener(\"storage\", this, false);\n window.addEventListener(\"unload\", this, false);\n this.broadcast(\"hello\");\n var that = this;\n\n var check = function check() {\n that.check();\n that._checkTimeout = setTimeout(check, 9000);\n };\n\n var ping = function ping() {\n that.sendPing();\n that._pingTimeout = setTimeout(ping, 17000);\n };\n\n this._checkTimeout = setTimeout(check, 500);\n this._pingTimeout = setTimeout(ping, 17000);\n}\n\nWindowController.prototype.destroy = function () {\n clearTimeout(this._pingTimeout);\n clearTimeout(this._checkTimeout);\n window.removeEventListener(\"storage\", this, false);\n window.removeEventListener(\"unload\", this, false);\n this.broadcast(\"bye\");\n};\n\nWindowController.prototype.handleEvent = function (event) {\n if (event.type === \"unload\") {\n this.destroy();\n } else if (event.key === \"broadcast\") {\n try {\n var data = JSON.parse(event.newValue);\n\n if (data.id !== this.id) {\n this[data.type](data);\n }\n } catch (error) {// Ignore error\n }\n }\n};\n\nWindowController.prototype.sendPing = function () {\n this.broadcast(\"ping\");\n};\n\nWindowController.prototype.hello = function (event) {\n this.ping(event);\n\n if (event.id < this.id) {\n this.check();\n } else {\n this.sendPing();\n }\n};\n\nWindowController.prototype.ping = function (event) {\n this.others[event.id] = +new Date();\n};\n\nWindowController.prototype.bye = function (event) {\n delete this.others[event.id];\n this.check();\n};\n\nWindowController.prototype.check = function (_event) {\n var now = +new Date(),\n takeMaster = true,\n id;\n\n for (id in this.others) {\n if (this.others[id] + 23000 < now) {\n delete this.others[id];\n } else if (id < this.id) {\n takeMaster = false;\n }\n }\n\n if (this.isMaster !== takeMaster) {\n this.isMaster = takeMaster;\n this.masterDidChange();\n }\n};\n\nWindowController.prototype.masterDidChange = function () {};\n\nWindowController.prototype.broadcast = function (type, data) {\n var event = {\n id: this.id,\n type: type\n };\n\n for (var x in data) {\n event[x] = data[x];\n }\n\n try {\n localStorage.setItem(\"broadcast\", JSON.stringify(event));\n } catch (error) {\n // eslint-disable-next-line no-console\n console.log(error);\n }\n};\n\nfunction colorHash(str) {\n var hash = 0;\n\n for (var i = 0; i < str.length; i++) {\n hash = str.charCodeAt(i) + ((hash << 5) - hash);\n }\n\n var colour = \"#\";\n\n for (var _i = 0; _i < 3; _i++) {\n var value = hash >> _i * 8 & 0xff;\n colour += (\"00\" + value.toString(16)).substr(-2);\n }\n\n return colour;\n}\n\nfunction htmlEntities(string) {\n return (0, _jquery.default)(\"
\").text(string).html();\n}\n\nfunction cumulativeSum(arr) {\n var result = arr.concat();\n\n for (var i = 0; i < arr.length; i++) {\n result[i] = arr.slice(0, i + 1).reduce(function (p, i) {\n return p + i;\n });\n }\n\n return result;\n}\n\nvar storage = window.localStorage;\nvar counter_key = \"unread_notifications\";\n\nfunction init_notification_counter() {\n var count = storage.getItem(counter_key);\n\n if (count === null) {\n storage.setItem(counter_key, 0);\n } else {\n if (count > 0) {\n (0, _jquery.default)(\".badge-notification\").text(count);\n }\n }\n}\n\nfunction set_notification_counter(count) {\n storage.setItem(counter_key, count);\n}\n\nfunction inc_notification_counter() {\n var count = storage.getItem(counter_key) || 0;\n storage.setItem(counter_key, ++count);\n (0, _jquery.default)(\".badge-notification\").text(count);\n}\n\nfunction dec_notification_counter() {\n var count = storage.getItem(counter_key) || 0;\n\n if (count > 0) {\n storage.setItem(counter_key, --count);\n (0, _jquery.default)(\".badge-notification\").text(count);\n } // Always clear if count is 0\n\n\n if (count == 0) {\n clear_notification_counter();\n }\n}\n\nfunction clear_notification_counter() {\n storage.setItem(counter_key, 0);\n (0, _jquery.default)(\".badge-notification\").empty();\n}\n\nfunction copyToClipboard(event, selector) {\n // Select element\n (0, _jquery.default)(selector).select(); // Copy to clipboard\n\n document.execCommand(\"copy\"); // Show tooltip to user\n\n (0, _jquery.default)(event.target).tooltip({\n title: \"Copied!\",\n trigger: \"manual\"\n });\n (0, _jquery.default)(event.target).tooltip(\"show\");\n setTimeout(function () {\n (0, _jquery.default)(event.target).tooltip(\"hide\");\n }, 1500);\n}\n\nfunction makeSortableTables() {\n (0, _jquery.default)(\"th.sort-col\").append(\" \");\n (0, _jquery.default)(\"th.sort-col\").click(function () {\n var table = (0, _jquery.default)(this).parents(\"table\").eq(0);\n var rows = table.find(\"tr:gt(0)\").toArray().sort(comparer((0, _jquery.default)(this).index()));\n this.asc = !this.asc;\n\n if (!this.asc) {\n rows = rows.reverse();\n }\n\n for (var i = 0; i < rows.length; i++) {\n table.append(rows[i]);\n }\n });\n\n function comparer(index) {\n return function (a, b) {\n var valA = getCellValue(a, index),\n valB = getCellValue(b, index);\n return _jquery.default.isNumeric(valA) && _jquery.default.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB);\n };\n }\n\n function getCellValue(row, index) {\n return (0, _jquery.default)(row).children(\"td\").eq(index).text();\n }\n}\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/utils.js?");
+eval("\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.WindowController = WindowController;\nexports.colorHash = colorHash;\nexports.htmlEntities = htmlEntities;\nexports.cumulativeSum = cumulativeSum;\nexports.init_notification_counter = init_notification_counter;\nexports.set_notification_counter = set_notification_counter;\nexports.inc_notification_counter = inc_notification_counter;\nexports.dec_notification_counter = dec_notification_counter;\nexports.clear_notification_counter = clear_notification_counter;\nexports.copyToClipboard = copyToClipboard;\nexports.makeSortableTables = makeSortableTables;\n\nvar _jquery = _interopRequireDefault(__webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\"));\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n_jquery.default.fn.serializeJSON = function (omit_nulls) {\n var params = {};\n var form = (0, _jquery.default)(this);\n var values = form.serializeArray();\n values = values.concat(form.find(\"input[type=checkbox]:checked\").map(function () {\n return {\n name: this.name,\n value: true\n };\n }).get());\n values = values.concat(form.find(\"input[type=checkbox]:not(:checked)\").map(function () {\n return {\n name: this.name,\n value: false\n };\n }).get());\n values.map(function (x) {\n if (omit_nulls) {\n if (x.value !== null && x.value !== \"\") {\n params[x.name] = x.value;\n } else {\n var input = form.find(\":input[name=\".concat(x.name, \"]\"));\n\n if (input.data(\"initial\") !== input.val()) {\n params[x.name] = x.value;\n }\n }\n } else {\n params[x.name] = x.value;\n }\n });\n return params;\n}; //http://stackoverflow.com/a/2648463 - wizardry!\n\n\nString.prototype.format = String.prototype.f = function () {\n var s = this,\n i = arguments.length;\n\n while (i--) {\n s = s.replace(new RegExp(\"\\\\{\" + i + \"\\\\}\", \"gm\"), arguments[i]);\n }\n\n return s;\n}; //http://stackoverflow.com/a/7616484\n\n\nString.prototype.hashCode = function () {\n var hash = 0,\n i,\n chr,\n len;\n if (this.length == 0) return hash;\n\n for (i = 0, len = this.length; i < len; i++) {\n chr = this.charCodeAt(i);\n hash = (hash << 5) - hash + chr;\n hash |= 0; // Convert to 32bit integer\n }\n\n return hash;\n}; // https://gist.github.com/neilj/4146038\n// https://fastmail.blog/2012/11/26/inter-tab-communication-using-local-storage/\n\n\nfunction WindowController() {\n this.id = Math.random();\n this.isMaster = false;\n this.others = {};\n window.addEventListener(\"storage\", this, false);\n window.addEventListener(\"unload\", this, false);\n this.broadcast(\"hello\");\n var that = this;\n\n var check = function check() {\n that.check();\n that._checkTimeout = setTimeout(check, 9000);\n };\n\n var ping = function ping() {\n that.sendPing();\n that._pingTimeout = setTimeout(ping, 17000);\n };\n\n this._checkTimeout = setTimeout(check, 500);\n this._pingTimeout = setTimeout(ping, 17000);\n}\n\nWindowController.prototype.destroy = function () {\n clearTimeout(this._pingTimeout);\n clearTimeout(this._checkTimeout);\n window.removeEventListener(\"storage\", this, false);\n window.removeEventListener(\"unload\", this, false);\n this.broadcast(\"bye\");\n};\n\nWindowController.prototype.handleEvent = function (event) {\n if (event.type === \"unload\") {\n this.destroy();\n } else if (event.key === \"broadcast\") {\n try {\n var data = JSON.parse(event.newValue);\n\n if (data.id !== this.id) {\n this[data.type](data);\n }\n } catch (error) {// Ignore error\n }\n }\n};\n\nWindowController.prototype.sendPing = function () {\n this.broadcast(\"ping\");\n};\n\nWindowController.prototype.hello = function (event) {\n this.ping(event);\n\n if (event.id < this.id) {\n this.check();\n } else {\n this.sendPing();\n }\n};\n\nWindowController.prototype.ping = function (event) {\n this.others[event.id] = +new Date();\n};\n\nWindowController.prototype.bye = function (event) {\n delete this.others[event.id];\n this.check();\n};\n\nWindowController.prototype.check = function (_event) {\n var now = +new Date(),\n takeMaster = true,\n id;\n\n for (id in this.others) {\n if (this.others[id] + 23000 < now) {\n delete this.others[id];\n } else if (id < this.id) {\n takeMaster = false;\n }\n }\n\n if (this.isMaster !== takeMaster) {\n this.isMaster = takeMaster;\n this.masterDidChange();\n }\n};\n\nWindowController.prototype.masterDidChange = function () {};\n\nWindowController.prototype.broadcast = function (type, data) {\n var event = {\n id: this.id,\n type: type\n };\n\n for (var x in data) {\n event[x] = data[x];\n }\n\n try {\n localStorage.setItem(\"broadcast\", JSON.stringify(event));\n } catch (error) {\n // eslint-disable-next-line no-console\n console.log(error);\n }\n};\n\nfunction colorHash(str) {\n var hash = 0;\n\n for (var i = 0; i < str.length; i++) {\n hash = str.charCodeAt(i) + ((hash << 5) - hash);\n }\n\n var colour = \"#\";\n\n for (var _i = 0; _i < 3; _i++) {\n var value = hash >> _i * 4 & 0xff;\n colour += (\"00\" + value.toString(16)).substr(-2);\n }\n\n return colour;\n}\n\nfunction htmlEntities(string) {\n return (0, _jquery.default)(\"
\").text(string).html();\n}\n\nfunction cumulativeSum(arr) {\n var result = arr.concat();\n\n for (var i = 0; i < arr.length; i++) {\n result[i] = arr.slice(0, i + 1).reduce(function (p, i) {\n return p + i;\n });\n }\n\n return result;\n}\n\nvar storage = window.localStorage;\nvar counter_key = \"unread_notifications\";\n\nfunction init_notification_counter() {\n var count = storage.getItem(counter_key);\n\n if (count === null) {\n storage.setItem(counter_key, 0);\n } else {\n if (count > 0) {\n (0, _jquery.default)(\".badge-notification\").text(count);\n }\n }\n}\n\nfunction set_notification_counter(count) {\n storage.setItem(counter_key, count);\n}\n\nfunction inc_notification_counter() {\n var count = storage.getItem(counter_key) || 0;\n storage.setItem(counter_key, ++count);\n (0, _jquery.default)(\".badge-notification\").text(count);\n}\n\nfunction dec_notification_counter() {\n var count = storage.getItem(counter_key) || 0;\n\n if (count > 0) {\n storage.setItem(counter_key, --count);\n (0, _jquery.default)(\".badge-notification\").text(count);\n } // Always clear if count is 0\n\n\n if (count == 0) {\n clear_notification_counter();\n }\n}\n\nfunction clear_notification_counter() {\n storage.setItem(counter_key, 0);\n (0, _jquery.default)(\".badge-notification\").empty();\n}\n\nfunction copyToClipboard(event, selector) {\n // Select element\n (0, _jquery.default)(selector).select(); // Copy to clipboard\n\n document.execCommand(\"copy\"); // Show tooltip to user\n\n (0, _jquery.default)(event.target).tooltip({\n title: \"Copied!\",\n trigger: \"manual\"\n });\n (0, _jquery.default)(event.target).tooltip(\"show\");\n setTimeout(function () {\n (0, _jquery.default)(event.target).tooltip(\"hide\");\n }, 1500);\n}\n\nfunction makeSortableTables() {\n (0, _jquery.default)(\"th.sort-col\").append(\" \");\n (0, _jquery.default)(\"th.sort-col\").click(function () {\n var table = (0, _jquery.default)(this).parents(\"table\").eq(0);\n var rows = table.find(\"tr:gt(0)\").toArray().sort(comparer((0, _jquery.default)(this).index()));\n this.asc = !this.asc;\n\n if (!this.asc) {\n rows = rows.reverse();\n }\n\n for (var i = 0; i < rows.length; i++) {\n table.append(rows[i]);\n }\n });\n\n function comparer(index) {\n return function (a, b) {\n var valA = getCellValue(a, index),\n valB = getCellValue(b, index);\n return _jquery.default.isNumeric(valA) && _jquery.default.isNumeric(valB) ? valA - valB : valA.toString().localeCompare(valB);\n };\n }\n\n function getCellValue(row, index) {\n return (0, _jquery.default)(row).children(\"td\").eq(index).text();\n }\n}\n\n//# sourceURL=webpack:///./CTFd/themes/core/assets/js/utils.js?");
/***/ }),
diff --git a/CTFd/themes/admin/static/js/echarts.bundle.dev.js b/CTFd/themes/admin/static/js/echarts.bundle.dev.js
new file mode 100644
index 00000000..d34a6efc
--- /dev/null
+++ b/CTFd/themes/admin/static/js/echarts.bundle.dev.js
@@ -0,0 +1,15 @@
+(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["echarts"],{
+
+/***/ "./node_modules/echarts/dist/echarts-en.common.js":
+/*!********************************************************!*\
+ !*** ./node_modules/echarts/dist/echarts-en.common.js ***!
+ \********************************************************/
+/*! no static exports found */
+/***/ (function(module, exports, __webpack_require__) {
+
+;
+eval("/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;function _typeof(obj){if(typeof Symbol===\"function\"&&typeof Symbol.iterator===\"symbol\"){_typeof=function _typeof(obj){return typeof obj;};}else{_typeof=function _typeof(obj){return obj&&typeof Symbol===\"function\"&&obj.constructor===Symbol&&obj!==Symbol.prototype?\"symbol\":typeof obj;};}return _typeof(obj);}(function(global,factory){( false?undefined:_typeof(exports))==='object'&&typeof module!=='undefined'?factory(exports): true?!(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?\n\t\t\t\t(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),\n\t\t\t\t__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)):undefined;})(void 0,function(exports){'use strict';/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // (1) The code `if (__DEV__) ...` can be removed by build tool.\n// (2) If intend to use `__DEV__`, this module should be imported. Use a global\n// variable `__DEV__` may cause that miss the declaration (see #6535), or the\n// declaration is behind of the using position (for example in `Model.extent`,\n// And tools like rollup can not analysis the dependency if not import).\nvar dev;// In browser\nif(typeof window!=='undefined'){dev=window.__DEV__;}// In node\nelse if(typeof global!=='undefined'){dev=global.__DEV__;}if(typeof dev==='undefined'){dev=true;}var __DEV__=dev;/**\n * zrender: 生成唯一id\n *\n * @author errorrik (errorrik@gmail.com)\n */var idStart=0x0907;var guid=function guid(){return idStart++;};/**\n * echarts设备环境识别\n *\n * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。\n * @author firede[firede@firede.us]\n * @desc thanks zepto.\n */ /* global wx */var env={};if((typeof wx===\"undefined\"?\"undefined\":_typeof(wx))==='object'&&typeof wx.getSystemInfoSync==='function'){// In Weixin Application\nenv={browser:{},os:{},node:false,wxa:true,// Weixin Application\ncanvasSupported:true,svgSupported:false,touchEventsSupported:true,domSupported:false};}else if(typeof document==='undefined'&&typeof self!=='undefined'){// In worker\nenv={browser:{},os:{},node:false,worker:true,canvasSupported:true,domSupported:false};}else if(typeof navigator==='undefined'){// In node\nenv={browser:{},os:{},node:true,worker:false,// Assume canvas is supported\ncanvasSupported:true,svgSupported:true,domSupported:false};}else{env=detect(navigator.userAgent);}var env$1=env;// Zepto.js\n// (c) 2010-2013 Thomas Fuchs\n// Zepto.js may be freely distributed under the MIT license.\nfunction detect(ua){var os={};var browser={};// var webkit = ua.match(/Web[kK]it[\\/]{0,1}([\\d.]+)/);\n// var android = ua.match(/(Android);?[\\s\\/]+([\\d.]+)?/);\n// var ipad = ua.match(/(iPad).*OS\\s([\\d_]+)/);\n// var ipod = ua.match(/(iPod)(.*OS\\s([\\d_]+))?/);\n// var iphone = !ipad && ua.match(/(iPhone\\sOS)\\s([\\d_]+)/);\n// var webos = ua.match(/(webOS|hpwOS)[\\s\\/]([\\d.]+)/);\n// var touchpad = webos && ua.match(/TouchPad/);\n// var kindle = ua.match(/Kindle\\/([\\d.]+)/);\n// var silk = ua.match(/Silk\\/([\\d._]+)/);\n// var blackberry = ua.match(/(BlackBerry).*Version\\/([\\d.]+)/);\n// var bb10 = ua.match(/(BB10).*Version\\/([\\d.]+)/);\n// var rimtabletos = ua.match(/(RIM\\sTablet\\sOS)\\s([\\d.]+)/);\n// var playbook = ua.match(/PlayBook/);\n// var chrome = ua.match(/Chrome\\/([\\d.]+)/) || ua.match(/CriOS\\/([\\d.]+)/);\nvar firefox=ua.match(/Firefox\\/([\\d.]+)/);// var safari = webkit && ua.match(/Mobile\\//) && !chrome;\n// var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;\nvar ie=ua.match(/MSIE\\s([\\d.]+)/)// IE 11 Trident/7.0; rv:11.0\n||ua.match(/Trident\\/.+?rv:(([\\d.]+))/);var edge=ua.match(/Edge\\/([\\d.]+)/);// IE 12 and 12+\nvar weChat=/micromessenger/i.test(ua);// Todo: clean this up with a better OS/browser seperation:\n// - discern (more) between multiple browsers on android\n// - decide if kindle fire in silk mode is android or not\n// - Firefox on Android doesn't specify the Android version\n// - possibly devide in os, device and browser hashes\n// if (browser.webkit = !!webkit) browser.version = webkit[1];\n// if (android) os.android = true, os.version = android[2];\n// if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');\n// if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');\n// if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;\n// if (webos) os.webos = true, os.version = webos[2];\n// if (touchpad) os.touchpad = true;\n// if (blackberry) os.blackberry = true, os.version = blackberry[2];\n// if (bb10) os.bb10 = true, os.version = bb10[2];\n// if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];\n// if (playbook) browser.playbook = true;\n// if (kindle) os.kindle = true, os.version = kindle[1];\n// if (silk) browser.silk = true, browser.version = silk[1];\n// if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;\n// if (chrome) browser.chrome = true, browser.version = chrome[1];\nif(firefox){browser.firefox=true;browser.version=firefox[1];}// if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;\n// if (webview) browser.webview = true;\nif(ie){browser.ie=true;browser.version=ie[1];}if(edge){browser.edge=true;browser.version=edge[1];}// It is difficult to detect WeChat in Win Phone precisely, because ua can\n// not be set on win phone. So we do not consider Win Phone.\nif(weChat){browser.weChat=true;}// os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||\n// (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));\n// os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||\n// (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\\/([\\d.]+)/)) ||\n// (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));\nreturn{browser:browser,os:os,node:false,// 原生canvas支持,改极端点了\n// canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)\ncanvasSupported:!!document.createElement('canvas').getContext,svgSupported:typeof SVGRect!=='undefined',// works on most browsers\n// IE10/11 does not support touch event, and MS Edge supports them but not by\n// default, so we dont check navigator.maxTouchPoints for them here.\ntouchEventsSupported:'ontouchstart'in window&&!browser.ie&&!browser.edge,// .\npointerEventsSupported:// (1) Firefox supports pointer but not by default, only MS browsers are reliable on pointer\n// events currently. So we dont use that on other browsers unless tested sufficiently.\n// For example, in iOS 13 Mobile Chromium 78, if the touching behavior starts page\n// scroll, the `pointermove` event can not be fired any more. That will break some\n// features like \"pan horizontally to move something and pan vertically to page scroll\".\n// The horizontal pan probably be interrupted by the casually triggered page scroll.\n// (2) Although IE 10 supports pointer event, it use old style and is different from the\n// standard. So we exclude that. (IE 10 is hardly used on touch device)\n'onpointerdown'in window&&(browser.edge||browser.ie&&browser.version>=11),// passiveSupported: detectPassiveSupport()\ndomSupported:typeof document!=='undefined'};}// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection\n// function detectPassiveSupport() {\n// // Test via a getter in the options object to see if the passive property is accessed\n// var supportsPassive = false;\n// try {\n// var opts = Object.defineProperty({}, 'passive', {\n// get: function() {\n// supportsPassive = true;\n// }\n// });\n// window.addEventListener('testPassive', function() {}, opts);\n// } catch (e) {\n// }\n// return supportsPassive;\n// }\n/**\n * @module zrender/core/util\n */ // 用于处理merge时无法遍历Date等对象的问题\nvar BUILTIN_OBJECT={'[object Function]':1,'[object RegExp]':1,'[object Date]':1,'[object Error]':1,'[object CanvasGradient]':1,'[object CanvasPattern]':1,// For node-canvas\n'[object Image]':1,'[object Canvas]':1};var TYPED_ARRAY={'[object Int8Array]':1,'[object Uint8Array]':1,'[object Uint8ClampedArray]':1,'[object Int16Array]':1,'[object Uint16Array]':1,'[object Int32Array]':1,'[object Uint32Array]':1,'[object Float32Array]':1,'[object Float64Array]':1};var objToString=Object.prototype.toString;var arrayProto=Array.prototype;var nativeForEach=arrayProto.forEach;var nativeFilter=arrayProto.filter;var nativeSlice=arrayProto.slice;var nativeMap=arrayProto.map;var nativeReduce=arrayProto.reduce;// Avoid assign to an exported variable, for transforming to cjs.\nvar methods={};function $override(name,fn){// Clear ctx instance for different environment\nif(name==='createCanvas'){_ctx=null;}methods[name]=fn;}/**\n * Those data types can be cloned:\n * Plain object, Array, TypedArray, number, string, null, undefined.\n * Those data types will be assgined using the orginal data:\n * BUILTIN_OBJECT\n * Instance of user defined class will be cloned to a plain object, without\n * properties in prototype.\n * Other data types is not supported (not sure what will happen).\n *\n * Caution: do not support clone Date, for performance consideration.\n * (There might be a large number of date in `series.data`).\n * So date should not be modified in and out of echarts.\n *\n * @param {*} source\n * @return {*} new\n */function _clone(source){if(source==null||_typeof(source)!=='object'){return source;}var result=source;var typeStr=objToString.call(source);if(typeStr==='[object Array]'){if(!isPrimitive(source)){result=[];for(var i=0,len=source.length;i [3, 3, 3, 3]\n * [4, 2] => [4, 2, 4, 2]\n * [4, 3, 2] => [4, 3, 2, 3]\n * @param {number|Array.} val\n * @return {Array.}\n */function normalizeCssArray(val){if(typeof val==='number'){return[val,val,val,val];}var len=val.length;if(len===2){// vertical | horizontal\nreturn[val[0],val[1],val[0],val[1]];}else if(len===3){// top | horizontal | bottom\nreturn[val[0],val[1],val[2],val[1]];}return val;}/**\n * @memberOf module:zrender/core/util\n * @param {boolean} condition\n * @param {string} message\n */function assert$1(condition,message){if(!condition){throw new Error(message);}}/**\n * @memberOf module:zrender/core/util\n * @param {string} str string to be trimed\n * @return {string} trimed string\n */function trim(str){if(str==null){return null;}else if(typeof str.trim==='function'){return str.trim();}else{return str.replace(/^[\\s\\uFEFF\\xA0]+|[\\s\\uFEFF\\xA0]+$/g,'');}}var primitiveKey='__ec_primitive__';/**\n * Set an object as primitive to be ignored traversing children in clone or merge\n */function setAsPrimitive(obj){obj[primitiveKey]=true;}function isPrimitive(obj){return obj[primitiveKey];}/**\n * @constructor\n * @param {Object} obj Only apply `ownProperty`.\n */function HashMap(obj){var isArr=isArray(obj);// Key should not be set on this, otherwise\n// methods get/set/... may be overrided.\nthis.data={};var thisMap=this;obj instanceof HashMap?obj.each(visit):obj&&each$1(obj,visit);function visit(value,key){isArr?thisMap.set(value,key):thisMap.set(key,value);}}HashMap.prototype={constructor:HashMap,// Do not provide `has` method to avoid defining what is `has`.\n// (We usually treat `null` and `undefined` as the same, different\n// from ES6 Map).\nget:function get(key){return this.data.hasOwnProperty(key)?this.data[key]:null;},set:function set(key,value){// Comparing with invocation chaining, `return value` is more commonly\n// used in this case: `var someVal = map.set('a', genVal());`\nreturn this.data[key]=value;},// Although util.each can be performed on this hashMap directly, user\n// should not use the exposed keys, who are prefixed.\neach:function each(cb,context){context!==void 0&&(cb=bind(cb,context));/* eslint-disable guard-for-in */for(var key in this.data){this.data.hasOwnProperty(key)&&cb(this.data[key],key);}/* eslint-enable guard-for-in */},// Do not use this method if performance sensitive.\nremoveKey:function removeKey(key){delete this.data[key];}};function createHashMap(obj){return new HashMap(obj);}function concatArray(a,b){var newArray=new a.constructor(a.length+b.length);for(var i=0;i3){args=arrySlice.call(args,1);}var len=_h.length;for(var i=0;i4){args=arrySlice.call(args,1,args.length-1);}var ctx=args[args.length-1];var len=_h.length;for(var i=0;i} src source four points, [x0, y0, x1, y1, x2, y2, x3, y3]\n * @param {Array.} dest destination four points, [x0, y0, x1, y1, x2, y2, x3, y3]\n * @return {Function} transformer If fail, return null/undefined.\n */function buildTransformer(src,dest){var mA=[[src[0],src[1],1,0,0,0,-dest[0]*src[0],-dest[0]*src[1]],[0,0,0,src[0],src[1],1,-dest[1]*src[0],-dest[1]*src[1]],[src[2],src[3],1,0,0,0,-dest[2]*src[2],-dest[2]*src[3]],[0,0,0,src[2],src[3],1,-dest[3]*src[2],-dest[3]*src[3]],[src[4],src[5],1,0,0,0,-dest[4]*src[4],-dest[4]*src[5]],[0,0,0,src[4],src[5],1,-dest[5]*src[4],-dest[5]*src[5]],[src[6],src[7],1,0,0,0,-dest[6]*src[6],-dest[6]*src[7]],[0,0,0,src[6],src[7],1,-dest[7]*src[6],-dest[7]*src[7]]];var detCache={};var det=determinant(mA,8,0,0,0,detCache);if(det===0){// can not make transformer when and only when\n// any three of the markers are collinear.\nreturn;}// `invert(mA) * dest`, that is, `adj(mA) / det * dest`.\nvar vh=[];for(var i=0;i<8;i++){for(var j=0;j<8;j++){vh[j]==null&&(vh[j]=0);vh[j]+=((i+j)%2?-1:1)*// det(subMatrix(i, j))\ndeterminant(mA,7,i===0?1:0,1<} out [inX: number, inY: number] The output..\n * If can not transform, `out` will not be modified but return `false`.\n * @param {HTMLElement} elFrom The `[inX, inY]` is based on elFrom.\n * @param {HTMLElement} elTarget The `out` is based on elTarget.\n * @param {number} inX\n * @param {number} inY\n * @return {boolean} Whether transform successfully.\n */function transformLocalCoord(out,elFrom,elTarget,inX,inY){return transformCoordWithViewport(_calcOut$1,elFrom,inX,inY,true)&&transformCoordWithViewport(out,elTarget,_calcOut$1[0],_calcOut$1[1]);}/**\n * Transform between a \"viewport coord\" and a \"local coord\".\n * \"viewport coord\": the coord based on the left-top corner of the viewport\n * of the browser.\n * \"local coord\": the coord based on the input `el`. The origin point is at\n * the position of \"left: 0; top: 0;\" in the `el`.\n *\n * Support the case when CSS transform is used on el.\n *\n * @param {Array.} out [inX: number, inY: number] The output. If `inverse: false`,\n * it represents \"local coord\", otherwise \"vireport coord\".\n * If can not transform, `out` will not be modified but return `false`.\n * @param {HTMLElement} el The \"local coord\" is based on the `el`, see comment above.\n * @param {number} inX If `inverse: false`,\n * it represents \"vireport coord\", otherwise \"local coord\".\n * @param {number} inY If `inverse: false`,\n * it represents \"vireport coord\", otherwise \"local coord\".\n * @param {boolean} [inverse=false]\n * `true`: from \"viewport coord\" to \"local coord\".\n * `false`: from \"local coord\" to \"viewport coord\".\n * @return {boolean} Whether transform successfully.\n */function transformCoordWithViewport(out,el,inX,inY,inverse){if(el.getBoundingClientRect&&env$1.domSupported&&!isCanvasEl(el)){var saved=el[EVENT_SAVED_PROP]||(el[EVENT_SAVED_PROP]={});var markers=prepareCoordMarkers(el,saved);var transformer=preparePointerTransformer(markers,saved,inverse);if(transformer){transformer(out,inX,inY);return true;}}return false;}function prepareCoordMarkers(el,saved){var markers=saved.markers;if(markers){return markers;}markers=saved.markers=[];var propLR=['left','right'];var propTB=['top','bottom'];for(var i=0;i<4;i++){var marker=document.createElement('div');var stl=marker.style;var idxLR=i%2;var idxTB=(i>>1)%2;stl.cssText=['position: absolute','visibility: hidden','padding: 0','margin: 0','border-width: 0','user-select: none','width:0','height:0',// 'width: 5px',\n// 'height: 5px',\npropLR[idxLR]+':0',propTB[idxTB]+':0',propLR[1-idxLR]+':auto',propTB[1-idxTB]+':auto',''].join('!important;');el.appendChild(marker);markers.push(marker);}return markers;}function preparePointerTransformer(markers,saved,inverse){var transformerName=inverse?'invTrans':'trans';var transformer=saved[transformerName];var oldSrcCoords=saved.srcCoords;var oldCoordTheSame=true;var srcCoords=[];var destCoords=[];for(var i=0;i<4;i++){var rect=markers[i].getBoundingClientRect();var ii=2*i;var x=rect.left;var y=rect.top;srcCoords.push(x,y);oldCoordTheSame=oldCoordTheSame&&oldSrcCoords&&x===oldSrcCoords[ii]&&y===oldSrcCoords[ii+1];destCoords.push(markers[i].offsetLeft,markers[i].offsetTop);}// Cache to avoid time consuming of `buildTransformer`.\nreturn oldCoordTheSame&&transformer?transformer:(saved.srcCoords=srcCoords,saved[transformerName]=inverse?buildTransformer(destCoords,srcCoords):buildTransformer(srcCoords,destCoords));}function isCanvasEl(el){return el.nodeName.toUpperCase()==='CANVAS';}/**\n * Utilities for mouse or touch events.\n */var isDomLevel2=typeof window!=='undefined'&&!!window.addEventListener;var MOUSE_EVENT_REG=/^(?:mouse|pointer|contextmenu|drag|drop)|click/;var _calcOut=[];/**\n * Get the `zrX` and `zrY`, which are relative to the top-left of\n * the input `el`.\n * CSS transform (2D & 3D) is supported.\n *\n * The strategy to fetch the coords:\n * + If `calculate` is not set as `true`, users of this method should\n * ensure that `el` is the same or the same size & location as `e.target`.\n * Otherwise the result coords are probably not expected. Because we\n * firstly try to get coords from e.offsetX/e.offsetY.\n * + If `calculate` is set as `true`, the input `el` can be any element\n * and we force to calculate the coords based on `el`.\n * + The input `el` should be positionable (not position:static).\n *\n * The force `calculate` can be used in case like:\n * When mousemove event triggered on ec tooltip, `e.target` is not `el`(zr painter.dom).\n *\n * @param {HTMLElement} el DOM element.\n * @param {Event} e Mouse event or touch event.\n * @param {Object} out Get `out.zrX` and `out.zrY` as the result.\n * @param {boolean} [calculate=false] Whether to force calculate\n * the coordinates but not use ones provided by browser.\n */function clientToLocal(el,e,out,calculate){out=out||{};// According to the W3C Working Draft, offsetX and offsetY should be relative\n// to the padding edge of the target element. The only browser using this convention\n// is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does\n// not support the properties.\n// (see http://www.jacklmoore.com/notes/mouse-position/)\n// In zr painter.dom, padding edge equals to border edge.\nif(calculate||!env$1.canvasSupported){calculateZrXY(el,e,out);}// Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned\n// ancestor element, so we should make sure el is positioned (e.g., not position:static).\n// BTW1, Webkit don't return the same results as FF in non-simple cases (like add\n// zoom-factor, overflow / opacity layers, transforms ...)\n// BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.\n// \n// BTW3, In ff, offsetX/offsetY is always 0.\nelse if(env$1.browser.firefox&&e.layerX!=null&&e.layerX!==e.offsetX){out.zrX=e.layerX;out.zrY=e.layerY;}// For IE6+, chrome, safari, opera. (When will ff support offsetX?)\nelse if(e.offsetX!=null){out.zrX=e.offsetX;out.zrY=e.offsetY;}// For some other device, e.g., IOS safari.\nelse{calculateZrXY(el,e,out);}return out;}function calculateZrXY(el,e,out){// BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect.\nif(env$1.domSupported&&el.getBoundingClientRect){var ex=e.clientX;var ey=e.clientY;if(isCanvasEl(el)){// Original approach, which do not support CSS transform.\n// marker can not be locationed in a canvas container\n// (getBoundingClientRect is always 0). We do not support\n// that input a pre-created canvas to zr while using css\n// transform in iOS.\nvar box=el.getBoundingClientRect();out.zrX=ex-box.left;out.zrY=ey-box.top;return;}else{if(transformCoordWithViewport(_calcOut,el,ex,ey)){out.zrX=_calcOut[0];out.zrY=_calcOut[1];return;}}}out.zrX=out.zrY=0;}/**\n * Find native event compat for legency IE.\n * Should be called at the begining of a native event listener.\n *\n * @param {Event} [e] Mouse event or touch event or pointer event.\n * For lagency IE, we use `window.event` is used.\n * @return {Event} The native event.\n */function getNativeEvent(e){return e||window.event;}/**\n * Normalize the coordinates of the input event.\n *\n * Get the `e.zrX` and `e.zrY`, which are relative to the top-left of\n * the input `el`.\n * Get `e.zrDelta` if using mouse wheel.\n * Get `e.which`, see the comment inside this function.\n *\n * Do not calculate repeatly if `zrX` and `zrY` already exist.\n *\n * Notice: see comments in `clientToLocal`. check the relationship\n * between the result coords and the parameters `el` and `calculate`.\n *\n * @param {HTMLElement} el DOM element.\n * @param {Event} [e] See `getNativeEvent`.\n * @param {boolean} [calculate=false] Whether to force calculate\n * the coordinates but not use ones provided by browser.\n * @return {UIEvent} The normalized native UIEvent.\n */function normalizeEvent(el,e,calculate){e=getNativeEvent(e);if(e.zrX!=null){return e;}var eventType=e.type;var isTouch=eventType&&eventType.indexOf('touch')>=0;if(!isTouch){clientToLocal(el,e,e,calculate);e.zrDelta=e.wheelDelta?e.wheelDelta/120:-(e.detail||0)/3;}else{var touch=eventType!=='touchend'?e.targetTouches[0]:e.changedTouches[0];touch&&clientToLocal(el,touch,e,calculate);}// Add which for click: 1 === left; 2 === middle; 3 === right; otherwise: 0;\n// See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js\n// If e.which has been defined, it may be readonly,\n// see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which\nvar button=e.button;if(e.which==null&&button!==undefined&&MOUSE_EVENT_REG.test(e.type)){e.which=button&1?1:button&2?3:button&4?2:0;}// [Caution]: `e.which` from browser is not always reliable. For example,\n// when press left button and `mousemove (pointermove)` in Edge, the `e.which`\n// is 65536 and the `e.button` is -1. But the `mouseup (pointerup)` and\n// `mousedown (pointerdown)` is the same as Chrome does.\nreturn e;}/**\n * @param {HTMLElement} el\n * @param {string} name\n * @param {Function} handler\n * @param {Object|boolean} opt If boolean, means `opt.capture`\n * @param {boolean} [opt.capture=false]\n * @param {boolean} [opt.passive=false]\n */function addEventListener(el,name,handler,opt){if(isDomLevel2){// Reproduct the console warning:\n// [Violation] Added non-passive event listener to a scroll-blocking event.\n// Consider marking event handler as 'passive' to make the page more responsive.\n// Just set console log level: verbose in chrome dev tool.\n// then the warning log will be printed when addEventListener called.\n// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md\n// We have not yet found a neat way to using passive. Because in zrender the dom event\n// listener delegate all of the upper events of element. Some of those events need\n// to prevent default. For example, the feature `preventDefaultMouseMove` of echarts.\n// Before passive can be adopted, these issues should be considered:\n// (1) Whether and how a zrender user specifies an event listener passive. And by default,\n// passive or not.\n// (2) How to tread that some zrender event listener is passive, and some is not. If\n// we use other way but not preventDefault of mousewheel and touchmove, browser\n// compatibility should be handled.\n// var opts = (env.passiveSupported && name === 'mousewheel')\n// ? {passive: true}\n// // By default, the third param of el.addEventListener is `capture: false`.\n// : void 0;\n// el.addEventListener(name, handler /* , opts */);\nel.addEventListener(name,handler,opt);}else{// For simplicity, do not implement `setCapture` for IE9-.\nel.attachEvent('on'+name,handler);}}/**\n * Parameter are the same as `addEventListener`.\n *\n * Notice that if a listener is registered twice, one with capture and one without,\n * remove each one separately. Removal of a capturing listener does not affect a\n * non-capturing version of the same listener, and vice versa.\n */function removeEventListener(el,name,handler,opt){if(isDomLevel2){el.removeEventListener(name,handler,opt);}else{el.detachEvent('on'+name,handler);}}/**\n * preventDefault and stopPropagation.\n * Notice: do not use this method in zrender. It can only be\n * used by upper applications if necessary.\n *\n * @param {Event} e A mouse or touch event.\n */var stop=isDomLevel2?function(e){e.preventDefault();e.stopPropagation();e.cancelBubble=true;}:function(e){e.returnValue=false;e.cancelBubble=true;};/**\n * This method only works for mouseup and mousedown. The functionality is restricted\n * for fault tolerance, See the `e.which` compatibility above.\n *\n * @param {MouseEvent} e\n * @return {boolean}\n */function isMiddleOrRightButtonOnMouseUpDown(e){return e.which===2||e.which===3;}/**\n * To be removed.\n * @deprecated\n */ /**\n * Only implements needed gestures for mobile.\n */var GestureMgr=function GestureMgr(){/**\n * @private\n * @type {Array.}\n */this._track=[];};GestureMgr.prototype={constructor:GestureMgr,recognize:function recognize(event,target,root){this._doTrack(event,target,root);return this._recognize(event);},clear:function clear(){this._track.length=0;return this;},_doTrack:function _doTrack(event,target,root){var touches=event.touches;if(!touches){return;}var trackItem={points:[],touches:[],target:target,event:event};for(var i=0,len=touches.length;i1&&pinchEnd&&pinchEnd.length>1){var pinchScale=dist$1(pinchEnd)/dist$1(pinchPre);!isFinite(pinchScale)&&(pinchScale=1);event.pinchScale=pinchScale;var pinchCenter=center(pinchEnd);event.pinchX=pinchCenter[0];event.pinchY=pinchCenter[1];return{type:'pinch',target:track[0].target,event:event};}}// Only pinch currently.\n};/**\n * [The interface between `Handler` and `HandlerProxy`]:\n *\n * The default `HandlerProxy` only support the common standard web environment\n * (e.g., standalone browser, headless browser, embed browser in mobild APP, ...).\n * But `HandlerProxy` can be replaced to support more non-standard environment\n * (e.g., mini app), or to support more feature that the default `HandlerProxy`\n * not provided (like echarts-gl did).\n * So the interface between `Handler` and `HandlerProxy` should be stable. Do not\n * make break changes util inevitable. The interface include the public methods\n * of `Handler` and the events listed in `handlerNames` below, by which `HandlerProxy`\n * drives `Handler`.\n */ /**\n * [Drag outside]:\n *\n * That is, triggering `mousemove` and `mouseup` event when the pointer is out of the\n * zrender area when dragging. That is important for the improvement of the user experience\n * when dragging something near the boundary without being terminated unexpectedly.\n *\n * We originally consider to introduce new events like `pagemovemove` and `pagemouseup`\n * to resolve this issue. But some drawbacks of it is described in\n * https://github.com/ecomfe/zrender/pull/536#issuecomment-560286899\n *\n * Instead, we referenced the specifications:\n * https://www.w3.org/TR/touch-events/#the-touchmove-event\n * https://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#event-type-mousemove\n * where the the mousemove/touchmove can be continue to fire if the user began a drag\n * operation and the pointer has left the boundary. (for the mouse event, browsers\n * only do it on `document` and when the pointer has left the boundary of the browser.)\n *\n * So the default `HandlerProxy` supports this feature similarly: if it is in the dragging\n * state (see `pointerCapture` in `HandlerProxy`), the `mousemove` and `mouseup` continue\n * to fire until release the pointer. That is implemented by listen to those event on\n * `document`.\n * If we implement some other `HandlerProxy` only for touch device, that would be easier.\n * The touch event support this feature by default.\n *\n * Note:\n * There might be some cases that the mouse event can not be\n * received on `document`. For example,\n * (A) `useCapture` is not supported and some user defined event listeners on the ancestor\n * of zr dom throw Error .\n * (B) `useCapture` is not supported Some user defined event listeners on the ancestor of\n * zr dom call `stopPropagation`.\n * In these cases, the `mousemove` event might be keep triggered event\n * if the mouse is released. We try to reduce the side-effect in those cases.\n * That is, do nothing (especially, `findHover`) in those cases. See `isOutsideBoundary`.\n *\n * Note:\n * If `HandlerProxy` listens to `document` with `useCapture`, `HandlerProxy` needs to\n * make sure `stopPropagation` and `preventDefault` doing nothing if and only if the event\n * target is not zrender dom. Becuase it is dangerous to enable users to call them in\n * `document` capture phase to prevent the propagation to any listener of the webpage.\n * But they are needed to work when the pointer inside the zrender dom.\n */var SILENT='silent';function makeEventPacket(eveType,targetInfo,event){return{type:eveType,event:event,// target can only be an element that is not silent.\ntarget:targetInfo.target,// topTarget can be a silent element.\ntopTarget:targetInfo.topTarget,cancelBubble:false,offsetX:event.zrX,offsetY:event.zrY,gestureEvent:event.gestureEvent,pinchX:event.pinchX,pinchY:event.pinchY,pinchScale:event.pinchScale,wheelDelta:event.zrDelta,zrByTouch:event.zrByTouch,which:event.which,stop:stopEvent};}function stopEvent(){stop(this.event);}function EmptyProxy(){}EmptyProxy.prototype.dispose=function(){};var handlerNames=['click','dblclick','mousewheel','mouseout','mouseup','mousedown','mousemove','contextmenu'];/**\n * @alias module:zrender/Handler\n * @constructor\n * @extends module:zrender/mixin/Eventful\n * @param {module:zrender/Storage} storage Storage instance.\n * @param {module:zrender/Painter} painter Painter instance.\n * @param {module:zrender/dom/HandlerProxy} proxy HandlerProxy instance.\n * @param {HTMLElement} painterRoot painter.root (not painter.getViewportRoot()).\n */var Handler=function Handler(storage,painter,proxy,painterRoot){Eventful.call(this);this.storage=storage;this.painter=painter;this.painterRoot=painterRoot;proxy=proxy||new EmptyProxy();/**\n * Proxy of event. can be Dom, WebGLSurface, etc.\n */this.proxy=null;/**\n * {target, topTarget, x, y}\n * @private\n * @type {Object}\n */this._hovered={};/**\n * @private\n * @type {Date}\n */this._lastTouchMoment;/**\n * @private\n * @type {number}\n */this._lastX;/**\n * @private\n * @type {number}\n */this._lastY;/**\n * @private\n * @type {module:zrender/core/GestureMgr}\n */this._gestureMgr;Draggable.call(this);this.setHandlerProxy(proxy);};Handler.prototype={constructor:Handler,setHandlerProxy:function setHandlerProxy(proxy){if(this.proxy){this.proxy.dispose();}if(proxy){each$1(handlerNames,function(name){proxy.on&&proxy.on(name,this[name],this);},this);// Attach handler\nproxy.handler=this;}this.proxy=proxy;},mousemove:function mousemove(event){var x=event.zrX;var y=event.zrY;var isOutside=isOutsideBoundary(this,x,y);var lastHovered=this._hovered;var lastHoveredTarget=lastHovered.target;// If lastHoveredTarget is removed from zr (detected by '__zr') by some API call\n// (like 'setOption' or 'dispatchAction') in event handlers, we should find\n// lastHovered again here. Otherwise 'mouseout' can not be triggered normally.\n// See #6198.\nif(lastHoveredTarget&&!lastHoveredTarget.__zr){lastHovered=this.findHover(lastHovered.x,lastHovered.y);lastHoveredTarget=lastHovered.target;}var hovered=this._hovered=isOutside?{x:x,y:y}:this.findHover(x,y);var hoveredTarget=hovered.target;var proxy=this.proxy;proxy.setCursor&&proxy.setCursor(hoveredTarget?hoveredTarget.cursor:'default');// Mouse out on previous hovered element\nif(lastHoveredTarget&&hoveredTarget!==lastHoveredTarget){this.dispatchToElement(lastHovered,'mouseout',event);}// Mouse moving on one element\nthis.dispatchToElement(hovered,'mousemove',event);// Mouse over on a new element\nif(hoveredTarget&&hoveredTarget!==lastHoveredTarget){this.dispatchToElement(hovered,'mouseover',event);}},mouseout:function mouseout(event){var eventControl=event.zrEventControl;var zrIsToLocalDOM=event.zrIsToLocalDOM;if(eventControl!=='only_globalout'){this.dispatchToElement(this._hovered,'mouseout',event);}if(eventControl!=='no_globalout'){// FIXME: if the pointer moving from the extra doms to realy \"outside\",\n// the `globalout` should have been triggered. But currently not.\n!zrIsToLocalDOM&&this.trigger('globalout',{type:'globalout',event:event});}},/**\n * Resize\n */resize:function resize(event){this._hovered={};},/**\n * Dispatch event\n * @param {string} eventName\n * @param {event=} eventArgs\n */dispatch:function dispatch(eventName,eventArgs){var handler=this[eventName];handler&&handler.call(this,eventArgs);},/**\n * Dispose\n */dispose:function dispose(){this.proxy.dispose();this.storage=this.proxy=this.painter=null;},/**\n * 设置默认的cursor style\n * @param {string} [cursorStyle='default'] 例如 crosshair\n */setCursorStyle:function setCursorStyle(cursorStyle){var proxy=this.proxy;proxy.setCursor&&proxy.setCursor(cursorStyle);},/**\n * 事件分发代理\n *\n * @private\n * @param {Object} targetInfo {target, topTarget} 目标图形元素\n * @param {string} eventName 事件名称\n * @param {Object} event 事件对象\n */dispatchToElement:function dispatchToElement(targetInfo,eventName,event){targetInfo=targetInfo||{};var el=targetInfo.target;if(el&&el.silent){return;}var eventHandler='on'+eventName;var eventPacket=makeEventPacket(eventName,targetInfo,event);while(el){el[eventHandler]&&(eventPacket.cancelBubble=el[eventHandler].call(el,eventPacket));el.trigger(eventName,eventPacket);el=el.parent;if(eventPacket.cancelBubble){break;}}if(!eventPacket.cancelBubble){// 冒泡到顶级 zrender 对象\nthis.trigger(eventName,eventPacket);// 分发事件到用户自定义层\n// 用户有可能在全局 click 事件中 dispose,所以需要判断下 painter 是否存在\nthis.painter&&this.painter.eachOtherLayer(function(layer){if(typeof layer[eventHandler]==='function'){layer[eventHandler].call(layer,eventPacket);}if(layer.trigger){layer.trigger(eventName,eventPacket);}});}},/**\n * @private\n * @param {number} x\n * @param {number} y\n * @param {module:zrender/graphic/Displayable} exclude\n * @return {model:zrender/Element}\n * @method\n */findHover:function findHover(x,y,exclude){var list=this.storage.getDisplayList();var out={x:x,y:y};for(var i=list.length-1;i>=0;i--){var hoverCheckResult;if(list[i]!==exclude// getDisplayList may include ignored item in VML mode\n&&!list[i].ignore&&(hoverCheckResult=isHover(list[i],x,y))){!out.topTarget&&(out.topTarget=list[i]);if(hoverCheckResult!==SILENT){out.target=list[i];break;}}}return out;},processGesture:function processGesture(event,stage){if(!this._gestureMgr){this._gestureMgr=new GestureMgr();}var gestureMgr=this._gestureMgr;stage==='start'&&gestureMgr.clear();var gestureInfo=gestureMgr.recognize(event,this.findHover(event.zrX,event.zrY,null).target,this.proxy.dom);stage==='end'&&gestureMgr.clear();// Do not do any preventDefault here. Upper application do that if necessary.\nif(gestureInfo){var type=gestureInfo.type;event.gestureEvent=type;this.dispatchToElement({target:gestureInfo.target},type,gestureInfo.event);}}};// Common handlers\neach$1(['click','mousedown','mouseup','mousewheel','dblclick','contextmenu'],function(name){Handler.prototype[name]=function(event){var x=event.zrX;var y=event.zrY;var isOutside=isOutsideBoundary(this,x,y);var hovered;var hoveredTarget;if(name!=='mouseup'||!isOutside){// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover\nhovered=this.findHover(x,y);hoveredTarget=hovered.target;}if(name==='mousedown'){this._downEl=hoveredTarget;this._downPoint=[event.zrX,event.zrY];// In case click triggered before mouseup\nthis._upEl=hoveredTarget;}else if(name==='mouseup'){this._upEl=hoveredTarget;}else if(name==='click'){if(this._downEl!==this._upEl// Original click event is triggered on the whole canvas element,\n// including the case that `mousedown` - `mousemove` - `mouseup`,\n// which should be filtered, otherwise it will bring trouble to\n// pan and zoom.\n||!this._downPoint// Arbitrary value\n||dist(this._downPoint,[event.zrX,event.zrY])>4){return;}this._downPoint=null;}this.dispatchToElement(hovered,name,event);};});function isHover(displayable,x,y){if(displayable[displayable.rectHover?'rectContain':'contain'](x,y)){var el=displayable;var isSilent;while(el){// If clipped by ancestor.\n// FIXME: If clipPath has neither stroke nor fill,\n// el.clipPath.contain(x, y) will always return false.\nif(el.clipPath&&!el.clipPath.contain(x,y)){return false;}if(el.silent){isSilent=true;}el=el.parent;}return isSilent?SILENT:true;}return false;}/**\n * See [Drag outside].\n */function isOutsideBoundary(handlerInstance,x,y){var painter=handlerInstance.painter;return x<0||x>painter.getWidth()||y<0||y>painter.getHeight();}mixin(Handler,Eventful);mixin(Handler,Draggable);/**\n * 3x2矩阵操作类\n * @exports zrender/tool/matrix\n */ /* global Float32Array */var ArrayCtor$1=typeof Float32Array==='undefined'?Array:Float32Array;/**\n * Create a identity matrix.\n * @return {Float32Array|Array.}\n */function create$1(){var out=new ArrayCtor$1(6);identity(out);return out;}/**\n * 设置矩阵为单位矩阵\n * @param {Float32Array|Array.} out\n */function identity(out){out[0]=1;out[1]=0;out[2]=0;out[3]=1;out[4]=0;out[5]=0;return out;}/**\n * 复制矩阵\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} m\n */function copy$1(out,m){out[0]=m[0];out[1]=m[1];out[2]=m[2];out[3]=m[3];out[4]=m[4];out[5]=m[5];return out;}/**\n * 矩阵相乘\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} m1\n * @param {Float32Array|Array.} m2\n */function mul$1(out,m1,m2){// Consider matrix.mul(m, m2, m);\n// where out is the same as m2.\n// So use temp variable to escape error.\nvar out0=m1[0]*m2[0]+m1[2]*m2[1];var out1=m1[1]*m2[0]+m1[3]*m2[1];var out2=m1[0]*m2[2]+m1[2]*m2[3];var out3=m1[1]*m2[2]+m1[3]*m2[3];var out4=m1[0]*m2[4]+m1[2]*m2[5]+m1[4];var out5=m1[1]*m2[4]+m1[3]*m2[5]+m1[5];out[0]=out0;out[1]=out1;out[2]=out2;out[3]=out3;out[4]=out4;out[5]=out5;return out;}/**\n * 平移变换\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} a\n * @param {Float32Array|Array.} v\n */function translate(out,a,v){out[0]=a[0];out[1]=a[1];out[2]=a[2];out[3]=a[3];out[4]=a[4]+v[0];out[5]=a[5]+v[1];return out;}/**\n * 旋转变换\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} a\n * @param {number} rad\n */function rotate(out,a,rad){var aa=a[0];var ac=a[2];var atx=a[4];var ab=a[1];var ad=a[3];var aty=a[5];var st=Math.sin(rad);var ct=Math.cos(rad);out[0]=aa*ct+ab*st;out[1]=-aa*st+ab*ct;out[2]=ac*ct+ad*st;out[3]=-ac*st+ct*ad;out[4]=ct*atx+st*aty;out[5]=ct*aty-st*atx;return out;}/**\n * 缩放变换\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} a\n * @param {Float32Array|Array.} v\n */function scale$1(out,a,v){var vx=v[0];var vy=v[1];out[0]=a[0]*vx;out[1]=a[1]*vy;out[2]=a[2]*vx;out[3]=a[3]*vy;out[4]=a[4]*vx;out[5]=a[5]*vy;return out;}/**\n * 求逆矩阵\n * @param {Float32Array|Array.} out\n * @param {Float32Array|Array.} a\n */function invert(out,a){var aa=a[0];var ac=a[2];var atx=a[4];var ab=a[1];var ad=a[3];var aty=a[5];var det=aa*ad-ab*ac;if(!det){return null;}det=1.0/det;out[0]=ad*det;out[1]=-ab*det;out[2]=-ac*det;out[3]=aa*det;out[4]=(ac*aty-ad*atx)*det;out[5]=(ab*atx-aa*aty)*det;return out;}/**\n * Clone a new matrix.\n * @param {Float32Array|Array.} a\n */function clone$2(a){var b=create$1();copy$1(b,a);return b;}var matrix=(Object.freeze||Object)({create:create$1,identity:identity,copy:copy$1,mul:mul$1,translate:translate,rotate:rotate,scale:scale$1,invert:invert,clone:clone$2});/**\n * 提供变换扩展\n * @module zrender/mixin/Transformable\n * @author pissang (https://www.github.com/pissang)\n */var mIdentity=identity;var EPSILON=5e-5;function isNotAroundZero(val){return val>EPSILON||val<-EPSILON;}/**\n * @alias module:zrender/mixin/Transformable\n * @constructor\n */var Transformable=function Transformable(opts){opts=opts||{};// If there are no given position, rotation, scale\nif(!opts.position){/**\n * 平移\n * @type {Array.}\n * @default [0, 0]\n */this.position=[0,0];}if(opts.rotation==null){/**\n * 旋转\n * @type {Array.}\n * @default 0\n */this.rotation=0;}if(!opts.scale){/**\n * 缩放\n * @type {Array.}\n * @default [1, 1]\n */this.scale=[1,1];}/**\n * 旋转和缩放的原点\n * @type {Array.}\n * @default null\n */this.origin=this.origin||null;};var transformableProto=Transformable.prototype;transformableProto.transform=null;/**\n * 判断是否需要有坐标变换\n * 如果有坐标变换, 则从position, rotation, scale以及父节点的transform计算出自身的transform矩阵\n */transformableProto.needLocalTransform=function(){return isNotAroundZero(this.rotation)||isNotAroundZero(this.position[0])||isNotAroundZero(this.position[1])||isNotAroundZero(this.scale[0]-1)||isNotAroundZero(this.scale[1]-1);};var scaleTmp=[];transformableProto.updateTransform=function(){var parent=this.parent;var parentHasTransform=parent&&parent.transform;var needLocalTransform=this.needLocalTransform();var m=this.transform;if(!(needLocalTransform||parentHasTransform)){m&&mIdentity(m);return;}m=m||create$1();if(needLocalTransform){this.getLocalTransform(m);}else{mIdentity(m);}// 应用父节点变换\nif(parentHasTransform){if(needLocalTransform){mul$1(m,parent.transform,m);}else{copy$1(m,parent.transform);}}// 保存这个变换矩阵\nthis.transform=m;var globalScaleRatio=this.globalScaleRatio;if(globalScaleRatio!=null&&globalScaleRatio!==1){this.getGlobalScale(scaleTmp);var relX=scaleTmp[0]<0?-1:1;var relY=scaleTmp[1]<0?-1:1;var sx=((scaleTmp[0]-relX)*globalScaleRatio+relX)/scaleTmp[0]||0;var sy=((scaleTmp[1]-relY)*globalScaleRatio+relY)/scaleTmp[1]||0;m[0]*=sx;m[1]*=sx;m[2]*=sy;m[3]*=sy;}this.invTransform=this.invTransform||create$1();invert(this.invTransform,m);};transformableProto.getLocalTransform=function(m){return Transformable.getLocalTransform(this,m);};/**\n * 将自己的transform应用到context上\n * @param {CanvasRenderingContext2D} ctx\n */transformableProto.setTransform=function(ctx){var m=this.transform;var dpr=ctx.dpr||1;if(m){ctx.setTransform(dpr*m[0],dpr*m[1],dpr*m[2],dpr*m[3],dpr*m[4],dpr*m[5]);}else{ctx.setTransform(dpr,0,0,dpr,0,0);}};transformableProto.restoreTransform=function(ctx){var dpr=ctx.dpr||1;ctx.setTransform(dpr,0,0,dpr,0,0);};var tmpTransform=[];var originTransform=create$1();transformableProto.setLocalTransform=function(m){if(!m){// TODO return or set identity?\nreturn;}var sx=m[0]*m[0]+m[1]*m[1];var sy=m[2]*m[2]+m[3]*m[3];var position=this.position;var scale$$1=this.scale;if(isNotAroundZero(sx-1)){sx=Math.sqrt(sx);}if(isNotAroundZero(sy-1)){sy=Math.sqrt(sy);}if(m[0]<0){sx=-sx;}if(m[3]<0){sy=-sy;}position[0]=m[4];position[1]=m[5];scale$$1[0]=sx;scale$$1[1]=sy;this.rotation=Math.atan2(-m[1]/sy,m[0]/sx);};/**\n * 分解`transform`矩阵到`position`, `rotation`, `scale`\n */transformableProto.decomposeTransform=function(){if(!this.transform){return;}var parent=this.parent;var m=this.transform;if(parent&&parent.transform){// Get local transform and decompose them to position, scale, rotation\nmul$1(tmpTransform,parent.invTransform,m);m=tmpTransform;}var origin=this.origin;if(origin&&(origin[0]||origin[1])){originTransform[4]=origin[0];originTransform[5]=origin[1];mul$1(tmpTransform,m,originTransform);tmpTransform[4]-=origin[0];tmpTransform[5]-=origin[1];m=tmpTransform;}this.setLocalTransform(m);};/**\n * Get global scale\n * @return {Array.}\n */transformableProto.getGlobalScale=function(out){var m=this.transform;out=out||[];if(!m){out[0]=1;out[1]=1;return out;}out[0]=Math.sqrt(m[0]*m[0]+m[1]*m[1]);out[1]=Math.sqrt(m[2]*m[2]+m[3]*m[3]);if(m[0]<0){out[0]=-out[0];}if(m[3]<0){out[1]=-out[1];}return out;};/**\n * 变换坐标位置到 shape 的局部坐标空间\n * @method\n * @param {number} x\n * @param {number} y\n * @return {Array.}\n */transformableProto.transformCoordToLocal=function(x,y){var v2=[x,y];var invTransform=this.invTransform;if(invTransform){applyTransform(v2,v2,invTransform);}return v2;};/**\n * 变换局部坐标位置到全局坐标空间\n * @method\n * @param {number} x\n * @param {number} y\n * @return {Array.}\n */transformableProto.transformCoordToGlobal=function(x,y){var v2=[x,y];var transform=this.transform;if(transform){applyTransform(v2,v2,transform);}return v2;};/**\n * @static\n * @param {Object} target\n * @param {Array.} target.origin\n * @param {number} target.rotation\n * @param {Array.} target.position\n * @param {Array.} [m]\n */Transformable.getLocalTransform=function(target,m){m=m||[];mIdentity(m);var origin=target.origin;var scale$$1=target.scale||[1,1];var rotation=target.rotation||0;var position=target.position||[0,0];if(origin){// Translate to origin\nm[4]-=origin[0];m[5]-=origin[1];}scale$1(m,m,scale$$1);if(rotation){rotate(m,m,rotation);}if(origin){// Translate back from origin\nm[4]+=origin[0];m[5]+=origin[1];}m[4]+=position[0];m[5]+=position[1];return m;};/**\n * 缓动代码来自 https://github.com/sole/tween.js/blob/master/src/Tween.js\n * @see http://sole.github.io/tween.js/examples/03_graphs.html\n * @exports zrender/animation/easing\n */var easing={/**\n * @param {number} k\n * @return {number}\n */linear:function linear(k){return k;},/**\n * @param {number} k\n * @return {number}\n */quadraticIn:function quadraticIn(k){return k*k;},/**\n * @param {number} k\n * @return {number}\n */quadraticOut:function quadraticOut(k){return k*(2-k);},/**\n * @param {number} k\n * @return {number}\n */quadraticInOut:function quadraticInOut(k){if((k*=2)<1){return 0.5*k*k;}return-0.5*(--k*(k-2)-1);},// 三次方的缓动(t^3)\n/**\n * @param {number} k\n * @return {number}\n */cubicIn:function cubicIn(k){return k*k*k;},/**\n * @param {number} k\n * @return {number}\n */cubicOut:function cubicOut(k){return--k*k*k+1;},/**\n * @param {number} k\n * @return {number}\n */cubicInOut:function cubicInOut(k){if((k*=2)<1){return 0.5*k*k*k;}return 0.5*((k-=2)*k*k+2);},// 四次方的缓动(t^4)\n/**\n * @param {number} k\n * @return {number}\n */quarticIn:function quarticIn(k){return k*k*k*k;},/**\n * @param {number} k\n * @return {number}\n */quarticOut:function quarticOut(k){return 1- --k*k*k*k;},/**\n * @param {number} k\n * @return {number}\n */quarticInOut:function quarticInOut(k){if((k*=2)<1){return 0.5*k*k*k*k;}return-0.5*((k-=2)*k*k*k-2);},// 五次方的缓动(t^5)\n/**\n * @param {number} k\n * @return {number}\n */quinticIn:function quinticIn(k){return k*k*k*k*k;},/**\n * @param {number} k\n * @return {number}\n */quinticOut:function quinticOut(k){return--k*k*k*k*k+1;},/**\n * @param {number} k\n * @return {number}\n */quinticInOut:function quinticInOut(k){if((k*=2)<1){return 0.5*k*k*k*k*k;}return 0.5*((k-=2)*k*k*k*k+2);},// 正弦曲线的缓动(sin(t))\n/**\n * @param {number} k\n * @return {number}\n */sinusoidalIn:function sinusoidalIn(k){return 1-Math.cos(k*Math.PI/2);},/**\n * @param {number} k\n * @return {number}\n */sinusoidalOut:function sinusoidalOut(k){return Math.sin(k*Math.PI/2);},/**\n * @param {number} k\n * @return {number}\n */sinusoidalInOut:function sinusoidalInOut(k){return 0.5*(1-Math.cos(Math.PI*k));},// 指数曲线的缓动(2^t)\n/**\n * @param {number} k\n * @return {number}\n */exponentialIn:function exponentialIn(k){return k===0?0:Math.pow(1024,k-1);},/**\n * @param {number} k\n * @return {number}\n */exponentialOut:function exponentialOut(k){return k===1?1:1-Math.pow(2,-10*k);},/**\n * @param {number} k\n * @return {number}\n */exponentialInOut:function exponentialInOut(k){if(k===0){return 0;}if(k===1){return 1;}if((k*=2)<1){return 0.5*Math.pow(1024,k-1);}return 0.5*(-Math.pow(2,-10*(k-1))+2);},// 圆形曲线的缓动(sqrt(1-t^2))\n/**\n * @param {number} k\n * @return {number}\n */circularIn:function circularIn(k){return 1-Math.sqrt(1-k*k);},/**\n * @param {number} k\n * @return {number}\n */circularOut:function circularOut(k){return Math.sqrt(1- --k*k);},/**\n * @param {number} k\n * @return {number}\n */circularInOut:function circularInOut(k){if((k*=2)<1){return-0.5*(Math.sqrt(1-k*k)-1);}return 0.5*(Math.sqrt(1-(k-=2)*k)+1);},// 创建类似于弹簧在停止前来回振荡的动画\n/**\n * @param {number} k\n * @return {number}\n */elasticIn:function elasticIn(k){var s;var a=0.1;var p=0.4;if(k===0){return 0;}if(k===1){return 1;}if(!a||a<1){a=1;s=p/4;}else{s=p*Math.asin(1/a)/(2*Math.PI);}return-(a*Math.pow(2,10*(k-=1))*Math.sin((k-s)*(2*Math.PI)/p));},/**\n * @param {number} k\n * @return {number}\n */elasticOut:function elasticOut(k){var s;var a=0.1;var p=0.4;if(k===0){return 0;}if(k===1){return 1;}if(!a||a<1){a=1;s=p/4;}else{s=p*Math.asin(1/a)/(2*Math.PI);}return a*Math.pow(2,-10*k)*Math.sin((k-s)*(2*Math.PI)/p)+1;},/**\n * @param {number} k\n * @return {number}\n */elasticInOut:function elasticInOut(k){var s;var a=0.1;var p=0.4;if(k===0){return 0;}if(k===1){return 1;}if(!a||a<1){a=1;s=p/4;}else{s=p*Math.asin(1/a)/(2*Math.PI);}if((k*=2)<1){return-0.5*(a*Math.pow(2,10*(k-=1))*Math.sin((k-s)*(2*Math.PI)/p));}return a*Math.pow(2,-10*(k-=1))*Math.sin((k-s)*(2*Math.PI)/p)*0.5+1;},// 在某一动画开始沿指示的路径进行动画处理前稍稍收回该动画的移动\n/**\n * @param {number} k\n * @return {number}\n */backIn:function backIn(k){var s=1.70158;return k*k*((s+1)*k-s);},/**\n * @param {number} k\n * @return {number}\n */backOut:function backOut(k){var s=1.70158;return--k*k*((s+1)*k+s)+1;},/**\n * @param {number} k\n * @return {number}\n */backInOut:function backInOut(k){var s=1.70158*1.525;if((k*=2)<1){return 0.5*(k*k*((s+1)*k-s));}return 0.5*((k-=2)*k*((s+1)*k+s)+2);},// 创建弹跳效果\n/**\n * @param {number} k\n * @return {number}\n */bounceIn:function bounceIn(k){return 1-easing.bounceOut(1-k);},/**\n * @param {number} k\n * @return {number}\n */bounceOut:function bounceOut(k){if(k<1/2.75){return 7.5625*k*k;}else if(k<2/2.75){return 7.5625*(k-=1.5/2.75)*k+0.75;}else if(k<2.5/2.75){return 7.5625*(k-=2.25/2.75)*k+0.9375;}else{return 7.5625*(k-=2.625/2.75)*k+0.984375;}},/**\n * @param {number} k\n * @return {number}\n */bounceInOut:function bounceInOut(k){if(k<0.5){return easing.bounceIn(k*2)*0.5;}return easing.bounceOut(k*2-1)*0.5+0.5;}};/**\n * 动画主控制器\n * @config target 动画对象,可以是数组,如果是数组的话会批量分发onframe等事件\n * @config life(1000) 动画时长\n * @config delay(0) 动画延迟时间\n * @config loop(true)\n * @config gap(0) 循环的间隔时间\n * @config onframe\n * @config easing(optional)\n * @config ondestroy(optional)\n * @config onrestart(optional)\n *\n * TODO pause\n */function Clip(options){this._target=options.target;// 生命周期\nthis._life=options.life||1000;// 延时\nthis._delay=options.delay||0;// 开始时间\n// this._startTime = new Date().getTime() + this._delay;// 单位毫秒\nthis._initialized=false;// 是否循环\nthis.loop=options.loop==null?false:options.loop;this.gap=options.gap||0;this.easing=options.easing||'Linear';this.onframe=options.onframe;this.ondestroy=options.ondestroy;this.onrestart=options.onrestart;this._pausedTime=0;this._paused=false;}Clip.prototype={constructor:Clip,step:function step(globalTime,deltaTime){// Set startTime on first step, or _startTime may has milleseconds different between clips\n// PENDING\nif(!this._initialized){this._startTime=globalTime+this._delay;this._initialized=true;}if(this._paused){this._pausedTime+=deltaTime;return;}var percent=(globalTime-this._startTime-this._pausedTime)/this._life;// 还没开始\nif(percent<0){return;}percent=Math.min(percent,1);var easing$$1=this.easing;var easingFunc=typeof easing$$1==='string'?easing[easing$$1]:easing$$1;var schedule=typeof easingFunc==='function'?easingFunc(percent):percent;this.fire('frame',schedule);// 结束\nif(percent===1){if(this.loop){this.restart(globalTime);// 重新开始周期\n// 抛出而不是直接调用事件直到 stage.update 后再统一调用这些事件\nreturn'restart';}// 动画完成将这个控制器标识为待删除\n// 在Animation.update中进行批量删除\nthis._needsRemove=true;return'destroy';}return null;},restart:function restart(globalTime){var remainder=(globalTime-this._startTime-this._pausedTime)%this._life;this._startTime=globalTime-remainder+this.gap;this._pausedTime=0;this._needsRemove=false;},fire:function fire(eventType,arg){eventType='on'+eventType;if(this[eventType]){this[eventType](this._target,arg);}},pause:function pause(){this._paused=true;},resume:function resume(){this._paused=false;}};// Simple LRU cache use doubly linked list\n// @module zrender/core/LRU\n/**\n * Simple double linked list. Compared with array, it has O(1) remove operation.\n * @constructor\n */var LinkedList=function LinkedList(){/**\n * @type {module:zrender/core/LRU~Entry}\n */this.head=null;/**\n * @type {module:zrender/core/LRU~Entry}\n */this.tail=null;this._len=0;};var linkedListProto=LinkedList.prototype;/**\n * Insert a new value at the tail\n * @param {} val\n * @return {module:zrender/core/LRU~Entry}\n */linkedListProto.insert=function(val){var entry=new Entry(val);this.insertEntry(entry);return entry;};/**\n * Insert an entry at the tail\n * @param {module:zrender/core/LRU~Entry} entry\n */linkedListProto.insertEntry=function(entry){if(!this.head){this.head=this.tail=entry;}else{this.tail.next=entry;entry.prev=this.tail;entry.next=null;this.tail=entry;}this._len++;};/**\n * Remove entry.\n * @param {module:zrender/core/LRU~Entry} entry\n */linkedListProto.remove=function(entry){var prev=entry.prev;var next=entry.next;if(prev){prev.next=next;}else{// Is head\nthis.head=next;}if(next){next.prev=prev;}else{// Is tail\nthis.tail=prev;}entry.next=entry.prev=null;this._len--;};/**\n * @return {number}\n */linkedListProto.len=function(){return this._len;};/**\n * Clear list\n */linkedListProto.clear=function(){this.head=this.tail=null;this._len=0;};/**\n * @constructor\n * @param {} val\n */var Entry=function Entry(val){/**\n * @type {}\n */this.value=val;/**\n * @type {module:zrender/core/LRU~Entry}\n */this.next;/**\n * @type {module:zrender/core/LRU~Entry}\n */this.prev;};/**\n * LRU Cache\n * @constructor\n * @alias module:zrender/core/LRU\n */var LRU=function LRU(maxSize){this._list=new LinkedList();this._map={};this._maxSize=maxSize||10;this._lastRemovedEntry=null;};var LRUProto=LRU.prototype;/**\n * @param {string} key\n * @param {} value\n * @return {} Removed value\n */LRUProto.put=function(key,value){var list=this._list;var map=this._map;var removed=null;if(map[key]==null){var len=list.len();// Reuse last removed entry\nvar entry=this._lastRemovedEntry;if(len>=this._maxSize&&len>0){// Remove the least recently used\nvar leastUsedEntry=list.head;list.remove(leastUsedEntry);delete map[leastUsedEntry.key];removed=leastUsedEntry.value;this._lastRemovedEntry=leastUsedEntry;}if(entry){entry.value=value;}else{entry=new Entry(value);}entry.key=key;list.insertEntry(entry);map[key]=entry;}return removed;};/**\n * @param {string} key\n * @return {}\n */LRUProto.get=function(key){var entry=this._map[key];var list=this._list;if(entry!=null){// Put the latest used entry in the tail\nif(entry!==list.tail){list.remove(entry);list.insertEntry(entry);}return entry.value;}};/**\n * Clear the cache\n */LRUProto.clear=function(){this._list.clear();this._map={};};var kCSSColorTable={'transparent':[0,0,0,0],'aliceblue':[240,248,255,1],'antiquewhite':[250,235,215,1],'aqua':[0,255,255,1],'aquamarine':[127,255,212,1],'azure':[240,255,255,1],'beige':[245,245,220,1],'bisque':[255,228,196,1],'black':[0,0,0,1],'blanchedalmond':[255,235,205,1],'blue':[0,0,255,1],'blueviolet':[138,43,226,1],'brown':[165,42,42,1],'burlywood':[222,184,135,1],'cadetblue':[95,158,160,1],'chartreuse':[127,255,0,1],'chocolate':[210,105,30,1],'coral':[255,127,80,1],'cornflowerblue':[100,149,237,1],'cornsilk':[255,248,220,1],'crimson':[220,20,60,1],'cyan':[0,255,255,1],'darkblue':[0,0,139,1],'darkcyan':[0,139,139,1],'darkgoldenrod':[184,134,11,1],'darkgray':[169,169,169,1],'darkgreen':[0,100,0,1],'darkgrey':[169,169,169,1],'darkkhaki':[189,183,107,1],'darkmagenta':[139,0,139,1],'darkolivegreen':[85,107,47,1],'darkorange':[255,140,0,1],'darkorchid':[153,50,204,1],'darkred':[139,0,0,1],'darksalmon':[233,150,122,1],'darkseagreen':[143,188,143,1],'darkslateblue':[72,61,139,1],'darkslategray':[47,79,79,1],'darkslategrey':[47,79,79,1],'darkturquoise':[0,206,209,1],'darkviolet':[148,0,211,1],'deeppink':[255,20,147,1],'deepskyblue':[0,191,255,1],'dimgray':[105,105,105,1],'dimgrey':[105,105,105,1],'dodgerblue':[30,144,255,1],'firebrick':[178,34,34,1],'floralwhite':[255,250,240,1],'forestgreen':[34,139,34,1],'fuchsia':[255,0,255,1],'gainsboro':[220,220,220,1],'ghostwhite':[248,248,255,1],'gold':[255,215,0,1],'goldenrod':[218,165,32,1],'gray':[128,128,128,1],'green':[0,128,0,1],'greenyellow':[173,255,47,1],'grey':[128,128,128,1],'honeydew':[240,255,240,1],'hotpink':[255,105,180,1],'indianred':[205,92,92,1],'indigo':[75,0,130,1],'ivory':[255,255,240,1],'khaki':[240,230,140,1],'lavender':[230,230,250,1],'lavenderblush':[255,240,245,1],'lawngreen':[124,252,0,1],'lemonchiffon':[255,250,205,1],'lightblue':[173,216,230,1],'lightcoral':[240,128,128,1],'lightcyan':[224,255,255,1],'lightgoldenrodyellow':[250,250,210,1],'lightgray':[211,211,211,1],'lightgreen':[144,238,144,1],'lightgrey':[211,211,211,1],'lightpink':[255,182,193,1],'lightsalmon':[255,160,122,1],'lightseagreen':[32,178,170,1],'lightskyblue':[135,206,250,1],'lightslategray':[119,136,153,1],'lightslategrey':[119,136,153,1],'lightsteelblue':[176,196,222,1],'lightyellow':[255,255,224,1],'lime':[0,255,0,1],'limegreen':[50,205,50,1],'linen':[250,240,230,1],'magenta':[255,0,255,1],'maroon':[128,0,0,1],'mediumaquamarine':[102,205,170,1],'mediumblue':[0,0,205,1],'mediumorchid':[186,85,211,1],'mediumpurple':[147,112,219,1],'mediumseagreen':[60,179,113,1],'mediumslateblue':[123,104,238,1],'mediumspringgreen':[0,250,154,1],'mediumturquoise':[72,209,204,1],'mediumvioletred':[199,21,133,1],'midnightblue':[25,25,112,1],'mintcream':[245,255,250,1],'mistyrose':[255,228,225,1],'moccasin':[255,228,181,1],'navajowhite':[255,222,173,1],'navy':[0,0,128,1],'oldlace':[253,245,230,1],'olive':[128,128,0,1],'olivedrab':[107,142,35,1],'orange':[255,165,0,1],'orangered':[255,69,0,1],'orchid':[218,112,214,1],'palegoldenrod':[238,232,170,1],'palegreen':[152,251,152,1],'paleturquoise':[175,238,238,1],'palevioletred':[219,112,147,1],'papayawhip':[255,239,213,1],'peachpuff':[255,218,185,1],'peru':[205,133,63,1],'pink':[255,192,203,1],'plum':[221,160,221,1],'powderblue':[176,224,230,1],'purple':[128,0,128,1],'red':[255,0,0,1],'rosybrown':[188,143,143,1],'royalblue':[65,105,225,1],'saddlebrown':[139,69,19,1],'salmon':[250,128,114,1],'sandybrown':[244,164,96,1],'seagreen':[46,139,87,1],'seashell':[255,245,238,1],'sienna':[160,82,45,1],'silver':[192,192,192,1],'skyblue':[135,206,235,1],'slateblue':[106,90,205,1],'slategray':[112,128,144,1],'slategrey':[112,128,144,1],'snow':[255,250,250,1],'springgreen':[0,255,127,1],'steelblue':[70,130,180,1],'tan':[210,180,140,1],'teal':[0,128,128,1],'thistle':[216,191,216,1],'tomato':[255,99,71,1],'turquoise':[64,224,208,1],'violet':[238,130,238,1],'wheat':[245,222,179,1],'white':[255,255,255,1],'whitesmoke':[245,245,245,1],'yellow':[255,255,0,1],'yellowgreen':[154,205,50,1]};function clampCssByte(i){// Clamp to integer 0 .. 255.\ni=Math.round(i);// Seems to be what Chrome does (vs truncation).\nreturn i<0?0:i>255?255:i;}function clampCssAngle(i){// Clamp to integer 0 .. 360.\ni=Math.round(i);// Seems to be what Chrome does (vs truncation).\nreturn i<0?0:i>360?360:i;}function clampCssFloat(f){// Clamp to float 0.0 .. 1.0.\nreturn f<0?0:f>1?1:f;}function parseCssInt(str){// int or percentage.\nif(str.length&&str.charAt(str.length-1)==='%'){return clampCssByte(parseFloat(str)/100*255);}return clampCssByte(parseInt(str,10));}function parseCssFloat(str){// float or percentage.\nif(str.length&&str.charAt(str.length-1)==='%'){return clampCssFloat(parseFloat(str)/100);}return clampCssFloat(parseFloat(str));}function cssHueToRgb(m1,m2,h){if(h<0){h+=1;}else if(h>1){h-=1;}if(h*6<1){return m1+(m2-m1)*h*6;}if(h*2<1){return m2;}if(h*3<2){return m1+(m2-m1)*(2/3-h)*6;}return m1;}function lerpNumber(a,b,p){return a+(b-a)*p;}function setRgba(out,r,g,b,a){out[0]=r;out[1]=g;out[2]=b;out[3]=a;return out;}function copyRgba(out,a){out[0]=a[0];out[1]=a[1];out[2]=a[2];out[3]=a[3];return out;}var colorCache=new LRU(20);var lastRemovedArr=null;function putToCache(colorStr,rgbaArr){// Reuse removed array\nif(lastRemovedArr){copyRgba(lastRemovedArr,rgbaArr);}lastRemovedArr=colorCache.put(colorStr,lastRemovedArr||rgbaArr.slice());}/**\n * @param {string} colorStr\n * @param {Array.} out\n * @return {Array.}\n * @memberOf module:zrender/util/color\n */function parse(colorStr,rgbaArr){if(!colorStr){return;}rgbaArr=rgbaArr||[];var cached=colorCache.get(colorStr);if(cached){return copyRgba(rgbaArr,cached);}// colorStr may be not string\ncolorStr=colorStr+'';// Remove all whitespace, not compliant, but should just be more accepting.\nvar str=colorStr.replace(/ /g,'').toLowerCase();// Color keywords (and transparent) lookup.\nif(str in kCSSColorTable){copyRgba(rgbaArr,kCSSColorTable[str]);putToCache(colorStr,rgbaArr);return rgbaArr;}// #abc and #abc123 syntax.\nif(str.charAt(0)==='#'){if(str.length===4){var iv=parseInt(str.substr(1),16);// TODO(deanm): Stricter parsing.\nif(!(iv>=0&&iv<=0xfff)){setRgba(rgbaArr,0,0,0,1);return;// Covers NaN.\n}setRgba(rgbaArr,(iv&0xf00)>>4|(iv&0xf00)>>8,iv&0xf0|(iv&0xf0)>>4,iv&0xf|(iv&0xf)<<4,1);putToCache(colorStr,rgbaArr);return rgbaArr;}else if(str.length===7){var iv=parseInt(str.substr(1),16);// TODO(deanm): Stricter parsing.\nif(!(iv>=0&&iv<=0xffffff)){setRgba(rgbaArr,0,0,0,1);return;// Covers NaN.\n}setRgba(rgbaArr,(iv&0xff0000)>>16,(iv&0xff00)>>8,iv&0xff,1);putToCache(colorStr,rgbaArr);return rgbaArr;}return;}var op=str.indexOf('(');var ep=str.indexOf(')');if(op!==-1&&ep+1===str.length){var fname=str.substr(0,op);var params=str.substr(op+1,ep-(op+1)).split(',');var alpha=1;// To allow case fallthrough.\nswitch(fname){case'rgba':if(params.length!==4){setRgba(rgbaArr,0,0,0,1);return;}alpha=parseCssFloat(params.pop());// jshint ignore:line\n// Fall through.\ncase'rgb':if(params.length!==3){setRgba(rgbaArr,0,0,0,1);return;}setRgba(rgbaArr,parseCssInt(params[0]),parseCssInt(params[1]),parseCssInt(params[2]),alpha);putToCache(colorStr,rgbaArr);return rgbaArr;case'hsla':if(params.length!==4){setRgba(rgbaArr,0,0,0,1);return;}params[3]=parseCssFloat(params[3]);hsla2rgba(params,rgbaArr);putToCache(colorStr,rgbaArr);return rgbaArr;case'hsl':if(params.length!==3){setRgba(rgbaArr,0,0,0,1);return;}hsla2rgba(params,rgbaArr);putToCache(colorStr,rgbaArr);return rgbaArr;default:return;}}setRgba(rgbaArr,0,0,0,1);return;}/**\n * @param {Array.} hsla\n * @param {Array.} rgba\n * @return {Array.} rgba\n */function hsla2rgba(hsla,rgba){var h=(parseFloat(hsla[0])%360+360)%360/360;// 0 .. 1\n// NOTE(deanm): According to the CSS spec s/l should only be\n// percentages, but we don't bother and let float or percentage.\nvar s=parseCssFloat(hsla[1]);var l=parseCssFloat(hsla[2]);var m2=l<=0.5?l*(s+1):l+s-l*s;var m1=l*2-m2;rgba=rgba||[];setRgba(rgba,clampCssByte(cssHueToRgb(m1,m2,h+1/3)*255),clampCssByte(cssHueToRgb(m1,m2,h)*255),clampCssByte(cssHueToRgb(m1,m2,h-1/3)*255),1);if(hsla.length===4){rgba[3]=hsla[3];}return rgba;}/**\n * @param {Array.} rgba\n * @return {Array.} hsla\n */function rgba2hsla(rgba){if(!rgba){return;}// RGB from 0 to 255\nvar R=rgba[0]/255;var G=rgba[1]/255;var B=rgba[2]/255;var vMin=Math.min(R,G,B);// Min. value of RGB\nvar vMax=Math.max(R,G,B);// Max. value of RGB\nvar delta=vMax-vMin;// Delta RGB value\nvar L=(vMax+vMin)/2;var H;var S;// HSL results from 0 to 1\nif(delta===0){H=0;S=0;}else{if(L<0.5){S=delta/(vMax+vMin);}else{S=delta/(2-vMax-vMin);}var deltaR=((vMax-R)/6+delta/2)/delta;var deltaG=((vMax-G)/6+delta/2)/delta;var deltaB=((vMax-B)/6+delta/2)/delta;if(R===vMax){H=deltaB-deltaG;}else if(G===vMax){H=1/3+deltaR-deltaB;}else if(B===vMax){H=2/3+deltaG-deltaR;}if(H<0){H+=1;}if(H>1){H-=1;}}var hsla=[H*360,S,L];if(rgba[3]!=null){hsla.push(rgba[3]);}return hsla;}/**\n * @param {string} color\n * @param {number} level\n * @return {string}\n * @memberOf module:zrender/util/color\n */function lift(color,level){var colorArr=parse(color);if(colorArr){for(var i=0;i<3;i++){if(level<0){colorArr[i]=colorArr[i]*(1-level)|0;}else{colorArr[i]=(255-colorArr[i])*level+colorArr[i]|0;}if(colorArr[i]>255){colorArr[i]=255;}else if(color[i]<0){colorArr[i]=0;}}return stringify(colorArr,colorArr.length===4?'rgba':'rgb');}}/**\n * @param {string} color\n * @return {string}\n * @memberOf module:zrender/util/color\n */function toHex(color){var colorArr=parse(color);if(colorArr){return((1<<24)+(colorArr[0]<<16)+(colorArr[1]<<8)+ +colorArr[2]).toString(16).slice(1);}}/**\n * Map value to color. Faster than lerp methods because color is represented by rgba array.\n * @param {number} normalizedValue A float between 0 and 1.\n * @param {Array.>} colors List of rgba color array\n * @param {Array.} [out] Mapped gba color array\n * @return {Array.} will be null/undefined if input illegal.\n */function fastLerp(normalizedValue,colors,out){if(!(colors&&colors.length)||!(normalizedValue>=0&&normalizedValue<=1)){return;}out=out||[];var value=normalizedValue*(colors.length-1);var leftIndex=Math.floor(value);var rightIndex=Math.ceil(value);var leftColor=colors[leftIndex];var rightColor=colors[rightIndex];var dv=value-leftIndex;out[0]=clampCssByte(lerpNumber(leftColor[0],rightColor[0],dv));out[1]=clampCssByte(lerpNumber(leftColor[1],rightColor[1],dv));out[2]=clampCssByte(lerpNumber(leftColor[2],rightColor[2],dv));out[3]=clampCssFloat(lerpNumber(leftColor[3],rightColor[3],dv));return out;}/**\n * @deprecated\n */var fastMapToColor=fastLerp;/**\n * @param {number} normalizedValue A float between 0 and 1.\n * @param {Array.} colors Color list.\n * @param {boolean=} fullOutput Default false.\n * @return {(string|Object)} Result color. If fullOutput,\n * return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},\n * @memberOf module:zrender/util/color\n */function lerp$1(normalizedValue,colors,fullOutput){if(!(colors&&colors.length)||!(normalizedValue>=0&&normalizedValue<=1)){return;}var value=normalizedValue*(colors.length-1);var leftIndex=Math.floor(value);var rightIndex=Math.ceil(value);var leftColor=parse(colors[leftIndex]);var rightColor=parse(colors[rightIndex]);var dv=value-leftIndex;var color=stringify([clampCssByte(lerpNumber(leftColor[0],rightColor[0],dv)),clampCssByte(lerpNumber(leftColor[1],rightColor[1],dv)),clampCssByte(lerpNumber(leftColor[2],rightColor[2],dv)),clampCssFloat(lerpNumber(leftColor[3],rightColor[3],dv))],'rgba');return fullOutput?{color:color,leftIndex:leftIndex,rightIndex:rightIndex,value:value}:color;}/**\n * @deprecated\n */var mapToColor=lerp$1;/**\n * @param {string} color\n * @param {number=} h 0 ~ 360, ignore when null.\n * @param {number=} s 0 ~ 1, ignore when null.\n * @param {number=} l 0 ~ 1, ignore when null.\n * @return {string} Color string in rgba format.\n * @memberOf module:zrender/util/color\n */function modifyHSL(color,h,s,l){color=parse(color);if(color){color=rgba2hsla(color);h!=null&&(color[0]=clampCssAngle(h));s!=null&&(color[1]=parseCssFloat(s));l!=null&&(color[2]=parseCssFloat(l));return stringify(hsla2rgba(color),'rgba');}}/**\n * @param {string} color\n * @param {number=} alpha 0 ~ 1\n * @return {string} Color string in rgba format.\n * @memberOf module:zrender/util/color\n */function modifyAlpha(color,alpha){color=parse(color);if(color&&alpha!=null){color[3]=clampCssFloat(alpha);return stringify(color,'rgba');}}/**\n * @param {Array.} arrColor like [12,33,44,0.4]\n * @param {string} type 'rgba', 'hsva', ...\n * @return {string} Result color. (If input illegal, return undefined).\n */function stringify(arrColor,type){if(!arrColor||!arrColor.length){return;}var colorStr=arrColor[0]+','+arrColor[1]+','+arrColor[2];if(type==='rgba'||type==='hsva'||type==='hsla'){colorStr+=','+arrColor[3];}return type+'('+colorStr+')';}var color=(Object.freeze||Object)({parse:parse,lift:lift,toHex:toHex,fastLerp:fastLerp,fastMapToColor:fastMapToColor,lerp:lerp$1,mapToColor:mapToColor,modifyHSL:modifyHSL,modifyAlpha:modifyAlpha,stringify:stringify});/**\n * @module echarts/animation/Animator\n */var arraySlice=Array.prototype.slice;function defaultGetter(target,key){return target[key];}function defaultSetter(target,key,value){target[key]=value;}/**\n * @param {number} p0\n * @param {number} p1\n * @param {number} percent\n * @return {number}\n */function interpolateNumber(p0,p1,percent){return(p1-p0)*percent+p0;}/**\n * @param {string} p0\n * @param {string} p1\n * @param {number} percent\n * @return {string}\n */function interpolateString(p0,p1,percent){return percent>0.5?p1:p0;}/**\n * @param {Array} p0\n * @param {Array} p1\n * @param {number} percent\n * @param {Array} out\n * @param {number} arrDim\n */function interpolateArray(p0,p1,percent,out,arrDim){var len=p0.length;if(arrDim===1){for(var i=0;iarr1Len;if(isPreviousLarger){// Cut the previous\narr0.length=arr1Len;}else{// Fill the previous\nfor(var i=arr0Len;i=0;frame--){if(kfPercents[frame]<=percent){break;}}// PENDING really need to do this ?\nframe=Math.min(frame,trackLen-2);}else{for(frame=lastFrame;framepercent){break;}}frame=Math.min(frame-1,trackLen-2);}lastFrame=frame;lastFramePercent=percent;var range=kfPercents[frame+1]-kfPercents[frame];if(range===0){return;}else{w=(percent-kfPercents[frame])/range;}if(useSpline){p1=kfValues[frame];p0=kfValues[frame===0?frame:frame-1];p2=kfValues[frame>trackLen-2?trackLen-1:frame+1];p3=kfValues[frame>trackLen-3?trackLen-1:frame+2];if(isValueArray){catmullRomInterpolateArray(p0,p1,p2,p3,w,w*w,w*w*w,getter(target,propName),arrDim);}else{var value;if(isValueColor){value=catmullRomInterpolateArray(p0,p1,p2,p3,w,w*w,w*w*w,rgba,1);value=rgba2String(rgba);}else if(isValueString){// String is step(0.5)\nreturn interpolateString(p1,p2,w);}else{value=catmullRomInterpolate(p0,p1,p2,p3,w,w*w,w*w*w);}setter(target,propName,value);}}else{if(isValueArray){interpolateArray(kfValues[frame],kfValues[frame+1],w,getter(target,propName),arrDim);}else{var value;if(isValueColor){interpolateArray(kfValues[frame],kfValues[frame+1],w,rgba,1);value=rgba2String(rgba);}else if(isValueString){// String is step(0.5)\nreturn interpolateString(kfValues[frame],kfValues[frame+1],w);}else{value=interpolateNumber(kfValues[frame],kfValues[frame+1],w);}setter(target,propName,value);}}};var clip=new Clip({target:animator._target,life:trackMaxTime,loop:animator._loop,delay:animator._delay,onframe:onframe,ondestroy:oneTrackDone});if(easing&&easing!=='spline'){clip.easing=easing;}return clip;}/**\n * @alias module:zrender/animation/Animator\n * @constructor\n * @param {Object} target\n * @param {boolean} loop\n * @param {Function} getter\n * @param {Function} setter\n */var Animator=function Animator(target,loop,getter,setter){this._tracks={};this._target=target;this._loop=loop||false;this._getter=getter||defaultGetter;this._setter=setter||defaultSetter;this._clipCount=0;this._delay=0;this._doneList=[];this._onframeList=[];this._clipList=[];};Animator.prototype={/**\n * Set Animation keyframe\n * @param {number} time 关键帧时间,单位是ms\n * @param {Object} props 关键帧的属性值,key-value表示\n * @return {module:zrender/animation/Animator}\n */when:function when(time/* ms */,props){var tracks=this._tracks;for(var propName in props){if(!props.hasOwnProperty(propName)){continue;}if(!tracks[propName]){tracks[propName]=[];// Invalid value\nvar value=this._getter(this._target,propName);if(value==null){// zrLog('Invalid property ' + propName);\ncontinue;}// If time is 0\n// Then props is given initialize value\n// Else\n// Initialize value from current prop value\nif(time!==0){tracks[propName].push({time:0,value:cloneValue(value)});}}tracks[propName].push({time:time,value:props[propName]});}return this;},/**\n * 添加动画每一帧的回调函数\n * @param {Function} callback\n * @return {module:zrender/animation/Animator}\n */during:function during(callback){this._onframeList.push(callback);return this;},pause:function pause(){for(var i=0;i}\n */getClips:function getClips(){return this._clipList;}};var dpr=1;// If in browser environment\nif(typeof window!=='undefined'){dpr=Math.max(window.devicePixelRatio||1,1);}/**\n * config默认配置项\n * @exports zrender/config\n * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)\n */ /**\n * Debug log mode:\n * 0: Do nothing, for release.\n * 1: console.error, for debug.\n */var debugMode=0;// retina 屏幕优化\nvar devicePixelRatio=dpr;var logError=function logError(){};if(debugMode===1){logError=console.error;}var logError$1=logError;/**\n * @alias module:zrender/mixin/Animatable\n * @constructor\n */var Animatable=function Animatable(){/**\n * @type {Array.}\n * @readOnly\n */this.animators=[];};Animatable.prototype={constructor:Animatable,/**\n * 动画\n *\n * @param {string} path The path to fetch value from object, like 'a.b.c'.\n * @param {boolean} [loop] Whether to loop animation.\n * @return {module:zrender/animation/Animator}\n * @example:\n * el.animate('style', false)\n * .when(1000, {x: 10} )\n * .done(function(){ // Animation done })\n * .start()\n */animate:function animate(path,loop){var target;var animatingShape=false;var el=this;var zr=this.__zr;if(path){var pathSplitted=path.split('.');var prop=el;// If animating shape\nanimatingShape=pathSplitted[0]==='shape';for(var i=0,l=pathSplitted.length;i0){animatable.animate(path,false).when(time==null?500:time,objShallow).delay(delay||0);}}function setAttrByPath(el,path,name,value){// Attr directly if not has property\n// FIXME, if some property not needed for element ?\nif(!path){el.attr(name,value);}else{// Only support set shape or style\nvar props={};props[path]={};props[path][name]=value;el.attr(props);}}/**\n * @alias module:zrender/Element\n * @constructor\n * @extends {module:zrender/mixin/Animatable}\n * @extends {module:zrender/mixin/Transformable}\n * @extends {module:zrender/mixin/Eventful}\n */var Element=function Element(opts){// jshint ignore:line\nTransformable.call(this,opts);Eventful.call(this,opts);Animatable.call(this,opts);/**\n * 画布元素ID\n * @type {string}\n */this.id=opts.id||guid();};Element.prototype={/**\n * 元素类型\n * Element type\n * @type {string}\n */type:'element',/**\n * 元素名字\n * Element name\n * @type {string}\n */name:'',/**\n * ZRender 实例对象,会在 element 添加到 zrender 实例中后自动赋值\n * ZRender instance will be assigned when element is associated with zrender\n * @name module:/zrender/Element#__zr\n * @type {module:zrender/ZRender}\n */__zr:null,/**\n * 图形是否忽略,为true时忽略图形的绘制以及事件触发\n * If ignore drawing and events of the element object\n * @name module:/zrender/Element#ignore\n * @type {boolean}\n * @default false\n */ignore:false,/**\n * 用于裁剪的路径(shape),所有 Group 内的路径在绘制时都会被这个路径裁剪\n * 该路径会继承被裁减对象的变换\n * @type {module:zrender/graphic/Path}\n * @see http://www.w3.org/TR/2dcontext/#clipping-region\n * @readOnly\n */clipPath:null,/**\n * 是否是 Group\n * @type {boolean}\n */isGroup:false,/**\n * Drift element\n * @param {number} dx dx on the global space\n * @param {number} dy dy on the global space\n */drift:function drift(dx,dy){switch(this.draggable){case'horizontal':dy=0;break;case'vertical':dx=0;break;}var m=this.transform;if(!m){m=this.transform=[1,0,0,1,0,0];}m[4]+=dx;m[5]+=dy;this.decomposeTransform();this.dirty(false);},/**\n * Hook before update\n */beforeUpdate:function beforeUpdate(){},/**\n * Hook after update\n */afterUpdate:function afterUpdate(){},/**\n * Update each frame\n */update:function update(){this.updateTransform();},/**\n * @param {Function} cb\n * @param {} context\n */traverse:function traverse(cb,context){},/**\n * @protected\n */attrKV:function attrKV(key,value){if(key==='position'||key==='scale'||key==='origin'){// Copy the array\nif(value){var target=this[key];if(!target){target=this[key]=[];}target[0]=value[0];target[1]=value[1];}}else{this[key]=value;}},/**\n * Hide the element\n */hide:function hide(){this.ignore=true;this.__zr&&this.__zr.refresh();},/**\n * Show the element\n */show:function show(){this.ignore=false;this.__zr&&this.__zr.refresh();},/**\n * @param {string|Object} key\n * @param {*} value\n */attr:function attr(key,value){if(typeof key==='string'){this.attrKV(key,value);}else if(isObject$1(key)){for(var name in key){if(key.hasOwnProperty(name)){this.attrKV(name,key[name]);}}}this.dirty(false);return this;},/**\n * @param {module:zrender/graphic/Path} clipPath\n */setClipPath:function setClipPath(clipPath){var zr=this.__zr;if(zr){clipPath.addSelfToZr(zr);}// Remove previous clip path\nif(this.clipPath&&this.clipPath!==clipPath){this.removeClipPath();}this.clipPath=clipPath;clipPath.__zr=zr;clipPath.__clipTarget=this;this.dirty(false);},/**\n */removeClipPath:function removeClipPath(){var clipPath=this.clipPath;if(clipPath){if(clipPath.__zr){clipPath.removeSelfFromZr(clipPath.__zr);}clipPath.__zr=null;clipPath.__clipTarget=null;this.clipPath=null;this.dirty(false);}},/**\n * Add self from zrender instance.\n * Not recursively because it will be invoked when element added to storage.\n * @param {module:zrender/ZRender} zr\n */addSelfToZr:function addSelfToZr(zr){this.__zr=zr;// 添加动画\nvar animators=this.animators;if(animators){for(var i=0;i} m\n * @methods\n */applyTransform:function(){var lt=[];var rb=[];var lb=[];var rt=[];return function(m){// In case usage like this\n// el.getBoundingRect().applyTransform(el.transform)\n// And element has no transform\nif(!m){return;}lt[0]=lb[0]=this.x;lt[1]=rt[1]=this.y;rb[0]=rt[0]=this.x+this.width;rb[1]=lb[1]=this.y+this.height;v2ApplyTransform(lt,lt,m);v2ApplyTransform(rb,rb,m);v2ApplyTransform(lb,lb,m);v2ApplyTransform(rt,rt,m);this.x=mathMin(lt[0],rb[0],lb[0],rt[0]);this.y=mathMin(lt[1],rb[1],lb[1],rt[1]);var maxX=mathMax(lt[0],rb[0],lb[0],rt[0]);var maxY=mathMax(lt[1],rb[1],lb[1],rt[1]);this.width=maxX-this.x;this.height=maxY-this.y;};}(),/**\n * Calculate matrix of transforming from self to target rect\n * @param {module:zrender/core/BoundingRect} b\n * @return {Array.}\n */calculateTransform:function calculateTransform(b){var a=this;var sx=b.width/a.width;var sy=b.height/a.height;var m=create$1();// 矩阵右乘\ntranslate(m,m,[-a.x,-a.y]);scale$1(m,m,[sx,sy]);translate(m,m,[b.x,b.y]);return m;},/**\n * @param {(module:echarts/core/BoundingRect|Object)} b\n * @return {boolean}\n */intersect:function intersect(b){if(!b){return false;}if(!(b instanceof BoundingRect)){// Normalize negative width/height.\nb=BoundingRect.create(b);}var a=this;var ax0=a.x;var ax1=a.x+a.width;var ay0=a.y;var ay1=a.y+a.height;var bx0=b.x;var bx1=b.x+b.width;var by0=b.y;var by1=b.y+b.height;return!(ax1=rect.x&&x<=rect.x+rect.width&&y>=rect.y&&y<=rect.y+rect.height;},/**\n * @return {module:echarts/core/BoundingRect}\n */clone:function clone(){return new BoundingRect(this.x,this.y,this.width,this.height);},/**\n * Copy from another rect\n */copy:function copy(other){this.x=other.x;this.y=other.y;this.width=other.width;this.height=other.height;},plain:function plain(){return{x:this.x,y:this.y,width:this.width,height:this.height};}};/**\n * @param {Object|module:zrender/core/BoundingRect} rect\n * @param {number} rect.x\n * @param {number} rect.y\n * @param {number} rect.width\n * @param {number} rect.height\n * @return {module:zrender/core/BoundingRect}\n */BoundingRect.create=function(rect){return new BoundingRect(rect.x,rect.y,rect.width,rect.height);};/**\n * Group是一个容器,可以插入子节点,Group的变换也会被应用到子节点上\n * @module zrender/graphic/Group\n * @example\n * var Group = require('zrender/container/Group');\n * var Circle = require('zrender/graphic/shape/Circle');\n * var g = new Group();\n * g.position[0] = 100;\n * g.position[1] = 100;\n * g.add(new Circle({\n * style: {\n * x: 100,\n * y: 100,\n * r: 20,\n * }\n * }));\n * zr.add(g);\n */ /**\n * @alias module:zrender/graphic/Group\n * @constructor\n * @extends module:zrender/mixin/Transformable\n * @extends module:zrender/mixin/Eventful\n */var Group=function Group(opts){opts=opts||{};Element.call(this,opts);for(var key in opts){if(opts.hasOwnProperty(key)){this[key]=opts[key];}}this._children=[];this.__storage=null;this.__dirty=true;};Group.prototype={constructor:Group,isGroup:true,/**\n * @type {string}\n */type:'group',/**\n * 所有子孙元素是否响应鼠标事件\n * @name module:/zrender/container/Group#silent\n * @type {boolean}\n * @default false\n */silent:false,/**\n * @return {Array.}\n */children:function children(){return this._children.slice();},/**\n * 获取指定 index 的儿子节点\n * @param {number} idx\n * @return {module:zrender/Element}\n */childAt:function childAt(idx){return this._children[idx];},/**\n * 获取指定名字的儿子节点\n * @param {string} name\n * @return {module:zrender/Element}\n */childOfName:function childOfName(name){var children=this._children;for(var i=0;i=0){children.splice(idx,0,child);this._doAdd(child);}}return this;},_doAdd:function _doAdd(child){if(child.parent){child.parent.remove(child);}child.parent=this;var storage=this.__storage;var zr=this.__zr;if(storage&&storage!==child.__storage){storage.addToStorage(child);if(child instanceof Group){child.addChildrenToStorage(storage);}}zr&&zr.refresh();},/**\n * 移除子节点\n * @param {module:zrender/Element} child\n */remove:function remove(child){var zr=this.__zr;var storage=this.__storage;var children=this._children;var idx=indexOf(children,child);if(idx<0){return this;}children.splice(idx,1);child.parent=null;if(storage){storage.delFromStorage(child);if(child instanceof Group){child.delChildrenFromStorage(storage);}}zr&&zr.refresh();return this;},/**\n * 移除所有子节点\n */removeAll:function removeAll(){var children=this._children;var storage=this.__storage;var child;var i;for(i=0;i=DEFAULT_MIN_MERGE){r|=n&1;n>>=1;}return n+r;}function makeAscendingRun(array,lo,hi,compare){var runHi=lo+1;if(runHi===hi){return 1;}if(compare(array[runHi++],array[lo])<0){while(runHi=0){runHi++;}}return runHi-lo;}function reverseRun(array,lo,hi){hi--;while(lo>>1;if(compare(pivot,array[mid])<0){right=mid;}else{left=mid+1;}}var n=start-left;switch(n){case 3:array[left+3]=array[left+2];case 2:array[left+2]=array[left+1];case 1:array[left+1]=array[left];break;default:while(n>0){array[left+n]=array[left+n-1];n--;}}array[left]=pivot;}}function gallopLeft(value,array,start,length,hint,compare){var lastOffset=0;var maxOffset=0;var offset=1;if(compare(value,array[start+hint])>0){maxOffset=length-hint;while(offset0){lastOffset=offset;offset=(offset<<1)+1;if(offset<=0){offset=maxOffset;}}if(offset>maxOffset){offset=maxOffset;}lastOffset+=hint;offset+=hint;}else{maxOffset=hint+1;while(offsetmaxOffset){offset=maxOffset;}var tmp=lastOffset;lastOffset=hint-offset;offset=hint-tmp;}lastOffset++;while(lastOffset>>1);if(compare(value,array[start+m])>0){lastOffset=m+1;}else{offset=m;}}return offset;}function gallopRight(value,array,start,length,hint,compare){var lastOffset=0;var maxOffset=0;var offset=1;if(compare(value,array[start+hint])<0){maxOffset=hint+1;while(offsetmaxOffset){offset=maxOffset;}var tmp=lastOffset;lastOffset=hint-offset;offset=hint-tmp;}else{maxOffset=length-hint;while(offset=0){lastOffset=offset;offset=(offset<<1)+1;if(offset<=0){offset=maxOffset;}}if(offset>maxOffset){offset=maxOffset;}lastOffset+=hint;offset+=hint;}lastOffset++;while(lastOffset>>1);if(compare(value,array[start+m])<0){offset=m;}else{lastOffset=m+1;}}return offset;}function TimSort(array,compare){var minGallop=DEFAULT_MIN_GALLOPING;var runStart;var runLength;var stackSize=0;var tmp=[];runStart=[];runLength=[];function pushRun(_runStart,_runLength){runStart[stackSize]=_runStart;runLength[stackSize]=_runLength;stackSize+=1;}function mergeRuns(){while(stackSize>1){var n=stackSize-2;if(n>=1&&runLength[n-1]<=runLength[n]+runLength[n+1]||n>=2&&runLength[n-2]<=runLength[n]+runLength[n-1]){if(runLength[n-1]runLength[n+1]){break;}mergeAt(n);}}function forceMergeRuns(){while(stackSize>1){var n=stackSize-2;if(n>0&&runLength[n-1]=DEFAULT_MIN_GALLOPING||count2>=DEFAULT_MIN_GALLOPING);if(exit){break;}if(_minGallop<0){_minGallop=0;}_minGallop+=2;}minGallop=_minGallop;minGallop<1&&(minGallop=1);if(length1===1){for(i=0;i=0;i--){array[customDest+i]=array[customCursor+i];}array[dest]=tmp[cursor2];return;}var _minGallop=minGallop;while(true){var count1=0;var count2=0;var exit=false;do{if(compare(tmp[cursor2],array[cursor1])<0){array[dest--]=array[cursor1--];count1++;count2=0;if(--length1===0){exit=true;break;}}else{array[dest--]=tmp[cursor2--];count2++;count1=0;if(--length2===1){exit=true;break;}}}while((count1|count2)<_minGallop);if(exit){break;}do{count1=length1-gallopRight(tmp[cursor2],array,start1,length1,length1-1,compare);if(count1!==0){dest-=count1;cursor1-=count1;length1-=count1;customDest=dest+1;customCursor=cursor1+1;for(i=count1-1;i>=0;i--){array[customDest+i]=array[customCursor+i];}if(length1===0){exit=true;break;}}array[dest--]=tmp[cursor2--];if(--length2===1){exit=true;break;}count2=length2-gallopLeft(array[cursor1],tmp,0,length2,length2-1,compare);if(count2!==0){dest-=count2;cursor2-=count2;length2-=count2;customDest=dest+1;customCursor=cursor2+1;for(i=0;i=DEFAULT_MIN_GALLOPING||count2>=DEFAULT_MIN_GALLOPING);if(exit){break;}if(_minGallop<0){_minGallop=0;}_minGallop+=2;}minGallop=_minGallop;if(minGallop<1){minGallop=1;}if(length2===1){dest-=length1;cursor1-=length1;customDest=dest+1;customCursor=cursor1+1;for(i=length1-1;i>=0;i--){array[customDest+i]=array[customCursor+i];}array[dest]=tmp[cursor2];}else if(length2===0){throw new Error();// throw new Error('mergeHigh preconditions were not respected');\n}else{customCursor=dest-(length2-1);for(i=0;iminRun){force=minRun;}binaryInsertionSort(array,lo,lo+force,lo+runLength,compare);runLength=force;}ts.pushRun(lo,runLength);ts.mergeRuns();remaining-=runLength;lo+=runLength;}while(remaining!==0);ts.forceMergeRuns();}// Use timsort because in most case elements are partially sorted\n// https://jsfiddle.net/pissang/jr4x7mdm/8/\nfunction shapeCompareFunc(a,b){if(a.zlevel===b.zlevel){if(a.z===b.z){// if (a.z2 === b.z2) {\n// // FIXME Slow has renderidx compare\n// // http://stackoverflow.com/questions/20883421/sorting-in-javascript-should-every-compare-function-have-a-return-0-statement\n// // https://github.com/v8/v8/blob/47cce544a31ed5577ffe2963f67acb4144ee0232/src/js/array.js#L1012\n// return a.__renderidx - b.__renderidx;\n// }\nreturn a.z2-b.z2;}return a.z-b.z;}return a.zlevel-b.zlevel;}/**\n * 内容仓库 (M)\n * @alias module:zrender/Storage\n * @constructor\n */var Storage=function Storage(){// jshint ignore:line\nthis._roots=[];this._displayList=[];this._displayListLen=0;};Storage.prototype={constructor:Storage,/**\n * @param {Function} cb\n *\n */traverse:function traverse(cb,context){for(var i=0;i}\n */getDisplayList:function getDisplayList(update,includeIgnore){includeIgnore=includeIgnore||false;if(update){this.updateDisplayList(includeIgnore);}return this._displayList;},/**\n * 更新图形的绘制队列。\n * 每次绘制前都会调用,该方法会先深度优先遍历整个树,更新所有Group和Shape的变换并且把所有可见的Shape保存到数组中,\n * 最后根据绘制的优先级(zlevel > z > 插入顺序)排序得到绘制队列\n * @param {boolean} [includeIgnore=false] 是否包含 ignore 的数组\n */updateDisplayList:function updateDisplayList(includeIgnore){this._displayListLen=0;var roots=this._roots;var displayList=this._displayList;for(var i=0,len=roots.length;i} [el] 如果为空清空整个Storage\n */delRoot:function delRoot(el){if(el==null){// 不指定el清空\nfor(var i=0;i=0){this.delFromStorage(el);this._roots.splice(idx,1);if(el instanceof Group){el.delChildrenFromStorage(this);}}},addToStorage:function addToStorage(el){if(el){el.__storage=this;el.dirty(false);}return this;},delFromStorage:function delFromStorage(el){if(el){el.__storage=null;}return this;},/**\n * 清空并且释放Storage\n */dispose:function dispose(){this._renderList=this._roots=null;},displayableSortFunc:shapeCompareFunc};var SHADOW_PROPS={'shadowBlur':1,'shadowOffsetX':1,'shadowOffsetY':1,'textShadowBlur':1,'textShadowOffsetX':1,'textShadowOffsetY':1,'textBoxShadowBlur':1,'textBoxShadowOffsetX':1,'textBoxShadowOffsetY':1};var fixShadow=function fixShadow(ctx,propName,value){if(SHADOW_PROPS.hasOwnProperty(propName)){return value*=ctx.dpr;}return value;};var ContextCachedBy={NONE:0,STYLE_BIND:1,PLAIN_TEXT:2};// Avoid confused with 0/false.\nvar WILL_BE_RESTORED=9;var STYLE_COMMON_PROPS=[['shadowBlur',0],['shadowOffsetX',0],['shadowOffsetY',0],['shadowColor','#000'],['lineCap','butt'],['lineJoin','miter'],['miterLimit',10]];// var SHADOW_PROPS = STYLE_COMMON_PROPS.slice(0, 4);\n// var LINE_PROPS = STYLE_COMMON_PROPS.slice(4);\nvar Style=function Style(opts){this.extendFrom(opts,false);};function createLinearGradient(ctx,obj,rect){var x=obj.x==null?0:obj.x;var x2=obj.x2==null?1:obj.x2;var y=obj.y==null?0:obj.y;var y2=obj.y2==null?0:obj.y2;if(!obj.global){x=x*rect.width+rect.x;x2=x2*rect.width+rect.x;y=y*rect.height+rect.y;y2=y2*rect.height+rect.y;}// Fix NaN when rect is Infinity\nx=isNaN(x)?0:x;x2=isNaN(x2)?1:x2;y=isNaN(y)?0:y;y2=isNaN(y2)?0:y2;var canvasGradient=ctx.createLinearGradient(x,y,x2,y2);return canvasGradient;}function createRadialGradient(ctx,obj,rect){var width=rect.width;var height=rect.height;var min=Math.min(width,height);var x=obj.x==null?0.5:obj.x;var y=obj.y==null?0.5:obj.y;var r=obj.r==null?0.5:obj.r;if(!obj.global){x=x*width+rect.x;y=y*height+rect.y;r=r*min;}var canvasGradient=ctx.createRadialGradient(x,y,0,x,y,r);return canvasGradient;}Style.prototype={constructor:Style,/**\n * @type {string}\n */fill:'#000',/**\n * @type {string}\n */stroke:null,/**\n * @type {number}\n */opacity:1,/**\n * @type {number}\n */fillOpacity:null,/**\n * @type {number}\n */strokeOpacity:null,/**\n * `true` is not supported.\n * `false`/`null`/`undefined` are the same.\n * `false` is used to remove lineDash in some\n * case that `null`/`undefined` can not be set.\n * (e.g., emphasis.lineStyle in echarts)\n * @type {Array.|boolean}\n */lineDash:null,/**\n * @type {number}\n */lineDashOffset:0,/**\n * @type {number}\n */shadowBlur:0,/**\n * @type {number}\n */shadowOffsetX:0,/**\n * @type {number}\n */shadowOffsetY:0,/**\n * @type {number}\n */lineWidth:1,/**\n * If stroke ignore scale\n * @type {Boolean}\n */strokeNoScale:false,// Bounding rect text configuration\n// Not affected by element transform\n/**\n * @type {string}\n */text:null,/**\n * If `fontSize` or `fontFamily` exists, `font` will be reset by\n * `fontSize`, `fontStyle`, `fontWeight`, `fontFamily`.\n * So do not visit it directly in upper application (like echarts),\n * but use `contain/text#makeFont` instead.\n * @type {string}\n */font:null,/**\n * The same as font. Use font please.\n * @deprecated\n * @type {string}\n */textFont:null,/**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */fontStyle:null,/**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */fontWeight:null,/**\n * It helps merging respectively, rather than parsing an entire font string.\n * Should be 12 but not '12px'.\n * @type {number}\n */fontSize:null,/**\n * It helps merging respectively, rather than parsing an entire font string.\n * @type {string}\n */fontFamily:null,/**\n * Reserved for special functinality, like 'hr'.\n * @type {string}\n */textTag:null,/**\n * @type {string}\n */textFill:'#000',/**\n * @type {string}\n */textStroke:null,/**\n * @type {number}\n */textWidth:null,/**\n * Only for textBackground.\n * @type {number}\n */textHeight:null,/**\n * textStroke may be set as some color as a default\n * value in upper applicaion, where the default value\n * of textStrokeWidth should be 0 to make sure that\n * user can choose to do not use text stroke.\n * @type {number}\n */textStrokeWidth:0,/**\n * @type {number}\n */textLineHeight:null,/**\n * 'inside', 'left', 'right', 'top', 'bottom'\n * [x, y]\n * Based on x, y of rect.\n * @type {string|Array.}\n * @default 'inside'\n */textPosition:'inside',/**\n * If not specified, use the boundingRect of a `displayable`.\n * @type {Object}\n */textRect:null,/**\n * [x, y]\n * @type {Array.}\n */textOffset:null,/**\n * @type {string}\n */textAlign:null,/**\n * @type {string}\n */textVerticalAlign:null,/**\n * @type {number}\n */textDistance:5,/**\n * @type {string}\n */textShadowColor:'transparent',/**\n * @type {number}\n */textShadowBlur:0,/**\n * @type {number}\n */textShadowOffsetX:0,/**\n * @type {number}\n */textShadowOffsetY:0,/**\n * @type {string}\n */textBoxShadowColor:'transparent',/**\n * @type {number}\n */textBoxShadowBlur:0,/**\n * @type {number}\n */textBoxShadowOffsetX:0,/**\n * @type {number}\n */textBoxShadowOffsetY:0,/**\n * Whether transform text.\n * Only available in Path and Image element,\n * where the text is called as `RectText`.\n * @type {boolean}\n */transformText:false,/**\n * Text rotate around position of Path or Image.\n * The origin of the rotation can be specified by `textOrigin`.\n * Only available in Path and Image element,\n * where the text is called as `RectText`.\n */textRotation:0,/**\n * Text origin of text rotation.\n * Useful in the case like label rotation of circular symbol.\n * Only available in Path and Image element, where the text is called\n * as `RectText` and the element is called as \"host element\".\n * The value can be:\n * + If specified as a coordinate like `[10, 40]`, it is the `[x, y]`\n * base on the left-top corner of the rect of its host element.\n * + If specified as a string `center`, it is the center of the rect of\n * its host element.\n * + By default, this origin is the `textPosition`.\n * @type {string|Array.}\n */textOrigin:null,/**\n * @type {string}\n */textBackgroundColor:null,/**\n * @type {string}\n */textBorderColor:null,/**\n * @type {number}\n */textBorderWidth:0,/**\n * @type {number}\n */textBorderRadius:0,/**\n * Can be `2` or `[2, 4]` or `[2, 3, 4, 5]`\n * @type {number|Array.}\n */textPadding:null,/**\n * Text styles for rich text.\n * @type {Object}\n */rich:null,/**\n * {outerWidth, outerHeight, ellipsis, placeholder}\n * @type {Object}\n */truncate:null,/**\n * https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n * @type {string}\n */blend:null,/**\n * @param {CanvasRenderingContext2D} ctx\n */bind:function bind(ctx,el,prevEl){var style=this;var prevStyle=prevEl&&prevEl.style;// If no prevStyle, it means first draw.\n// Only apply cache if the last time cachced by this function.\nvar notCheckCache=!prevStyle||ctx.__attrCachedBy!==ContextCachedBy.STYLE_BIND;ctx.__attrCachedBy=ContextCachedBy.STYLE_BIND;for(var i=0;i0;},/**\n * Extend from other style\n * @param {zrender/graphic/Style} otherStyle\n * @param {boolean} overwrite true: overwrirte any way.\n * false: overwrite only when !target.hasOwnProperty\n * others: overwrite when property is not null/undefined.\n */extendFrom:function extendFrom(otherStyle,overwrite){if(otherStyle){for(var name in otherStyle){if(otherStyle.hasOwnProperty(name)&&(overwrite===true||(overwrite===false?!this.hasOwnProperty(name):otherStyle[name]!=null))){this[name]=otherStyle[name];}}}},/**\n * Batch setting style with a given object\n * @param {Object|string} obj\n * @param {*} [obj]\n */set:function set(obj,value){if(typeof obj==='string'){this[obj]=value;}else{this.extendFrom(obj,true);}},/**\n * Clone\n * @return {zrender/graphic/Style} [description]\n */clone:function clone(){var newStyle=new this.constructor();newStyle.extendFrom(this,true);return newStyle;},getGradient:function getGradient(ctx,obj,rect){var method=obj.type==='radial'?createRadialGradient:createLinearGradient;var canvasGradient=method(ctx,obj,rect);var colorStops=obj.colorStops;for(var i=0;iTEXT_CACHE_MAX){textWidthCacheCounter=0;textWidthCache={};}textWidthCacheCounter++;textWidthCache[key]=width;return width;}/**\n * @public\n * @param {string} text\n * @param {string} font\n * @param {string} [textAlign='left']\n * @param {string} [textVerticalAlign='top']\n * @param {Array.} [textPadding]\n * @param {Object} [rich]\n * @param {Object} [truncate]\n * @return {Object} {x, y, width, height, lineHeight}\n */function _getBoundingRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,rich,truncate){return rich?getRichTextRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,rich,truncate):getPlainTextRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,truncate);}function getPlainTextRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,truncate){var contentBlock=parsePlainText(text,font,textPadding,textLineHeight,truncate);var outerWidth=getWidth(text,font);if(textPadding){outerWidth+=textPadding[1]+textPadding[3];}var outerHeight=contentBlock.outerHeight;var x=adjustTextX(0,outerWidth,textAlign);var y=adjustTextY(0,outerHeight,textVerticalAlign);var rect=new BoundingRect(x,y,outerWidth,outerHeight);rect.lineHeight=contentBlock.lineHeight;return rect;}function getRichTextRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,rich,truncate){var contentBlock=parseRichText(text,{rich:rich,truncate:truncate,font:font,textAlign:textAlign,textPadding:textPadding,textLineHeight:textLineHeight});var outerWidth=contentBlock.outerWidth;var outerHeight=contentBlock.outerHeight;var x=adjustTextX(0,outerWidth,textAlign);var y=adjustTextY(0,outerHeight,textVerticalAlign);return new BoundingRect(x,y,outerWidth,outerHeight);}/**\n * @public\n * @param {number} x\n * @param {number} width\n * @param {string} [textAlign='left']\n * @return {number} Adjusted x.\n */function adjustTextX(x,width,textAlign){// FIXME Right to left language\nif(textAlign==='right'){x-=width;}else if(textAlign==='center'){x-=width/2;}return x;}/**\n * @public\n * @param {number} y\n * @param {number} height\n * @param {string} [textVerticalAlign='top']\n * @return {number} Adjusted y.\n */function adjustTextY(y,height,textVerticalAlign){if(textVerticalAlign==='middle'){y-=height/2;}else if(textVerticalAlign==='bottom'){y-=height;}return y;}/**\n * Follow same interface to `Displayable.prototype.calculateTextPosition`.\n * @public\n * @param {Obejct} [out] Prepared out object. If not input, auto created in the method.\n * @param {module:zrender/graphic/Style} style where `textPosition` and `textDistance` are visited.\n * @param {Object} rect {x, y, width, height} Rect of the host elment, according to which the text positioned.\n * @return {Object} The input `out`. Set: {x, y, textAlign, textVerticalAlign}\n */function _calculateTextPosition(out,style,rect){var textPosition=style.textPosition;var distance=style.textDistance;var x=rect.x;var y=rect.y;distance=distance||0;var height=rect.height;var width=rect.width;var halfHeight=height/2;var textAlign='left';var textVerticalAlign='top';switch(textPosition){case'left':x-=distance;y+=halfHeight;textAlign='right';textVerticalAlign='middle';break;case'right':x+=distance+width;y+=halfHeight;textVerticalAlign='middle';break;case'top':x+=width/2;y-=distance;textAlign='center';textVerticalAlign='bottom';break;case'bottom':x+=width/2;y+=height+distance;textAlign='center';break;case'inside':x+=width/2;y+=halfHeight;textAlign='center';textVerticalAlign='middle';break;case'insideLeft':x+=distance;y+=halfHeight;textVerticalAlign='middle';break;case'insideRight':x+=width-distance;y+=halfHeight;textAlign='right';textVerticalAlign='middle';break;case'insideTop':x+=width/2;y+=distance;textAlign='center';break;case'insideBottom':x+=width/2;y+=height-distance;textAlign='center';textVerticalAlign='bottom';break;case'insideTopLeft':x+=distance;y+=distance;break;case'insideTopRight':x+=width-distance;y+=distance;textAlign='right';break;case'insideBottomLeft':x+=distance;y+=height-distance;textVerticalAlign='bottom';break;case'insideBottomRight':x+=width-distance;y+=height-distance;textAlign='right';textVerticalAlign='bottom';break;}out=out||{};out.x=x;out.y=y;out.textAlign=textAlign;out.textVerticalAlign=textVerticalAlign;return out;}/**\n * To be removed. But still do not remove in case that some one has imported it.\n * @deprecated\n * @public\n * @param {stirng} textPosition\n * @param {Object} rect {x, y, width, height}\n * @param {number} distance\n * @return {Object} {x, y, textAlign, textVerticalAlign}\n */ /**\n * Show ellipsis if overflow.\n *\n * @public\n * @param {string} text\n * @param {string} containerWidth\n * @param {string} font\n * @param {number} [ellipsis='...']\n * @param {Object} [options]\n * @param {number} [options.maxIterations=3]\n * @param {number} [options.minChar=0] If truncate result are less\n * then minChar, ellipsis will not show, which is\n * better for user hint in some cases.\n * @param {number} [options.placeholder=''] When all truncated, use the placeholder.\n * @return {string}\n */function truncateText(text,containerWidth,font,ellipsis,options){if(!containerWidth){return'';}var textLines=(text+'').split('\\n');options=prepareTruncateOptions(containerWidth,font,ellipsis,options);// FIXME\n// It is not appropriate that every line has '...' when truncate multiple lines.\nfor(var i=0,len=textLines.length;i=ascCharWidth;i++){contentWidth-=ascCharWidth;}var ellipsisWidth=getWidth(ellipsis,font);if(ellipsisWidth>contentWidth){ellipsis='';ellipsisWidth=0;}contentWidth=containerWidth-ellipsisWidth;options.ellipsis=ellipsis;options.ellipsisWidth=ellipsisWidth;options.contentWidth=contentWidth;options.containerWidth=containerWidth;return options;}function truncateSingleLine(textLine,options){var containerWidth=options.containerWidth;var font=options.font;var contentWidth=options.contentWidth;if(!containerWidth){return'';}var lineWidth=getWidth(textLine,font);if(lineWidth<=containerWidth){return textLine;}for(var j=0;;j++){if(lineWidth<=contentWidth||j>=options.maxIterations){textLine+=options.ellipsis;break;}var subLength=j===0?estimateLength(textLine,contentWidth,options.ascCharWidth,options.cnCharWidth):lineWidth>0?Math.floor(textLine.length*contentWidth/lineWidth):0;textLine=textLine.substr(0,subLength);lineWidth=getWidth(textLine,font);}if(textLine===''){textLine=options.placeholder;}return textLine;}function estimateLength(text,contentWidth,ascCharWidth,cnCharWidth){var width=0;var i=0;for(var len=text.length;itruncOuterHeight){text='';lines=[];}else if(truncOuterWidth!=null){var options=prepareTruncateOptions(truncOuterWidth-(padding?padding[1]+padding[3]:0),font,truncate.ellipsis,{minChar:truncate.minChar,placeholder:truncate.placeholder});// FIXME\n// It is not appropriate that every line has '...' when truncate multiple lines.\nfor(var i=0,len=lines.length;ilastIndex){pushTokens(contentBlock,text.substring(lastIndex,matchedIndex));}pushTokens(contentBlock,result[2],result[1]);lastIndex=STYLE_REG.lastIndex;}if(lastIndextruncateHeight){return{lines:[],width:0,height:0};}token.textWidth=getWidth(token.text,font);var tokenWidth=tokenStyle.textWidth;var tokenWidthNotSpecified=tokenWidth==null||tokenWidth==='auto';// Percent width, can be `100%`, can be used in drawing separate\n// line when box width is needed to be auto.\nif(typeof tokenWidth==='string'&&tokenWidth.charAt(tokenWidth.length-1)==='%'){token.percentWidth=tokenWidth;pendingList.push(token);tokenWidth=0;// Do not truncate in this case, because there is no user case\n// and it is too complicated.\n}else{if(tokenWidthNotSpecified){tokenWidth=token.textWidth;// FIXME: If image is not loaded and textWidth is not specified, calling\n// `getBoundingRect()` will not get correct result.\nvar textBackgroundColor=tokenStyle.textBackgroundColor;var bgImg=textBackgroundColor&&textBackgroundColor.image;// Use cases:\n// (1) If image is not loaded, it will be loaded at render phase and call\n// `dirty()` and `textBackgroundColor.image` will be replaced with the loaded\n// image, and then the right size will be calculated here at the next tick.\n// See `graphic/helper/text.js`.\n// (2) If image loaded, and `textBackgroundColor.image` is image src string,\n// use `imageHelper.findExistImage` to find cached image.\n// `imageHelper.findExistImage` will always be called here before\n// `imageHelper.createOrUpdateImage` in `graphic/helper/text.js#renderRichText`\n// which ensures that image will not be rendered before correct size calcualted.\nif(bgImg){bgImg=findExistImage(bgImg);if(isImageReady(bgImg)){tokenWidth=Math.max(tokenWidth,bgImg.width*tokenHeight/bgImg.height);}}}var paddingW=textPadding?textPadding[1]+textPadding[3]:0;tokenWidth+=paddingW;var remianTruncWidth=truncateWidth!=null?truncateWidth-lineWidth:null;if(remianTruncWidth!=null&&remianTruncWidth ['', '\\n', ''], the '' at the first item\n// (which is a placeholder) should be replaced by new token.\n// (2) A image backage, where token likes {a|}.\n// (3) A redundant '' will affect textAlign in line.\n// (4) tokens with the same tplName should not be merged, because\n// they should be displayed in different box (with border and padding).\nvar tokensLen=tokens.length;tokensLen===1&&tokens[0].isLineHolder?tokens[0]=token:// Consider text is '', only insert when it is the \"lineHolder\" or\n// \"emptyStr\". Otherwise a redundant '' will affect textAlign in line.\n(text||!tokensLen||isEmptyStr)&&tokens.push(token);}// Other tokens always start a new line.\nelse{// If there is '', insert it as a placeholder.\nlines.push({tokens:[token]});}}}function makeFont(style){// FIXME in node-canvas fontWeight is before fontStyle\n// Use `fontSize` `fontFamily` to check whether font properties are defined.\nvar font=(style.fontSize||style.fontFamily)&&[style.fontStyle,style.fontWeight,(style.fontSize||12)+'px',// If font properties are defined, `fontFamily` should not be ignored.\nstyle.fontFamily||'sans-serif'].join(' ');return font&&trim(font)||style.textFont||style.font;}/**\n * @param {Object} ctx\n * @param {Object} shape\n * @param {number} shape.x\n * @param {number} shape.y\n * @param {number} shape.width\n * @param {number} shape.height\n * @param {number} shape.r\n */function _buildPath(ctx,shape){var x=shape.x;var y=shape.y;var width=shape.width;var height=shape.height;var r=shape.r;var r1;var r2;var r3;var r4;// Convert width and height to positive for better borderRadius\nif(width<0){x=x+width;width=-width;}if(height<0){y=y+height;height=-height;}if(typeof r==='number'){r1=r2=r3=r4=r;}else if(r instanceof Array){if(r.length===1){r1=r2=r3=r4=r[0];}else if(r.length===2){r1=r3=r[0];r2=r4=r[1];}else if(r.length===3){r1=r[0];r2=r4=r[1];r3=r[2];}else{r1=r[0];r2=r[1];r3=r[2];r4=r[3];}}else{r1=r2=r3=r4=0;}var total;if(r1+r2>width){total=r1+r2;r1*=width/total;r2*=width/total;}if(r3+r4>width){total=r3+r4;r3*=width/total;r4*=width/total;}if(r2+r3>height){total=r2+r3;r2*=height/total;r3*=height/total;}if(r1+r4>height){total=r1+r4;r1*=height/total;r4*=height/total;}ctx.moveTo(x+r1,y);ctx.lineTo(x+width-r2,y);r2!==0&&ctx.arc(x+width-r2,y+r2,r2,-Math.PI/2,0);ctx.lineTo(x+width,y+height-r3);r3!==0&&ctx.arc(x+width-r3,y+height-r3,r3,0,Math.PI/2);ctx.lineTo(x+r4,y+height);r4!==0&&ctx.arc(x+r4,y+height-r4,r4,Math.PI/2,Math.PI);ctx.lineTo(x,y+r1);r1!==0&&ctx.arc(x+r1,y+r1,r1,Math.PI,Math.PI*1.5);}var DEFAULT_FONT=DEFAULT_FONT$1;// TODO: Have not support 'start', 'end' yet.\nvar VALID_TEXT_ALIGN={left:1,right:1,center:1};var VALID_TEXT_VERTICAL_ALIGN={top:1,bottom:1,middle:1};// Different from `STYLE_COMMON_PROPS` of `graphic/Style`,\n// the default value of shadowColor is `'transparent'`.\nvar SHADOW_STYLE_COMMON_PROPS=[['textShadowBlur','shadowBlur',0],['textShadowOffsetX','shadowOffsetX',0],['textShadowOffsetY','shadowOffsetY',0],['textShadowColor','shadowColor','transparent']];var _tmpTextPositionResult={};var _tmpBoxPositionResult={};/**\n * @param {module:zrender/graphic/Style} style\n * @return {module:zrender/graphic/Style} The input style.\n */function normalizeTextStyle(style){normalizeStyle(style);each$1(style.rich,normalizeStyle);return style;}function normalizeStyle(style){if(style){style.font=makeFont(style);var textAlign=style.textAlign;textAlign==='middle'&&(textAlign='center');style.textAlign=textAlign==null||VALID_TEXT_ALIGN[textAlign]?textAlign:'left';// Compatible with textBaseline.\nvar textVerticalAlign=style.textVerticalAlign||style.textBaseline;textVerticalAlign==='center'&&(textVerticalAlign='middle');style.textVerticalAlign=textVerticalAlign==null||VALID_TEXT_VERTICAL_ALIGN[textVerticalAlign]?textVerticalAlign:'top';var textPadding=style.textPadding;if(textPadding){style.textPadding=normalizeCssArray(style.textPadding);}}}/**\n * @param {CanvasRenderingContext2D} ctx\n * @param {string} text\n * @param {module:zrender/graphic/Style} style\n * @param {Object|boolean} [rect] {x, y, width, height}\n * If set false, rect text is not used.\n * @param {Element|module:zrender/graphic/helper/constant.WILL_BE_RESTORED} [prevEl] For ctx prop cache.\n */function renderText(hostEl,ctx,text,style,rect,prevEl){style.rich?renderRichText(hostEl,ctx,text,style,rect,prevEl):renderPlainText(hostEl,ctx,text,style,rect,prevEl);}// Avoid setting to ctx according to prevEl if possible for\n// performance in scenarios of large amount text.\nfunction renderPlainText(hostEl,ctx,text,style,rect,prevEl){'use strict';var needDrawBg=needDrawBackground(style);var prevStyle;var checkCache=false;var cachedByMe=ctx.__attrCachedBy===ContextCachedBy.PLAIN_TEXT;// Only take and check cache for `Text` el, but not RectText.\nif(prevEl!==WILL_BE_RESTORED){if(prevEl){prevStyle=prevEl.style;checkCache=!needDrawBg&&cachedByMe&&prevStyle;}// Prevent from using cache in `Style::bind`, because of the case:\n// ctx property is modified by other properties than `Style::bind`\n// used, and Style::bind is called next.\nctx.__attrCachedBy=needDrawBg?ContextCachedBy.NONE:ContextCachedBy.PLAIN_TEXT;}// Since this will be restored, prevent from using these props to check cache in the next\n// entering of this method. But do not need to clear other cache like `Style::bind`.\nelse if(cachedByMe){ctx.__attrCachedBy=ContextCachedBy.NONE;}var styleFont=style.font||DEFAULT_FONT;// PENDING\n// Only `Text` el set `font` and keep it (`RectText` will restore). So theoretically\n// we can make font cache on ctx, which can cache for text el that are discontinuous.\n// But layer save/restore needed to be considered.\n// if (styleFont !== ctx.__fontCache) {\n// ctx.font = styleFont;\n// if (prevEl !== WILL_BE_RESTORED) {\n// ctx.__fontCache = styleFont;\n// }\n// }\nif(!checkCache||styleFont!==(prevStyle.font||DEFAULT_FONT)){ctx.font=styleFont;}// Use the final font from context-2d, because the final\n// font might not be the style.font when it is illegal.\n// But get `ctx.font` might be time consuming.\nvar computedFont=hostEl.__computedFont;if(hostEl.__styleFont!==styleFont){hostEl.__styleFont=styleFont;computedFont=hostEl.__computedFont=ctx.font;}var textPadding=style.textPadding;var textLineHeight=style.textLineHeight;var contentBlock=hostEl.__textCotentBlock;if(!contentBlock||hostEl.__dirtyText){contentBlock=hostEl.__textCotentBlock=parsePlainText(text,computedFont,textPadding,textLineHeight,style.truncate);}var outerHeight=contentBlock.outerHeight;var textLines=contentBlock.lines;var lineHeight=contentBlock.lineHeight;var boxPos=getBoxPosition(_tmpBoxPositionResult,hostEl,style,rect);var baseX=boxPos.baseX;var baseY=boxPos.baseY;var textAlign=boxPos.textAlign||'left';var textVerticalAlign=boxPos.textVerticalAlign;// Origin of textRotation should be the base point of text drawing.\napplyTextRotation(ctx,style,rect,baseX,baseY);var boxY=adjustTextY(baseY,outerHeight,textVerticalAlign);var textX=baseX;var textY=boxY;if(needDrawBg||textPadding){// Consider performance, do not call getTextWidth util necessary.\nvar textWidth=getWidth(text,computedFont);var outerWidth=textWidth;textPadding&&(outerWidth+=textPadding[1]+textPadding[3]);var boxX=adjustTextX(baseX,outerWidth,textAlign);needDrawBg&&drawBackground(hostEl,ctx,style,boxX,boxY,outerWidth,outerHeight);if(textPadding){textX=getTextXForPadding(baseX,textAlign,textPadding);textY+=textPadding[0];}}// Always set textAlign and textBase line, because it is difficute to calculate\n// textAlign from prevEl, and we dont sure whether textAlign will be reset if\n// font set happened.\nctx.textAlign=textAlign;// Force baseline to be \"middle\". Otherwise, if using \"top\", the\n// text will offset downward a little bit in font \"Microsoft YaHei\".\nctx.textBaseline='middle';// Set text opacity\nctx.globalAlpha=style.opacity||1;// Always set shadowBlur and shadowOffset to avoid leak from displayable.\nfor(var i=0;i=0&&(token=tokens[rightIndex],token.textAlign==='right')){placeToken(hostEl,ctx,token,style,lineHeight,lineTop,lineXRight,'right');usedWidth-=token.width;lineXRight-=token.width;rightIndex--;}// The other tokens are placed as textAlign 'center' if there is enough space.\nlineXLeft+=(contentWidth-(lineXLeft-xLeft)-(xRight-lineXRight)-usedWidth)/2;while(leftIndex<=rightIndex){token=tokens[leftIndex];// Consider width specified by user, use 'center' rather than 'left'.\nplaceToken(hostEl,ctx,token,style,lineHeight,lineTop,lineXLeft+token.width/2,'center');lineXLeft+=token.width;leftIndex++;}lineTop+=lineHeight;}}function applyTextRotation(ctx,style,rect,x,y){// textRotation only apply in RectText.\nif(rect&&style.textRotation){var origin=style.textOrigin;if(origin==='center'){x=rect.width/2+rect.x;y=rect.height/2+rect.y;}else if(origin){x=origin[0]+rect.x;y=origin[1]+rect.y;}ctx.translate(x,y);// Positive: anticlockwise\nctx.rotate(-style.textRotation);ctx.translate(-x,-y);}}function placeToken(hostEl,ctx,token,style,lineHeight,lineTop,x,textAlign){var tokenStyle=style.rich[token.styleName]||{};tokenStyle.text=token.text;// 'ctx.textBaseline' is always set as 'middle', for sake of\n// the bias of \"Microsoft YaHei\".\nvar textVerticalAlign=token.textVerticalAlign;var y=lineTop+lineHeight/2;if(textVerticalAlign==='top'){y=lineTop+token.height/2;}else if(textVerticalAlign==='bottom'){y=lineTop+lineHeight-token.height/2;}!token.isLineHolder&&needDrawBackground(tokenStyle)&&drawBackground(hostEl,ctx,tokenStyle,textAlign==='right'?x-token.width:textAlign==='center'?x-token.width/2:x,y-token.height/2,token.width,token.height);var textPadding=token.textPadding;if(textPadding){x=getTextXForPadding(x,textAlign,textPadding);y-=token.height/2-textPadding[2]-token.textHeight/2;}setCtx(ctx,'shadowBlur',retrieve3(tokenStyle.textShadowBlur,style.textShadowBlur,0));setCtx(ctx,'shadowColor',tokenStyle.textShadowColor||style.textShadowColor||'transparent');setCtx(ctx,'shadowOffsetX',retrieve3(tokenStyle.textShadowOffsetX,style.textShadowOffsetX,0));setCtx(ctx,'shadowOffsetY',retrieve3(tokenStyle.textShadowOffsetY,style.textShadowOffsetY,0));setCtx(ctx,'textAlign',textAlign);// Force baseline to be \"middle\". Otherwise, if using \"top\", the\n// text will offset downward a little bit in font \"Microsoft YaHei\".\nsetCtx(ctx,'textBaseline','middle');setCtx(ctx,'font',token.font||DEFAULT_FONT);var textStroke=getStroke(tokenStyle.textStroke||style.textStroke,textStrokeWidth);var textFill=getFill(tokenStyle.textFill||style.textFill);var textStrokeWidth=retrieve2(tokenStyle.textStrokeWidth,style.textStrokeWidth);// Fill after stroke so the outline will not cover the main part.\nif(textStroke){setCtx(ctx,'lineWidth',textStrokeWidth);setCtx(ctx,'strokeStyle',textStroke);ctx.strokeText(token.text,x,y);}if(textFill){setCtx(ctx,'fillStyle',textFill);ctx.fillText(token.text,x,y);}}function needDrawBackground(style){return!!(style.textBackgroundColor||style.textBorderWidth&&style.textBorderColor);}// style: {textBackgroundColor, textBorderWidth, textBorderColor, textBorderRadius, text}\n// shape: {x, y, width, height}\nfunction drawBackground(hostEl,ctx,style,x,y,width,height){var textBackgroundColor=style.textBackgroundColor;var textBorderWidth=style.textBorderWidth;var textBorderColor=style.textBorderColor;var isPlainBg=isString(textBackgroundColor);setCtx(ctx,'shadowBlur',style.textBoxShadowBlur||0);setCtx(ctx,'shadowColor',style.textBoxShadowColor||'transparent');setCtx(ctx,'shadowOffsetX',style.textBoxShadowOffsetX||0);setCtx(ctx,'shadowOffsetY',style.textBoxShadowOffsetY||0);if(isPlainBg||textBorderWidth&&textBorderColor){ctx.beginPath();var textBorderRadius=style.textBorderRadius;if(!textBorderRadius){ctx.rect(x,y,width,height);}else{_buildPath(ctx,{x:x,y:y,width:width,height:height,r:textBorderRadius});}ctx.closePath();}if(isPlainBg){setCtx(ctx,'fillStyle',textBackgroundColor);if(style.fillOpacity!=null){var originalGlobalAlpha=ctx.globalAlpha;ctx.globalAlpha=style.fillOpacity*style.opacity;ctx.fill();ctx.globalAlpha=originalGlobalAlpha;}else{ctx.fill();}}else if(isObject$1(textBackgroundColor)){var image=textBackgroundColor.image;image=createOrUpdateImage(image,null,hostEl,onBgImageLoaded,textBackgroundColor);if(image&&isImageReady(image)){ctx.drawImage(image,x,y,width,height);}}if(textBorderWidth&&textBorderColor){setCtx(ctx,'lineWidth',textBorderWidth);setCtx(ctx,'strokeStyle',textBorderColor);if(style.strokeOpacity!=null){var originalGlobalAlpha=ctx.globalAlpha;ctx.globalAlpha=style.strokeOpacity*style.opacity;ctx.stroke();ctx.globalAlpha=originalGlobalAlpha;}else{ctx.stroke();}}}function onBgImageLoaded(image,textBackgroundColor){// Replace image, so that `contain/text.js#parseRichText`\n// will get correct result in next tick.\ntextBackgroundColor.image=image;}function getBoxPosition(out,hostEl,style,rect){var baseX=style.x||0;var baseY=style.y||0;var textAlign=style.textAlign;var textVerticalAlign=style.textVerticalAlign;// Text position represented by coord\nif(rect){var textPosition=style.textPosition;if(textPosition instanceof Array){// Percent\nbaseX=rect.x+parsePercent(textPosition[0],rect.width);baseY=rect.y+parsePercent(textPosition[1],rect.height);}else{var res=hostEl&&hostEl.calculateTextPosition?hostEl.calculateTextPosition(_tmpTextPositionResult,style,rect):_calculateTextPosition(_tmpTextPositionResult,style,rect);baseX=res.x;baseY=res.y;// Default align and baseline when has textPosition\ntextAlign=textAlign||res.textAlign;textVerticalAlign=textVerticalAlign||res.textVerticalAlign;}// textOffset is only support in RectText, otherwise\n// we have to adjust boundingRect for textOffset.\nvar textOffset=style.textOffset;if(textOffset){baseX+=textOffset[0];baseY+=textOffset[1];}}out=out||{};out.baseX=baseX;out.baseY=baseY;out.textAlign=textAlign;out.textVerticalAlign=textVerticalAlign;return out;}function setCtx(ctx,prop,value){ctx[prop]=fixShadow(ctx,prop,value);return ctx[prop];}/**\n * @param {string} [stroke] If specified, do not check style.textStroke.\n * @param {string} [lineWidth] If specified, do not check style.textStroke.\n * @param {number} style\n */function getStroke(stroke,lineWidth){return stroke==null||lineWidth<=0||stroke==='transparent'||stroke==='none'?null// TODO pattern and gradient?\n:stroke.image||stroke.colorStops?'#000':stroke;}function getFill(fill){return fill==null||fill==='none'?null// TODO pattern and gradient?\n:fill.image||fill.colorStops?'#000':fill;}function parsePercent(value,maxValue){if(typeof value==='string'){if(value.lastIndexOf('%')>=0){return parseFloat(value)/100*maxValue;}return parseFloat(value);}return value;}function getTextXForPadding(x,textAlign,textPadding){return textAlign==='right'?x-textPadding[1]:textAlign==='center'?x+textPadding[3]/2-textPadding[1]/2:x+textPadding[3];}/**\n * @param {string} text\n * @param {module:zrender/Style} style\n * @return {boolean}\n */function needDrawText(text,style){return text!=null&&(text||style.textBackgroundColor||style.textBorderWidth&&style.textBorderColor||style.textPadding);}/**\n * Mixin for drawing text in a element bounding rect\n * @module zrender/mixin/RectText\n */var tmpRect$1=new BoundingRect();var RectText=function RectText(){};RectText.prototype={constructor:RectText,/**\n * Draw text in a rect with specified position.\n * @param {CanvasRenderingContext2D} ctx\n * @param {Object} rect Displayable rect\n */drawRectText:function drawRectText(ctx,rect){var style=this.style;rect=style.textRect||rect;// Optimize, avoid normalize every time.\nthis.__dirty&&normalizeTextStyle(style,true);var text=style.text;// Convert to string\ntext!=null&&(text+='');if(!needDrawText(text,style)){return;}// FIXME\n// Do not provide prevEl to `textHelper.renderText` for ctx prop cache,\n// but use `ctx.save()` and `ctx.restore()`. Because the cache for rect\n// text propably break the cache for its host elements.\nctx.save();// Transform rect to view space\nvar transform=this.transform;if(!style.transformText){if(transform){tmpRect$1.copy(rect);tmpRect$1.applyTransform(transform);rect=tmpRect$1;}}else{this.setTransform(ctx);}// transformText and textRotation can not be used at the same time.\nrenderText(this,ctx,text,style,rect,WILL_BE_RESTORED);ctx.restore();}};/**\n * Base class of all displayable graphic objects\n * @module zrender/graphic/Displayable\n */ /**\n * @alias module:zrender/graphic/Displayable\n * @extends module:zrender/Element\n * @extends module:zrender/graphic/mixin/RectText\n */function Displayable(opts){opts=opts||{};Element.call(this,opts);// Extend properties\nfor(var name in opts){if(opts.hasOwnProperty(name)&&name!=='style'){this[name]=opts[name];}}/**\n * @type {module:zrender/graphic/Style}\n */this.style=new Style(opts.style,this);this._rect=null;// Shapes for cascade clipping.\n// Can only be `null`/`undefined` or an non-empty array, MUST NOT be an empty array.\n// because it is easy to only using null to check whether clipPaths changed.\nthis.__clipPaths=null;// FIXME Stateful must be mixined after style is setted\n// Stateful.call(this, opts);\n}Displayable.prototype={constructor:Displayable,type:'displayable',/**\n * Dirty flag. From which painter will determine if this displayable object needs brush.\n * @name module:zrender/graphic/Displayable#__dirty\n * @type {boolean}\n */__dirty:true,/**\n * Whether the displayable object is visible. when it is true, the displayable object\n * is not drawn, but the mouse event can still trigger the object.\n * @name module:/zrender/graphic/Displayable#invisible\n * @type {boolean}\n * @default false\n */invisible:false,/**\n * @name module:/zrender/graphic/Displayable#z\n * @type {number}\n * @default 0\n */z:0,/**\n * @name module:/zrender/graphic/Displayable#z\n * @type {number}\n * @default 0\n */z2:0,/**\n * The z level determines the displayable object can be drawn in which layer canvas.\n * @name module:/zrender/graphic/Displayable#zlevel\n * @type {number}\n * @default 0\n */zlevel:0,/**\n * Whether it can be dragged.\n * @name module:/zrender/graphic/Displayable#draggable\n * @type {boolean}\n * @default false\n */draggable:false,/**\n * Whether is it dragging.\n * @name module:/zrender/graphic/Displayable#draggable\n * @type {boolean}\n * @default false\n */dragging:false,/**\n * Whether to respond to mouse events.\n * @name module:/zrender/graphic/Displayable#silent\n * @type {boolean}\n * @default false\n */silent:false,/**\n * If enable culling\n * @type {boolean}\n * @default false\n */culling:false,/**\n * Mouse cursor when hovered\n * @name module:/zrender/graphic/Displayable#cursor\n * @type {string}\n */cursor:'pointer',/**\n * If hover area is bounding rect\n * @name module:/zrender/graphic/Displayable#rectHover\n * @type {string}\n */rectHover:false,/**\n * Render the element progressively when the value >= 0,\n * usefull for large data.\n * @type {boolean}\n */progressive:false,/**\n * @type {boolean}\n */incremental:false,/**\n * Scale ratio for global scale.\n * @type {boolean}\n */globalScaleRatio:1,beforeBrush:function beforeBrush(ctx){},afterBrush:function afterBrush(ctx){},/**\n * Graphic drawing method.\n * @param {CanvasRenderingContext2D} ctx\n */ // Interface\nbrush:function brush(ctx,prevEl){},/**\n * Get the minimum bounding box.\n * @return {module:zrender/core/BoundingRect}\n */ // Interface\ngetBoundingRect:function getBoundingRect(){},/**\n * If displayable element contain coord x, y\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */contain:function contain(x,y){return this.rectContain(x,y);},/**\n * @param {Function} cb\n * @param {} context\n */traverse:function traverse(cb,context){cb.call(context,this);},/**\n * If bounding rect of element contain coord x, y\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */rectContain:function rectContain(x,y){var coord=this.transformCoordToLocal(x,y);var rect=this.getBoundingRect();return rect.contain(coord[0],coord[1]);},/**\n * Mark displayable element dirty and refresh next frame\n */dirty:function dirty(){this.__dirty=this.__dirtyText=true;this._rect=null;this.__zr&&this.__zr.refresh();},/**\n * If displayable object binded any event\n * @return {boolean}\n */ // TODO, events bound by bind\n// isSilent: function () {\n// return !(\n// this.hoverable || this.draggable\n// || this.onmousemove || this.onmouseover || this.onmouseout\n// || this.onmousedown || this.onmouseup || this.onclick\n// || this.ondragenter || this.ondragover || this.ondragleave\n// || this.ondrop\n// );\n// },\n/**\n * Alias for animate('style')\n * @param {boolean} loop\n */animateStyle:function animateStyle(loop){return this.animate('style',loop);},attrKV:function attrKV(key,value){if(key!=='style'){Element.prototype.attrKV.call(this,key,value);}else{this.style.set(value);}},/**\n * @param {Object|string} key\n * @param {*} value\n */setStyle:function setStyle(key,value){this.style.set(key,value);this.dirty(false);return this;},/**\n * Use given style object\n * @param {Object} obj\n */useStyle:function useStyle(obj){this.style=new Style(obj,this);this.dirty(false);return this;},/**\n * The string value of `textPosition` needs to be calculated to a real postion.\n * For example, `'inside'` is calculated to `[rect.width/2, rect.height/2]`\n * by default. See `contain/text.js#calculateTextPosition` for more details.\n * But some coutom shapes like \"pin\", \"flag\" have center that is not exactly\n * `[width/2, height/2]`. So we provide this hook to customize the calculation\n * for those shapes. It will be called if the `style.textPosition` is a string.\n * @param {Obejct} [out] Prepared out object. If not provided, this method should\n * be responsible for creating one.\n * @param {module:zrender/graphic/Style} style\n * @param {Object} rect {x, y, width, height}\n * @return {Obejct} out The same as the input out.\n * {\n * x: number. mandatory.\n * y: number. mandatory.\n * textAlign: string. optional. use style.textAlign by default.\n * textVerticalAlign: string. optional. use style.textVerticalAlign by default.\n * }\n */calculateTextPosition:null};inherits(Displayable,Element);mixin(Displayable,RectText);/**\n * @alias zrender/graphic/Image\n * @extends module:zrender/graphic/Displayable\n * @constructor\n * @param {Object} opts\n */function ZImage(opts){Displayable.call(this,opts);}ZImage.prototype={constructor:ZImage,type:'image',brush:function brush(ctx,prevEl){var style=this.style;var src=style.image;// Must bind each time\nstyle.bind(ctx,this,prevEl);var image=this._image=createOrUpdateImage(src,this._image,this,this.onload);if(!image||!isImageReady(image)){return;}// 图片已经加载完成\n// if (image.nodeName.toUpperCase() == 'IMG') {\n// if (!image.complete) {\n// return;\n// }\n// }\n// Else is canvas\nvar x=style.x||0;var y=style.y||0;var width=style.width;var height=style.height;var aspect=image.width/image.height;if(width==null&&height!=null){// Keep image/height ratio\nwidth=height*aspect;}else if(height==null&&width!=null){height=width/aspect;}else if(width==null&&height==null){width=image.width;height=image.height;}// 设置transform\nthis.setTransform(ctx);if(style.sWidth&&style.sHeight){var sx=style.sx||0;var sy=style.sy||0;ctx.drawImage(image,sx,sy,style.sWidth,style.sHeight,x,y,width,height);}else if(style.sx&&style.sy){var sx=style.sx;var sy=style.sy;var sWidth=width-sx;var sHeight=height-sy;ctx.drawImage(image,sx,sy,sWidth,sHeight,x,y,width,height);}else{ctx.drawImage(image,x,y,width,height);}// Draw rect text\nif(style.text!=null){// Only restore transform when needs draw text.\nthis.restoreTransform(ctx);this.drawRectText(ctx,this.getBoundingRect());}},getBoundingRect:function getBoundingRect(){var style=this.style;if(!this._rect){this._rect=new BoundingRect(style.x||0,style.y||0,style.width||0,style.height||0);}return this._rect;}};inherits(ZImage,Displayable);var HOVER_LAYER_ZLEVEL=1e5;var CANVAS_ZLEVEL=314159;var EL_AFTER_INCREMENTAL_INC=0.01;var INCREMENTAL_INC=0.001;function parseInt10(val){return parseInt(val,10);}function isLayerValid(layer){if(!layer){return false;}if(layer.__builtin__){return true;}if(typeof layer.resize!=='function'||typeof layer.refresh!=='function'){return false;}return true;}var tmpRect=new BoundingRect(0,0,0,0);var viewRect=new BoundingRect(0,0,0,0);function isDisplayableCulled(el,width,height){tmpRect.copy(el.getBoundingRect());if(el.transform){tmpRect.applyTransform(el.transform);}viewRect.width=width;viewRect.height=height;return!tmpRect.intersect(viewRect);}function isClipPathChanged(clipPaths,prevClipPaths){// displayable.__clipPaths can only be `null`/`undefined` or an non-empty array.\nif(clipPaths===prevClipPaths){return false;}if(!clipPaths||!prevClipPaths||clipPaths.length!==prevClipPaths.length){return true;}for(var i=0;i}\n * @private\n */var zlevelList=this._zlevelList=[];/**\n * @type {Object.}\n * @private\n */var layers=this._layers={};/**\n * @type {Object.}\n * @private\n */this._layerConfig={};/**\n * zrender will do compositing when root is a canvas and have multiple zlevels.\n */this._needsManuallyCompositing=false;if(!singleCanvas){this._width=this._getSize(0);this._height=this._getSize(1);var domRoot=this._domRoot=createRoot(this._width,this._height);root.appendChild(domRoot);}else{var width=root.width;var height=root.height;if(opts.width!=null){width=opts.width;}if(opts.height!=null){height=opts.height;}this.dpr=opts.devicePixelRatio||1;// Use canvas width and height directly\nroot.width=width*this.dpr;root.height=height*this.dpr;this._width=width;this._height=height;// Create layer if only one given canvas\n// Device can be specified to create a high dpi image.\nvar mainLayer=new Layer(root,this,this.dpr);mainLayer.__builtin__=true;mainLayer.initContext();// FIXME Use canvas width and height\n// mainLayer.resize(width, height);\nlayers[CANVAS_ZLEVEL]=mainLayer;mainLayer.zlevel=CANVAS_ZLEVEL;// Not use common zlevel.\nzlevelList.push(CANVAS_ZLEVEL);this._domRoot=root;}/**\n * @type {module:zrender/Layer}\n * @private\n */this._hoverlayer=null;this._hoverElements=[];};Painter.prototype={constructor:Painter,getType:function getType(){return'canvas';},/**\n * If painter use a single canvas\n * @return {boolean}\n */isSingleCanvas:function isSingleCanvas(){return this._singleCanvas;},/**\n * @return {HTMLDivElement}\n */getViewportRoot:function getViewportRoot(){return this._domRoot;},getViewportRootOffset:function getViewportRootOffset(){var viewportRoot=this.getViewportRoot();if(viewportRoot){return{offsetLeft:viewportRoot.offsetLeft||0,offsetTop:viewportRoot.offsetTop||0};}},/**\n * 刷新\n * @param {boolean} [paintAll=false] 强制绘制所有displayable\n */refresh:function refresh(paintAll){var list=this.storage.getDisplayList(true);var zlevelList=this._zlevelList;this._redrawId=Math.random();this._paintList(list,paintAll,this._redrawId);// Paint custum layers\nfor(var i=0;i=0){hoverElements.splice(idx,1);}el.__hoverMir=null;},clearHover:function clearHover(el){var hoverElements=this._hoverElements;for(var i=0;i15){break;}}}layer.__drawIndex=i;if(layer.__drawIndex0&&zlevel>zlevelList[0]){for(i=0;izlevel){break;}}prevLayer=layersMap[zlevelList[i]];}zlevelList.splice(i+1,0,zlevel);layersMap[zlevel]=layer;// Vitual layer will not directly show on the screen.\n// (It can be a WebGL layer and assigned to a ZImage element)\n// But it still under management of zrender.\nif(!layer.virtual){if(prevLayer){var prevDom=prevLayer.dom;if(prevDom.nextSibling){domRoot.insertBefore(layer.dom,prevDom.nextSibling);}else{domRoot.appendChild(layer.dom);}}else{if(domRoot.firstChild){domRoot.insertBefore(layer.dom,domRoot.firstChild);}else{domRoot.appendChild(layer.dom);}}}},// Iterate each layer\neachLayer:function eachLayer(cb,context){var zlevelList=this._zlevelList;var z;var i;for(i=0;i} [prevLayer]\n */getLayers:function getLayers(){return this._layers;},_updateLayerStatus:function _updateLayerStatus(list){this.eachBuiltinLayer(function(layer,z){layer.__dirty=layer.__used=false;});function updatePrevLayer(idx){if(prevLayer){if(prevLayer.__endIndex!==idx){prevLayer.__dirty=true;}prevLayer.__endIndex=idx;}}if(this._singleCanvas){for(var i=1;i0?EL_AFTER_INCREMENTAL_INC:0),this._needsManuallyCompositing);}if(!layer.__builtin__){logError$1('ZLevel '+zlevel+' has been used by unkown layer '+layer.id);}if(layer!==prevLayer){layer.__used=true;if(layer.__startIndex!==i){layer.__dirty=true;}layer.__startIndex=i;if(!layer.incremental){layer.__drawIndex=i;}else{// Mark layer draw index needs to update.\nlayer.__drawIndex=-1;}updatePrevLayer(i);prevLayer=layer;}if(el.__dirty){layer.__dirty=true;if(layer.incremental&&layer.__drawIndex<0){// Start draw from the first dirty element.\nlayer.__drawIndex=i;}}}updatePrevLayer(i);this.eachBuiltinLayer(function(layer,z){// Used in last frame but not in this frame. Needs clear\nif(!layer.__used&&layer.getElementCount()>0){layer.__dirty=true;layer.__startIndex=layer.__endIndex=layer.__drawIndex=0;}// For incremental layer. In case start index changed and no elements are dirty.\nif(layer.__dirty&&layer.__drawIndex<0){layer.__drawIndex=layer.__startIndex;}});},/**\n * 清除hover层外所有内容\n */clear:function clear(){this.eachBuiltinLayer(this._clearLayer);return this;},_clearLayer:function _clearLayer(layer){layer.clear();},setBackgroundColor:function setBackgroundColor(backgroundColor){this._backgroundColor=backgroundColor;},/**\n * 修改指定zlevel的绘制参数\n *\n * @param {string} zlevel\n * @param {Object} config 配置对象\n * @param {string} [config.clearColor=0] 每次清空画布的颜色\n * @param {string} [config.motionBlur=false] 是否开启动态模糊\n * @param {number} [config.lastFrameAlpha=0.7]\n * 在开启动态模糊的时候使用,与上一帧混合的alpha值,值越大尾迹越明显\n */configLayer:function configLayer(zlevel,config){if(config){var layerConfig=this._layerConfig;if(!layerConfig[zlevel]){layerConfig[zlevel]=config;}else{merge(layerConfig[zlevel],config,true);}for(var i=0;i=0){this._clips.splice(idx,1);}},/**\n * Delete animation clip\n * @param {module:zrender/animation/Animator} animator\n */removeAnimator:function removeAnimator(animator){var clips=animator.getClips();for(var i=0;i\n * 1. Mobile browsers dispatch mouse events 300ms after touchend.\n * 2. Chrome for Android dispatch mousedown for long-touch about 650ms\n * Result: Blocking Mouse Events for 700ms.\n *\n * @param {DOMHandlerScope} scope\n */function setTouchTimer(scope){scope.touching=true;if(scope.touchTimer!=null){clearTimeout(scope.touchTimer);scope.touchTimer=null;}scope.touchTimer=setTimeout(function(){scope.touching=false;scope.touchTimer=null;},700);}// Mark touch, which is useful in distinguish touch and\n// mouse event in upper applicatoin.\nfunction markTouch(event){event&&(event.zrByTouch=true);}// function markTriggeredFromLocal(event) {\n// event && (event.__zrIsFromLocal = true);\n// }\n// function isTriggeredFromLocal(instance, event) {\n// return !!(event && event.__zrIsFromLocal);\n// }\nfunction normalizeGlobalEvent(instance,event){// offsetX, offsetY still need to be calculated. They are necessary in the event\n// handlers of the upper applications. Set `true` to force calculate them.\nreturn normalizeEvent(instance.dom,new FakeGlobalEvent(instance,event),true);}/**\n * Detect whether the given el is in `painterRoot`.\n */function isLocalEl(instance,el){var elTmp=el;var isLocal=false;while(elTmp&&elTmp.nodeType!==9&&!(isLocal=elTmp.domBelongToZr||elTmp!==el&&elTmp===instance.painterRoot)){elTmp=elTmp.parentNode;}return isLocal;}/**\n * Make a fake event but not change the original event,\n * becuase the global event probably be used by other\n * listeners not belonging to zrender.\n * @class\n */function FakeGlobalEvent(instance,event){this.type=event.type;this.target=this.currentTarget=instance.dom;this.pointerType=event.pointerType;// Necessray for the force calculation of zrX, zrY\nthis.clientX=event.clientX;this.clientY=event.clientY;// Because we do not mount global listeners to touch events,\n// we do not copy `targetTouches` and `changedTouches` here.\n}var fakeGlobalEventProto=FakeGlobalEvent.prototype;// we make the default methods on the event do nothing,\n// otherwise it is dangerous. See more details in\n// [Drag outside] in `Handler.js`.\nfakeGlobalEventProto.stopPropagation=fakeGlobalEventProto.stopImmediatePropagation=fakeGlobalEventProto.preventDefault=noop;/**\n * Local DOM Handlers\n * @this {HandlerProxy}\n */var localDOMHandlers={mousedown:function mousedown(event){event=normalizeEvent(this.dom,event);this._mayPointerCapture=[event.zrX,event.zrY];this.trigger('mousedown',event);},mousemove:function mousemove(event){event=normalizeEvent(this.dom,event);var downPoint=this._mayPointerCapture;if(downPoint&&(event.zrX!==downPoint[0]||event.zrY!==downPoint[1])){togglePointerCapture(this,true);}this.trigger('mousemove',event);},mouseup:function mouseup(event){event=normalizeEvent(this.dom,event);togglePointerCapture(this,false);this.trigger('mouseup',event);},mouseout:function mouseout(event){event=normalizeEvent(this.dom,event);// Similarly to the browser did on `document` and touch event,\n// `globalout` will be delayed to final pointer cature release.\nif(this._pointerCapturing){event.zrEventControl='no_globalout';}// There might be some doms created by upper layer application\n// at the same level of painter.getViewportRoot() (e.g., tooltip\n// dom created by echarts), where 'globalout' event should not\n// be triggered when mouse enters these doms. (But 'mouseout'\n// should be triggered at the original hovered element as usual).\nvar element=event.toElement||event.relatedTarget;event.zrIsToLocalDOM=isLocalEl(this,element);this.trigger('mouseout',event);},touchstart:function touchstart(event){// Default mouse behaviour should not be disabled here.\n// For example, page may needs to be slided.\nevent=normalizeEvent(this.dom,event);markTouch(event);this._lastTouchMoment=new Date();this.handler.processGesture(event,'start');// For consistent event listener for both touch device and mouse device,\n// we simulate \"mouseover-->mousedown\" in touch device. So we trigger\n// `mousemove` here (to trigger `mouseover` inside), and then trigger\n// `mousedown`.\nlocalDOMHandlers.mousemove.call(this,event);localDOMHandlers.mousedown.call(this,event);},touchmove:function touchmove(event){event=normalizeEvent(this.dom,event);markTouch(event);this.handler.processGesture(event,'change');// Mouse move should always be triggered no matter whether\n// there is gestrue event, because mouse move and pinch may\n// be used at the same time.\nlocalDOMHandlers.mousemove.call(this,event);},touchend:function touchend(event){event=normalizeEvent(this.dom,event);markTouch(event);this.handler.processGesture(event,'end');localDOMHandlers.mouseup.call(this,event);// Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is\n// triggered in `touchstart`. This seems to be illogical, but by this mechanism,\n// we can conveniently implement \"hover style\" in both PC and touch device just\n// by listening to `mouseover` to add \"hover style\" and listening to `mouseout`\n// to remove \"hover style\" on an element, without any additional code for\n// compatibility. (`mouseout` will not be triggered in `touchend`, so \"hover\n// style\" will remain for user view)\n// click event should always be triggered no matter whether\n// there is gestrue event. System click can not be prevented.\nif(+new Date()-this._lastTouchMoment\n// if (typeof MSGesture === 'function') {\n// (this._msGesture = new MSGesture()).target = dom; // jshint ignore:line\n// dom.addEventListener('MSGestureChange', onMSGestureChange);\n// }\n}else{if(env$1.touchEventsSupported){each$1(localNativeListenerNames.touch,function(nativeEventName){mountSingleDOMEventListener(scope,nativeEventName,function(event){// markTriggeredFromLocal(event);\ndomHandlers[nativeEventName].call(instance,event);setTouchTimer(scope);});});// Handler of 'mouseout' event is needed in touch mode, which will be mounted below.\n// addEventListener(root, 'mouseout', this._mouseoutHandler);\n}// 1. Considering some devices that both enable touch and mouse event (like on MS Surface\n// and lenovo X240, @see #2350), we make mouse event be always listened, otherwise\n// mouse event can not be handle in those devices.\n// 2. On MS Surface, Chrome will trigger both touch event and mouse event. How to prevent\n// mouseevent after touch event triggered, see `setTouchTimer`.\neach$1(localNativeListenerNames.mouse,function(nativeEventName){mountSingleDOMEventListener(scope,nativeEventName,function(event){event=getNativeEvent(event);if(!scope.touching){// markTriggeredFromLocal(event);\ndomHandlers[nativeEventName].call(instance,event);}});});}}/**\n * @param {HandlerProxy} instance\n * @param {DOMHandlerScope} scope\n */function mountGlobalDOMEventListeners(instance,scope){// Only IE11+/Edge. See the comment in `mountLocalDOMEventListeners`.\nif(env$1.pointerEventsSupported){each$1(globalNativeListenerNames.pointer,mount);}// Touch event has implemented \"drag outside\" so we do not mount global listener for touch event.\n// (see https://www.w3.org/TR/touch-events/#the-touchmove-event)\n// We do not consider \"both-support-touch-and-mouse device\" for this feature (see the comment of\n// `mountLocalDOMEventListeners`) to avoid bugs util some requirements come.\nelse if(!env$1.touchEventsSupported){each$1(globalNativeListenerNames.mouse,mount);}function mount(nativeEventName){function nativeEventListener(event){event=getNativeEvent(event);// See the reason in [Drag outside] in `Handler.js`\n// This checking supports both `useCapture` or not.\n// PENDING: if there is performance issue in some devices,\n// we probably can not use `useCapture` and change a easier\n// to judes whether local (mark).\nif(!isLocalEl(instance,event.target)){event=normalizeGlobalEvent(instance,event);scope.domHandlers[nativeEventName].call(instance,event);}}mountSingleDOMEventListener(scope,nativeEventName,nativeEventListener,{capture:true// See [Drag Outside] in `Handler.js`\n});}}function mountSingleDOMEventListener(scope,nativeEventName,listener,opt){scope.mounted[nativeEventName]=listener;scope.listenerOpts[nativeEventName]=opt;addEventListener(scope.domTarget,eventNameFix(nativeEventName),listener,opt);}function unmountDOMEventListeners(scope){var mounted=scope.mounted;for(var nativeEventName in mounted){if(mounted.hasOwnProperty(nativeEventName)){removeEventListener(scope.domTarget,eventNameFix(nativeEventName),mounted[nativeEventName],scope.listenerOpts[nativeEventName]);}}scope.mounted={};}/**\n * See [Drag Outside] in `Handler.js`.\n * @implement\n * @param {boolean} isPointerCapturing Should never be `null`/`undefined`.\n * `true`: start to capture pointer if it is not capturing.\n * `false`: end the capture if it is capturing.\n */function togglePointerCapture(instance,isPointerCapturing){instance._mayPointerCapture=null;if(globalEventSupported&&instance._pointerCapturing^isPointerCapturing){instance._pointerCapturing=isPointerCapturing;var globalHandlerScope=instance._globalHandlerScope;isPointerCapturing?mountGlobalDOMEventListeners(instance,globalHandlerScope):unmountDOMEventListeners(globalHandlerScope);}}/**\n * @inner\n * @class\n */function DOMHandlerScope(domTarget,domHandlers){this.domTarget=domTarget;this.domHandlers=domHandlers;// Key: eventName, value: mounted handler funcitons.\n// Used for unmount.\nthis.mounted={};this.listenerOpts={};this.touchTimer=null;this.touching=false;}/**\n * @public\n * @class\n */function HandlerDomProxy(dom,painterRoot){Eventful.call(this);this.dom=dom;this.painterRoot=painterRoot;this._localHandlerScope=new DOMHandlerScope(dom,localDOMHandlers);if(globalEventSupported){this._globalHandlerScope=new DOMHandlerScope(document,globalDOMHandlers);}/**\n * @type {boolean}\n */this._pointerCapturing=false;/**\n * @type {Array.} [x, y] or null.\n */this._mayPointerCapture=null;mountLocalDOMEventListeners(this,this._localHandlerScope);}var handlerDomProxyProto=HandlerDomProxy.prototype;handlerDomProxyProto.dispose=function(){unmountDOMEventListeners(this._localHandlerScope);if(globalEventSupported){unmountDOMEventListeners(this._globalHandlerScope);}};handlerDomProxyProto.setCursor=function(cursorStyle){this.dom.style&&(this.dom.style.cursor=cursorStyle||'default');};mixin(HandlerDomProxy,Eventful);/*!\n* ZRender, a high performance 2d drawing library.\n*\n* Copyright (c) 2013, Baidu Inc.\n* All rights reserved.\n*\n* LICENSE\n* https://github.com/ecomfe/zrender/blob/master/LICENSE.txt\n*/var useVML=!env$1.canvasSupported;var painterCtors={canvas:Painter};var instances$1={};// ZRender实例map索引\n/**\n * @type {string}\n */var version$1='4.3.1';/**\n * Initializing a zrender instance\n * @param {HTMLElement} dom\n * @param {Object} [opts]\n * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'\n * @param {number} [opts.devicePixelRatio]\n * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)\n * @return {module:zrender/ZRender}\n */function init$1(dom,opts){var zr=new ZRender(guid(),dom,opts);instances$1[zr.id]=zr;return zr;}/**\n * Dispose zrender instance\n * @param {module:zrender/ZRender} zr\n */function dispose$1(zr){if(zr){zr.dispose();}else{for(var key in instances$1){if(instances$1.hasOwnProperty(key)){instances$1[key].dispose();}}instances$1={};}return this;}/**\n * Get zrender instance by id\n * @param {string} id zrender instance id\n * @return {module:zrender/ZRender}\n */function getInstance(id){return instances$1[id];}function registerPainter(name,Ctor){painterCtors[name]=Ctor;}function delInstance(id){delete instances$1[id];}/**\n * @module zrender/ZRender\n */ /**\n * @constructor\n * @alias module:zrender/ZRender\n * @param {string} id\n * @param {HTMLElement} dom\n * @param {Object} opts\n * @param {string} [opts.renderer='canvas'] 'canvas' or 'svg'\n * @param {number} [opts.devicePixelRatio]\n * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)\n */var ZRender=function ZRender(id,dom,opts){opts=opts||{};/**\n * @type {HTMLDomElement}\n */this.dom=dom;/**\n * @type {string}\n */this.id=id;var self=this;var storage=new Storage();var rendererType=opts.renderer;// TODO WebGL\nif(useVML){if(!painterCtors.vml){throw new Error('You need to require \\'zrender/vml/vml\\' to support IE8');}rendererType='vml';}else if(!rendererType||!painterCtors[rendererType]){rendererType='canvas';}var painter=new painterCtors[rendererType](dom,storage,opts,id);this.storage=storage;this.painter=painter;var handerProxy=!env$1.node&&!env$1.worker?new HandlerDomProxy(painter.getViewportRoot(),painter.root):null;this.handler=new Handler(storage,painter,handerProxy,painter.root);/**\n * @type {module:zrender/animation/Animation}\n */this.animation=new Animation({stage:{update:bind(this.flush,this)}});this.animation.start();/**\n * @type {boolean}\n * @private\n */this._needsRefresh;// 修改 storage.delFromStorage, 每次删除元素之前删除动画\n// FIXME 有点ugly\nvar oldDelFromStorage=storage.delFromStorage;var oldAddToStorage=storage.addToStorage;storage.delFromStorage=function(el){oldDelFromStorage.call(storage,el);el&&el.removeSelfFromZr(self);};storage.addToStorage=function(el){oldAddToStorage.call(storage,el);el.addSelfToZr(self);};};ZRender.prototype={constructor:ZRender,/**\n * 获取实例唯一标识\n * @return {string}\n */getId:function getId(){return this.id;},/**\n * 添加元素\n * @param {module:zrender/Element} el\n */add:function add(el){this.storage.addRoot(el);this._needsRefresh=true;},/**\n * 删除元素\n * @param {module:zrender/Element} el\n */remove:function remove(el){this.storage.delRoot(el);this._needsRefresh=true;},/**\n * Change configuration of layer\n * @param {string} zLevel\n * @param {Object} config\n * @param {string} [config.clearColor=0] Clear color\n * @param {string} [config.motionBlur=false] If enable motion blur\n * @param {number} [config.lastFrameAlpha=0.7] Motion blur factor. Larger value cause longer trailer\n */configLayer:function configLayer(zLevel,config){if(this.painter.configLayer){this.painter.configLayer(zLevel,config);}this._needsRefresh=true;},/**\n * Set background color\n * @param {string} backgroundColor\n */setBackgroundColor:function setBackgroundColor(backgroundColor){if(this.painter.setBackgroundColor){this.painter.setBackgroundColor(backgroundColor);}this._needsRefresh=true;},/**\n * Repaint the canvas immediately\n */refreshImmediately:function refreshImmediately(){// var start = new Date();\n// Clear needsRefresh ahead to avoid something wrong happens in refresh\n// Or it will cause zrender refreshes again and again.\nthis._needsRefresh=this._needsRefreshHover=false;this.painter.refresh();// Avoid trigger zr.refresh in Element#beforeUpdate hook\nthis._needsRefresh=this._needsRefreshHover=false;// var end = new Date();\n// var log = document.getElementById('log');\n// if (log) {\n// log.innerHTML = log.innerHTML + ' ' + (end - start);\n// }\n},/**\n * Mark and repaint the canvas in the next frame of browser\n */refresh:function refresh(){this._needsRefresh=true;},/**\n * Perform all refresh\n */flush:function flush(){var triggerRendered;if(this._needsRefresh){triggerRendered=true;this.refreshImmediately();}if(this._needsRefreshHover){triggerRendered=true;this.refreshHoverImmediately();}triggerRendered&&this.trigger('rendered');},/**\n * Add element to hover layer\n * @param {module:zrender/Element} el\n * @param {Object} style\n */addHover:function addHover(el,style){if(this.painter.addHover){var elMirror=this.painter.addHover(el,style);this.refreshHover();return elMirror;}},/**\n * Add element from hover layer\n * @param {module:zrender/Element} el\n */removeHover:function removeHover(el){if(this.painter.removeHover){this.painter.removeHover(el);this.refreshHover();}},/**\n * Clear all hover elements in hover layer\n * @param {module:zrender/Element} el\n */clearHover:function clearHover(){if(this.painter.clearHover){this.painter.clearHover();this.refreshHover();}},/**\n * Refresh hover in next frame\n */refreshHover:function refreshHover(){this._needsRefreshHover=true;},/**\n * Refresh hover immediately\n */refreshHoverImmediately:function refreshHoverImmediately(){this._needsRefreshHover=false;this.painter.refreshHover&&this.painter.refreshHover();},/**\n * Resize the canvas.\n * Should be invoked when container size is changed\n * @param {Object} [opts]\n * @param {number|string} [opts.width] Can be 'auto' (the same as null/undefined)\n * @param {number|string} [opts.height] Can be 'auto' (the same as null/undefined)\n */resize:function resize(opts){opts=opts||{};this.painter.resize(opts.width,opts.height);this.handler.resize();},/**\n * Stop and clear all animation immediately\n */clearAnimation:function clearAnimation(){this.animation.clear();},/**\n * Get container width\n */getWidth:function getWidth(){return this.painter.getWidth();},/**\n * Get container height\n */getHeight:function getHeight(){return this.painter.getHeight();},/**\n * Export the canvas as Base64 URL\n * @param {string} type\n * @param {string} [backgroundColor='#fff']\n * @return {string} Base64 URL\n */ // toDataURL: function(type, backgroundColor) {\n// return this.painter.getRenderedCanvas({\n// backgroundColor: backgroundColor\n// }).toDataURL(type);\n// },\n/**\n * Converting a path to image.\n * It has much better performance of drawing image rather than drawing a vector path.\n * @param {module:zrender/graphic/Path} e\n * @param {number} width\n * @param {number} height\n */pathToImage:function pathToImage(e,dpr){return this.painter.pathToImage(e,dpr);},/**\n * Set default cursor\n * @param {string} [cursorStyle='default'] 例如 crosshair\n */setCursorStyle:function setCursorStyle(cursorStyle){this.handler.setCursorStyle(cursorStyle);},/**\n * Find hovered element\n * @param {number} x\n * @param {number} y\n * @return {Object} {target, topTarget}\n */findHover:function findHover(x,y){return this.handler.findHover(x,y);},/**\n * Bind event\n *\n * @param {string} eventName Event name\n * @param {Function} eventHandler Handler function\n * @param {Object} [context] Context object\n */on:function on(eventName,eventHandler,context){this.handler.on(eventName,eventHandler,context);},/**\n * Unbind event\n * @param {string} eventName Event name\n * @param {Function} [eventHandler] Handler function\n */off:function off(eventName,eventHandler){this.handler.off(eventName,eventHandler);},/**\n * Trigger event manually\n *\n * @param {string} eventName Event name\n * @param {event=} event Event object\n */trigger:function trigger(eventName,event){this.handler.trigger(eventName,event);},/**\n * Clear all objects and the canvas.\n */clear:function clear(){this.storage.delRoot();this.painter.clear();},/**\n * Dispose self.\n */dispose:function dispose(){this.animation.stop();this.clear();this.storage.dispose();this.painter.dispose();this.handler.dispose();this.animation=this.storage=this.painter=this.handler=null;delInstance(this.id);}};var zrender=(Object.freeze||Object)({version:version$1,init:init$1,dispose:dispose$1,getInstance:getInstance,registerPainter:registerPainter});/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var each$2=each$1;var isObject$2=isObject$1;var isArray$1=isArray;/**\n * Make the name displayable. But we should\n * make sure it is not duplicated with user\n * specified name, so use '\\0';\n */var DUMMY_COMPONENT_NAME_PREFIX='series\\0';/**\n * If value is not array, then translate it to array.\n * @param {*} value\n * @return {Array} [value] or value\n */function normalizeToArray(value){return value instanceof Array?value:value==null?[]:[value];}/**\n * Sync default option between normal and emphasis like `position` and `show`\n * In case some one will write code like\n * label: {\n * show: false,\n * position: 'outside',\n * fontSize: 18\n * },\n * emphasis: {\n * label: { show: true }\n * }\n * @param {Object} opt\n * @param {string} key\n * @param {Array.} subOpts\n */function defaultEmphasis(opt,key,subOpts){// Caution: performance sensitive.\nif(opt){opt[key]=opt[key]||{};opt.emphasis=opt.emphasis||{};opt.emphasis[key]=opt.emphasis[key]||{};// Default emphasis option from normal\nfor(var i=0,len=subOpts.length;i}\n */function getDataItemValue(dataItem){return isObject$2(dataItem)&&!isArray$1(dataItem)&&!(dataItem instanceof Date)?dataItem.value:dataItem;}/**\n * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}]\n * This helper method determine if dataItem has extra option besides value\n * @param {string|number|Date|Array|Object} dataItem\n */function isDataItemOption(dataItem){return isObject$2(dataItem)&&!(dataItem instanceof Array);// // markLine data can be array\n// && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array));\n}/**\n * Mapping to exists for merge.\n *\n * @public\n * @param {Array.|Array.} exists\n * @param {Object|Array.} newCptOptions\n * @return {Array.} Result, like [{exist: ..., option: ...}, {}],\n * index of which is the same as exists.\n */function mappingToExists(exists,newCptOptions){// Mapping by the order by original option (but not order of\n// new option) in merge mode. Because we should ensure\n// some specified index (like xAxisIndex) is consistent with\n// original option, which is easy to understand, espatially in\n// media query. And in most case, merge option is used to\n// update partial option but not be expected to change order.\nnewCptOptions=(newCptOptions||[]).slice();var result=map(exists||[],function(obj,index){return{exist:obj};});// Mapping by id or name if specified.\neach$2(newCptOptions,function(cptOption,index){if(!isObject$2(cptOption)){return;}// id has highest priority.\nfor(var i=0;i=result.length){result.push({option:cptOption});}});return result;}/**\n * Make id and name for mapping result (result of mappingToExists)\n * into `keyInfo` field.\n *\n * @public\n * @param {Array.} Result, like [{exist: ..., option: ...}, {}],\n * which order is the same as exists.\n * @return {Array.} The input.\n */function makeIdAndName(mapResult){// We use this id to hash component models and view instances\n// in echarts. id can be specified by user, or auto generated.\n// The id generation rule ensures new view instance are able\n// to mapped to old instance when setOption are called in\n// no-merge mode. So we generate model id by name and plus\n// type in view id.\n// name can be duplicated among components, which is convenient\n// to specify multi components (like series) by one name.\n// Ensure that each id is distinct.\nvar idMap=createHashMap();each$2(mapResult,function(item,index){var existCpt=item.exist;existCpt&&idMap.set(existCpt.id,item);});each$2(mapResult,function(item,index){var opt=item.option;assert$1(!opt||opt.id==null||!idMap.get(opt.id)||idMap.get(opt.id)===item,'id duplicates: '+(opt&&opt.id));opt&&opt.id!=null&&idMap.set(opt.id,item);!item.keyInfo&&(item.keyInfo={});});// Make name and id.\neach$2(mapResult,function(item,index){var existCpt=item.exist;var opt=item.option;var keyInfo=item.keyInfo;if(!isObject$2(opt)){return;}// name can be overwitten. Consider case: axis.name = '20km'.\n// But id generated by name will not be changed, which affect\n// only in that case: setOption with 'not merge mode' and view\n// instance will be recreated, which can be accepted.\nkeyInfo.name=opt.name!=null?opt.name+'':existCpt?existCpt.name// Avoid diffferent series has the same name,\n// because name may be used like in color pallet.\n:DUMMY_COMPONENT_NAME_PREFIX+index;if(existCpt){keyInfo.id=existCpt.id;}else if(opt.id!=null){keyInfo.id=opt.id+'';}else{// Consider this situatoin:\n// optionA: [{name: 'a'}, {name: 'a'}, {..}]\n// optionB [{..}, {name: 'a'}, {name: 'a'}]\n// Series with the same name between optionA and optionB\n// should be mapped.\nvar idNum=0;do{keyInfo.id='\\0'+keyInfo.name+'\\0'+idNum++;}while(idMap.get(keyInfo.id));}idMap.set(keyInfo.id,item);});}function isNameSpecified(componentModel){var name=componentModel.name;// Is specified when `indexOf` get -1 or > 0.\nreturn!!(name&&name.indexOf(DUMMY_COMPONENT_NAME_PREFIX));}/**\n * @public\n * @param {Object} cptOption\n * @return {boolean}\n */function isIdInner(cptOption){return isObject$2(cptOption)&&cptOption.id&&(cptOption.id+'').indexOf('\\0_ec_\\0')===0;}/**\n * A helper for removing duplicate items between batchA and batchB,\n * and in themselves, and categorize by series.\n *\n * @param {Array.} batchA Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @param {Array.} batchB Like: [{seriesId: 2, dataIndex: [32, 4, 5]}, ...]\n * @return {Array., Array.>} result: [resultBatchA, resultBatchB]\n */ /**\n * @param {module:echarts/data/List} data\n * @param {Object} payload Contains dataIndex (means rawIndex) / dataIndexInside / name\n * each of which can be Array or primary type.\n * @return {number|Array.} dataIndex If not found, return undefined/null.\n */function queryDataIndex(data,payload){if(payload.dataIndexInside!=null){return payload.dataIndexInside;}else if(payload.dataIndex!=null){return isArray(payload.dataIndex)?map(payload.dataIndex,function(value){return data.indexOfRawIndex(value);}):data.indexOfRawIndex(payload.dataIndex);}else if(payload.name!=null){return isArray(payload.name)?map(payload.name,function(value){return data.indexOfName(value);}):data.indexOfName(payload.name);}}/**\n * Enable property storage to any host object.\n * Notice: Serialization is not supported.\n *\n * For example:\n * var inner = zrUitl.makeInner();\n *\n * function some1(hostObj) {\n * inner(hostObj).someProperty = 1212;\n * ...\n * }\n * function some2() {\n * var fields = inner(this);\n * fields.someProperty1 = 1212;\n * fields.someProperty2 = 'xx';\n * ...\n * }\n *\n * @return {Function}\n */function makeInner(){// Consider different scope by es module import.\nvar key='__\\0ec_inner_'+innerUniqueIndex++ +'_'+Math.random().toFixed(5);return function(hostObj){return hostObj[key]||(hostObj[key]={});};}var innerUniqueIndex=0;/**\n * @param {module:echarts/model/Global} ecModel\n * @param {string|Object} finder\n * If string, e.g., 'geo', means {geoIndex: 0}.\n * If Object, could contain some of these properties below:\n * {\n * seriesIndex, seriesId, seriesName,\n * geoIndex, geoId, geoName,\n * bmapIndex, bmapId, bmapName,\n * xAxisIndex, xAxisId, xAxisName,\n * yAxisIndex, yAxisId, yAxisName,\n * gridIndex, gridId, gridName,\n * ... (can be extended)\n * }\n * Each properties can be number|string|Array.|Array.\n * For example, a finder could be\n * {\n * seriesIndex: 3,\n * geoId: ['aa', 'cc'],\n * gridName: ['xx', 'rr']\n * }\n * xxxIndex can be set as 'all' (means all xxx) or 'none' (means not specify)\n * If nothing or null/undefined specified, return nothing.\n * @param {Object} [opt]\n * @param {string} [opt.defaultMainType]\n * @param {Array.} [opt.includeMainTypes]\n * @return {Object} result like:\n * {\n * seriesModels: [seriesModel1, seriesModel2],\n * seriesModel: seriesModel1, // The first model\n * geoModels: [geoModel1, geoModel2],\n * geoModel: geoModel1, // The first model\n * ...\n * }\n */function parseFinder(ecModel,finder,opt){if(isString(finder)){var obj={};obj[finder+'Index']=0;finder=obj;}var defaultMainType=opt&&opt.defaultMainType;if(defaultMainType&&!has(finder,defaultMainType+'Index')&&!has(finder,defaultMainType+'Id')&&!has(finder,defaultMainType+'Name')){finder[defaultMainType+'Index']=0;}var result={};each$2(finder,function(value,key){var value=finder[key];// Exclude 'dataIndex' and other illgal keys.\nif(key==='dataIndex'||key==='dataIndexInside'){result[key]=value;return;}var parsedKey=key.match(/^(\\w+)(Index|Id|Name)$/)||[];var mainType=parsedKey[1];var queryType=(parsedKey[2]||'').toLowerCase();if(!mainType||!queryType||value==null||queryType==='index'&&value==='none'||opt&&opt.includeMainTypes&&indexOf(opt.includeMainTypes,mainType)<0){return;}var queryParam={mainType:mainType};if(queryType!=='index'||value!=='all'){queryParam[queryType]=value;}var models=ecModel.queryComponents(queryParam);result[mainType+'Models']=models;result[mainType+'Model']=models[0];});return result;}function has(obj,prop){return obj&&obj.hasOwnProperty(prop);}function setAttribute(dom,key,value){dom.setAttribute?dom.setAttribute(key,value):dom[key]=value;}function getAttribute(dom,key){return dom.getAttribute?dom.getAttribute(key):dom[key];}function getTooltipRenderMode(renderModeOption){if(renderModeOption==='auto'){// Using html when `document` exists, use richText otherwise\nreturn env$1.domSupported?'html':'richText';}else{return renderModeOption||'html';}}/**\n * Group a list by key.\n *\n * @param {Array} array\n * @param {Function} getKey\n * param {*} Array item\n * return {string} key\n * @return {Object} Result\n * {Array}: keys,\n * {module:zrender/core/util/HashMap} buckets: {key -> Array}\n */ /*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var TYPE_DELIMITER='.';var IS_CONTAINER='___EC__COMPONENT__CONTAINER___';/**\n * Notice, parseClassType('') should returns {main: '', sub: ''}\n * @public\n */function parseClassType$1(componentType){var ret={main:'',sub:''};if(componentType){componentType=componentType.split(TYPE_DELIMITER);ret.main=componentType[0]||'';ret.sub=componentType[1]||'';}return ret;}/**\n * @public\n */function checkClassType(componentType){assert$1(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType),'componentType \"'+componentType+'\" illegal');}/**\n * @public\n */function enableClassExtend(RootClass,mandatoryMethods){RootClass.$constructor=RootClass;RootClass.extend=function(proto){if(__DEV__){each$1(mandatoryMethods,function(method){if(!proto[method]){console.warn('Method `'+method+'` should be implemented'+(proto.type?' in '+proto.type:'')+'.');}});}var superClass=this;var ExtendedClass=function ExtendedClass(){if(!proto.$constructor){superClass.apply(this,arguments);}else{proto.$constructor.apply(this,arguments);}};extend(ExtendedClass.prototype,proto);ExtendedClass.extend=this.extend;ExtendedClass.superCall=superCall;ExtendedClass.superApply=superApply;inherits(ExtendedClass,this);ExtendedClass.superClass=superClass;return ExtendedClass;};}var classBase=0;/**\n * Can not use instanceof, consider different scope by\n * cross domain or es module import in ec extensions.\n * Mount a method \"isInstance()\" to Clz.\n */function enableClassCheck(Clz){var classAttr=['__\\0is_clz',classBase++,Math.random().toFixed(3)].join('_');Clz.prototype[classAttr]=true;if(__DEV__){assert$1(!Clz.isInstance,'The method \"is\" can not be defined.');}Clz.isInstance=function(obj){return!!(obj&&obj[classAttr]);};}// superCall should have class info, which can not be fetch from 'this'.\n// Consider this case:\n// class A has method f,\n// class B inherits class A, overrides method f, f call superApply('f'),\n// class C inherits class B, do not overrides method f,\n// then when method of class C is called, dead loop occured.\nfunction superCall(context,methodName){var args=slice(arguments,2);return this.superClass.prototype[methodName].apply(context,args);}function superApply(context,methodName,args){return this.superClass.prototype[methodName].apply(context,args);}/**\n * @param {Object} entity\n * @param {Object} options\n * @param {boolean} [options.registerWhenExtend]\n * @public\n */function enableClassManagement(entity,options){options=options||{};/**\n * Component model classes\n * key: componentType,\n * value:\n * componentClass, when componentType is 'xxx'\n * or Object., when componentType is 'xxx.yy'\n * @type {Object}\n */var storage={};entity.registerClass=function(Clazz,componentType){if(componentType){checkClassType(componentType);componentType=parseClassType$1(componentType);if(!componentType.sub){if(__DEV__){if(storage[componentType.main]){console.warn(componentType.main+' exists.');}}storage[componentType.main]=Clazz;}else if(componentType.sub!==IS_CONTAINER){var container=makeContainer(componentType);container[componentType.sub]=Clazz;}}return Clazz;};entity.getClass=function(componentMainType,subType,throwWhenNotFound){var Clazz=storage[componentMainType];if(Clazz&&Clazz[IS_CONTAINER]){Clazz=subType?Clazz[subType]:null;}if(throwWhenNotFound&&!Clazz){throw new Error(!subType?componentMainType+'.'+'type should be specified.':'Component '+componentMainType+'.'+(subType||'')+' not exists. Load it first.');}return Clazz;};entity.getClassesByMainType=function(componentType){componentType=parseClassType$1(componentType);var result=[];var obj=storage[componentType.main];if(obj&&obj[IS_CONTAINER]){each$1(obj,function(o,type){type!==IS_CONTAINER&&result.push(o);});}else{result.push(obj);}return result;};entity.hasClass=function(componentType){// Just consider componentType.main.\ncomponentType=parseClassType$1(componentType);return!!storage[componentType.main];};/**\n * @return {Array.} Like ['aa', 'bb'], but can not be ['aa.xx']\n */entity.getAllClassMainTypes=function(){var types=[];each$1(storage,function(obj,type){types.push(type);});return types;};/**\n * If a main type is container and has sub types\n * @param {string} mainType\n * @return {boolean}\n */entity.hasSubTypes=function(componentType){componentType=parseClassType$1(componentType);var obj=storage[componentType.main];return obj&&obj[IS_CONTAINER];};entity.parseClassType=parseClassType$1;function makeContainer(componentType){var container=storage[componentType.main];if(!container||!container[IS_CONTAINER]){container=storage[componentType.main]={};container[IS_CONTAINER]=true;}return container;}if(options.registerWhenExtend){var originalExtend=entity.extend;if(originalExtend){entity.extend=function(proto){var ExtendedClass=originalExtend.call(this,proto);return entity.registerClass(ExtendedClass,proto.type);};}}return entity;}/**\n * @param {string|Array.} properties\n */ /*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // TODO Parse shadow style\n// TODO Only shallow path support\nvar makeStyleMapper=function makeStyleMapper(properties){// Normalize\nfor(var i=0;i=0||includes&&indexOf(includes,propName)<0){continue;}var val=model.getShallow(propName);if(val!=null){style[properties[i][0]]=val;}}return style;};};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var _getLineStyle=makeStyleMapper([['lineWidth','width'],['stroke','color'],['opacity'],['shadowBlur'],['shadowOffsetX'],['shadowOffsetY'],['shadowColor']]);var lineStyleMixin={getLineStyle:function getLineStyle(excludes){var style=_getLineStyle(this,excludes);// Always set lineDash whether dashed, otherwise we can not\n// erase the previous style when assigning to el.style.\nstyle.lineDash=this.getLineDash(style.lineWidth);return style;},getLineDash:function getLineDash(lineWidth){if(lineWidth==null){lineWidth=1;}var lineType=this.get('type');var dotSize=Math.max(lineWidth,2);var dashSize=lineWidth*4;return lineType==='solid'||lineType==null?// Use `false` but not `null` for the solid line here, because `null` might be\n// ignored when assigning to `el.style`. e.g., when setting `lineStyle.type` as\n// `'dashed'` and `emphasis.lineStyle.type` as `'solid'` in graph series, the\n// `lineDash` gotten form the latter one is not able to erase that from the former\n// one if using `null` here according to the emhpsis strategy in `util/graphic.js`.\nfalse:lineType==='dashed'?[dashSize,dashSize]:[dotSize,dotSize];}};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var _getAreaStyle=makeStyleMapper([['fill','color'],['shadowBlur'],['shadowOffsetX'],['shadowOffsetY'],['opacity'],['shadowColor']]);var areaStyleMixin={getAreaStyle:function getAreaStyle(excludes,includes){return _getAreaStyle(this,excludes,includes);}};/**\n * 曲线辅助模块\n * @module zrender/core/curve\n * @author pissang(https://www.github.com/pissang)\n */var mathPow=Math.pow;var mathSqrt$2=Math.sqrt;var EPSILON$1=1e-8;var EPSILON_NUMERIC=1e-4;var THREE_SQRT=mathSqrt$2(3);var ONE_THIRD=1/3;// 临时变量\nvar _v0=create();var _v1=create();var _v2=create();function isAroundZero(val){return val>-EPSILON$1&&valEPSILON$1||val<-EPSILON$1;}/**\n * 计算三次贝塞尔值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @return {number}\n */function cubicAt(p0,p1,p2,p3,t){var onet=1-t;return onet*onet*(onet*p0+3*t*p1)+t*t*(t*p3+3*onet*p2);}/**\n * 计算三次贝塞尔导数值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @return {number}\n */function cubicDerivativeAt(p0,p1,p2,p3,t){var onet=1-t;return 3*(((p1-p0)*onet+2*(p2-p1)*t)*onet+(p3-p2)*t*t);}/**\n * 计算三次贝塞尔方程根,使用盛金公式\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} val\n * @param {Array.} roots\n * @return {number} 有效根数目\n */function cubicRootAt(p0,p1,p2,p3,val,roots){// Evaluate roots of cubic functions\nvar a=p3+3*(p1-p2)-p0;var b=3*(p2-p1*2+p0);var c=3*(p1-p0);var d=p0-val;var A=b*b-3*a*c;var B=b*c-9*a*d;var C=c*c-3*b*d;var n=0;if(isAroundZero(A)&&isAroundZero(B)){if(isAroundZero(b)){roots[0]=0;}else{var t1=-c/b;//t1, t2, t3, b is not zero\nif(t1>=0&&t1<=1){roots[n++]=t1;}}}else{var disc=B*B-4*A*C;if(isAroundZero(disc)){var K=B/A;var t1=-b/a+K;// t1, a is not zero\nvar t2=-K/2;// t2, t3\nif(t1>=0&&t1<=1){roots[n++]=t1;}if(t2>=0&&t2<=1){roots[n++]=t2;}}else if(disc>0){var discSqrt=mathSqrt$2(disc);var Y1=A*b+1.5*a*(-B+discSqrt);var Y2=A*b+1.5*a*(-B-discSqrt);if(Y1<0){Y1=-mathPow(-Y1,ONE_THIRD);}else{Y1=mathPow(Y1,ONE_THIRD);}if(Y2<0){Y2=-mathPow(-Y2,ONE_THIRD);}else{Y2=mathPow(Y2,ONE_THIRD);}var t1=(-b-(Y1+Y2))/(3*a);if(t1>=0&&t1<=1){roots[n++]=t1;}}else{var T=(2*A*b-3*a*B)/(2*mathSqrt$2(A*A*A));var theta=Math.acos(T)/3;var ASqrt=mathSqrt$2(A);var tmp=Math.cos(theta);var t1=(-b-2*ASqrt*tmp)/(3*a);var t2=(-b+ASqrt*(tmp+THREE_SQRT*Math.sin(theta)))/(3*a);var t3=(-b+ASqrt*(tmp-THREE_SQRT*Math.sin(theta)))/(3*a);if(t1>=0&&t1<=1){roots[n++]=t1;}if(t2>=0&&t2<=1){roots[n++]=t2;}if(t3>=0&&t3<=1){roots[n++]=t3;}}}return n;}/**\n * 计算三次贝塞尔方程极限值的位置\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {Array.} extrema\n * @return {number} 有效数目\n */function cubicExtrema(p0,p1,p2,p3,extrema){var b=6*p2-12*p1+6*p0;var a=9*p1+3*p3-3*p0-9*p2;var c=3*p1-3*p0;var n=0;if(isAroundZero(a)){if(isNotAroundZero$1(b)){var t1=-c/b;if(t1>=0&&t1<=1){extrema[n++]=t1;}}}else{var disc=b*b-4*a*c;if(isAroundZero(disc)){extrema[0]=-b/(2*a);}else if(disc>0){var discSqrt=mathSqrt$2(disc);var t1=(-b+discSqrt)/(2*a);var t2=(-b-discSqrt)/(2*a);if(t1>=0&&t1<=1){extrema[n++]=t1;}if(t2>=0&&t2<=1){extrema[n++]=t2;}}}return n;}/**\n * 细分三次贝塞尔曲线\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} p3\n * @param {number} t\n * @param {Array.} out\n */function cubicSubdivide(p0,p1,p2,p3,t,out){var p01=(p1-p0)*t+p0;var p12=(p2-p1)*t+p1;var p23=(p3-p2)*t+p2;var p012=(p12-p01)*t+p01;var p123=(p23-p12)*t+p12;var p0123=(p123-p012)*t+p012;// Seg0\nout[0]=p0;out[1]=p01;out[2]=p012;out[3]=p0123;// Seg1\nout[4]=p0123;out[5]=p123;out[6]=p23;out[7]=p3;}/**\n * 投射点到三次贝塞尔曲线上,返回投射距离。\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @param {number} x\n * @param {number} y\n * @param {Array.} [out] 投射点\n * @return {number}\n */function cubicProjectPoint(x0,y0,x1,y1,x2,y2,x3,y3,x,y,out){// http://pomax.github.io/bezierinfo/#projections\nvar t;var interval=0.005;var d=Infinity;var prev;var next;var d1;var d2;_v0[0]=x;_v0[1]=y;// 先粗略估计一下可能的最小距离的 t 值\n// PENDING\nfor(var _t=0;_t<1;_t+=0.05){_v1[0]=cubicAt(x0,x1,x2,x3,_t);_v1[1]=cubicAt(y0,y1,y2,y3,_t);d1=distSquare(_v0,_v1);if(d1=0&&d1} roots\n * @return {number} 有效根数目\n */function quadraticRootAt(p0,p1,p2,val,roots){var a=p0-2*p1+p2;var b=2*(p1-p0);var c=p0-val;var n=0;if(isAroundZero(a)){if(isNotAroundZero$1(b)){var t1=-c/b;if(t1>=0&&t1<=1){roots[n++]=t1;}}}else{var disc=b*b-4*a*c;if(isAroundZero(disc)){var t1=-b/(2*a);if(t1>=0&&t1<=1){roots[n++]=t1;}}else if(disc>0){var discSqrt=mathSqrt$2(disc);var t1=(-b+discSqrt)/(2*a);var t2=(-b-discSqrt)/(2*a);if(t1>=0&&t1<=1){roots[n++]=t1;}if(t2>=0&&t2<=1){roots[n++]=t2;}}}return n;}/**\n * 计算二次贝塞尔方程极限值\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @return {number}\n */function quadraticExtremum(p0,p1,p2){var divider=p0+p2-2*p1;if(divider===0){// p1 is center of p0 and p2\nreturn 0.5;}else{return(p0-p1)/divider;}}/**\n * 细分二次贝塞尔曲线\n * @memberOf module:zrender/core/curve\n * @param {number} p0\n * @param {number} p1\n * @param {number} p2\n * @param {number} t\n * @param {Array.} out\n */function quadraticSubdivide(p0,p1,p2,t,out){var p01=(p1-p0)*t+p0;var p12=(p2-p1)*t+p1;var p012=(p12-p01)*t+p01;// Seg0\nout[0]=p0;out[1]=p01;out[2]=p012;// Seg1\nout[3]=p012;out[4]=p12;out[5]=p2;}/**\n * 投射点到二次贝塞尔曲线上,返回投射距离。\n * 投射点有可能会有一个或者多个,这里只返回其中距离最短的一个。\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x\n * @param {number} y\n * @param {Array.} out 投射点\n * @return {number}\n */function quadraticProjectPoint(x0,y0,x1,y1,x2,y2,x,y,out){// http://pomax.github.io/bezierinfo/#projections\nvar t;var interval=0.005;var d=Infinity;_v0[0]=x;_v0[1]=y;// 先粗略估计一下可能的最小距离的 t 值\n// PENDING\nfor(var _t=0;_t<1;_t+=0.05){_v1[0]=quadraticAt(x0,x1,x2,_t);_v1[1]=quadraticAt(y0,y1,y2,_t);var d1=distSquare(_v0,_v1);if(d1=0&&d1} points 顶点数组\n * @param {number} min\n * @param {number} max\n */function fromPoints(points,min$$1,max$$1){if(points.length===0){return;}var p=points[0];var left=p[0];var right=p[0];var top=p[1];var bottom=p[1];var i;for(i=1;i} min\n * @param {Array.} max\n */function fromLine(x0,y0,x1,y1,min$$1,max$$1){min$$1[0]=mathMin$3(x0,x1);min$$1[1]=mathMin$3(y0,y1);max$$1[0]=mathMax$3(x0,x1);max$$1[1]=mathMax$3(y0,y1);}var xDim=[];var yDim=[];/**\n * 从三阶贝塞尔曲线(p0, p1, p2, p3)中计算出最小包围盒,写入`min`和`max`中\n * @memberOf module:zrender/core/bbox\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @param {Array.} min\n * @param {Array.} max\n */function fromCubic(x0,y0,x1,y1,x2,y2,x3,y3,min$$1,max$$1){var cubicExtrema$$1=cubicExtrema;var cubicAt$$1=cubicAt;var i;var n=cubicExtrema$$1(x0,x1,x2,x3,xDim);min$$1[0]=Infinity;min$$1[1]=Infinity;max$$1[0]=-Infinity;max$$1[1]=-Infinity;for(i=0;i} min\n * @param {Array.} max\n */function fromQuadratic(x0,y0,x1,y1,x2,y2,min$$1,max$$1){var quadraticExtremum$$1=quadraticExtremum;var quadraticAt$$1=quadraticAt;// Find extremities, where derivative in x dim or y dim is zero\nvar tx=mathMax$3(mathMin$3(quadraticExtremum$$1(x0,x1,x2),1),0);var ty=mathMax$3(mathMin$3(quadraticExtremum$$1(y0,y1,y2),1),0);var x=quadraticAt$$1(x0,x1,x2,tx);var y=quadraticAt$$1(y0,y1,y2,ty);min$$1[0]=mathMin$3(x0,x2,x);min$$1[1]=mathMin$3(y0,y2,y);max$$1[0]=mathMax$3(x0,x2,x);max$$1[1]=mathMax$3(y0,y2,y);}/**\n * 从圆弧中计算出最小包围盒,写入`min`和`max`中\n * @method\n * @memberOf module:zrender/core/bbox\n * @param {number} x\n * @param {number} y\n * @param {number} rx\n * @param {number} ry\n * @param {number} startAngle\n * @param {number} endAngle\n * @param {number} anticlockwise\n * @param {Array.} min\n * @param {Array.} max\n */function fromArc(x,y,rx,ry,startAngle,endAngle,anticlockwise,min$$1,max$$1){var vec2Min=min;var vec2Max=max;var diff=Math.abs(startAngle-endAngle);if(diff%PI2<1e-4&&diff>1e-4){// Is a circle\nmin$$1[0]=x-rx;min$$1[1]=y-ry;max$$1[0]=x+rx;max$$1[1]=y+ry;return;}start[0]=mathCos$2(startAngle)*rx+x;start[1]=mathSin$2(startAngle)*ry+y;end[0]=mathCos$2(endAngle)*rx+x;end[1]=mathSin$2(endAngle)*ry+y;vec2Min(min$$1,start,end);vec2Max(max$$1,start,end);// Thresh to [0, Math.PI * 2]\nstartAngle=startAngle%PI2;if(startAngle<0){startAngle=startAngle+PI2;}endAngle=endAngle%PI2;if(endAngle<0){endAngle=endAngle+PI2;}if(startAngle>endAngle&&!anticlockwise){endAngle+=PI2;}else if(startAnglestartAngle){extremity[0]=mathCos$2(angle)*rx+x;extremity[1]=mathSin$2(angle)*ry+y;vec2Min(min$$1,extremity,min$$1);vec2Max(max$$1,extremity,max$$1);}}}/**\n * Path 代理,可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中\n * 可以用于 isInsidePath 判断以及获取boundingRect\n *\n * @module zrender/core/PathProxy\n * @author Yi Shen (http://www.github.com/pissang)\n */ // TODO getTotalLength, getPointAtLength\n/* global Float32Array */var CMD={M:1,L:2,C:3,Q:4,A:5,Z:6,// Rect\nR:7};// var CMD_MEM_SIZE = {\n// M: 3,\n// L: 3,\n// C: 7,\n// Q: 5,\n// A: 9,\n// R: 5,\n// Z: 1\n// };\nvar min$1=[];var max$1=[];var min2=[];var max2=[];var mathMin$2=Math.min;var mathMax$2=Math.max;var mathCos$1=Math.cos;var mathSin$1=Math.sin;var mathSqrt$1=Math.sqrt;var mathAbs=Math.abs;var hasTypedArray=typeof Float32Array!=='undefined';/**\n * @alias module:zrender/core/PathProxy\n * @constructor\n */var PathProxy=function PathProxy(notSaveData){this._saveData=!(notSaveData||false);if(this._saveData){/**\n * Path data. Stored as flat array\n * @type {Array.}\n */this.data=[];}this._ctx=null;};/**\n * 快速计算Path包围盒(并不是最小包围盒)\n * @return {Object}\n */PathProxy.prototype={constructor:PathProxy,_xi:0,_yi:0,_x0:0,_y0:0,// Unit x, Unit y. Provide for avoiding drawing that too short line segment\n_ux:0,_uy:0,_len:0,_lineDash:null,_dashOffset:0,_dashIdx:0,_dashSum:0,/**\n * @readOnly\n */setScale:function setScale(sx,sy,segmentIgnoreThreshold){// Compat. Previously there is no segmentIgnoreThreshold.\nsegmentIgnoreThreshold=segmentIgnoreThreshold||0;this._ux=mathAbs(segmentIgnoreThreshold/devicePixelRatio/sx)||0;this._uy=mathAbs(segmentIgnoreThreshold/devicePixelRatio/sy)||0;},getContext:function getContext(){return this._ctx;},/**\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */beginPath:function beginPath(ctx){this._ctx=ctx;ctx&&ctx.beginPath();ctx&&(this.dpr=ctx.dpr);// Reset\nif(this._saveData){this._len=0;}if(this._lineDash){this._lineDash=null;this._dashOffset=0;}return this;},/**\n * @param {number} x\n * @param {number} y\n * @return {module:zrender/core/PathProxy}\n */moveTo:function moveTo(x,y){this.addData(CMD.M,x,y);this._ctx&&this._ctx.moveTo(x,y);// x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用\n// xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。\n// 有可能在 beginPath 之后直接调用 lineTo,这时候 x0, y0 需要\n// 在 lineTo 方法中记录,这里先不考虑这种情况,dashed line 也只在 IE10- 中不支持\nthis._x0=x;this._y0=y;this._xi=x;this._yi=y;return this;},/**\n * @param {number} x\n * @param {number} y\n * @return {module:zrender/core/PathProxy}\n */lineTo:function lineTo(x,y){var exceedUnit=mathAbs(x-this._xi)>this._ux||mathAbs(y-this._yi)>this._uy// Force draw the first segment\n||this._len<5;this.addData(CMD.L,x,y);if(this._ctx&&exceedUnit){this._needsDash()?this._dashedLineTo(x,y):this._ctx.lineTo(x,y);}if(exceedUnit){this._xi=x;this._yi=y;}return this;},/**\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @param {number} x3\n * @param {number} y3\n * @return {module:zrender/core/PathProxy}\n */bezierCurveTo:function bezierCurveTo(x1,y1,x2,y2,x3,y3){this.addData(CMD.C,x1,y1,x2,y2,x3,y3);if(this._ctx){this._needsDash()?this._dashedBezierTo(x1,y1,x2,y2,x3,y3):this._ctx.bezierCurveTo(x1,y1,x2,y2,x3,y3);}this._xi=x3;this._yi=y3;return this;},/**\n * @param {number} x1\n * @param {number} y1\n * @param {number} x2\n * @param {number} y2\n * @return {module:zrender/core/PathProxy}\n */quadraticCurveTo:function quadraticCurveTo(x1,y1,x2,y2){this.addData(CMD.Q,x1,y1,x2,y2);if(this._ctx){this._needsDash()?this._dashedQuadraticTo(x1,y1,x2,y2):this._ctx.quadraticCurveTo(x1,y1,x2,y2);}this._xi=x2;this._yi=y2;return this;},/**\n * @param {number} cx\n * @param {number} cy\n * @param {number} r\n * @param {number} startAngle\n * @param {number} endAngle\n * @param {boolean} anticlockwise\n * @return {module:zrender/core/PathProxy}\n */arc:function arc(cx,cy,r,startAngle,endAngle,anticlockwise){this.addData(CMD.A,cx,cy,r,r,startAngle,endAngle-startAngle,0,anticlockwise?0:1);this._ctx&&this._ctx.arc(cx,cy,r,startAngle,endAngle,anticlockwise);this._xi=mathCos$1(endAngle)*r+cx;this._yi=mathSin$1(endAngle)*r+cy;return this;},// TODO\narcTo:function arcTo(x1,y1,x2,y2,radius){if(this._ctx){this._ctx.arcTo(x1,y1,x2,y2,radius);}return this;},// TODO\nrect:function rect(x,y,w,h){this._ctx&&this._ctx.rect(x,y,w,h);this.addData(CMD.R,x,y,w,h);return this;},/**\n * @return {module:zrender/core/PathProxy}\n */closePath:function closePath(){this.addData(CMD.Z);var ctx=this._ctx;var x0=this._x0;var y0=this._y0;if(ctx){this._needsDash()&&this._dashedLineTo(x0,y0);ctx.closePath();}this._xi=x0;this._yi=y0;return this;},/**\n * Context 从外部传入,因为有可能是 rebuildPath 完之后再 fill。\n * stroke 同样\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */fill:function fill(ctx){ctx&&ctx.fill();this.toStatic();},/**\n * @param {CanvasRenderingContext2D} ctx\n * @return {module:zrender/core/PathProxy}\n */stroke:function stroke(ctx){ctx&&ctx.stroke();this.toStatic();},/**\n * 必须在其它绘制命令前调用\n * Must be invoked before all other path drawing methods\n * @return {module:zrender/core/PathProxy}\n */setLineDash:function setLineDash(lineDash){if(lineDash instanceof Array){this._lineDash=lineDash;this._dashIdx=0;var lineDashSum=0;for(var i=0;i} path\n */appendPath:function appendPath(path){if(!(path instanceof Array)){path=[path];}var len$$1=path.length;var appendSize=0;var offset=this._len;for(var i=0;idata.length){// 因为之前的数组已经转换成静态的 Float32Array\n// 所以不够用时需要扩展一个新的动态数组\nthis._expandData();data=this.data;}for(var i=0;i0&&x<=x1||dx<0&&x>=x1||dx===0&&(dy>0&&y<=y1||dy<0&&y>=y1)){idx=this._dashIdx;dash=lineDash[idx];x+=dx*dash;y+=dy*dash;this._dashIdx=(idx+1)%nDash;// Skip positive offset\nif(dx>0&&xx0||dy>0&&yy0){continue;}ctx[idx%2?'moveTo':'lineTo'](dx>=0?mathMin$2(x,x1):mathMax$2(x,x1),dy>=0?mathMin$2(y,y1):mathMax$2(y,y1));}// Offset for next lineTo\ndx=x-x1;dy=y-y1;this._dashOffset=-mathSqrt$1(dx*dx+dy*dy);},// Not accurate dashed line to\n_dashedBezierTo:function _dashedBezierTo(x1,y1,x2,y2,x3,y3){var dashSum=this._dashSum;var offset=this._dashOffset;var lineDash=this._lineDash;var ctx=this._ctx;var x0=this._xi;var y0=this._yi;var t;var dx;var dy;var cubicAt$$1=cubicAt;var bezierLen=0;var idx=this._dashIdx;var nDash=lineDash.length;var x;var y;var tmpLen=0;if(offset<0){// Convert to positive offset\noffset=dashSum+offset;}offset%=dashSum;// Bezier approx length\nfor(t=0;t<1;t+=0.1){dx=cubicAt$$1(x0,x1,x2,x3,t+0.1)-cubicAt$$1(x0,x1,x2,x3,t);dy=cubicAt$$1(y0,y1,y2,y3,t+0.1)-cubicAt$$1(y0,y1,y2,y3,t);bezierLen+=mathSqrt$1(dx*dx+dy*dy);}// Find idx after add offset\nfor(;idxoffset){break;}}t=(tmpLen-offset)/bezierLen;while(t<=1){x=cubicAt$$1(x0,x1,x2,x3,t);y=cubicAt$$1(y0,y1,y2,y3,t);// Use line to approximate dashed bezier\n// Bad result if dash is long\nidx%2?ctx.moveTo(x,y):ctx.lineTo(x,y);t+=lineDash[idx]/bezierLen;idx=(idx+1)%nDash;}// Finish the last segment and calculate the new offset\nidx%2!==0&&ctx.lineTo(x3,y3);dx=x3-x;dy=y3-y;this._dashOffset=-mathSqrt$1(dx*dx+dy*dy);},_dashedQuadraticTo:function _dashedQuadraticTo(x1,y1,x2,y2){// Convert quadratic to cubic using degree elevation\nvar x3=x2;var y3=y2;x2=(x2+2*x1)/3;y2=(y2+2*y1)/3;x1=(this._xi+2*x1)/3;y1=(this._yi+2*y1)/3;this._dashedBezierTo(x1,y1,x2,y2,x3,y3);},/**\n * 转成静态的 Float32Array 减少堆内存占用\n * Convert dynamic array to static Float32Array\n */toStatic:function toStatic(){var data=this.data;if(data instanceof Array){data.length=this._len;if(hasTypedArray){this.data=new Float32Array(data);}}},/**\n * @return {module:zrender/core/BoundingRect}\n */getBoundingRect:function getBoundingRect(){min$1[0]=min$1[1]=min2[0]=min2[1]=Number.MAX_VALUE;max$1[0]=max$1[1]=max2[0]=max2[1]=-Number.MAX_VALUE;var data=this.data;var xi=0;var yi=0;var x0=0;var y0=0;for(var i=0;iux||mathAbs(y-yi)>uy||i===len$$1-1){ctx.lineTo(x,y);xi=x;yi=y;}break;case CMD.C:ctx.bezierCurveTo(d[i++],d[i++],d[i++],d[i++],d[i++],d[i++]);xi=d[i-2];yi=d[i-1];break;case CMD.Q:ctx.quadraticCurveTo(d[i++],d[i++],d[i++],d[i++]);xi=d[i-2];yi=d[i-1];break;case CMD.A:var cx=d[i++];var cy=d[i++];var rx=d[i++];var ry=d[i++];var theta=d[i++];var dTheta=d[i++];var psi=d[i++];var fs=d[i++];var r=rx>ry?rx:ry;var scaleX=rx>ry?1:rx/ry;var scaleY=rx>ry?ry/rx:1;var isEllipse=Math.abs(rx-ry)>1e-3;var endAngle=theta+dTheta;if(isEllipse){ctx.translate(cx,cy);ctx.rotate(psi);ctx.scale(scaleX,scaleY);ctx.arc(0,0,r,theta,endAngle,1-fs);ctx.scale(1/scaleX,1/scaleY);ctx.rotate(-psi);ctx.translate(-cx,-cy);}else{ctx.arc(cx,cy,r,theta,endAngle,1-fs);}if(i===1){// 直接使用 arc 命令\n// 第一个命令起点还未定义\nx0=mathCos$1(theta)*rx+cx;y0=mathSin$1(theta)*ry+cy;}xi=mathCos$1(endAngle)*rx+cx;yi=mathSin$1(endAngle)*ry+cy;break;case CMD.R:x0=xi=d[i];y0=yi=d[i+1];ctx.rect(d[i++],d[i++],d[i++],d[i++]);break;case CMD.Z:ctx.closePath();xi=x0;yi=y0;}}}};PathProxy.CMD=CMD;/**\n * 线段包含判断\n * @param {number} x0\n * @param {number} y0\n * @param {number} x1\n * @param {number} y1\n * @param {number} lineWidth\n * @param {number} x\n * @param {number} y\n * @return {boolean}\n */function containStroke$1(x0,y0,x1,y1,lineWidth,x,y){if(lineWidth===0){return false;}var _l=lineWidth;var _a=0;var _b=x0;// Quick reject\nif(y>y0+_l&&y>y1+_l||yx0+_l&&x>x1+_l||xy0+_l&&y>y1+_l&&y>y2+_l&&y>y3+_l||yx0+_l&&x>x1+_l&&x>x2+_l&&x>x3+_l||xy0+_l&&y>y1+_l&&y>y2+_l||yx0+_l&&x>x1+_l&&x>x2+_l||xr||d+_lendAngle){endAngle+=PI2$2;}var angle=Math.atan2(y,x);if(angle<0){angle+=PI2$2;}return angle>=startAngle&&angle<=endAngle||angle+PI2$2>=startAngle&&angle+PI2$2<=endAngle;}function windingLine(x0,y0,x1,y1,x,y){if(y>y0&&y>y1||yx?dir:0;}var CMD$1=PathProxy.CMD;var PI2$1=Math.PI*2;var EPSILON$2=1e-4;function isAroundEqual(a,b){return Math.abs(a-b)y0&&y>y1&&y>y2&&y>y3||y1){swapExtrema();}y0_=cubicAt(y0,y1,y2,y3,extrema[0]);if(nExtrema>1){y1_=cubicAt(y0,y1,y2,y3,extrema[1]);}}if(nExtrema===2){// 分成三段单调函数\nif(ty0&&y>y1&&y>y2||y=0&&t<=1){var w=0;var y_=quadraticAt(y0,y1,y2,t);for(var i=0;ir||y<-r){return 0;}var tmp=Math.sqrt(r*r-y*y);roots[0]=-tmp;roots[1]=tmp;var diff=Math.abs(startAngle-endAngle);if(diff<1e-4){return 0;}if(diff%PI2$1<1e-4){// Is a circle\nstartAngle=0;endAngle=PI2$1;var dir=anticlockwise?1:-1;if(x>=roots[0]+cx&&x<=roots[1]+cx){return dir;}else{return 0;}}if(anticlockwise){var tmp=startAngle;startAngle=normalizeRadian(endAngle);endAngle=normalizeRadian(tmp);}else{startAngle=normalizeRadian(startAngle);endAngle=normalizeRadian(endAngle);}if(startAngle>endAngle){endAngle+=PI2$1;}var w=0;for(var i=0;i<2;i++){var x_=roots[i];if(x_+cx>x){var angle=Math.atan2(y,x_);var dir=anticlockwise?1:-1;if(angle<0){angle=PI2$1+angle;}if(angle>=startAngle&&angle<=endAngle||angle+PI2$1>=startAngle&&angle+PI2$1<=endAngle){if(angle>Math.PI/2&&angle1){// Close previous subpath\nif(!isStroke){w+=windingLine(xi,yi,x0,y0,x,y);}// 如果被任何一个 subpath 包含\n// if (w !== 0) {\n// return true;\n// }\n}if(i===1){// 如果第一个命令是 L, C, Q\n// 则 previous point 同绘制命令的第一个 point\n//\n// 第一个命令为 Arc 的情况下会在后面特殊处理\nxi=data[i];yi=data[i+1];x0=xi;y0=yi;}switch(cmd){case CMD$1.M:// moveTo 命令重新创建一个新的 subpath, 并且更新新的起点\n// 在 closePath 的时候使用\nx0=data[i++];y0=data[i++];xi=x0;yi=y0;break;case CMD$1.L:if(isStroke){if(containStroke$1(xi,yi,data[i],data[i+1],lineWidth,x,y)){return true;}}else{// NOTE 在第一个命令为 L, C, Q 的时候会计算出 NaN\nw+=windingLine(xi,yi,data[i],data[i+1],x,y)||0;}xi=data[i++];yi=data[i++];break;case CMD$1.C:if(isStroke){if(containStroke$2(xi,yi,data[i++],data[i++],data[i++],data[i++],data[i],data[i+1],lineWidth,x,y)){return true;}}else{w+=windingCubic(xi,yi,data[i++],data[i++],data[i++],data[i++],data[i],data[i+1],x,y)||0;}xi=data[i++];yi=data[i++];break;case CMD$1.Q:if(isStroke){if(containStroke$3(xi,yi,data[i++],data[i++],data[i],data[i+1],lineWidth,x,y)){return true;}}else{w+=windingQuadratic(xi,yi,data[i++],data[i++],data[i],data[i+1],x,y)||0;}xi=data[i++];yi=data[i++];break;case CMD$1.A:// TODO Arc 判断的开销比较大\nvar cx=data[i++];var cy=data[i++];var rx=data[i++];var ry=data[i++];var theta=data[i++];var dTheta=data[i++];// TODO Arc 旋转\ni+=1;var anticlockwise=1-data[i++];var x1=Math.cos(theta)*rx+cx;var y1=Math.sin(theta)*ry+cy;// 不是直接使用 arc 命令\nif(i>1){w+=windingLine(xi,yi,x1,y1,x,y);}else{// 第一个命令起点还未定义\nx0=x1;y0=y1;}// zr 使用scale来模拟椭圆, 这里也对x做一定的缩放\nvar _x=(x-cx)*ry/rx+cx;if(isStroke){if(containStroke$4(cx,cy,ry,theta,theta+dTheta,anticlockwise,lineWidth,_x,y)){return true;}}else{w+=windingArc(cx,cy,ry,theta,theta+dTheta,anticlockwise,_x,y);}xi=Math.cos(theta+dTheta)*rx+cx;yi=Math.sin(theta+dTheta)*ry+cy;break;case CMD$1.R:x0=xi=data[i++];y0=yi=data[i++];var width=data[i++];var height=data[i++];var x1=x0+width;var y1=y0+height;if(isStroke){if(containStroke$1(x0,y0,x1,y0,lineWidth,x,y)||containStroke$1(x1,y0,x1,y1,lineWidth,x,y)||containStroke$1(x1,y1,x0,y1,lineWidth,x,y)||containStroke$1(x0,y1,x0,y0,lineWidth,x,y)){return true;}}else{// FIXME Clockwise ?\nw+=windingLine(x1,y0,x1,y1,x,y);w+=windingLine(x0,y1,x0,y0,x,y);}break;case CMD$1.Z:if(isStroke){if(containStroke$1(xi,yi,x0,y0,lineWidth,x,y)){return true;}}else{// Close a subpath\nw+=windingLine(xi,yi,x0,y0,x,y);// 如果被任何一个 subpath 包含\n// FIXME subpaths may overlap\n// if (w !== 0) {\n// return true;\n// }\n}xi=x0;yi=y0;break;}}if(!isStroke&&!isAroundEqual(yi,y0)){w+=windingLine(xi,yi,x0,y0,x,y)||0;}return w!==0;}function _contain(pathData,x,y){return containPath(pathData,0,false,x,y);}function containStroke(pathData,lineWidth,x,y){return containPath(pathData,lineWidth,true,x,y);}var getCanvasPattern=Pattern.prototype.getCanvasPattern;var abs=Math.abs;var pathProxyForDraw=new PathProxy(true);/**\n * @alias module:zrender/graphic/Path\n * @extends module:zrender/graphic/Displayable\n * @constructor\n * @param {Object} opts\n */function Path(opts){Displayable.call(this,opts);/**\n * @type {module:zrender/core/PathProxy}\n * @readOnly\n */this.path=null;}Path.prototype={constructor:Path,type:'path',__dirtyPath:true,strokeContainThreshold:5,// This item default to be false. But in map series in echarts,\n// in order to improve performance, it should be set to true,\n// so the shorty segment won't draw.\nsegmentIgnoreThreshold:0,/**\n * See `module:zrender/src/graphic/helper/subPixelOptimize`.\n * @type {boolean}\n */subPixelOptimize:false,brush:function brush(ctx,prevEl){var style=this.style;var path=this.path||pathProxyForDraw;var hasStroke=style.hasStroke();var hasFill=style.hasFill();var fill=style.fill;var stroke=style.stroke;var hasFillGradient=hasFill&&!!fill.colorStops;var hasStrokeGradient=hasStroke&&!!stroke.colorStops;var hasFillPattern=hasFill&&!!fill.image;var hasStrokePattern=hasStroke&&!!stroke.image;style.bind(ctx,this,prevEl);this.setTransform(ctx);if(this.__dirty){var rect;// Update gradient because bounding rect may changed\nif(hasFillGradient){rect=rect||this.getBoundingRect();this._fillGradient=style.getGradient(ctx,fill,rect);}if(hasStrokeGradient){rect=rect||this.getBoundingRect();this._strokeGradient=style.getGradient(ctx,stroke,rect);}}// Use the gradient or pattern\nif(hasFillGradient){// PENDING If may have affect the state\nctx.fillStyle=this._fillGradient;}else if(hasFillPattern){ctx.fillStyle=getCanvasPattern.call(fill,ctx);}if(hasStrokeGradient){ctx.strokeStyle=this._strokeGradient;}else if(hasStrokePattern){ctx.strokeStyle=getCanvasPattern.call(stroke,ctx);}var lineDash=style.lineDash;var lineDashOffset=style.lineDashOffset;var ctxLineDash=!!ctx.setLineDash;// Update path sx, sy\nvar scale=this.getGlobalScale();path.setScale(scale[0],scale[1],this.segmentIgnoreThreshold);// Proxy context\n// Rebuild path in following 2 cases\n// 1. Path is dirty\n// 2. Path needs javascript implemented lineDash stroking.\n// In this case, lineDash information will not be saved in PathProxy\nif(this.__dirtyPath||lineDash&&!ctxLineDash&&hasStroke){path.beginPath(ctx);// Setting line dash before build path\nif(lineDash&&!ctxLineDash){path.setLineDash(lineDash);path.setLineDashOffset(lineDashOffset);}this.buildPath(path,this.shape,false);// Clear path dirty flag\nif(this.path){this.__dirtyPath=false;}}else{// Replay path building\nctx.beginPath();this.path.rebuildPath(ctx);}if(hasFill){if(style.fillOpacity!=null){var originalGlobalAlpha=ctx.globalAlpha;ctx.globalAlpha=style.fillOpacity*style.opacity;path.fill(ctx);ctx.globalAlpha=originalGlobalAlpha;}else{path.fill(ctx);}}if(lineDash&&ctxLineDash){ctx.setLineDash(lineDash);ctx.lineDashOffset=lineDashOffset;}if(hasStroke){if(style.strokeOpacity!=null){var originalGlobalAlpha=ctx.globalAlpha;ctx.globalAlpha=style.strokeOpacity*style.opacity;path.stroke(ctx);ctx.globalAlpha=originalGlobalAlpha;}else{path.stroke(ctx);}}if(lineDash&&ctxLineDash){// PENDING\n// Remove lineDash\nctx.setLineDash([]);}// Draw rect text\nif(style.text!=null){// Only restore transform when needs draw text.\nthis.restoreTransform(ctx);this.drawRectText(ctx,this.getBoundingRect());}},// When bundling path, some shape may decide if use moveTo to begin a new subpath or closePath\n// Like in circle\nbuildPath:function buildPath(ctx,shapeCfg,inBundle){},createPathProxy:function createPathProxy(){this.path=new PathProxy();},getBoundingRect:function getBoundingRect(){var rect=this._rect;var style=this.style;var needsUpdateRect=!rect;if(needsUpdateRect){var path=this.path;if(!path){// Create path on demand.\npath=this.path=new PathProxy();}if(this.__dirtyPath){path.beginPath();this.buildPath(path,this.shape,false);}rect=path.getBoundingRect();}this._rect=rect;if(style.hasStroke()){// Needs update rect with stroke lineWidth when\n// 1. Element changes scale or lineWidth\n// 2. Shape is changed\nvar rectWithStroke=this._rectWithStroke||(this._rectWithStroke=rect.clone());if(this.__dirty||needsUpdateRect){rectWithStroke.copy(rect);// FIXME Must after updateTransform\nvar w=style.lineWidth;// PENDING, Min line width is needed when line is horizontal or vertical\nvar lineScale=style.strokeNoScale?this.getLineScale():1;// Only add extra hover lineWidth when there are no fill\nif(!style.hasFill()){w=Math.max(w,this.strokeContainThreshold||4);}// Consider line width\n// Line scale can't be 0;\nif(lineScale>1e-10){rectWithStroke.width+=w/lineScale;rectWithStroke.height+=w/lineScale;rectWithStroke.x-=w/lineScale/2;rectWithStroke.y-=w/lineScale/2;}}// Return rect with stroke\nreturn rectWithStroke;}return rect;},contain:function contain(x,y){var localPos=this.transformCoordToLocal(x,y);var rect=this.getBoundingRect();var style=this.style;x=localPos[0];y=localPos[1];if(rect.contain(x,y)){var pathData=this.path.data;if(style.hasStroke()){var lineWidth=style.lineWidth;var lineScale=style.strokeNoScale?this.getLineScale():1;// Line scale can't be 0;\nif(lineScale>1e-10){// Only add extra hover lineWidth when there are no fill\nif(!style.hasFill()){lineWidth=Math.max(lineWidth,this.strokeContainThreshold);}if(containStroke(pathData,lineWidth/lineScale,x,y)){return true;}}}if(style.hasFill()){return _contain(pathData,x,y);}}return false;},/**\n * @param {boolean} dirtyPath\n */dirty:function dirty(dirtyPath){if(dirtyPath==null){dirtyPath=true;}// Only mark dirty, not mark clean\nif(dirtyPath){this.__dirtyPath=dirtyPath;this._rect=null;}this.__dirty=this.__dirtyText=true;this.__zr&&this.__zr.refresh();// Used as a clipping path\nif(this.__clipTarget){this.__clipTarget.dirty();}},/**\n * Alias for animate('shape')\n * @param {boolean} loop\n */animateShape:function animateShape(loop){return this.animate('shape',loop);},// Overwrite attrKV\nattrKV:function attrKV(key,value){// FIXME\nif(key==='shape'){this.setShape(value);this.__dirtyPath=true;this._rect=null;}else{Displayable.prototype.attrKV.call(this,key,value);}},/**\n * @param {Object|string} key\n * @param {*} value\n */setShape:function setShape(key,value){var shape=this.shape;// Path from string may not have shape\nif(shape){if(isObject$1(key)){for(var name in key){if(key.hasOwnProperty(name)){shape[name]=key[name];}}}else{shape[key]=value;}this.dirty(true);}return this;},getLineScale:function getLineScale(){var m=this.transform;// Get the line scale.\n// Determinant of `m` means how much the area is enlarged by the\n// transformation. So its square root can be used as a scale factor\n// for width.\nreturn m&&abs(m[0]-1)>1e-10&&abs(m[3]-1)>1e-10?Math.sqrt(abs(m[0]*m[3]-m[2]*m[1])):1;}};/**\n * 扩展一个 Path element, 比如星形,圆等。\n * Extend a path element\n * @param {Object} props\n * @param {string} props.type Path type\n * @param {Function} props.init Initialize\n * @param {Function} props.buildPath Overwrite buildPath method\n * @param {Object} [props.style] Extended default style config\n * @param {Object} [props.shape] Extended default shape config\n */Path.extend=function(defaults$$1){var Sub=function Sub(opts){Path.call(this,opts);if(defaults$$1.style){// Extend default style\nthis.style.extendFrom(defaults$$1.style,false);}// Extend default shape\nvar defaultShape=defaults$$1.shape;if(defaultShape){this.shape=this.shape||{};var thisShape=this.shape;for(var name in defaultShape){if(!thisShape.hasOwnProperty(name)&&defaultShape.hasOwnProperty(name)){thisShape[name]=defaultShape[name];}}}defaults$$1.init&&defaults$$1.init.call(this,opts);};inherits(Sub,Path);// FIXME 不能 extend position, rotation 等引用对象\nfor(var name in defaults$$1){// Extending prototype values and methods\nif(name!=='style'&&name!=='shape'){Sub.prototype[name]=defaults$$1[name];}}return Sub;};inherits(Path,Displayable);var CMD$2=PathProxy.CMD;var points=[[],[],[]];var mathSqrt$3=Math.sqrt;var mathAtan2=Math.atan2;var transformPath=function transformPath(path,m){var data=path.data;var cmd;var nPoint;var i;var j;var k;var p;var M=CMD$2.M;var C=CMD$2.C;var L=CMD$2.L;var R=CMD$2.R;var A=CMD$2.A;var Q=CMD$2.Q;for(i=0,j=0;i1){rx*=mathSqrt(lambda);ry*=mathSqrt(lambda);}var f=(fa===fs?-1:1)*mathSqrt((rx*rx*(ry*ry)-rx*rx*(yp*yp)-ry*ry*(xp*xp))/(rx*rx*(yp*yp)+ry*ry*(xp*xp)))||0;var cxp=f*rx*yp/ry;var cyp=f*-ry*xp/rx;var cx=(x1+x2)/2.0+mathCos(psi)*cxp-mathSin(psi)*cyp;var cy=(y1+y2)/2.0+mathSin(psi)*cxp+mathCos(psi)*cyp;var theta=vAngle([1,0],[(xp-cxp)/rx,(yp-cyp)/ry]);var u=[(xp-cxp)/rx,(yp-cyp)/ry];var v=[(-1*xp-cxp)/rx,(-1*yp-cyp)/ry];var dTheta=vAngle(u,v);if(vRatio(u,v)<=-1){dTheta=PI;}if(vRatio(u,v)>=1){dTheta=0;}if(fs===0&&dTheta>0){dTheta=dTheta-2*PI;}if(fs===1&&dTheta<0){dTheta=dTheta+2*PI;}path.addData(cmd,cx,cy,rx,ry,theta,dTheta,psi,fs);}var commandReg=/([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig;// Consider case:\n// (1) delimiter can be comma or space, where continuous commas\n// or spaces should be seen as one comma.\n// (2) value can be like:\n// '2e-4', 'l.5.9' (ignore 0), 'M-10-10', 'l-2.43e-1,34.9983',\n// 'l-.5E1,54', '121-23-44-11' (no delimiter)\nvar numberReg=/-?([0-9]*\\.)?[0-9]+([eE]-?[0-9]+)?/g;// var valueSplitReg = /[\\s,]+/;\nfunction createPathProxyFromString(data){if(!data){return new PathProxy();}// var data = data.replace(/-/g, ' -')\n// .replace(/ /g, ' ')\n// .replace(/ /g, ',')\n// .replace(/,,/g, ',');\n// var n;\n// create pipes so that we can split the data\n// for (n = 0; n < cc.length; n++) {\n// cs = cs.replace(new RegExp(cc[n], 'g'), '|' + cc[n]);\n// }\n// data = data.replace(/-/g, ',-');\n// create array\n// var arr = cs.split('|');\n// init context point\nvar cpx=0;var cpy=0;var subpathX=cpx;var subpathY=cpy;var prevCmd;var path=new PathProxy();var CMD=PathProxy.CMD;// commandReg.lastIndex = 0;\n// var cmdResult;\n// while ((cmdResult = commandReg.exec(data)) != null) {\n// var cmdStr = cmdResult[1];\n// var cmdContent = cmdResult[2];\nvar cmdList=data.match(commandReg);for(var l=0;l NaN\n// var val = parseFloat(p[i]);\n// !isNaN(val) && (p[pLen++] = val);\n// }\nvar p=cmdText.match(numberReg)||[];var pLen=p.length;for(var i=0;i=11?function(){var clipPaths=this.__clipPaths;var style=this.style;var modified;if(clipPaths){for(var i=0;ilen$$1-2?len$$1-1:idx+1];p3=points[idx>len$$1-3?len$$1-1:idx+2];}else{p0=points[(idx-1+len$$1)%len$$1];p2=points[(idx+1)%len$$1];p3=points[(idx+2)%len$$1];}var w2=w*w;var w3=w*w2;ret.push([interpolate(p0[0],p1[0],p2[0],p3[0],w,w2,w3),interpolate(p0[1],p1[1],p2[1],p3[1],w,w2,w3)]);}return ret;};/**\n * 贝塞尔平滑曲线\n * @module zrender/shape/util/smoothBezier\n * @author pissang (https://www.github.com/pissang)\n * Kener (@Kener-林峰, kener.linfeng@gmail.com)\n * errorrik (errorrik@gmail.com)\n */ /**\n * 贝塞尔平滑曲线\n * @alias module:zrender/shape/util/smoothBezier\n * @param {Array} points 线段顶点数组\n * @param {number} smooth 平滑等级, 0-1\n * @param {boolean} isLoop\n * @param {Array} constraint 将计算出来的控制点约束在一个包围盒内\n * 比如 [[0, 0], [100, 100]], 这个包围盒会与\n * 整个折线的包围盒做一个并集用来约束控制点。\n * @param {Array} 计算出来的控制点数组\n */var smoothBezier=function smoothBezier(points,smooth,isLoop,constraint){var cps=[];var v=[];var v1=[];var v2=[];var prevPoint;var nextPoint;var min$$1;var max$$1;if(constraint){min$$1=[Infinity,Infinity];max$$1=[-Infinity,-Infinity];for(var i=0,len$$1=points.length;i=2){if(smooth&&smooth!=='spline'){var controlPoints=smoothBezier(points,smooth,closePath,shape.smoothConstraint);ctx.moveTo(points[0][0],points[0][1]);var len=points.length;for(var i=0;i<(closePath?len:len-1);i++){var cp1=controlPoints[i*2];var cp2=controlPoints[i*2+1];var p=points[(i+1)%len];ctx.bezierCurveTo(cp1[0],cp1[1],cp2[0],cp2[1],p[0],p[1]);}}else{if(smooth==='spline'){points=smoothSpline(points,closePath);}ctx.moveTo(points[0][0],points[0][1]);for(var i=1,l=points.length;i}\n */pointAt:function pointAt(p){var shape=this.shape;return[shape.x1*(1-p)+shape.x2*p,shape.y1*(1-p)+shape.y2*p];}});/**\n * 贝塞尔曲线\n * @module zrender/shape/BezierCurve\n */var out=[];function someVectorAt(shape,t,isTangent){var cpx2=shape.cpx2;var cpy2=shape.cpy2;if(cpx2===null||cpy2===null){return[(isTangent?cubicDerivativeAt:cubicAt)(shape.x1,shape.cpx1,shape.cpx2,shape.x2,t),(isTangent?cubicDerivativeAt:cubicAt)(shape.y1,shape.cpy1,shape.cpy2,shape.y2,t)];}else{return[(isTangent?quadraticDerivativeAt:quadraticAt)(shape.x1,shape.cpx1,shape.x2,t),(isTangent?quadraticDerivativeAt:quadraticAt)(shape.y1,shape.cpy1,shape.y2,t)];}}var BezierCurve=Path.extend({type:'bezier-curve',shape:{x1:0,y1:0,x2:0,y2:0,cpx1:0,cpy1:0,// cpx2: 0,\n// cpy2: 0\n// Curve show percent, for animating\npercent:1},style:{stroke:'#000',fill:null},buildPath:function buildPath(ctx,shape){var x1=shape.x1;var y1=shape.y1;var x2=shape.x2;var y2=shape.y2;var cpx1=shape.cpx1;var cpy1=shape.cpy1;var cpx2=shape.cpx2;var cpy2=shape.cpy2;var percent=shape.percent;if(percent===0){return;}ctx.moveTo(x1,y1);if(cpx2==null||cpy2==null){if(percent<1){quadraticSubdivide(x1,cpx1,x2,percent,out);cpx1=out[1];x2=out[2];quadraticSubdivide(y1,cpy1,y2,percent,out);cpy1=out[1];y2=out[2];}ctx.quadraticCurveTo(cpx1,cpy1,x2,y2);}else{if(percent<1){cubicSubdivide(x1,cpx1,cpx2,x2,percent,out);cpx1=out[1];cpx2=out[2];x2=out[3];cubicSubdivide(y1,cpy1,cpy2,y2,percent,out);cpy1=out[1];cpy2=out[2];y2=out[3];}ctx.bezierCurveTo(cpx1,cpy1,cpx2,cpy2,x2,y2);}},/**\n * Get point at percent\n * @param {number} t\n * @return {Array.}\n */pointAt:function pointAt(t){return someVectorAt(this.shape,t,false);},/**\n * Get tangent at percent\n * @param {number} t\n * @return {Array.}\n */tangentAt:function tangentAt(t){var p=someVectorAt(this.shape,t,true);return normalize(p,p);}});/**\n * 圆弧\n * @module zrender/graphic/shape/Arc\n */var Arc=Path.extend({type:'arc',shape:{cx:0,cy:0,r:0,startAngle:0,endAngle:Math.PI*2,clockwise:true},style:{stroke:'#000',fill:null},buildPath:function buildPath(ctx,shape){var x=shape.cx;var y=shape.cy;var r=Math.max(shape.r,0);var startAngle=shape.startAngle;var endAngle=shape.endAngle;var clockwise=shape.clockwise;var unitX=Math.cos(startAngle);var unitY=Math.sin(startAngle);ctx.moveTo(unitX*r+x,unitY*r+y);ctx.arc(x,y,r,startAngle,endAngle,!clockwise);}});// CompoundPath to improve performance\nvar CompoundPath=Path.extend({type:'compound',shape:{paths:null},_updatePathDirty:function _updatePathDirty(){var dirtyPath=this.__dirtyPath;var paths=this.shape.paths;for(var i=0;i} colorStops\n */var Gradient=function Gradient(colorStops){this.colorStops=colorStops||[];};Gradient.prototype={constructor:Gradient,addColorStop:function addColorStop(offset,color){this.colorStops.push({offset:offset,color:color});}};/**\n * x, y, x2, y2 are all percent from 0 to 1\n * @param {number} [x=0]\n * @param {number} [y=0]\n * @param {number} [x2=1]\n * @param {number} [y2=0]\n * @param {Array.} colorStops\n * @param {boolean} [globalCoord=false]\n */var LinearGradient=function LinearGradient(x,y,x2,y2,colorStops,globalCoord){// Should do nothing more in this constructor. Because gradient can be\n// declard by `color: {type: 'linear', colorStops: ...}`, where\n// this constructor will not be called.\nthis.x=x==null?0:x;this.y=y==null?0:y;this.x2=x2==null?1:x2;this.y2=y2==null?0:y2;// Can be cloned\nthis.type='linear';// If use global coord\nthis.global=globalCoord||false;Gradient.call(this,colorStops);};LinearGradient.prototype={constructor:LinearGradient};inherits(LinearGradient,Gradient);/**\n * x, y, r are all percent from 0 to 1\n * @param {number} [x=0.5]\n * @param {number} [y=0.5]\n * @param {number} [r=0.5]\n * @param {Array.} [colorStops]\n * @param {boolean} [globalCoord=false]\n */var RadialGradient=function RadialGradient(x,y,r,colorStops,globalCoord){// Should do nothing more in this constructor. Because gradient can be\n// declard by `color: {type: 'radial', colorStops: ...}`, where\n// this constructor will not be called.\nthis.x=x==null?0.5:x;this.y=y==null?0.5:y;this.r=r==null?0.5:r;// Can be cloned\nthis.type='radial';// If use global coord\nthis.global=globalCoord||false;Gradient.call(this,colorStops);};RadialGradient.prototype={constructor:RadialGradient};inherits(RadialGradient,Gradient);/**\n * Displayable for incremental rendering. It will be rendered in a separate layer\n * IncrementalDisplay have two main methods. `clearDisplayables` and `addDisplayables`\n * addDisplayables will render the added displayables incremetally.\n *\n * It use a not clearFlag to tell the painter don't clear the layer if it's the first element.\n */ // TODO Style override ?\nfunction IncrementalDisplayble(opts){Displayable.call(this,opts);this._displayables=[];this._temporaryDisplayables=[];this._cursor=0;this.notClear=true;}IncrementalDisplayble.prototype.incremental=true;IncrementalDisplayble.prototype.clearDisplaybles=function(){this._displayables=[];this._temporaryDisplayables=[];this._cursor=0;this.dirty();this.notClear=false;};IncrementalDisplayble.prototype.addDisplayable=function(displayable,notPersistent){if(notPersistent){this._temporaryDisplayables.push(displayable);}else{this._displayables.push(displayable);}this.dirty();};IncrementalDisplayble.prototype.addDisplayables=function(displayables,notPersistent){notPersistent=notPersistent||false;for(var i=0;i This method MUST be called after all of the normal styles having been adopted\n * to the `el`.\n * The input `hoverStyle` (that is, \"emphasis style\") MUST be the subset of the\n * \"normal style\" having been set to the el.\n * `color` MUST be one of the \"normal styles\" (because color might be lifted as\n * a default hover style).\n *\n * The reason: this method treat the current style of the `el` as the \"normal style\"\n * and cache them when enter/update the \"emphasis style\". Consider the case: the `el`\n * is in \"emphasis\" state and `setOption`/`dispatchAction` trigger the style updating\n * logic, where the el should shift from the original emphasis style to the new\n * \"emphasis style\" and should be able to \"downplay\" back to the new \"normal style\".\n *\n * Indeed, it is error-prone to make a interface has so many constraints, but I have\n * not found a better solution yet to fit the backward compatibility, performance and\n * the current programming style.\n *\n * (2)\n * Call the method for a \"root\" element once. Do not call it for each descendants.\n * If the descendants elemenets of a group has itself hover style different from the\n * root group, we can simply mount the style on `el.hoverStyle` for them, but should\n * not call this method for them.\n *\n * (3) These input parameters can be set directly on `el`:\n *\n * @param {module:zrender/Element} el\n * @param {Object} [el.hoverStyle] See `graphic.setElementHoverStyle`.\n * @param {boolean} [el.highDownSilentOnTouch=false] See `graphic.setAsHighDownDispatcher`.\n * @param {Function} [el.highDownOnUpdate] See `graphic.setAsHighDownDispatcher`.\n * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`.\n */function setHoverStyle(el,hoverStyle){setAsHighDownDispatcher(el,true);traverseUpdate(el,setElementHoverStyle,hoverStyle);}/**\n * @param {module:zrender/Element} el\n * @param {Function} [el.highDownOnUpdate] Called when state updated.\n * Since `setHoverStyle` has the constraint that it must be called after\n * all of the normal style updated, `highDownOnUpdate` is not needed to\n * trigger if both `fromState` and `toState` is 'normal', and needed to\n * trigger if both `fromState` and `toState` is 'emphasis', which enables\n * to sync outside style settings to \"emphasis\" state.\n * @this {string} This dispatcher `el`.\n * @param {string} fromState Can be \"normal\" or \"emphasis\".\n * `fromState` might equal to `toState`,\n * for example, when this method is called when `el` is\n * on \"emphasis\" state.\n * @param {string} toState Can be \"normal\" or \"emphasis\".\n *\n * FIXME\n * CAUTION: Do not expose `highDownOnUpdate` outside echarts.\n * Because it is not a complete solution. The update\n * listener should not have been mount in element,\n * and the normal/emphasis state should not have\n * mantained on elements.\n *\n * @param {boolean} [el.highDownSilentOnTouch=false]\n * In touch device, mouseover event will be trigger on touchstart event\n * (see module:zrender/dom/HandlerProxy). By this mechanism, we can\n * conveniently use hoverStyle when tap on touch screen without additional\n * code for compatibility.\n * But if the chart/component has select feature, which usually also use\n * hoverStyle, there might be conflict between 'select-highlight' and\n * 'hover-highlight' especially when roam is enabled (see geo for example).\n * In this case, `highDownSilentOnTouch` should be used to disable\n * hover-highlight on touch device.\n * @param {boolean} [asDispatcher=true] If `false`, do not set as \"highDownDispatcher\".\n */function setAsHighDownDispatcher(el,asDispatcher){var disable=asDispatcher===false;// Make `highDownSilentOnTouch` and `highDownOnUpdate` only work after\n// `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.\nel.__highDownSilentOnTouch=el.highDownSilentOnTouch;el.__highDownOnUpdate=el.highDownOnUpdate;// Simple optimize, since this method might be\n// called for each elements of a group in some cases.\nif(!disable||el.__highDownDispatcher){var method=disable?'off':'on';// Duplicated function will be auto-ignored, see Eventful.js.\nel[method]('mouseover',onElementMouseOver)[method]('mouseout',onElementMouseOut);// Emphasis, normal can be triggered manually by API or other components like hover link.\nel[method]('emphasis',onElementEmphasisEvent)[method]('normal',onElementNormalEvent);// Also keep previous record.\nel.__highByOuter=el.__highByOuter||0;el.__highDownDispatcher=!disable;}}/**\n * @param {module:zrender/src/Element} el\n * @return {boolean}\n */function isHighDownDispatcher(el){return!!(el&&el.__highDownDispatcher);}/**\n * Support hightlight/downplay record on each elements.\n * For the case: hover highlight/downplay (legend, visualMap, ...) and\n * user triggerred hightlight/downplay should not conflict.\n * Only all of the highlightDigit cleared, return to normal.\n * @param {string} highlightKey\n * @return {number} highlightDigit\n */function getHighlightDigit(highlightKey){var highlightDigit=_highlightKeyMap[highlightKey];if(highlightDigit==null&&_highlightNextDigit<=32){highlightDigit=_highlightKeyMap[highlightKey]=_highlightNextDigit++;}return highlightDigit;}/**\n * See more info in `setTextStyleCommon`.\n * @param {Object|module:zrender/graphic/Style} normalStyle\n * @param {Object} emphasisStyle\n * @param {module:echarts/model/Model} normalModel\n * @param {module:echarts/model/Model} emphasisModel\n * @param {Object} opt Check `opt` of `setTextStyleCommon` to find other props.\n * @param {string|Function} [opt.defaultText]\n * @param {module:echarts/model/Model} [opt.labelFetcher] Fetch text by\n * `opt.labelFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`\n * @param {number} [opt.labelDataIndex] Fetch text by\n * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`\n * @param {number} [opt.labelDimIndex] Fetch text by\n * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`\n * @param {string} [opt.labelProp] Fetch text by\n * `opt.textFetcher.getFormattedLabel(opt.labelDataIndex, 'normal'/'emphasis', null, opt.labelDimIndex, opt.labelProp)`\n * @param {Object} [normalSpecified]\n * @param {Object} [emphasisSpecified]\n */function setLabelStyle(normalStyle,emphasisStyle,normalModel,emphasisModel,opt,normalSpecified,emphasisSpecified){opt=opt||EMPTY_OBJ;var labelFetcher=opt.labelFetcher;var labelDataIndex=opt.labelDataIndex;var labelDimIndex=opt.labelDimIndex;var labelProp=opt.labelProp;// This scenario, `label.normal.show = true; label.emphasis.show = false`,\n// is not supported util someone requests.\nvar showNormal=normalModel.getShallow('show');var showEmphasis=emphasisModel.getShallow('show');// Consider performance, only fetch label when necessary.\n// If `normal.show` is `false` and `emphasis.show` is `true` and `emphasis.formatter` is not set,\n// label should be displayed, where text is fetched by `normal.formatter` or `opt.defaultText`.\nvar baseText;if(showNormal||showEmphasis){if(labelFetcher){baseText=labelFetcher.getFormattedLabel(labelDataIndex,'normal',null,labelDimIndex,labelProp);}if(baseText==null){baseText=isFunction$1(opt.defaultText)?opt.defaultText(labelDataIndex,opt):opt.defaultText;}}var normalStyleText=showNormal?baseText:null;var emphasisStyleText=showEmphasis?retrieve2(labelFetcher?labelFetcher.getFormattedLabel(labelDataIndex,'emphasis',null,labelDimIndex,labelProp):null,baseText):null;// Optimize: If style.text is null, text will not be drawn.\nif(normalStyleText!=null||emphasisStyleText!=null){// Always set `textStyle` even if `normalStyle.text` is null, because default\n// values have to be set on `normalStyle`.\n// If we set default values on `emphasisStyle`, consider case:\n// Firstly, `setOption(... label: {normal: {text: null}, emphasis: {show: true}} ...);`\n// Secondly, `setOption(... label: {noraml: {show: true, text: 'abc', color: 'red'} ...);`\n// Then the 'red' will not work on emphasis.\nsetTextStyle(normalStyle,normalModel,normalSpecified,opt);setTextStyle(emphasisStyle,emphasisModel,emphasisSpecified,opt,true);}normalStyle.text=normalStyleText;emphasisStyle.text=emphasisStyleText;}/**\n * Modify label style manually.\n * Only works after `setLabelStyle` and `setElementHoverStyle` called.\n *\n * @param {module:zrender/src/Element} el\n * @param {Object} [normalStyleProps] optional\n * @param {Object} [emphasisStyleProps] optional\n */function modifyLabelStyle(el,normalStyleProps,emphasisStyleProps){var elStyle=el.style;if(normalStyleProps){rollbackDefaultTextStyle(elStyle);el.setStyle(normalStyleProps);applyDefaultTextStyle(elStyle);}elStyle=el.__hoverStl;if(emphasisStyleProps&&elStyle){rollbackDefaultTextStyle(elStyle);extend(elStyle,emphasisStyleProps);applyDefaultTextStyle(elStyle);}}/**\n * Set basic textStyle properties.\n * See more info in `setTextStyleCommon`.\n * @param {Object|module:zrender/graphic/Style} textStyle\n * @param {module:echarts/model/Model} model\n * @param {Object} [specifiedTextStyle] Can be overrided by settings in model.\n * @param {Object} [opt] See `opt` of `setTextStyleCommon`.\n * @param {boolean} [isEmphasis]\n */function setTextStyle(textStyle,textStyleModel,specifiedTextStyle,opt,isEmphasis){setTextStyleCommon(textStyle,textStyleModel,opt,isEmphasis);specifiedTextStyle&&extend(textStyle,specifiedTextStyle);// textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\nreturn textStyle;}/**\n * Set text option in the style.\n * See more info in `setTextStyleCommon`.\n * @deprecated\n * @param {Object} textStyle\n * @param {module:echarts/model/Model} labelModel\n * @param {string|boolean} defaultColor Default text color.\n * If set as false, it will be processed as a emphasis style.\n */function setText(textStyle,labelModel,defaultColor){var opt={isRectText:true};var isEmphasis;if(defaultColor===false){isEmphasis=true;}else{// Support setting color as 'auto' to get visual color.\nopt.autoColor=defaultColor;}setTextStyleCommon(textStyle,labelModel,opt,isEmphasis);// textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false);\n}/**\n * The uniform entry of set text style, that is, retrieve style definitions\n * from `model` and set to `textStyle` object.\n *\n * Never in merge mode, but in overwrite mode, that is, all of the text style\n * properties will be set. (Consider the states of normal and emphasis and\n * default value can be adopted, merge would make the logic too complicated\n * to manage.)\n *\n * The `textStyle` object can either be a plain object or an instance of\n * `zrender/src/graphic/Style`, and either be the style of normal or emphasis.\n * After this mothod called, the `textStyle` object can then be used in\n * `el.setStyle(textStyle)` or `el.hoverStyle = textStyle`.\n *\n * Default value will be adopted and `insideRollbackOpt` will be created.\n * See `applyDefaultTextStyle` `rollbackDefaultTextStyle` for more details.\n *\n * opt: {\n * disableBox: boolean, Whether diable drawing box of block (outer most).\n * isRectText: boolean,\n * autoColor: string, specify a color when color is 'auto',\n * for textFill, textStroke, textBackgroundColor, and textBorderColor.\n * If autoColor specified, it is used as default textFill.\n * useInsideStyle:\n * `true`: Use inside style (textFill, textStroke, textStrokeWidth)\n * if `textFill` is not specified.\n * `false`: Do not use inside style.\n * `null/undefined`: use inside style if `isRectText` is true and\n * `textFill` is not specified and textPosition contains `'inside'`.\n * forceRich: boolean\n * }\n */function setTextStyleCommon(textStyle,textStyleModel,opt,isEmphasis){// Consider there will be abnormal when merge hover style to normal style if given default value.\nopt=opt||EMPTY_OBJ;if(opt.isRectText){var textPosition;if(opt.getTextPosition){textPosition=opt.getTextPosition(textStyleModel,isEmphasis);}else{textPosition=textStyleModel.getShallow('position')||(isEmphasis?null:'inside');// 'outside' is not a valid zr textPostion value, but used\n// in bar series, and magric type should be considered.\ntextPosition==='outside'&&(textPosition='top');}textStyle.textPosition=textPosition;textStyle.textOffset=textStyleModel.getShallow('offset');var labelRotate=textStyleModel.getShallow('rotate');labelRotate!=null&&(labelRotate*=Math.PI/180);textStyle.textRotation=labelRotate;textStyle.textDistance=retrieve2(textStyleModel.getShallow('distance'),isEmphasis?null:5);}var ecModel=textStyleModel.ecModel;var globalTextStyle=ecModel&&ecModel.option.textStyle;// Consider case:\n// {\n// data: [{\n// value: 12,\n// label: {\n// rich: {\n// // no 'a' here but using parent 'a'.\n// }\n// }\n// }],\n// rich: {\n// a: { ... }\n// }\n// }\nvar richItemNames=getRichItemNames(textStyleModel);var richResult;if(richItemNames){richResult={};for(var name in richItemNames){if(richItemNames.hasOwnProperty(name)){// Cascade is supported in rich.\nvar richTextStyle=textStyleModel.getModel(['rich',name]);// In rich, never `disableBox`.\n// FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`,\n// the default color `'blue'` will not be adopted if no color declared in `rich`.\n// That might confuses users. So probably we should put `textStyleModel` as the\n// root ancestor of the `richTextStyle`. But that would be a break change.\nsetTokenTextStyle(richResult[name]={},richTextStyle,globalTextStyle,opt,isEmphasis);}}}textStyle.rich=richResult;setTokenTextStyle(textStyle,textStyleModel,globalTextStyle,opt,isEmphasis,true);if(opt.forceRich&&!opt.textStyle){opt.textStyle={};}return textStyle;}// Consider case:\n// {\n// data: [{\n// value: 12,\n// label: {\n// rich: {\n// // no 'a' here but using parent 'a'.\n// }\n// }\n// }],\n// rich: {\n// a: { ... }\n// }\n// }\nfunction getRichItemNames(textStyleModel){// Use object to remove duplicated names.\nvar richItemNameMap;while(textStyleModel&&textStyleModel!==textStyleModel.ecModel){var rich=(textStyleModel.option||EMPTY_OBJ).rich;if(rich){richItemNameMap=richItemNameMap||{};for(var name in rich){if(rich.hasOwnProperty(name)){richItemNameMap[name]=1;}}}textStyleModel=textStyleModel.parentModel;}return richItemNameMap;}function setTokenTextStyle(textStyle,textStyleModel,globalTextStyle,opt,isEmphasis,isBlock){// In merge mode, default value should not be given.\nglobalTextStyle=!isEmphasis&&globalTextStyle||EMPTY_OBJ;textStyle.textFill=getAutoColor(textStyleModel.getShallow('color'),opt)||globalTextStyle.color;textStyle.textStroke=getAutoColor(textStyleModel.getShallow('textBorderColor'),opt)||globalTextStyle.textBorderColor;textStyle.textStrokeWidth=retrieve2(textStyleModel.getShallow('textBorderWidth'),globalTextStyle.textBorderWidth);if(!isEmphasis){if(isBlock){textStyle.insideRollbackOpt=opt;applyDefaultTextStyle(textStyle);}// Set default finally.\nif(textStyle.textFill==null){textStyle.textFill=opt.autoColor;}}// Do not use `getFont` here, because merge should be supported, where\n// part of these properties may be changed in emphasis style, and the\n// others should remain their original value got from normal style.\ntextStyle.fontStyle=textStyleModel.getShallow('fontStyle')||globalTextStyle.fontStyle;textStyle.fontWeight=textStyleModel.getShallow('fontWeight')||globalTextStyle.fontWeight;textStyle.fontSize=textStyleModel.getShallow('fontSize')||globalTextStyle.fontSize;textStyle.fontFamily=textStyleModel.getShallow('fontFamily')||globalTextStyle.fontFamily;textStyle.textAlign=textStyleModel.getShallow('align');textStyle.textVerticalAlign=textStyleModel.getShallow('verticalAlign')||textStyleModel.getShallow('baseline');textStyle.textLineHeight=textStyleModel.getShallow('lineHeight');textStyle.textWidth=textStyleModel.getShallow('width');textStyle.textHeight=textStyleModel.getShallow('height');textStyle.textTag=textStyleModel.getShallow('tag');if(!isBlock||!opt.disableBox){textStyle.textBackgroundColor=getAutoColor(textStyleModel.getShallow('backgroundColor'),opt);textStyle.textPadding=textStyleModel.getShallow('padding');textStyle.textBorderColor=getAutoColor(textStyleModel.getShallow('borderColor'),opt);textStyle.textBorderWidth=textStyleModel.getShallow('borderWidth');textStyle.textBorderRadius=textStyleModel.getShallow('borderRadius');textStyle.textBoxShadowColor=textStyleModel.getShallow('shadowColor');textStyle.textBoxShadowBlur=textStyleModel.getShallow('shadowBlur');textStyle.textBoxShadowOffsetX=textStyleModel.getShallow('shadowOffsetX');textStyle.textBoxShadowOffsetY=textStyleModel.getShallow('shadowOffsetY');}textStyle.textShadowColor=textStyleModel.getShallow('textShadowColor')||globalTextStyle.textShadowColor;textStyle.textShadowBlur=textStyleModel.getShallow('textShadowBlur')||globalTextStyle.textShadowBlur;textStyle.textShadowOffsetX=textStyleModel.getShallow('textShadowOffsetX')||globalTextStyle.textShadowOffsetX;textStyle.textShadowOffsetY=textStyleModel.getShallow('textShadowOffsetY')||globalTextStyle.textShadowOffsetY;}function getAutoColor(color,opt){return color!=='auto'?color:opt&&opt.autoColor?opt.autoColor:null;}/**\n * Give some default value to the input `textStyle` object, based on the current settings\n * in this `textStyle` object.\n *\n * The Scenario:\n * when text position is `inside` and `textFill` is not specified, we show\n * text border by default for better view. But it should be considered that text position\n * might be changed when hovering or being emphasis, where the `insideRollback` is used to\n * restore the style.\n *\n * Usage (& NOTICE):\n * When a style object (eithor plain object or instance of `zrender/src/graphic/Style`) is\n * about to be modified on its text related properties, `rollbackDefaultTextStyle` should\n * be called before the modification and `applyDefaultTextStyle` should be called after that.\n * (For the case that all of the text related properties is reset, like `setTextStyleCommon`\n * does, `rollbackDefaultTextStyle` is not needed to be called).\n */function applyDefaultTextStyle(textStyle){var textPosition=textStyle.textPosition;var opt=textStyle.insideRollbackOpt;var insideRollback;if(opt&&textStyle.textFill==null){var autoColor=opt.autoColor;var isRectText=opt.isRectText;var useInsideStyle=opt.useInsideStyle;var useInsideStyleCache=useInsideStyle!==false&&(useInsideStyle===true||isRectText&&textPosition// textPosition can be [10, 30]\n&&typeof textPosition==='string'&&textPosition.indexOf('inside')>=0);var useAutoColorCache=!useInsideStyleCache&&autoColor!=null;// All of the props declared in `CACHED_LABEL_STYLE_PROPERTIES` are to be cached.\nif(useInsideStyleCache||useAutoColorCache){insideRollback={textFill:textStyle.textFill,textStroke:textStyle.textStroke,textStrokeWidth:textStyle.textStrokeWidth};}if(useInsideStyleCache){textStyle.textFill='#fff';// Consider text with #fff overflow its container.\nif(textStyle.textStroke==null){textStyle.textStroke=autoColor;textStyle.textStrokeWidth==null&&(textStyle.textStrokeWidth=2);}}if(useAutoColorCache){textStyle.textFill=autoColor;}}// Always set `insideRollback`, so that the previous one can be cleared.\ntextStyle.insideRollback=insideRollback;}/**\n * Consider the case: in a scatter,\n * label: {\n * normal: {position: 'inside'},\n * emphasis: {position: 'top'}\n * }\n * In the normal state, the `textFill` will be set as '#fff' for pretty view (see\n * `applyDefaultTextStyle`), but when switching to emphasis state, the `textFill`\n * should be retured to 'autoColor', but not keep '#fff'.\n */function rollbackDefaultTextStyle(style){var insideRollback=style.insideRollback;if(insideRollback){// Reset all of the props in `CACHED_LABEL_STYLE_PROPERTIES`.\nstyle.textFill=insideRollback.textFill;style.textStroke=insideRollback.textStroke;style.textStrokeWidth=insideRollback.textStrokeWidth;style.insideRollback=null;}}function _getFont(opt,ecModel){var gTextStyleModel=ecModel&&ecModel.getModel('textStyle');return trim([// FIXME in node-canvas fontWeight is before fontStyle\nopt.fontStyle||gTextStyleModel&&gTextStyleModel.getShallow('fontStyle')||'',opt.fontWeight||gTextStyleModel&&gTextStyleModel.getShallow('fontWeight')||'',(opt.fontSize||gTextStyleModel&&gTextStyleModel.getShallow('fontSize')||12)+'px',opt.fontFamily||gTextStyleModel&&gTextStyleModel.getShallow('fontFamily')||'sans-serif'].join(' '));}function animateOrSetProps(isUpdate,el,props,animatableModel,dataIndex,cb){if(typeof dataIndex==='function'){cb=dataIndex;dataIndex=null;}// Do not check 'animation' property directly here. Consider this case:\n// animation model is an `itemModel`, whose does not have `isAnimationEnabled`\n// but its parent model (`seriesModel`) does.\nvar animationEnabled=animatableModel&&animatableModel.isAnimationEnabled();if(animationEnabled){var postfix=isUpdate?'Update':'';var duration=animatableModel.getShallow('animationDuration'+postfix);var animationEasing=animatableModel.getShallow('animationEasing'+postfix);var animationDelay=animatableModel.getShallow('animationDelay'+postfix);if(typeof animationDelay==='function'){animationDelay=animationDelay(dataIndex,animatableModel.getAnimationDelayParams?animatableModel.getAnimationDelayParams(el,dataIndex):null);}if(typeof duration==='function'){duration=duration(dataIndex);}duration>0?el.animateTo(props,duration,animationDelay||0,animationEasing,cb,!!cb):(el.stopAnimation(),el.attr(props),cb&&cb());}else{el.stopAnimation();el.attr(props);cb&&cb();}}/**\n * Update graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n *\n * @param {module:zrender/Element} el\n * @param {Object} props\n * @param {module:echarts/model/Model} [animatableModel]\n * @param {number} [dataIndex]\n * @param {Function} [cb]\n * @example\n * graphic.updateProps(el, {\n * position: [100, 100]\n * }, seriesModel, dataIndex, function () { console.log('Animation done!'); });\n * // Or\n * graphic.updateProps(el, {\n * position: [100, 100]\n * }, seriesModel, function () { console.log('Animation done!'); });\n */function updateProps(el,props,animatableModel,dataIndex,cb){animateOrSetProps(true,el,props,animatableModel,dataIndex,cb);}/**\n * Init graphic element properties with or without animation according to the\n * configuration in series.\n *\n * Caution: this method will stop previous animation.\n * So do not use this method to one element twice before\n * animation starts, unless you know what you are doing.\n *\n * @param {module:zrender/Element} el\n * @param {Object} props\n * @param {module:echarts/model/Model} [animatableModel]\n * @param {number} [dataIndex]\n * @param {Function} cb\n */function initProps(el,props,animatableModel,dataIndex,cb){animateOrSetProps(false,el,props,animatableModel,dataIndex,cb);}/**\n * Get transform matrix of target (param target),\n * in coordinate of its ancestor (param ancestor)\n *\n * @param {module:zrender/mixin/Transformable} target\n * @param {module:zrender/mixin/Transformable} [ancestor]\n */function getTransform(target,ancestor){var mat=identity([]);while(target&&target!==ancestor){mul$1(mat,target.getLocalTransform(),mat);target=target.parent;}return mat;}/**\n * Apply transform to an vertex.\n * @param {Array.} target [x, y]\n * @param {Array.|TypedArray.|Object} transform Can be:\n * + Transform matrix: like [1, 0, 0, 1, 0, 0]\n * + {position, rotation, scale}, the same as `zrender/Transformable`.\n * @param {boolean=} invert Whether use invert matrix.\n * @return {Array.} [x, y]\n */function applyTransform$1(target,transform,invert$$1){if(transform&&!isArrayLike(transform)){transform=Transformable.getLocalTransform(transform);}if(invert$$1){transform=invert([],transform);}return applyTransform([],target,transform);}/**\n * @param {string} direction 'left' 'right' 'top' 'bottom'\n * @param {Array.} transform Transform matrix: like [1, 0, 0, 1, 0, 0]\n * @param {boolean=} invert Whether use invert matrix.\n * @return {string} Transformed direction. 'left' 'right' 'top' 'bottom'\n */function transformDirection(direction,transform,invert$$1){// Pick a base, ensure that transform result will not be (0, 0).\nvar hBase=transform[4]===0||transform[5]===0||transform[0]===0?1:Math.abs(2*transform[4]/transform[0]);var vBase=transform[4]===0||transform[5]===0||transform[2]===0?1:Math.abs(2*transform[4]/transform[2]);var vertex=[direction==='left'?-hBase:direction==='right'?hBase:0,direction==='top'?-vBase:direction==='bottom'?vBase:0];vertex=applyTransform$1(vertex,transform,invert$$1);return Math.abs(vertex[0])>Math.abs(vertex[1])?vertex[0]>0?'right':'left':vertex[1]>0?'bottom':'top';}/**\n * Apply group transition animation from g1 to g2.\n * If no animatableModel, no animation.\n */function groupTransition(g1,g2,animatableModel,cb){if(!g1||!g2){return;}function getElMap(g){var elMap={};g.traverse(function(el){if(!el.isGroup&&el.anid){elMap[el.anid]=el;}});return elMap;}function getAnimatableProps(el){var obj={position:clone$1(el.position),rotation:el.rotation};if(el.shape){obj.shape=extend({},el.shape);}return obj;}var elMap1=getElMap(g1);g2.traverse(function(el){if(!el.isGroup&&el.anid){var oldEl=elMap1[el.anid];if(oldEl){var newProp=getAnimatableProps(el);el.attr(getAnimatableProps(oldEl));updateProps(el,newProp,animatableModel,el.dataIndex);}// else {\n// if (el.previousProps) {\n// graphic.updateProps\n// }\n// }\n}});}/**\n * @param {Array.>} points Like: [[23, 44], [53, 66], ...]\n * @param {Object} rect {x, y, width, height}\n * @return {Array.>} A new clipped points.\n */function clipPointsByRect(points,rect){// FIXME: this way migth be incorrect when grpahic clipped by a corner.\n// and when element have border.\nreturn map(points,function(point){var x=point[0];x=mathMax$1(x,rect.x);x=mathMin$1(x,rect.x+rect.width);var y=point[1];y=mathMax$1(y,rect.y);y=mathMin$1(y,rect.y+rect.height);return[x,y];});}/**\n * @param {Object} targetRect {x, y, width, height}\n * @param {Object} rect {x, y, width, height}\n * @return {Object} A new clipped rect. If rect size are negative, return undefined.\n */function clipRectByRect(targetRect,rect){var x=mathMax$1(targetRect.x,rect.x);var x2=mathMin$1(targetRect.x+targetRect.width,rect.x+rect.width);var y=mathMax$1(targetRect.y,rect.y);var y2=mathMin$1(targetRect.y+targetRect.height,rect.y+rect.height);// If the total rect is cliped, nothing, including the border,\n// should be painted. So return undefined.\nif(x2>=x&&y2>=y){return{x:x,y:y,width:x2-x,height:y2-y};}}/**\n * @param {string} iconStr Support 'image://' or 'path://' or direct svg path.\n * @param {Object} [opt] Properties of `module:zrender/Element`, except `style`.\n * @param {Object} [rect] {x, y, width, height}\n * @return {module:zrender/Element} Icon path or image element.\n */function createIcon(iconStr,opt,rect){opt=extend({rectHover:true},opt);var style=opt.style={strokeNoScale:true};rect=rect||{x:-1,y:-1,width:2,height:2};if(iconStr){return iconStr.indexOf('image://')===0?(style.image=iconStr.slice(8),defaults(style,rect),new ZImage(opt)):makePath(iconStr.replace('path://',''),opt,rect,'center');}}/**\n * Return `true` if the given line (line `a`) and the given polygon\n * are intersect.\n * Note that we do not count colinear as intersect here because no\n * requirement for that. We could do that if required in future.\n *\n * @param {number} a1x\n * @param {number} a1y\n * @param {number} a2x\n * @param {number} a2y\n * @param {Array.>} points Points of the polygon.\n * @return {boolean}\n */function linePolygonIntersect(a1x,a1y,a2x,a2y,points){for(var i=0,p2=points[points.length-1];i1){return false;}var p=crossProduct2d(b1a1x,b1a1y,nx,ny)/nmCrossProduct;if(p<0||p>1){return false;}return true;}/**\n * Cross product of 2-dimension vector.\n */function crossProduct2d(x1,y1,x2,y2){return x1*y2-x2*y1;}function nearZero(val){return val<=1e-6&&val>=-1e-6;}// Register built-in shapes. These shapes might be overwirtten\n// by users, although we do not recommend that.\nregisterShape('circle',Circle);registerShape('sector',Sector);registerShape('ring',Ring);registerShape('polygon',Polygon);registerShape('polyline',Polyline);registerShape('rect',Rect);registerShape('line',Line);registerShape('bezierCurve',BezierCurve);registerShape('arc',Arc);var graphic=(Object.freeze||Object)({Z2_EMPHASIS_LIFT:Z2_EMPHASIS_LIFT,CACHED_LABEL_STYLE_PROPERTIES:CACHED_LABEL_STYLE_PROPERTIES,extendShape:extendShape,extendPath:extendPath,registerShape:registerShape,getShapeClass:getShapeClass,makePath:makePath,makeImage:makeImage,mergePath:mergePath,resizePath:resizePath,subPixelOptimizeLine:subPixelOptimizeLine,subPixelOptimizeRect:subPixelOptimizeRect,subPixelOptimize:subPixelOptimize,setElementHoverStyle:setElementHoverStyle,setHoverStyle:setHoverStyle,setAsHighDownDispatcher:setAsHighDownDispatcher,isHighDownDispatcher:isHighDownDispatcher,getHighlightDigit:getHighlightDigit,setLabelStyle:setLabelStyle,modifyLabelStyle:modifyLabelStyle,setTextStyle:setTextStyle,setText:setText,getFont:_getFont,updateProps:updateProps,initProps:initProps,getTransform:getTransform,applyTransform:applyTransform$1,transformDirection:transformDirection,groupTransition:groupTransition,clipPointsByRect:clipPointsByRect,clipRectByRect:clipRectByRect,createIcon:createIcon,linePolygonIntersect:linePolygonIntersect,lineLineIntersect:lineLineIntersect,Group:Group,Image:ZImage,Text:Text,Circle:Circle,Sector:Sector,Ring:Ring,Polygon:Polygon,Polyline:Polyline,Rect:Rect,Line:Line,BezierCurve:BezierCurve,Arc:Arc,IncrementalDisplayable:IncrementalDisplayble,CompoundPath:CompoundPath,LinearGradient:LinearGradient,RadialGradient:RadialGradient,BoundingRect:BoundingRect});/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var PATH_COLOR=['textStyle','color'];var textStyleMixin={/**\n * Get color property or get color from option.textStyle.color\n * @param {boolean} [isEmphasis]\n * @return {string}\n */getTextColor:function getTextColor(isEmphasis){var ecModel=this.ecModel;return this.getShallow('color')||(!isEmphasis&&ecModel?ecModel.get(PATH_COLOR):null);},/**\n * Create font string from fontStyle, fontWeight, fontSize, fontFamily\n * @return {string}\n */getFont:function getFont(){return _getFont({fontStyle:this.getShallow('fontStyle'),fontWeight:this.getShallow('fontWeight'),fontSize:this.getShallow('fontSize'),fontFamily:this.getShallow('fontFamily')},this.ecModel);},getTextRect:function getTextRect(text){return _getBoundingRect(text,this.getFont(),this.getShallow('align'),this.getShallow('verticalAlign')||this.getShallow('baseline'),this.getShallow('padding'),this.getShallow('lineHeight'),this.getShallow('rich'),this.getShallow('truncateText'));}};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var _getItemStyle=makeStyleMapper([['fill','color'],['stroke','borderColor'],['lineWidth','borderWidth'],['opacity'],['shadowBlur'],['shadowOffsetX'],['shadowOffsetY'],['shadowColor'],['textPosition'],['textAlign']]);var itemStyleMixin={getItemStyle:function getItemStyle(excludes,includes){var style=_getItemStyle(this,excludes,includes);var lineDash=this.getBorderLineDash();lineDash&&(style.lineDash=lineDash);return style;},getBorderLineDash:function getBorderLineDash(){var lineType=this.get('borderType');return lineType==='solid'||lineType==null?null:lineType==='dashed'?[5,5]:[1,1];}};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ /**\n * @module echarts/model/Model\n */var mixin$1=mixin;var inner=makeInner();/**\n * @alias module:echarts/model/Model\n * @constructor\n * @param {Object} [option]\n * @param {module:echarts/model/Model} [parentModel]\n * @param {module:echarts/model/Global} [ecModel]\n */function Model(option,parentModel,ecModel){/**\n * @type {module:echarts/model/Model}\n * @readOnly\n */this.parentModel=parentModel;/**\n * @type {module:echarts/model/Global}\n * @readOnly\n */this.ecModel=ecModel;/**\n * @type {Object}\n * @protected\n */this.option=option;// Simple optimization\n// if (this.init) {\n// if (arguments.length <= 4) {\n// this.init(option, parentModel, ecModel, extraOpt);\n// }\n// else {\n// this.init.apply(this, arguments);\n// }\n// }\n}Model.prototype={constructor:Model,/**\n * Model 的初始化函数\n * @param {Object} option\n */init:null,/**\n * 从新的 Option merge\n */mergeOption:function mergeOption(option){merge(this.option,option,true);},/**\n * @param {string|Array.} path\n * @param {boolean} [ignoreParent=false]\n * @return {*}\n */get:function get(path,ignoreParent){if(path==null){return this.option;}return doGet(this.option,this.parsePath(path),!ignoreParent&&getParent(this,path));},/**\n * @param {string} key\n * @param {boolean} [ignoreParent=false]\n * @return {*}\n */getShallow:function getShallow(key,ignoreParent){var option=this.option;var val=option==null?option:option[key];var parentModel=!ignoreParent&&getParent(this,key);if(val==null&&parentModel){val=parentModel.getShallow(key);}return val;},/**\n * @param {string|Array.} [path]\n * @param {module:echarts/model/Model} [parentModel]\n * @return {module:echarts/model/Model}\n */getModel:function getModel(path,parentModel){var obj=path==null?this.option:doGet(this.option,path=this.parsePath(path));var thisParentModel;parentModel=parentModel||(thisParentModel=getParent(this,path))&&thisParentModel.getModel(path);return new Model(obj,parentModel,this.ecModel);},/**\n * If model has option\n */isEmpty:function isEmpty(){return this.option==null;},restoreData:function restoreData(){},// Pending\nclone:function clone(){var Ctor=this.constructor;return new Ctor(_clone(this.option));},setReadOnly:function setReadOnly(properties){// clazzUtil.setReadOnly(this, properties);\n},// If path is null/undefined, return null/undefined.\nparsePath:function parsePath(path){if(typeof path==='string'){path=path.split('.');}return path;},/**\n * @param {Function} getParentMethod\n * param {Array.|string} path\n * return {module:echarts/model/Model}\n */customizeGetParent:function customizeGetParent(getParentMethod){inner(this).getParent=getParentMethod;},isAnimationEnabled:function isAnimationEnabled(){if(!env$1.node){if(this.option.animation!=null){return!!this.option.animation;}else if(this.parentModel){return this.parentModel.isAnimationEnabled();}}}};function doGet(obj,pathArr,parentModel){for(var i=0;i} targetNameList Target Component type list.\n * Can be ['aa', 'bb', 'aa.xx']\n * @param {Array.} fullNameList By which we can build dependency graph.\n * @param {Function} callback Params: componentType, dependencies.\n * @param {Object} context Scope of callback.\n */entity.topologicalTravel=function(targetNameList,fullNameList,callback,context){if(!targetNameList.length){return;}var result=makeDepndencyGraph(fullNameList);var graph=result.graph;var stack=result.noEntryList;var targetNameSet={};each$1(targetNameList,function(name){targetNameSet[name]=true;});while(stack.length){var currComponentType=stack.pop();var currVertex=graph[currComponentType];var isInTargetNameSet=!!targetNameSet[currComponentType];if(isInTargetNameSet){callback.call(context,currComponentType,currVertex.originalDeps.slice());delete targetNameSet[currComponentType];}each$1(currVertex.successor,isInTargetNameSet?removeEdgeAndAdd:removeEdge);}each$1(targetNameSet,function(){throw new Error('Circle dependency may exists');});function removeEdge(succComponentType){graph[succComponentType].entryCount--;if(graph[succComponentType].entryCount===0){stack.push(succComponentType);}}// Consider this case: legend depends on series, and we call\n// chart.setOption({series: [...]}), where only series is in option.\n// If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will\n// not be called, but only sereis.mergeOption is called. Thus legend\n// have no chance to update its local record about series (like which\n// name of series is available in legend).\nfunction removeEdgeAndAdd(succComponentType){targetNameSet[succComponentType]=true;removeEdge(succComponentType);}};/**\n * DepndencyGraph: {Object}\n * key: conponentType,\n * value: {\n * successor: [conponentTypes...],\n * originalDeps: [conponentTypes...],\n * entryCount: {number}\n * }\n */function makeDepndencyGraph(fullNameList){var graph={};var noEntryList=[];each$1(fullNameList,function(name){var thisItem=createDependencyGraphItem(graph,name);var originalDeps=thisItem.originalDeps=dependencyGetter(name);var availableDeps=getAvailableDependencies(originalDeps,fullNameList);thisItem.entryCount=availableDeps.length;if(thisItem.entryCount===0){noEntryList.push(name);}each$1(availableDeps,function(dependentName){if(indexOf(thisItem.predecessor,dependentName)<0){thisItem.predecessor.push(dependentName);}var thatItem=createDependencyGraphItem(graph,dependentName);if(indexOf(thatItem.successor,dependentName)<0){thatItem.successor.push(name);}});});return{graph:graph,noEntryList:noEntryList};}function createDependencyGraphItem(graph,name){if(!graph[name]){graph[name]={predecessor:[],successor:[]};}return graph[name];}function getAvailableDependencies(originalDeps,fullNameList){var availableDeps=[];each$1(originalDeps,function(dep){indexOf(fullNameList,dep)>=0&&availableDeps.push(dep);});return availableDeps;}}/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ /*\n* A third-party license is embeded for some of the code in this file:\n* The method \"quantile\" was copied from \"d3.js\".\n* (See more details in the comment of the method below.)\n* The use of the source code of this file is also subject to the terms\n* and consitions of the license of \"d3.js\" (BSD-3Clause, see\n* ).\n*/var RADIAN_EPSILON=1e-4;function _trim(str){return str.replace(/^\\s+|\\s+$/g,'');}/**\n * Linear mapping a value from domain to range\n * @memberOf module:echarts/util/number\n * @param {(number|Array.)} val\n * @param {Array.} domain Domain extent domain[0] can be bigger than domain[1]\n * @param {Array.} range Range extent range[0] can be bigger than range[1]\n * @param {boolean} clamp\n * @return {(number|Array.}\n */function linearMap(val,domain,range,clamp){var subDomain=domain[1]-domain[0];var subRange=range[1]-range[0];if(subDomain===0){return subRange===0?range[0]:(range[0]+range[1])/2;}// Avoid accuracy problem in edge, such as\n// 146.39 - 62.83 === 83.55999999999999.\n// See echarts/test/ut/spec/util/number.js#linearMap#accuracyError\n// It is a little verbose for efficiency considering this method\n// is a hotspot.\nif(clamp){if(subDomain>0){if(val<=domain[0]){return range[0];}else if(val>=domain[1]){return range[1];}}else{if(val>=domain[0]){return range[0];}else if(val<=domain[1]){return range[1];}}}else{if(val===domain[0]){return range[0];}if(val===domain[1]){return range[1];}}return(val-domain[0])/subDomain*subRange+range[0];}/**\n * Convert a percent string to absolute number.\n * Returns NaN if percent is not a valid string or number\n * @memberOf module:echarts/util/number\n * @param {string|number} percent\n * @param {number} all\n * @return {number}\n */function parsePercent$1(percent,all){switch(percent){case'center':case'middle':percent='50%';break;case'left':case'top':percent='0%';break;case'right':case'bottom':percent='100%';break;}if(typeof percent==='string'){if(_trim(percent).match(/%$/)){return parseFloat(percent)/100*all;}return parseFloat(percent);}return percent==null?NaN:+percent;}/**\n * (1) Fix rounding error of float numbers.\n * (2) Support return string to avoid scientific notation like '3.5e-7'.\n *\n * @param {number} x\n * @param {number} [precision]\n * @param {boolean} [returnStr]\n * @return {number|string}\n */function round$1(x,precision,returnStr){if(precision==null){precision=10;}// Avoid range error\nprecision=Math.min(Math.max(0,precision),20);x=(+x).toFixed(precision);return returnStr?x:+x;}/**\n * asc sort arr.\n * The input arr will be modified.\n *\n * @param {Array} arr\n * @return {Array} The input arr.\n */function asc(arr){arr.sort(function(a,b){return a-b;});return arr;}/**\n * Get precision\n * @param {number} val\n */function getPrecision(val){val=+val;if(isNaN(val)){return 0;}// It is much faster than methods converting number to string as follows\n// var tmp = val.toString();\n// return tmp.length - 1 - tmp.indexOf('.');\n// especially when precision is low\nvar e=1;var count=0;while(Math.round(val*e)/e!==val){e*=10;count++;}return count;}/**\n * @param {string|number} val\n * @return {number}\n */function getPrecisionSafe(val){var str=val.toString();// Consider scientific notation: '3.4e-12' '3.4e+12'\nvar eIndex=str.indexOf('e');if(eIndex>0){var precision=+str.slice(eIndex+1);return precision<0?-precision:0;}else{var dotIndex=str.indexOf('.');return dotIndex<0?0:str.length-1-dotIndex;}}/**\n * Minimal dicernible data precisioin according to a single pixel.\n *\n * @param {Array.} dataExtent\n * @param {Array.} pixelExtent\n * @return {number} precision\n */function _getPixelPrecision(dataExtent,pixelExtent){var log=Math.log;var LN10=Math.LN10;var dataQuantity=Math.floor(log(dataExtent[1]-dataExtent[0])/LN10);var sizeQuantity=Math.round(log(Math.abs(pixelExtent[1]-pixelExtent[0]))/LN10);// toFixed() digits argument must be between 0 and 20.\nvar precision=Math.min(Math.max(-dataQuantity+sizeQuantity,0),20);return!isFinite(precision)?20:precision;}/**\n * Get a data of given precision, assuring the sum of percentages\n * in valueList is 1.\n * The largest remainer method is used.\n * https://en.wikipedia.org/wiki/Largest_remainder_method\n *\n * @param {Array.} valueList a list of all data\n * @param {number} idx index of the data to be processed in valueList\n * @param {number} precision integer number showing digits of precision\n * @return {number} percent ranging from 0 to 100\n */function getPercentWithPrecision(valueList,idx,precision){if(!valueList[idx]){return 0;}var sum=reduce(valueList,function(acc,val){return acc+(isNaN(val)?0:val);},0);if(sum===0){return 0;}var digits=Math.pow(10,precision);var votesPerQuota=map(valueList,function(val){return(isNaN(val)?0:val)/sum*digits*100;});var targetSeats=digits*100;var seats=map(votesPerQuota,function(votes){// Assign automatic seats.\nreturn Math.floor(votes);});var currentSum=reduce(seats,function(acc,val){return acc+val;},0);var remainder=map(votesPerQuota,function(votes,idx){return votes-seats[idx];});// Has remainding votes.\nwhile(currentSummax){max=remainder[i];maxId=i;}}// Add a vote to max remainder.\n++seats[maxId];remainder[maxId]=0;++currentSum;}return seats[idx]/digits;}// Number.MAX_SAFE_INTEGER, ie do not support.\nvar MAX_SAFE_INTEGER=9007199254740991;/**\n * To 0 - 2 * PI, considering negative radian.\n * @param {number} radian\n * @return {number}\n */function remRadian(radian){var pi2=Math.PI*2;return(radian%pi2+pi2)%pi2;}/**\n * @param {type} radian\n * @return {boolean}\n */function isRadianAroundZero(val){return val>-RADIAN_EPSILON&&val ).\n * + Or other string format, including (all of which will be treated as loacal time):\n * '2012', '2012-3-1', '2012/3/1', '2012/03/01',\n * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123'\n * + a timestamp, which represent a time in UTC.\n * @return {Date} date\n */function parseDate(value){if(value instanceof Date){return value;}else if(typeof value==='string'){// Different browsers parse date in different way, so we parse it manually.\n// Some other issues:\n// new Date('1970-01-01') is UTC,\n// new Date('1970/01/01') and new Date('1970-1-01') is local.\n// See issue #3623\nvar match=TIME_REG.exec(value);if(!match){// return Invalid Date.\nreturn new Date(NaN);}// Use local time when no timezone offset specifed.\nif(!match[8]){// match[n] can only be string or undefined.\n// But take care of '12' + 1 => '121'.\nreturn new Date(+match[1],+(match[2]||1)-1,+match[3]||1,+match[4]||0,+(match[5]||0),+match[6]||0,+match[7]||0);}// Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time,\n// https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment).\n// For example, system timezone is set as \"Time Zone: America/Toronto\",\n// then these code will get different result:\n// `new Date(1478411999999).getTimezoneOffset(); // get 240`\n// `new Date(1478412000000).getTimezoneOffset(); // get 300`\n// So we should not use `new Date`, but use `Date.UTC`.\nelse{var hour=+match[4]||0;if(match[8].toUpperCase()!=='Z'){hour-=match[8].slice(0,3);}return new Date(Date.UTC(+match[1],+(match[2]||1)-1,+match[3]||1,hour,+(match[5]||0),+match[6]||0,+match[7]||0));}}else if(value==null){return new Date(NaN);}return new Date(Math.round(value));}/**\n * Quantity of a number. e.g. 0.1, 1, 10, 100\n *\n * @param {number} val\n * @return {number}\n */function quantity(val){return Math.pow(10,quantityExponent(val));}/**\n * Exponent of the quantity of a number\n * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3\n *\n * @param {number} val non-negative value\n * @return {number}\n */function quantityExponent(val){if(val===0){return 0;}var exp=Math.floor(Math.log(val)/Math.LN10);/**\n * exp is expected to be the rounded-down result of the base-10 log of val.\n * But due to the precision loss with Math.log(val), we need to restore it\n * using 10^exp to make sure we can get val back from exp. #11249\n */if(val/Math.pow(10,exp)>=10){exp++;}return exp;}/**\n * find a “nice” number approximately equal to x. Round the number if round = true,\n * take ceiling if round = false. The primary observation is that the “nicest”\n * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers.\n *\n * See \"Nice Numbers for Graph Labels\" of Graphic Gems.\n *\n * @param {number} val Non-negative value.\n * @param {boolean} round\n * @return {number}\n */function nice(val,round){var exponent=quantityExponent(val);var exp10=Math.pow(10,exponent);var f=val/exp10;// 1 <= f < 10\nvar nf;if(round){if(f<1.5){nf=1;}else if(f<2.5){nf=2;}else if(f<4){nf=3;}else if(f<7){nf=5;}else{nf=10;}}else{if(f<1){nf=1;}else if(f<2){nf=2;}else if(f<3){nf=3;}else if(f<5){nf=5;}else{nf=10;}}val=nf*exp10;// Fix 3 * 0.1 === 0.30000000000000004 issue (see IEEE 754).\n// 20 is the uppper bound of toFixed.\nreturn exponent>=-20?+val.toFixed(exponent<0?-exponent:0):val;}/**\n * This code was copied from \"d3.js\"\n * .\n * See the license statement at the head of this file.\n * @param {Array.} ascArr\n */function quantile(ascArr,p){var H=(ascArr.length-1)*p+1;var h=Math.floor(H);var v=+ascArr[h-1];var e=H-h;return e?v+e*(ascArr[h]-v):v;}/**\n * Order intervals asc, and split them when overlap.\n * expect(numberUtil.reformIntervals([\n * {interval: [18, 62], close: [1, 1]},\n * {interval: [-Infinity, -70], close: [0, 0]},\n * {interval: [-70, -26], close: [1, 1]},\n * {interval: [-26, 18], close: [1, 1]},\n * {interval: [62, 150], close: [1, 1]},\n * {interval: [106, 150], close: [1, 1]},\n * {interval: [150, Infinity], close: [0, 0]}\n * ])).toEqual([\n * {interval: [-Infinity, -70], close: [0, 0]},\n * {interval: [-70, -26], close: [1, 1]},\n * {interval: [-26, 18], close: [0, 1]},\n * {interval: [18, 62], close: [0, 1]},\n * {interval: [62, 150], close: [0, 1]},\n * {interval: [150, Infinity], close: [0, 0]}\n * ]);\n * @param {Array.} list, where `close` mean open or close\n * of the interval, and Infinity can be used.\n * @return {Array.} The origin list, which has been reformed.\n */function reformIntervals(list){list.sort(function(a,b){return littleThan(a,b,0)?-1:1;});var curr=-Infinity;var currClose=1;for(var i=0;i=0;}var number=(Object.freeze||Object)({linearMap:linearMap,parsePercent:parsePercent$1,round:round$1,asc:asc,getPrecision:getPrecision,getPrecisionSafe:getPrecisionSafe,getPixelPrecision:_getPixelPrecision,getPercentWithPrecision:getPercentWithPrecision,MAX_SAFE_INTEGER:MAX_SAFE_INTEGER,remRadian:remRadian,isRadianAroundZero:isRadianAroundZero,parseDate:parseDate,quantity:quantity,quantityExponent:quantityExponent,nice:nice,quantile:quantile,reformIntervals:reformIntervals,isNumeric:isNumeric});/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // import Text from 'zrender/src/graphic/Text';\n/**\n * add commas after every three numbers\n * @param {string|number} x\n * @return {string}\n */function addCommas(x){if(isNaN(x)){return'-';}x=(x+'').split('.');return x[0].replace(/(\\d{1,3})(?=(?:\\d{3})+(?!\\d))/g,'$1,')+(x.length>1?'.'+x[1]:'');}/**\n * @param {string} str\n * @param {boolean} [upperCaseFirst=false]\n * @return {string} str\n */function toCamelCase(str,upperCaseFirst){str=(str||'').toLowerCase().replace(/-(.)/g,function(match,group1){return group1.toUpperCase();});if(upperCaseFirst&&str){str=str.charAt(0).toUpperCase()+str.slice(1);}return str;}var normalizeCssArray$1=normalizeCssArray;var replaceReg=/([&<>\"'])/g;var replaceMap={'&':'&','<':'<','>':'>','\"':'"','\\'':'''};function encodeHTML(source){return source==null?'':(source+'').replace(replaceReg,function(str,c){return replaceMap[c];});}var TPL_VAR_ALIAS=['a','b','c','d','e','f','g'];var wrapVar=function wrapVar(varName,seriesIdx){return'{'+varName+(seriesIdx==null?'':seriesIdx)+'}';};/**\n * Template formatter\n * @param {string} tpl\n * @param {Array.|Object} paramsList\n * @param {boolean} [encode=false]\n * @return {string}\n */function formatTpl(tpl,paramsList,encode){if(!isArray(paramsList)){paramsList=[paramsList];}var seriesLen=paramsList.length;if(!seriesLen){return'';}var $vars=paramsList[0].$vars||[];for(var i=0;i<$vars.length;i++){var alias=TPL_VAR_ALIAS[i];tpl=tpl.replace(wrapVar(alias),wrapVar(alias,0));}for(var seriesIdx=0;seriesIdx':' ';}else{// Space for rich element marker\nreturn{renderMode:renderMode,content:'{marker'+markerId+'|} ',style:{color:color}};}}function pad(str,len){str+='';return'0000'.substr(0,len-str.length)+str;}/**\n * ISO Date format\n * @param {string} tpl\n * @param {number} value\n * @param {boolean} [isUTC=false] Default in local time.\n * see `module:echarts/scale/Time`\n * and `module:echarts/util/number#parseDate`.\n * @inner\n */function formatTime(tpl,value,isUTC){if(tpl==='week'||tpl==='month'||tpl==='quarter'||tpl==='half-year'||tpl==='year'){tpl='MM-dd\\nyyyy';}var date=parseDate(value);var utc=isUTC?'UTC':'';var y=date['get'+utc+'FullYear']();var M=date['get'+utc+'Month']()+1;var d=date['get'+utc+'Date']();var h=date['get'+utc+'Hours']();var m=date['get'+utc+'Minutes']();var s=date['get'+utc+'Seconds']();var S=date['get'+utc+'Milliseconds']();tpl=tpl.replace('MM',pad(M,2)).replace('M',M).replace('yyyy',y).replace('yy',y%100).replace('dd',pad(d,2)).replace('d',d).replace('hh',pad(h,2)).replace('h',h).replace('mm',pad(m,2)).replace('m',m).replace('ss',pad(s,2)).replace('s',s).replace('SSS',pad(S,3));return tpl;}/**\n * Capital first\n * @param {string} str\n * @return {string}\n */function capitalFirst(str){return str?str.charAt(0).toUpperCase()+str.substr(1):str;}var truncateText$1=truncateText;/**\n * @public\n * @param {Object} opt\n * @param {string} opt.text\n * @param {string} opt.font\n * @param {string} [opt.textAlign='left']\n * @param {string} [opt.textVerticalAlign='top']\n * @param {Array.} [opt.textPadding]\n * @param {number} [opt.textLineHeight]\n * @param {Object} [opt.rich]\n * @param {Object} [opt.truncate]\n * @return {Object} {x, y, width, height, lineHeight}\n */function getTextBoundingRect(opt){return _getBoundingRect(opt.text,opt.font,opt.textAlign,opt.textVerticalAlign,opt.textPadding,opt.textLineHeight,opt.rich,opt.truncate);}/**\n * @deprecated\n * the `textLineHeight` was added later.\n * For backward compatiblility, put it as the last parameter.\n * But deprecated this interface. Please use `getTextBoundingRect` instead.\n */function getTextRect(text,font,textAlign,textVerticalAlign,textPadding,rich,truncate,textLineHeight){return _getBoundingRect(text,font,textAlign,textVerticalAlign,textPadding,textLineHeight,rich,truncate);}/**\n * open new tab\n * @param {string} link url\n * @param {string} target blank or self\n */function windowOpen(link,target){if(target==='_blank'||target==='blank'){var blank=window.open();blank.opener=null;blank.location=link;}else{window.open(link,target);}}var format=(Object.freeze||Object)({addCommas:addCommas,toCamelCase:toCamelCase,normalizeCssArray:normalizeCssArray$1,encodeHTML:encodeHTML,formatTpl:formatTpl,formatTplSimple:formatTplSimple,getTooltipMarker:getTooltipMarker,formatTime:formatTime,capitalFirst:capitalFirst,truncateText:truncateText$1,getTextBoundingRect:getTextBoundingRect,getTextRect:getTextRect,windowOpen:windowOpen});/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // Layout helpers for each component positioning\nvar each$3=each$1;/**\n * @public\n */var LOCATION_PARAMS=['left','right','top','bottom','width','height'];/**\n * @public\n */var HV_NAMES=[['width','left','right'],['height','top','bottom']];function boxLayout(orient,group,gap,maxWidth,maxHeight){var x=0;var y=0;if(maxWidth==null){maxWidth=Infinity;}if(maxHeight==null){maxHeight=Infinity;}var currentLineMaxSize=0;group.eachChild(function(child,idx){var position=child.position;var rect=child.getBoundingRect();var nextChild=group.childAt(idx+1);var nextChildRect=nextChild&&nextChild.getBoundingRect();var nextX;var nextY;if(orient==='horizontal'){var moveX=rect.width+(nextChildRect?-nextChildRect.x+rect.x:0);nextX=x+moveX;// Wrap when width exceeds maxWidth or meet a `newline` group\n// FIXME compare before adding gap?\nif(nextX>maxWidth||child.newline){x=0;nextX=moveX;y+=currentLineMaxSize+gap;currentLineMaxSize=rect.height;}else{// FIXME: consider rect.y is not `0`?\ncurrentLineMaxSize=Math.max(currentLineMaxSize,rect.height);}}else{var moveY=rect.height+(nextChildRect?-nextChildRect.y+rect.y:0);nextY=y+moveY;// Wrap when width exceeds maxHeight or meet a `newline` group\nif(nextY>maxHeight||child.newline){x+=currentLineMaxSize+gap;y=0;nextY=moveY;currentLineMaxSize=rect.width;}else{currentLineMaxSize=Math.max(currentLineMaxSize,rect.width);}}if(child.newline){return;}position[0]=x;position[1]=y;orient==='horizontal'?x=nextX+gap:y=nextY+gap;});}/**\n * VBox or HBox layouting\n * @param {string} orient\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */var box=boxLayout;/**\n * VBox layouting\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */var vbox=curry(boxLayout,'vertical');/**\n * HBox layouting\n * @param {module:zrender/container/Group} group\n * @param {number} gap\n * @param {number} [width=Infinity]\n * @param {number} [height=Infinity]\n */var hbox=curry(boxLayout,'horizontal');/**\n * If x or x2 is not specified or 'center' 'left' 'right',\n * the width would be as long as possible.\n * If y or y2 is not specified or 'middle' 'top' 'bottom',\n * the height would be as long as possible.\n *\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.x]\n * @param {number|string} [positionInfo.y]\n * @param {number|string} [positionInfo.x2]\n * @param {number|string} [positionInfo.y2]\n * @param {Object} containerRect {width, height}\n * @param {string|number} margin\n * @return {Object} {width, height}\n */ /**\n * Parse position info.\n *\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.left]\n * @param {number|string} [positionInfo.top]\n * @param {number|string} [positionInfo.right]\n * @param {number|string} [positionInfo.bottom]\n * @param {number|string} [positionInfo.width]\n * @param {number|string} [positionInfo.height]\n * @param {number|string} [positionInfo.aspect] Aspect is width / height\n * @param {Object} containerRect\n * @param {string|number} [margin]\n *\n * @return {module:zrender/core/BoundingRect}\n */function getLayoutRect(positionInfo,containerRect,margin){margin=normalizeCssArray$1(margin||0);var containerWidth=containerRect.width;var containerHeight=containerRect.height;var left=parsePercent$1(positionInfo.left,containerWidth);var top=parsePercent$1(positionInfo.top,containerHeight);var right=parsePercent$1(positionInfo.right,containerWidth);var bottom=parsePercent$1(positionInfo.bottom,containerHeight);var width=parsePercent$1(positionInfo.width,containerWidth);var height=parsePercent$1(positionInfo.height,containerHeight);var verticalMargin=margin[2]+margin[0];var horizontalMargin=margin[1]+margin[3];var aspect=positionInfo.aspect;// If width is not specified, calculate width from left and right\nif(isNaN(width)){width=containerWidth-right-horizontalMargin-left;}if(isNaN(height)){height=containerHeight-bottom-verticalMargin-top;}if(aspect!=null){// If width and height are not given\n// 1. Graph should not exceeds the container\n// 2. Aspect must be keeped\n// 3. Graph should take the space as more as possible\n// FIXME\n// Margin is not considered, because there is no case that both\n// using margin and aspect so far.\nif(isNaN(width)&&isNaN(height)){if(aspect>containerWidth/containerHeight){width=containerWidth*0.8;}else{height=containerHeight*0.8;}}// Calculate width or height with given aspect\nif(isNaN(width)){width=aspect*height;}if(isNaN(height)){height=width/aspect;}}// If left is not specified, calculate left from right and width\nif(isNaN(left)){left=containerWidth-right-width-horizontalMargin;}if(isNaN(top)){top=containerHeight-bottom-height-verticalMargin;}// Align left and top\nswitch(positionInfo.left||positionInfo.right){case'center':left=containerWidth/2-width/2-margin[3];break;case'right':left=containerWidth-width-horizontalMargin;break;}switch(positionInfo.top||positionInfo.bottom){case'middle':case'center':top=containerHeight/2-height/2-margin[0];break;case'bottom':top=containerHeight-height-verticalMargin;break;}// If something is wrong and left, top, width, height are calculated as NaN\nleft=left||0;top=top||0;if(isNaN(width)){// Width may be NaN if only one value is given except width\nwidth=containerWidth-horizontalMargin-left-(right||0);}if(isNaN(height)){// Height may be NaN if only one value is given except height\nheight=containerHeight-verticalMargin-top-(bottom||0);}var rect=new BoundingRect(left+margin[3],top+margin[0],width,height);rect.margin=margin;return rect;}/**\n * Position a zr element in viewport\n * Group position is specified by either\n * {left, top}, {right, bottom}\n * If all properties exists, right and bottom will be igonred.\n *\n * Logic:\n * 1. Scale (against origin point in parent coord)\n * 2. Rotate (against origin point in parent coord)\n * 3. Traslate (with el.position by this method)\n * So this method only fixes the last step 'Traslate', which does not affect\n * scaling and rotating.\n *\n * If be called repeatly with the same input el, the same result will be gotten.\n *\n * @param {module:zrender/Element} el Should have `getBoundingRect` method.\n * @param {Object} positionInfo\n * @param {number|string} [positionInfo.left]\n * @param {number|string} [positionInfo.top]\n * @param {number|string} [positionInfo.right]\n * @param {number|string} [positionInfo.bottom]\n * @param {number|string} [positionInfo.width] Only for opt.boundingModel: 'raw'\n * @param {number|string} [positionInfo.height] Only for opt.boundingModel: 'raw'\n * @param {Object} containerRect\n * @param {string|number} margin\n * @param {Object} [opt]\n * @param {Array.} [opt.hv=[1,1]] Only horizontal or only vertical.\n * @param {Array.} [opt.boundingMode='all']\n * Specify how to calculate boundingRect when locating.\n * 'all': Position the boundingRect that is transformed and uioned\n * both itself and its descendants.\n * This mode simplies confine the elements in the bounding\n * of their container (e.g., using 'right: 0').\n * 'raw': Position the boundingRect that is not transformed and only itself.\n * This mode is useful when you want a element can overflow its\n * container. (Consider a rotated circle needs to be located in a corner.)\n * In this mode positionInfo.width/height can only be number.\n */function positionElement(el,positionInfo,containerRect,margin,opt){var h=!opt||!opt.hv||opt.hv[0];var v=!opt||!opt.hv||opt.hv[1];var boundingMode=opt&&opt.boundingMode||'all';if(!h&&!v){return;}var rect;if(boundingMode==='raw'){rect=el.type==='group'?new BoundingRect(0,0,+positionInfo.width||0,+positionInfo.height||0):el.getBoundingRect();}else{rect=el.getBoundingRect();if(el.needLocalTransform()){var transform=el.getLocalTransform();// Notice: raw rect may be inner object of el,\n// which should not be modified.\nrect=rect.clone();rect.applyTransform(transform);}}// The real width and height can not be specified but calculated by the given el.\npositionInfo=getLayoutRect(defaults({width:rect.width,height:rect.height},positionInfo),containerRect,margin);// Because 'tranlate' is the last step in transform\n// (see zrender/core/Transformable#getLocalTransform),\n// we can just only modify el.position to get final result.\nvar elPos=el.position;var dx=h?positionInfo.x-rect.x:0;var dy=v?positionInfo.y-rect.y:0;el.attr('position',boundingMode==='raw'?[dx,dy]:[elPos[0]+dx,elPos[1]+dy]);}/**\n * @param {Object} option Contains some of the properties in HV_NAMES.\n * @param {number} hvIdx 0: horizontal; 1: vertical.\n */ /**\n * Consider Case:\n * When defulat option has {left: 0, width: 100}, and we set {right: 0}\n * through setOption or media query, using normal zrUtil.merge will cause\n * {right: 0} does not take effect.\n *\n * @example\n * ComponentModel.extend({\n * init: function () {\n * ...\n * var inputPositionParams = layout.getLayoutParams(option);\n * this.mergeOption(inputPositionParams);\n * },\n * mergeOption: function (newOption) {\n * newOption && zrUtil.merge(thisOption, newOption, true);\n * layout.mergeLayoutParam(thisOption, newOption);\n * }\n * });\n *\n * @param {Object} targetOption\n * @param {Object} newOption\n * @param {Object|string} [opt]\n * @param {boolean|Array.} [opt.ignoreSize=false] Used for the components\n * that width (or height) should not be calculated by left and right (or top and bottom).\n */function mergeLayoutParam(targetOption,newOption,opt){!isObject$1(opt)&&(opt={});var ignoreSize=opt.ignoreSize;!isArray(ignoreSize)&&(ignoreSize=[ignoreSize,ignoreSize]);var hResult=merge$$1(HV_NAMES[0],0);var vResult=merge$$1(HV_NAMES[1],1);copy(HV_NAMES[0],targetOption,hResult);copy(HV_NAMES[1],targetOption,vResult);function merge$$1(names,hvIdx){var newParams={};var newValueCount=0;var merged={};var mergedValueCount=0;var enoughParamNumber=2;each$3(names,function(name){merged[name]=targetOption[name];});each$3(names,function(name){// Consider case: newOption.width is null, which is\n// set by user for removing width setting.\nhasProp(newOption,name)&&(newParams[name]=merged[name]=newOption[name]);hasValue(newParams,name)&&newValueCount++;hasValue(merged,name)&&mergedValueCount++;});if(ignoreSize[hvIdx]){// Only one of left/right is premitted to exist.\nif(hasValue(newOption,names[1])){merged[names[2]]=null;}else if(hasValue(newOption,names[2])){merged[names[1]]=null;}return merged;}// Case: newOption: {width: ..., right: ...},\n// or targetOption: {right: ...} and newOption: {width: ...},\n// There is no conflict when merged only has params count\n// little than enoughParamNumber.\nif(mergedValueCount===enoughParamNumber||!newValueCount){return merged;}// Case: newOption: {width: ..., right: ...},\n// Than we can make sure user only want those two, and ignore\n// all origin params in targetOption.\nelse if(newValueCount>=enoughParamNumber){return newParams;}else{// Chose another param from targetOption by priority.\nfor(var i=0;i>}\n * @readOnly\n */dependentModels:[],/**\n * @type {string}\n * @readOnly\n */uid:null,/**\n * Support merge layout params.\n * Only support 'box' now (left/right/top/bottom/width/height).\n * @type {string|Object} Object can be {ignoreSize: true}\n * @readOnly\n */layoutMode:null,$constructor:function $constructor(option,parentModel,ecModel,extraOpt){Model.call(this,option,parentModel,ecModel,extraOpt);this.uid=getUID('ec_cpt_model');},init:function init(option,parentModel,ecModel,extraOpt){this.mergeDefaultAndTheme(option,ecModel);},mergeDefaultAndTheme:function mergeDefaultAndTheme(option,ecModel){var layoutMode=this.layoutMode;var inputPositionParams=layoutMode?getLayoutParams(option):{};var themeModel=ecModel.getTheme();merge(option,themeModel.get(this.mainType));merge(option,this.getDefaultOption());if(layoutMode){mergeLayoutParam(option,inputPositionParams,layoutMode);}},mergeOption:function mergeOption(option,extraOpt){merge(this.option,option,true);var layoutMode=this.layoutMode;if(layoutMode){mergeLayoutParam(this.option,option,layoutMode);}},// Hooker after init or mergeOption\noptionUpdated:function optionUpdated(newCptOption,isInit){},getDefaultOption:function getDefaultOption(){var fields=inner$1(this);if(!fields.defaultOption){var optList=[];var Class=this.constructor;while(Class){var opt=Class.prototype.defaultOption;opt&&optList.push(opt);Class=Class.superClass;}var defaultOption={};for(var i=optList.length-1;i>=0;i--){defaultOption=merge(defaultOption,optList[i],true);}fields.defaultOption=defaultOption;}return fields.defaultOption;},getReferringComponents:function getReferringComponents(mainType){return this.ecModel.queryComponents({mainType:mainType,index:this.get(mainType+'Index',true),id:this.get(mainType+'Id',true)});}});// Reset ComponentModel.extend, add preConstruct.\n// clazzUtil.enableClassExtend(\n// ComponentModel,\n// function (option, parentModel, ecModel, extraOpt) {\n// // Set dependentModels, componentIndex, name, id, mainType, subType.\n// zrUtil.extend(this, extraOpt);\n// this.uid = componentUtil.getUID('componentModel');\n// // this.setReadOnly([\n// // 'type', 'id', 'uid', 'name', 'mainType', 'subType',\n// // 'dependentModels', 'componentIndex'\n// // ]);\n// }\n// );\n// Add capability of registerClass, getClass, hasClass, registerSubTypeDefaulter and so on.\nenableClassManagement(ComponentModel,{registerWhenExtend:true});enableSubTypeDefaulter(ComponentModel);// Add capability of ComponentModel.topologicalTravel.\nenableTopologicalTravel(ComponentModel,getDependencies);function getDependencies(componentType){var deps=[];each$1(ComponentModel.getClassesByMainType(componentType),function(Clazz){deps=deps.concat(Clazz.prototype.dependencies||[]);});// Ensure main type.\ndeps=map(deps,function(type){return parseClassType$1(type).main;});// Hack dataset for convenience.\nif(componentType!=='dataset'&&indexOf(deps,'dataset')<=0){deps.unshift('dataset');}return deps;}mixin(ComponentModel,boxLayoutMixin);/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var platform='';// Navigator not exists in node\nif(typeof navigator!=='undefined'){platform=navigator.platform||'';}var globalDefault={// backgroundColor: 'rgba(0,0,0,0)',\n// https://dribbble.com/shots/1065960-Infographic-Pie-chart-visualization\n// color: ['#5793f3', '#d14a61', '#fd9c35', '#675bba', '#fec42c', '#dd4444', '#d4df5a', '#cd4870'],\n// Light colors:\n// color: ['#bcd3bb', '#e88f70', '#edc1a5', '#9dc5c8', '#e1e8c8', '#7b7c68', '#e5b5b5', '#f0b489', '#928ea8', '#bda29a'],\n// color: ['#cc5664', '#9bd6ec', '#ea946e', '#8acaaa', '#f1ec64', '#ee8686', '#a48dc1', '#5da6bc', '#b9dcae'],\n// Dark colors:\ncolor:['#c23531','#2f4554','#61a0a8','#d48265','#91c7ae','#749f83','#ca8622','#bda29a','#6e7074','#546570','#c4ccd3'],gradientColor:['#f6efa6','#d88273','#bf444c'],// If xAxis and yAxis declared, grid is created by default.\n// grid: {},\ntextStyle:{// color: '#000',\n// decoration: 'none',\n// PENDING\nfontFamily:platform.match(/^Win/)?'Microsoft YaHei':'sans-serif',// fontFamily: 'Arial, Verdana, sans-serif',\nfontSize:12,fontStyle:'normal',fontWeight:'normal'},// http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/\n// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation\n// Default is source-over\nblendMode:null,animation:'auto',animationDuration:1000,animationDurationUpdate:300,animationEasing:'exponentialOut',animationEasingUpdate:'cubicOut',animationThreshold:2000,// Configuration for progressive/incremental rendering\nprogressiveThreshold:3000,progressive:400,// Threshold of if use single hover layer to optimize.\n// It is recommended that `hoverLayerThreshold` is equivalent to or less than\n// `progressiveThreshold`, otherwise hover will cause restart of progressive,\n// which is unexpected.\n// see example .\nhoverLayerThreshold:3000,// See: module:echarts/scale/Time\nuseUTC:false};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var inner$2=makeInner();function getNearestColorPalette(colors,requestColorNum){var paletteNum=colors.length;// TODO colors must be in order\nfor(var i=0;irequestColorNum){return colors[i];}}return colors[paletteNum-1];}var colorPaletteMixin={clearColorPalette:function clearColorPalette(){inner$2(this).colorIdx=0;inner$2(this).colorNameMap={};},/**\n * @param {string} name MUST NOT be null/undefined. Otherwise call this function\n * twise with the same parameters will get different result.\n * @param {Object} [scope=this]\n * @param {Object} [requestColorNum]\n * @return {string} color string.\n */getColorFromPalette:function getColorFromPalette(name,scope,requestColorNum){scope=scope||this;var scopeFields=inner$2(scope);var colorIdx=scopeFields.colorIdx||0;var colorNameMap=scopeFields.colorNameMap=scopeFields.colorNameMap||{};// Use `hasOwnProperty` to avoid conflict with Object.prototype.\nif(colorNameMap.hasOwnProperty(name)){return colorNameMap[name];}var defaultColorPalette=normalizeToArray(this.get('color',true));var layeredColorPalette=this.get('colorLayer',true);var colorPalette=requestColorNum==null||!layeredColorPalette?defaultColorPalette:getNearestColorPalette(layeredColorPalette,requestColorNum);// In case can't find in layered color palette.\ncolorPalette=colorPalette||defaultColorPalette;if(!colorPalette||!colorPalette.length){return;}var color=colorPalette[colorIdx];if(name){colorNameMap[name]=color;}scopeFields.colorIdx=(colorIdx+1)%colorPalette.length;return color;}};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // Avoid typo.\nvar SOURCE_FORMAT_ORIGINAL='original';var SOURCE_FORMAT_ARRAY_ROWS='arrayRows';var SOURCE_FORMAT_OBJECT_ROWS='objectRows';var SOURCE_FORMAT_KEYED_COLUMNS='keyedColumns';var SOURCE_FORMAT_UNKNOWN='unknown';// ??? CHANGE A NAME\nvar SOURCE_FORMAT_TYPED_ARRAY='typedArray';var SERIES_LAYOUT_BY_COLUMN='column';var SERIES_LAYOUT_BY_ROW='row';/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ /**\n * [sourceFormat]\n *\n * + \"original\":\n * This format is only used in series.data, where\n * itemStyle can be specified in data item.\n *\n * + \"arrayRows\":\n * [\n * ['product', 'score', 'amount'],\n * ['Matcha Latte', 89.3, 95.8],\n * ['Milk Tea', 92.1, 89.4],\n * ['Cheese Cocoa', 94.4, 91.2],\n * ['Walnut Brownie', 85.4, 76.9]\n * ]\n *\n * + \"objectRows\":\n * [\n * {product: 'Matcha Latte', score: 89.3, amount: 95.8},\n * {product: 'Milk Tea', score: 92.1, amount: 89.4},\n * {product: 'Cheese Cocoa', score: 94.4, amount: 91.2},\n * {product: 'Walnut Brownie', score: 85.4, amount: 76.9}\n * ]\n *\n * + \"keyedColumns\":\n * {\n * 'product': ['Matcha Latte', 'Milk Tea', 'Cheese Cocoa', 'Walnut Brownie'],\n * 'count': [823, 235, 1042, 988],\n * 'score': [95.8, 81.4, 91.2, 76.9]\n * }\n *\n * + \"typedArray\"\n *\n * + \"unknown\"\n */ /**\n * @constructor\n * @param {Object} fields\n * @param {string} fields.sourceFormat\n * @param {Array|Object} fields.fromDataset\n * @param {Array|Object} [fields.data]\n * @param {string} [seriesLayoutBy='column']\n * @param {Array.} [dimensionsDefine]\n * @param {Objet|HashMap} [encodeDefine]\n * @param {number} [startIndex=0]\n * @param {number} [dimensionsDetectCount]\n */function Source(fields){/**\n * @type {boolean}\n */this.fromDataset=fields.fromDataset;/**\n * Not null/undefined.\n * @type {Array|Object}\n */this.data=fields.data||(fields.sourceFormat===SOURCE_FORMAT_KEYED_COLUMNS?{}:[]);/**\n * See also \"detectSourceFormat\".\n * Not null/undefined.\n * @type {string}\n */this.sourceFormat=fields.sourceFormat||SOURCE_FORMAT_UNKNOWN;/**\n * 'row' or 'column'\n * Not null/undefined.\n * @type {string} seriesLayoutBy\n */this.seriesLayoutBy=fields.seriesLayoutBy||SERIES_LAYOUT_BY_COLUMN;/**\n * dimensions definition in option.\n * can be null/undefined.\n * @type {Array.}\n */this.dimensionsDefine=fields.dimensionsDefine;/**\n * encode definition in option.\n * can be null/undefined.\n * @type {Objet|HashMap}\n */this.encodeDefine=fields.encodeDefine&&createHashMap(fields.encodeDefine);/**\n * Not null/undefined, uint.\n * @type {number}\n */this.startIndex=fields.startIndex||0;/**\n * Can be null/undefined (when unknown), uint.\n * @type {number}\n */this.dimensionsDetectCount=fields.dimensionsDetectCount;}/**\n * Wrap original series data for some compatibility cases.\n */Source.seriesDataToSource=function(data){return new Source({data:data,sourceFormat:isTypedArray(data)?SOURCE_FORMAT_TYPED_ARRAY:SOURCE_FORMAT_ORIGINAL,fromDataset:false});};enableClassCheck(Source);/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // The result of `guessOrdinal`.\nvar BE_ORDINAL={Must:1,// Encounter string but not '-' and not number-like.\nMight:2,// Encounter string but number-like.\nNot:3// Other cases\n};var inner$3=makeInner();/**\n * @see {module:echarts/data/Source}\n * @param {module:echarts/component/dataset/DatasetModel} datasetModel\n * @return {string} sourceFormat\n */function detectSourceFormat(datasetModel){var data=datasetModel.option.source;var sourceFormat=SOURCE_FORMAT_UNKNOWN;if(isTypedArray(data)){sourceFormat=SOURCE_FORMAT_TYPED_ARRAY;}else if(isArray(data)){// FIXME Whether tolerate null in top level array?\nif(data.length===0){sourceFormat=SOURCE_FORMAT_ARRAY_ROWS;}for(var i=0,len=data.length;i} coordDimensions [{name: , type: , dimsDef: }, ...]\n * @param {module:model/Series} seriesModel\n * @param {module:data/Source} source\n * @return {Object} encode Never be `null/undefined`.\n */function makeSeriesEncodeForAxisCoordSys(coordDimensions,seriesModel,source){var encode={};var datasetModel=getDatasetModel(seriesModel);// Currently only make default when using dataset, util more reqirements occur.\nif(!datasetModel||!coordDimensions){return encode;}var encodeItemName=[];var encodeSeriesName=[];var ecModel=seriesModel.ecModel;var datasetMap=inner$3(ecModel).datasetMap;var key=datasetModel.uid+'_'+source.seriesLayoutBy;var baseCategoryDimIndex;var categoryWayValueDimStart;coordDimensions=coordDimensions.slice();each$1(coordDimensions,function(coordDimInfo,coordDimIdx){!isObject$1(coordDimInfo)&&(coordDimensions[coordDimIdx]={name:coordDimInfo});if(coordDimInfo.type==='ordinal'&&baseCategoryDimIndex==null){baseCategoryDimIndex=coordDimIdx;categoryWayValueDimStart=getDataDimCountOnCoordDim(coordDimensions[coordDimIdx]);}encode[coordDimInfo.name]=[];});var datasetRecord=datasetMap.get(key)||datasetMap.set(key,{categoryWayDim:categoryWayValueDimStart,valueWayDim:0});// TODO\n// Auto detect first time axis and do arrangement.\neach$1(coordDimensions,function(coordDimInfo,coordDimIdx){var coordDimName=coordDimInfo.name;var count=getDataDimCountOnCoordDim(coordDimInfo);// In value way.\nif(baseCategoryDimIndex==null){var start=datasetRecord.valueWayDim;pushDim(encode[coordDimName],start,count);pushDim(encodeSeriesName,start,count);datasetRecord.valueWayDim+=count;// ??? TODO give a better default series name rule?\n// especially when encode x y specified.\n// consider: when mutiple series share one dimension\n// category axis, series name should better use\n// the other dimsion name. On the other hand, use\n// both dimensions name.\n}// In category way, the first category axis.\nelse if(baseCategoryDimIndex===coordDimIdx){pushDim(encode[coordDimName],0,count);pushDim(encodeItemName,0,count);}// In category way, the other axis.\nelse{var start=datasetRecord.categoryWayDim;pushDim(encode[coordDimName],start,count);pushDim(encodeSeriesName,start,count);datasetRecord.categoryWayDim+=count;}});function pushDim(dimIdxArr,idxFrom,idxCount){for(var i=0;i \"other dim\" > \"the value dim itself\".\nif(isPureNumber&&idxRes0.v==null&&i!==potentialNameDimIndex){idxRes0.v=i;}if(idxRes0.n==null||idxRes0.n===idxRes0.v||!isPureNumber&&guessRecords[idxRes0.n]===BE_ORDINAL.Not){idxRes0.n=i;}if(fulfilled(idxRes0)&&guessRecords[idxRes0.n]!==BE_ORDINAL.Not){return idxRes0;}// [Strategy of idxRes1]: if idxRes0 not satisfied (that is, no BE_ORDINAL.Not),\n// find the first BE_ORDINAL.Might as the value dim,\n// and then find a name dim with the priority:\n// \"other dim\" > \"the value dim itself\".\n// That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be\n// treated as number.\nif(!isPureNumber){if(guessResult===BE_ORDINAL.Might&&idxRes1.v==null&&i!==potentialNameDimIndex){idxRes1.v=i;}if(idxRes1.n==null||idxRes1.n===idxRes1.v){idxRes1.n=i;}}}function fulfilled(idxResult){return idxResult.v!=null&&idxResult.n!=null;}return fulfilled(idxRes0)?idxRes0:fulfilled(idxRes1)?idxRes1:null;}();if(idxResult){encode.value=idxResult.v;// `potentialNameDimIndex` has highest priority.\nvar nameDimIndex=potentialNameDimIndex!=null?potentialNameDimIndex:idxResult.n;// By default, label use itemName in charts.\n// So we dont set encodeLabel here.\nencode.itemName=[nameDimIndex];encode.seriesName=[nameDimIndex];}return encode;}/**\n * If return null/undefined, indicate that should not use datasetModel.\n */function getDatasetModel(seriesModel){var option=seriesModel.option;// Caution: consider the scenario:\n// A dataset is declared and a series is not expected to use the dataset,\n// and at the beginning `setOption({series: { noData })` (just prepare other\n// option but no data), then `setOption({series: {data: [...]}); In this case,\n// the user should set an empty array to avoid that dataset is used by default.\nvar thisData=option.data;if(!thisData){return seriesModel.ecModel.getComponent('dataset',option.datasetIndex||0);}}/**\n * The rule should not be complex, otherwise user might not\n * be able to known where the data is wrong.\n * The code is ugly, but how to make it neat?\n *\n * @param {module:echars/data/Source} source\n * @param {number} dimIndex\n * @return {BE_ORDINAL} guess result.\n */function guessOrdinal(source,dimIndex){return doGuessOrdinal(source.data,source.sourceFormat,source.seriesLayoutBy,source.dimensionsDefine,source.startIndex,dimIndex);}// dimIndex may be overflow source data.\n// return {BE_ORDINAL}\nfunction doGuessOrdinal(data,sourceFormat,seriesLayoutBy,dimensionsDefine,startIndex,dimIndex){var result;// Experience value.\nvar maxLoop=5;if(isTypedArray(data)){return BE_ORDINAL.Not;}// When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine\n// always exists in source.\nvar dimName;var dimType;if(dimensionsDefine){var dimDefItem=dimensionsDefine[dimIndex];if(isObject$1(dimDefItem)){dimName=dimDefItem.name;dimType=dimDefItem.type;}else if(isString(dimDefItem)){dimName=dimDefItem;}}if(dimType!=null){return dimType==='ordinal'?BE_ORDINAL.Must:BE_ORDINAL.Not;}if(sourceFormat===SOURCE_FORMAT_ARRAY_ROWS){if(seriesLayoutBy===SERIES_LAYOUT_BY_ROW){var sample=data[dimIndex];for(var i=0;i<(sample||[]).length&&i=0;i--){// Remove options with inner id.\nif(isIdInner(opts[i])){opts.splice(i,1);}}option[mainType]=opts;}});delete option[OPTION_INNER_KEY];return option;},/**\n * @return {module:echarts/model/Model}\n */getTheme:function getTheme(){return this._theme;},/**\n * @param {string} mainType\n * @param {number} [idx=0]\n * @return {module:echarts/model/Component}\n */getComponent:function getComponent(mainType,idx){var list=this._componentsMap.get(mainType);if(list){return list[idx||0];}},/**\n * If none of index and id and name used, return all components with mainType.\n * @param {Object} condition\n * @param {string} condition.mainType\n * @param {string} [condition.subType] If ignore, only query by mainType\n * @param {number|Array.} [condition.index] Either input index or id or name.\n * @param {string|Array.} [condition.id] Either input index or id or name.\n * @param {string|Array.} [condition.name] Either input index or id or name.\n * @return {Array.}\n */queryComponents:function queryComponents(condition){var mainType=condition.mainType;if(!mainType){return[];}var index=condition.index;var id=condition.id;var name=condition.name;var cpts=this._componentsMap.get(mainType);if(!cpts||!cpts.length){return[];}var result;if(index!=null){if(!isArray(index)){index=[index];}result=filter(map(index,function(idx){return cpts[idx];}),function(val){return!!val;});}else if(id!=null){var isIdArray=isArray(id);result=filter(cpts,function(cpt){return isIdArray&&indexOf(id,cpt.id)>=0||!isIdArray&&cpt.id===id;});}else if(name!=null){var isNameArray=isArray(name);result=filter(cpts,function(cpt){return isNameArray&&indexOf(name,cpt.name)>=0||!isNameArray&&cpt.name===name;});}else{// Return all components with mainType\nresult=cpts.slice();}return filterBySubType(result,condition);},/**\n * The interface is different from queryComponents,\n * which is convenient for inner usage.\n *\n * @usage\n * var result = findComponents(\n * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}}\n * );\n * var result = findComponents(\n * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}}\n * );\n * var result = findComponents(\n * {mainType: 'series',\n * filter: function (model, index) {...}}\n * );\n * // result like [component0, componnet1, ...]\n *\n * @param {Object} condition\n * @param {string} condition.mainType Mandatory.\n * @param {string} [condition.subType] Optional.\n * @param {Object} [condition.query] like {xxxIndex, xxxId, xxxName},\n * where xxx is mainType.\n * If query attribute is null/undefined or has no index/id/name,\n * do not filtering by query conditions, which is convenient for\n * no-payload situations or when target of action is global.\n * @param {Function} [condition.filter] parameter: component, return boolean.\n * @return {Array.}\n */findComponents:function findComponents(condition){var query=condition.query;var mainType=condition.mainType;var queryCond=getQueryCond(query);var result=queryCond?this.queryComponents(queryCond):this._componentsMap.get(mainType);return doFilter(filterBySubType(result,condition));function getQueryCond(q){var indexAttr=mainType+'Index';var idAttr=mainType+'Id';var nameAttr=mainType+'Name';return q&&(q[indexAttr]!=null||q[idAttr]!=null||q[nameAttr]!=null)?{mainType:mainType,// subType will be filtered finally.\nindex:q[indexAttr],id:q[idAttr],name:q[nameAttr]}:null;}function doFilter(res){return condition.filter?filter(res,condition.filter):res;}},/**\n * @usage\n * eachComponent('legend', function (legendModel, index) {\n * ...\n * });\n * eachComponent(function (componentType, model, index) {\n * // componentType does not include subType\n * // (componentType is 'xxx' but not 'xxx.aa')\n * });\n * eachComponent(\n * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}},\n * function (model, index) {...}\n * );\n * eachComponent(\n * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}},\n * function (model, index) {...}\n * );\n *\n * @param {string|Object=} mainType When mainType is object, the definition\n * is the same as the method 'findComponents'.\n * @param {Function} cb\n * @param {*} context\n */eachComponent:function eachComponent(mainType,cb,context){var componentsMap=this._componentsMap;if(typeof mainType==='function'){context=cb;cb=mainType;componentsMap.each(function(components,componentType){each$1(components,function(component,index){cb.call(context,componentType,component,index);});});}else if(isString(mainType)){each$1(componentsMap.get(mainType),cb,context);}else if(isObject$1(mainType)){var queryResult=this.findComponents(mainType);each$1(queryResult,cb,context);}},/**\n * @param {string} name\n * @return {Array.}\n */getSeriesByName:function getSeriesByName(name){var series=this._componentsMap.get('series');return filter(series,function(oneSeries){return oneSeries.name===name;});},/**\n * @param {number} seriesIndex\n * @return {module:echarts/model/Series}\n */getSeriesByIndex:function getSeriesByIndex(seriesIndex){return this._componentsMap.get('series')[seriesIndex];},/**\n * Get series list before filtered by type.\n * FIXME: rename to getRawSeriesByType?\n *\n * @param {string} subType\n * @return {Array.}\n */getSeriesByType:function getSeriesByType(subType){var series=this._componentsMap.get('series');return filter(series,function(oneSeries){return oneSeries.subType===subType;});},/**\n * @return {Array.}\n */getSeries:function getSeries(){return this._componentsMap.get('series').slice();},/**\n * @return {number}\n */getSeriesCount:function getSeriesCount(){return this._componentsMap.get('series').length;},/**\n * After filtering, series may be different\n * frome raw series.\n *\n * @param {Function} cb\n * @param {*} context\n */eachSeries:function eachSeries(cb,context){assertSeriesInitialized(this);each$1(this._seriesIndices,function(rawSeriesIndex){var series=this._componentsMap.get('series')[rawSeriesIndex];cb.call(context,series,rawSeriesIndex);},this);},/**\n * Iterate raw series before filtered.\n *\n * @param {Function} cb\n * @param {*} context\n */eachRawSeries:function eachRawSeries(cb,context){each$1(this._componentsMap.get('series'),cb,context);},/**\n * After filtering, series may be different.\n * frome raw series.\n *\n * @param {string} subType.\n * @param {Function} cb\n * @param {*} context\n */eachSeriesByType:function eachSeriesByType(subType,cb,context){assertSeriesInitialized(this);each$1(this._seriesIndices,function(rawSeriesIndex){var series=this._componentsMap.get('series')[rawSeriesIndex];if(series.subType===subType){cb.call(context,series,rawSeriesIndex);}},this);},/**\n * Iterate raw series before filtered of given type.\n *\n * @parma {string} subType\n * @param {Function} cb\n * @param {*} context\n */eachRawSeriesByType:function eachRawSeriesByType(subType,cb,context){return each$1(this.getSeriesByType(subType),cb,context);},/**\n * @param {module:echarts/model/Series} seriesModel\n */isSeriesFiltered:function isSeriesFiltered(seriesModel){assertSeriesInitialized(this);return this._seriesIndicesMap.get(seriesModel.componentIndex)==null;},/**\n * @return {Array.}\n */getCurrentSeriesIndices:function getCurrentSeriesIndices(){return(this._seriesIndices||[]).slice();},/**\n * @param {Function} cb\n * @param {*} context\n */filterSeries:function filterSeries(cb,context){assertSeriesInitialized(this);var filteredSeries=filter(this._componentsMap.get('series'),cb,context);createSeriesIndices(this,filteredSeries);},restoreData:function restoreData(payload){var componentsMap=this._componentsMap;createSeriesIndices(this,componentsMap.get('series'));var componentTypes=[];componentsMap.each(function(components,componentType){componentTypes.push(componentType);});ComponentModel.topologicalTravel(componentTypes,ComponentModel.getAllClassMainTypes(),function(componentType,dependencies){each$1(componentsMap.get(componentType),function(component){(componentType!=='series'||!isNotTargetSeries(component,payload))&&component.restoreData();});});}});function isNotTargetSeries(seriesModel,payload){if(payload){var index=payload.seiresIndex;var id=payload.seriesId;var name=payload.seriesName;return index!=null&&seriesModel.componentIndex!==index||id!=null&&seriesModel.id!==id||name!=null&&seriesModel.name!==name;}}/**\n * @inner\n */function mergeTheme(option,theme){// PENDING\n// NOT use `colorLayer` in theme if option has `color`\nvar notMergeColorLayer=option.color&&!option.colorLayer;each$1(theme,function(themeItem,name){if(name==='colorLayer'&¬MergeColorLayer){return;}// 如果有 component model 则把具体的 merge 逻辑交给该 model 处理\nif(!ComponentModel.hasClass(name)){if(_typeof(themeItem)==='object'){option[name]=!option[name]?_clone(themeItem):merge(option[name],themeItem,false);}else{if(option[name]==null){option[name]=themeItem;}}}});}function initBase(baseOption){baseOption=baseOption;// Using OPTION_INNER_KEY to mark that this option can not be used outside,\n// i.e. `chart.setOption(chart.getModel().option);` is forbiden.\nthis.option={};this.option[OPTION_INNER_KEY]=1;/**\n * Init with series: [], in case of calling findSeries method\n * before series initialized.\n * @type {Object.>}\n * @private\n */this._componentsMap=createHashMap({series:[]});/**\n * Mapping between filtered series list and raw series list.\n * key: filtered series indices, value: raw series indices.\n * @type {Array.}\n * @private\n */this._seriesIndices;this._seriesIndicesMap;mergeTheme(baseOption,this._theme.option);// TODO Needs clone when merging to the unexisted property\nmerge(baseOption,globalDefault,false);this.mergeOption(baseOption);}/**\n * @inner\n * @param {Array.|string} types model types\n * @return {Object} key: {string} type, value: {Array.} models\n */function getComponentsByTypes(componentsMap,types){if(!isArray(types)){types=types?[types]:[];}var ret={};each$1(types,function(type){ret[type]=(componentsMap.get(type)||[]).slice();});return ret;}/**\n * @inner\n */function determineSubType(mainType,newCptOption,existComponent){var subType=newCptOption.type?newCptOption.type:existComponent?existComponent.subType// Use determineSubType only when there is no existComponent.\n:ComponentModel.determineSubType(mainType,newCptOption);// tooltip, markline, markpoint may always has no subType\nreturn subType;}/**\n * @inner\n */function createSeriesIndices(ecModel,seriesModels){ecModel._seriesIndicesMap=createHashMap(ecModel._seriesIndices=map(seriesModels,function(series){return series.componentIndex;})||[]);}/**\n * @inner\n */function filterBySubType(components,condition){// Using hasOwnProperty for restrict. Consider\n// subType is undefined in user payload.\nreturn condition.hasOwnProperty('subType')?filter(components,function(cpt){return cpt.subType===condition.subType;}):components;}/**\n * @inner\n */function assertSeriesInitialized(ecModel){// Components that use _seriesIndices should depends on series component,\n// which make sure that their initialization is after series.\nif(__DEV__){if(!ecModel._seriesIndices){throw new Error('Option should contains series.');}}}mixin(GlobalModel,colorPaletteMixin);/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var echartsAPIList=['getDom','getZr','getWidth','getHeight','getDevicePixelRatio','dispatchAction','isDisposed','on','off','getDataURL','getConnectedDataURL','getModel','getOption','getViewOfComponentModel','getViewOfSeriesModel'];// And `getCoordinateSystems` and `getComponentByElement` will be injected in echarts.js\nfunction ExtensionAPI(chartInstance){each$1(echartsAPIList,function(name){this[name]=bind(chartInstance[name],chartInstance);},this);}/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var coordinateSystemCreators={};function CoordinateSystemManager(){this._coordinateSystems=[];}CoordinateSystemManager.prototype={constructor:CoordinateSystemManager,create:function create(ecModel,api){var coordinateSystems=[];each$1(coordinateSystemCreators,function(creater,type){var list=creater.create(ecModel,api);coordinateSystems=coordinateSystems.concat(list||[]);});this._coordinateSystems=coordinateSystems;},update:function update(ecModel,api){each$1(this._coordinateSystems,function(coordSys){coordSys.update&&coordSys.update(ecModel,api);});},getCoordinateSystems:function getCoordinateSystems(){return this._coordinateSystems.slice();}};CoordinateSystemManager.register=function(type,coordinateSystemCreator){coordinateSystemCreators[type]=coordinateSystemCreator;};CoordinateSystemManager.get=function(type){return coordinateSystemCreators[type];};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ /**\n * ECharts option manager\n *\n * @module {echarts/model/OptionManager}\n */var each$4=each$1;var clone$3=_clone;var map$1=map;var merge$1=merge;var QUERY_REG=/^(min|max)?(.+)$/;/**\n * TERM EXPLANATIONS:\n *\n * [option]:\n *\n * An object that contains definitions of components. For example:\n * var option = {\n * title: {...},\n * legend: {...},\n * visualMap: {...},\n * series: [\n * {data: [...]},\n * {data: [...]},\n * ...\n * ]\n * };\n *\n * [rawOption]:\n *\n * An object input to echarts.setOption. 'rawOption' may be an\n * 'option', or may be an object contains multi-options. For example:\n * var option = {\n * baseOption: {\n * title: {...},\n * legend: {...},\n * series: [\n * {data: [...]},\n * {data: [...]},\n * ...\n * ]\n * },\n * timeline: {...},\n * options: [\n * {title: {...}, series: {data: [...]}},\n * {title: {...}, series: {data: [...]}},\n * ...\n * ],\n * media: [\n * {\n * query: {maxWidth: 320},\n * option: {series: {x: 20}, visualMap: {show: false}}\n * },\n * {\n * query: {minWidth: 320, maxWidth: 720},\n * option: {series: {x: 500}, visualMap: {show: true}}\n * },\n * {\n * option: {series: {x: 1200}, visualMap: {show: true}}\n * }\n * ]\n * };\n *\n * @alias module:echarts/model/OptionManager\n * @param {module:echarts/ExtensionAPI} api\n */function OptionManager(api){/**\n * @private\n * @type {module:echarts/ExtensionAPI}\n */this._api=api;/**\n * @private\n * @type {Array.}\n */this._timelineOptions=[];/**\n * @private\n * @type {Array.}\n */this._mediaList=[];/**\n * @private\n * @type {Object}\n */this._mediaDefault;/**\n * -1, means default.\n * empty means no media.\n * @private\n * @type {Array.}\n */this._currentMediaIndices=[];/**\n * @private\n * @type {Object}\n */this._optionBackup;/**\n * @private\n * @type {Object}\n */this._newBaseOption;}// timeline.notMerge is not supported in ec3. Firstly there is rearly\n// case that notMerge is needed. Secondly supporting 'notMerge' requires\n// rawOption cloned and backuped when timeline changed, which does no\n// good to performance. What's more, that both timeline and setOption\n// method supply 'notMerge' brings complex and some problems.\n// Consider this case:\n// (step1) chart.setOption({timeline: {notMerge: false}, ...}, false);\n// (step2) chart.setOption({timeline: {notMerge: true}, ...}, false);\nOptionManager.prototype={constructor:OptionManager,/**\n * @public\n * @param {Object} rawOption Raw option.\n * @param {module:echarts/model/Global} ecModel\n * @param {Array.} optionPreprocessorFuncs\n * @return {Object} Init option\n */setOption:function setOption(rawOption,optionPreprocessorFuncs){if(rawOption){// That set dat primitive is dangerous if user reuse the data when setOption again.\neach$1(normalizeToArray(rawOption.series),function(series){series&&series.data&&isTypedArray(series.data)&&setAsPrimitive(series.data);});}// Caution: some series modify option data, if do not clone,\n// it should ensure that the repeat modify correctly\n// (create a new object when modify itself).\nrawOption=clone$3(rawOption);// FIXME\n// 如果 timeline options 或者 media 中设置了某个属性,而baseOption中没有设置,则进行警告。\nvar oldOptionBackup=this._optionBackup;var newParsedOption=parseRawOption.call(this,rawOption,optionPreprocessorFuncs,!oldOptionBackup);this._newBaseOption=newParsedOption.baseOption;// For setOption at second time (using merge mode);\nif(oldOptionBackup){// Only baseOption can be merged.\nmergeOption(oldOptionBackup.baseOption,newParsedOption.baseOption);// For simplicity, timeline options and media options do not support merge,\n// that is, if you `setOption` twice and both has timeline options, the latter\n// timeline opitons will not be merged to the formers, but just substitude them.\nif(newParsedOption.timelineOptions.length){oldOptionBackup.timelineOptions=newParsedOption.timelineOptions;}if(newParsedOption.mediaList.length){oldOptionBackup.mediaList=newParsedOption.mediaList;}if(newParsedOption.mediaDefault){oldOptionBackup.mediaDefault=newParsedOption.mediaDefault;}}else{this._optionBackup=newParsedOption;}},/**\n * @param {boolean} isRecreate\n * @return {Object}\n */mountOption:function mountOption(isRecreate){var optionBackup=this._optionBackup;// TODO\n// 如果没有reset功能则不clone。\nthis._timelineOptions=map$1(optionBackup.timelineOptions,clone$3);this._mediaList=map$1(optionBackup.mediaList,clone$3);this._mediaDefault=clone$3(optionBackup.mediaDefault);this._currentMediaIndices=[];return clone$3(isRecreate// this._optionBackup.baseOption, which is created at the first `setOption`\n// called, and is merged into every new option by inner method `mergeOption`\n// each time `setOption` called, can be only used in `isRecreate`, because\n// its reliability is under suspicion. In other cases option merge is\n// performed by `model.mergeOption`.\n?optionBackup.baseOption:this._newBaseOption);},/**\n * @param {module:echarts/model/Global} ecModel\n * @return {Object}\n */getTimelineOption:function getTimelineOption(ecModel){var option;var timelineOptions=this._timelineOptions;if(timelineOptions.length){// getTimelineOption can only be called after ecModel inited,\n// so we can get currentIndex from timelineModel.\nvar timelineModel=ecModel.getComponent('timeline');if(timelineModel){option=clone$3(timelineOptions[timelineModel.getCurrentIndex()],true);}}return option;},/**\n * @param {module:echarts/model/Global} ecModel\n * @return {Array.}\n */getMediaOption:function getMediaOption(ecModel){var ecWidth=this._api.getWidth();var ecHeight=this._api.getHeight();var mediaList=this._mediaList;var mediaDefault=this._mediaDefault;var indices=[];var result=[];// No media defined.\nif(!mediaList.length&&!mediaDefault){return result;}// Multi media may be applied, the latter defined media has higher priority.\nfor(var i=0,len=mediaList.length;i\n * Support: width, height, aspectRatio\n * Can use max or min as prefix.\n */function applyMediaQuery(query,ecWidth,ecHeight){var realMap={width:ecWidth,height:ecHeight,aspectratio:ecWidth/ecHeight// lowser case for convenientce.\n};var applicatable=true;each$1(query,function(value,attr){var matched=attr.match(QUERY_REG);if(!matched||!matched[1]||!matched[2]){return;}var operator=matched[1];var realAttr=matched[2].toLowerCase();if(!compare(realMap[realAttr],value,operator)){applicatable=false;}});return applicatable;}function compare(real,expect,operator){if(operator==='min'){return real>=expect;}else if(operator==='max'){return real<=expect;}else{// Equals\nreturn real===expect;}}function indicesEquals(indices1,indices2){// indices is always order by asc and has only finite number.\nreturn indices1.join(',')===indices2.join(',');}/**\n * Consider case:\n * `chart.setOption(opt1);`\n * Then user do some interaction like dataZoom, dataView changing.\n * `chart.setOption(opt2);`\n * Then user press 'reset button' in toolbox.\n *\n * After doing that all of the interaction effects should be reset, the\n * chart should be the same as the result of invoke\n * `chart.setOption(opt1); chart.setOption(opt2);`.\n *\n * Although it is not able ensure that\n * `chart.setOption(opt1); chart.setOption(opt2);` is equivalents to\n * `chart.setOption(merge(opt1, opt2));` exactly,\n * this might be the only simple way to implement that feature.\n *\n * MEMO: We've considered some other approaches:\n * 1. Each model handle its self restoration but not uniform treatment.\n * (Too complex in logic and error-prone)\n * 2. Use a shadow ecModel. (Performace expensive)\n */function mergeOption(oldOption,newOption){newOption=newOption||{};each$4(newOption,function(newCptOpt,mainType){if(newCptOpt==null){return;}var oldCptOpt=oldOption[mainType];if(!ComponentModel.hasClass(mainType)){oldOption[mainType]=merge$1(oldCptOpt,newCptOpt,true);}else{newCptOpt=normalizeToArray(newCptOpt);oldCptOpt=normalizeToArray(oldCptOpt);var mapResult=mappingToExists(oldCptOpt,newCptOpt);oldOption[mainType]=map$1(mapResult,function(item){return item.option&&item.exist?merge$1(item.exist,item.option,true):item.exist||item.option;});}});}/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var each$5=each$1;var isObject$3=isObject$1;var POSSIBLE_STYLES=['areaStyle','lineStyle','nodeStyle','linkStyle','chordStyle','label','labelLine'];function compatEC2ItemStyle(opt){var itemStyleOpt=opt&&opt.itemStyle;if(!itemStyleOpt){return;}for(var i=0,len=POSSIBLE_STYLES.length;i\n// (2) Only register once when import repeatly.\n// Should be executed after series filtered and before stack calculation.\nvar dataStack=function dataStack(ecModel){var stackInfoMap=createHashMap();ecModel.eachSeries(function(seriesModel){var stack=seriesModel.get('stack');// Compatibal: when `stack` is set as '', do not stack.\nif(stack){var stackInfoList=stackInfoMap.get(stack)||stackInfoMap.set(stack,[]);var data=seriesModel.getData();var stackInfo={// Used for calculate axis extent automatically.\nstackResultDimension:data.getCalculationInfo('stackResultDimension'),stackedOverDimension:data.getCalculationInfo('stackedOverDimension'),stackedDimension:data.getCalculationInfo('stackedDimension'),stackedByDimension:data.getCalculationInfo('stackedByDimension'),isStackedByIndex:data.getCalculationInfo('isStackedByIndex'),data:data,seriesModel:seriesModel};// If stacked on axis that do not support data stack.\nif(!stackInfo.stackedDimension||!(stackInfo.isStackedByIndex||stackInfo.stackedByDimension)){return;}stackInfoList.length&&data.setCalculationInfo('stackedOnSeries',stackInfoList[stackInfoList.length-1].seriesModel);stackInfoList.push(stackInfo);}});stackInfoMap.each(calculateStack);};function calculateStack(stackInfoList){each$1(stackInfoList,function(targetStackInfo,idxInStack){var resultVal=[];var resultNaN=[NaN,NaN];var dims=[targetStackInfo.stackResultDimension,targetStackInfo.stackedOverDimension];var targetData=targetStackInfo.data;var isStackedByIndex=targetStackInfo.isStackedByIndex;// Should not write on raw data, because stack series model list changes\n// depending on legend selection.\nvar newData=targetData.map(dims,function(v0,v1,dataIndex){var sum=targetData.get(targetStackInfo.stackedDimension,dataIndex);// Consider `connectNulls` of line area, if value is NaN, stackedOver\n// should also be NaN, to draw a appropriate belt area.\nif(isNaN(sum)){return resultNaN;}var byValue;var stackedDataRawIndex;if(isStackedByIndex){stackedDataRawIndex=targetData.getRawIndex(dataIndex);}else{byValue=targetData.get(targetStackInfo.stackedByDimension,dataIndex);}// If stackOver is NaN, chart view will render point on value start.\nvar stackedOver=NaN;for(var j=idxInStack-1;j>=0;j--){var stackInfo=stackInfoList[j];// Has been optimized by inverted indices on `stackedByDimension`.\nif(!isStackedByIndex){stackedDataRawIndex=stackInfo.data.rawIndexOf(stackInfo.stackedByDimension,byValue);}if(stackedDataRawIndex>=0){var val=stackInfo.data.getByRawIndex(stackInfo.stackResultDimension,stackedDataRawIndex);// Considering positive stack, negative stack and empty data\nif(sum>=0&&val>0||// Positive stack\nsum<=0&&val<0// Negative stack\n){sum+=val;stackedOver=val;break;}}}resultVal[0]=sum;resultVal[1]=stackedOver;return resultVal;});targetData.hostModel.setData(newData);// Update for consequent calculation\ntargetStackInfo.data=newData;});}/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ // TODO\n// ??? refactor? check the outer usage of data provider.\n// merge with defaultDimValueGetter?\n/**\n * If normal array used, mutable chunk size is supported.\n * If typed array used, chunk size must be fixed.\n */function DefaultDataProvider(source,dimSize){if(!Source.isInstance(source)){source=Source.seriesDataToSource(source);}this._source=source;var data=this._data=source.data;var sourceFormat=source.sourceFormat;// Typed array. TODO IE10+?\nif(sourceFormat===SOURCE_FORMAT_TYPED_ARRAY){if(__DEV__){if(dimSize==null){throw new Error('Typed array data must specify dimension size');}}this._offset=0;this._dimSize=dimSize;this._data=data;}var methods=providerMethods[sourceFormat===SOURCE_FORMAT_ARRAY_ROWS?sourceFormat+'_'+source.seriesLayoutBy:sourceFormat];if(__DEV__){assert$1(methods,'Invalide sourceFormat: '+sourceFormat);}extend(this,methods);}var providerProto=DefaultDataProvider.prototype;// If data is pure without style configuration\nproviderProto.pure=false;// If data is persistent and will not be released after use.\nproviderProto.persistent=true;// ???! FIXME legacy data provider do not has method getSource\nproviderProto.getSource=function(){return this._source;};var providerMethods={'arrayRows_column':{pure:true,count:function count(){return Math.max(0,this._data.length-this._source.startIndex);},getItem:function getItem(idx){return this._data[idx+this._source.startIndex];},appendData:appendDataSimply},'arrayRows_row':{pure:true,count:function count(){var row=this._data[0];return row?Math.max(0,row.length-this._source.startIndex):0;},getItem:function getItem(idx){idx+=this._source.startIndex;var item=[];var data=this._data;for(var i=0;i|string|number} can be null/undefined.\n */function retrieveRawValue(data,dataIndex,dim){if(!data){return;}// Consider data may be not persistent.\nvar dataItem=data.getRawDataItem(dataIndex);if(dataItem==null){return;}var sourceFormat=data.getProvider().getSource().sourceFormat;var dimName;var dimIndex;var dimInfo=data.getDimensionInfo(dim);if(dimInfo){dimName=dimInfo.name;dimIndex=dimInfo.index;}return rawValueGetters[sourceFormat](dataItem,dataIndex,dimIndex,dimName);}/**\n * Compatible with some cases (in pie, map) like:\n * data: [{name: 'xx', value: 5, selected: true}, ...]\n * where only sourceFormat is 'original' and 'objectRows' supported.\n *\n * ??? TODO\n * Supported detail options in data item when using 'arrayRows'.\n *\n * @param {module:echarts/data/List} data\n * @param {number} dataIndex\n * @param {string} attr like 'selected'\n */function retrieveRawAttr(data,dataIndex,attr){if(!data){return;}var sourceFormat=data.getProvider().getSource().sourceFormat;if(sourceFormat!==SOURCE_FORMAT_ORIGINAL&&sourceFormat!==SOURCE_FORMAT_OBJECT_ROWS){return;}var dataItem=data.getRawDataItem(dataIndex);if(sourceFormat===SOURCE_FORMAT_ORIGINAL&&!isObject$1(dataItem)){dataItem=null;}if(dataItem){return dataItem[attr];}}/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/var DIMENSION_LABEL_REG=/\\{@(.+?)\\}/g;// PENDING A little ugly\nvar dataFormatMixin={/**\n * Get params for formatter\n * @param {number} dataIndex\n * @param {string} [dataType]\n * @return {Object}\n */getDataParams:function getDataParams(dataIndex,dataType){var data=this.getData(dataType);var rawValue=this.getRawValue(dataIndex,dataType);var rawDataIndex=data.getRawIndex(dataIndex);var name=data.getName(dataIndex);var itemOpt=data.getRawDataItem(dataIndex);var color=data.getItemVisual(dataIndex,'color');var borderColor=data.getItemVisual(dataIndex,'borderColor');var tooltipModel=this.ecModel.getComponent('tooltip');var renderModeOption=tooltipModel&&tooltipModel.get('renderMode');var renderMode=getTooltipRenderMode(renderModeOption);var mainType=this.mainType;var isSeries=mainType==='series';var userOutput=data.userOutput;return{componentType:mainType,componentSubType:this.subType,componentIndex:this.componentIndex,seriesType:isSeries?this.subType:null,seriesIndex:this.seriesIndex,seriesId:isSeries?this.id:null,seriesName:isSeries?this.name:null,name:name,dataIndex:rawDataIndex,data:itemOpt,dataType:dataType,value:rawValue,color:color,borderColor:borderColor,dimensionNames:userOutput?userOutput.dimensionNames:null,encode:userOutput?userOutput.encode:null,marker:getTooltipMarker({color:color,renderMode:renderMode}),// Param name list for mapping `a`, `b`, `c`, `d`, `e`\n$vars:['seriesName','name','value']};},/**\n * Format label\n * @param {number} dataIndex\n * @param {string} [status='normal'] 'normal' or 'emphasis'\n * @param {string} [dataType]\n * @param {number} [dimIndex] Only used in some chart that\n * use formatter in different dimensions, like radar.\n * @param {string} [labelProp='label']\n * @return {string} If not formatter, return null/undefined\n */getFormattedLabel:function getFormattedLabel(dataIndex,status,dataType,dimIndex,labelProp){status=status||'normal';var data=this.getData(dataType);var itemModel=data.getItemModel(dataIndex);var params=this.getDataParams(dataIndex,dataType);if(dimIndex!=null&¶ms.value instanceof Array){params.value=params.value[dimIndex];}var formatter=itemModel.get(status==='normal'?[labelProp||'label','formatter']:[status,labelProp||'label','formatter']);if(typeof formatter==='function'){params.status=status;params.dimensionIndex=dimIndex;return formatter(params);}else if(typeof formatter==='string'){var str=formatTpl(formatter,params);// Support 'aaa{@[3]}bbb{@product}ccc'.\n// Do not support '}' in dim name util have to.\nreturn str.replace(DIMENSION_LABEL_REG,function(origin,dim){var len=dim.length;if(dim.charAt(0)==='['&&dim.charAt(len-1)===']'){dim=+dim.slice(1,len-1);// Also: '[]' => 0\n}return retrieveRawValue(data,dataIndex,dim);});}},/**\n * Get raw value in option\n * @param {number} idx\n * @param {string} [dataType]\n * @return {Array|number|string}\n */getRawValue:function getRawValue(idx,dataType){return retrieveRawValue(this.getData(dataType),idx);},/**\n * Should be implemented.\n * @param {number} dataIndex\n * @param {boolean} [multipleSeries=false]\n * @param {number} [dataType]\n * @return {string} tooltip string\n */formatTooltip:function formatTooltip(){// Empty function\n}};/*\n* Licensed to the Apache Software Foundation (ASF) under one\n* or more contributor license agreements. See the NOTICE file\n* distributed with this work for additional information\n* regarding copyright ownership. The ASF licenses this file\n* to you under the Apache License, Version 2.0 (the\n* \"License\"); you may not use this file except in compliance\n* with the License. You may obtain a copy of the License at\n*\n* http://www.apache.org/licenses/LICENSE-2.0\n*\n* Unless required by applicable law or agreed to in writing,\n* software distributed under the License is distributed on an\n* \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n* KIND, either express or implied. See the License for the\n* specific language governing permissions and limitations\n* under the License.\n*/ /**\n * @param {Object} define\n * @return See the return of `createTask`.\n */function createTask(define){return new Task(define);}/**\n * @constructor\n * @param {Object} define\n * @param {Function} define.reset Custom reset\n * @param {Function} [define.plan] Returns 'reset' indicate reset immediately.\n * @param {Function} [define.count] count is used to determin data task.\n * @param {Function} [define.onDirty] count is used to determin data task.\n */function Task(define){define=define||{};this._reset=define.reset;this._plan=define.plan;this._count=define.count;this._onDirty=define.onDirty;this._dirty=true;// Context must be specified implicitly, to\n// avoid miss update context when model changed.\nthis.context;}var taskProto=Task.prototype;/**\n * @param {Object} performArgs\n * @param {number} [performArgs.step] Specified step.\n * @param {number} [performArgs.skip] Skip customer perform call.\n * @param {number} [performArgs.modBy] Sampling window size.\n * @param {number} [performArgs.modDataCount] Sampling count.\n */taskProto.perform=function(performArgs){var upTask=this._upstream;var skip=performArgs&&performArgs.skip;// TODO some refactor.\n// Pull data. Must pull data each time, because context.data\n// may be updated by Series.setData.\nif(this._dirty&&upTask){var context=this.context;context.data=context.outputData=upTask.context.outputData;}if(this.__pipeline){this.__pipeline.currentTask=this;}var planResult;if(this._plan&&!skip){planResult=this._plan(this.context);}// Support sharding by mod, which changes the render sequence and makes the rendered graphic\n// elements uniformed distributed when progress, especially when moving or zooming.\nvar lastModBy=normalizeModBy(this._modBy);var lastModDataCount=this._modDataCount||0;var modBy=normalizeModBy(performArgs&&performArgs.modBy);var modDataCount=performArgs&&performArgs.modDataCount||0;if(lastModBy!==modBy||lastModDataCount!==modDataCount){planResult='reset';}function normalizeModBy(val){!(val>=1)&&(val=1);// jshint ignore:line\nreturn val;}var forceFirstProgress;if(this._dirty||planResult==='reset'){this._dirty=false;forceFirstProgress=reset(this,skip);}this._modBy=modBy;this._modDataCount=modDataCount;var step=performArgs&&performArgs.step;if(upTask){if(__DEV__){assert$1(upTask._outputDueEnd!=null);}this._dueEnd=upTask._outputDueEnd;}// DataTask or overallTask\nelse{if(__DEV__){assert$1(!this._progress||this._count);}this._dueEnd=this._count?this._count(this.context):Infinity;}// Note: Stubs, that its host overall task let it has progress, has progress.\n// If no progress, pass index from upstream to downstream each time plan called.\nif(this._progress){var start=this._dueIndex;var end=Math.min(step!=null?this._dueIndex+step:Infinity,this._dueEnd);if(!skip&&(forceFirstProgress||start=this._outputDueEnd);}this._outputDueEnd=outputDueEnd;}else{// (1) Some overall task has no progress.\n// (2) Stubs, that its host overall task do not let it has progress, has no progress.\n// This should always be performed so it can be passed to downstream.\nthis._dueIndex=this._outputDueEnd=this._settedOutputEnd!=null?this._settedOutputEnd:this._dueEnd;}return this.unfinished();};var iterator=function(){var end;var current;var modBy;var modDataCount;var winCount;var it={reset:function reset(s,e,sStep,sCount){current=s;end=e;modBy=sStep;modDataCount=sCount;winCount=Math.ceil(modDataCount/modBy);it.next=modBy>1&&modDataCount>0?modNext:sequentialNext;}};return it;function sequentialNext(){return current=end?null:dataIndex