From e9f81f3d7bd8d057e131389d8e58e75d202e0771 Mon Sep 17 00:00:00 2001
From: pan <1029559041@qq.com>
Date: Thu, 11 Jun 2020 10:22:09 +0800
Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E6=8D=AE=E7=BB=9F=E8=AE=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
WebRoot/admin/report.jsp | 252 +-
WebRoot/static/echarts.js | 98328 ++++++++++++++++
WebRoot/static/echarts.min.js | 22 +
.../static/jquery.datetimepicker.full.min.js | 1 +
WebRoot/static/jquery.datetimepicker.min.css | 1 +
src/com/app/action/ApplyAction.java | 86 +-
src/com/app/bean/Count.java | 23 +
src/com/app/bean/CountSplit.java | 29 +
src/com/app/service/CountService.java | 12 +
src/com/app/service/CountServiceImpl.java | 48 +
src/com/app/service/HouseServiceImpl.java | 3 +
src/com/app/service/UsersService.java | 18 +-
src/com/app/service/UsersServiceImpl.java | 5 +
src/com/app/utils/Constants.java | 2 +
src/test/SpringTest.java | 5 +
15 files changed, 98693 insertions(+), 142 deletions(-)
create mode 100644 WebRoot/static/echarts.js
create mode 100644 WebRoot/static/echarts.min.js
create mode 100644 WebRoot/static/jquery.datetimepicker.full.min.js
create mode 100644 WebRoot/static/jquery.datetimepicker.min.css
create mode 100644 src/com/app/bean/Count.java
create mode 100644 src/com/app/bean/CountSplit.java
create mode 100644 src/com/app/service/CountService.java
create mode 100644 src/com/app/service/CountServiceImpl.java
create mode 100644 src/test/SpringTest.java
diff --git a/WebRoot/admin/report.jsp b/WebRoot/admin/report.jsp
index bf73fac..d978067 100644
--- a/WebRoot/admin/report.jsp
+++ b/WebRoot/admin/report.jsp
@@ -1,151 +1,149 @@
-<%@ page language="java" pageEncoding="UTF-8"%>
-<%@ taglib uri="/struts-tags" prefix="s"%>
-<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
+<%@ page language="java" pageEncoding="UTF-8" %>
+<%@ taglib uri="/struts-tags" prefix="s" %>
+<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%
-String path = request.getContextPath();
-String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
+ String path = request.getContextPath();
+ String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
-String usertype = session.getAttribute("userType")+"";
+ String usertype = session.getAttribute("userType") + "";
%>
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/WebRoot/static/echarts.js b/WebRoot/static/echarts.js
new file mode 100644
index 0000000..dd55582
--- /dev/null
+++ b/WebRoot/static/echarts.js
@@ -0,0 +1,98328 @@
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory((global.echarts = {})));
+}(this, (function (exports) { 'use strict';
+
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements. See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership. The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License. You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+// (1) The code `if (__DEV__) ...` can be removed by build tool.
+// (2) If intend to use `__DEV__`, this module should be imported. Use a global
+// variable `__DEV__` may cause that miss the declaration (see #6535), or the
+// declaration is behind of the using position (for example in `Model.extent`,
+// And tools like rollup can not analysis the dependency if not import).
+
+var dev;
+
+// In browser
+if (typeof window !== 'undefined') {
+ dev = window.__DEV__;
+}
+// In node
+else if (typeof global !== 'undefined') {
+ dev = global.__DEV__;
+}
+
+if (typeof dev === 'undefined') {
+ dev = true;
+}
+
+var __DEV__ = dev;
+
+/**
+ * zrender: 生成唯一id
+ *
+ * @author errorrik (errorrik@gmail.com)
+ */
+
+var idStart = 0x0907;
+
+var guid = function () {
+ return idStart++;
+};
+
+/**
+ * echarts设备环境识别
+ *
+ * @desc echarts基于Canvas,纯Javascript图表库,提供直观,生动,可交互,可个性化定制的数据统计图表。
+ * @author firede[firede@firede.us]
+ * @desc thanks zepto.
+ */
+
+/* global wx */
+
+var env = {};
+
+if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') {
+ // In Weixin Application
+ env = {
+ browser: {},
+ os: {},
+ node: false,
+ wxa: true, // Weixin Application
+ canvasSupported: true,
+ svgSupported: false,
+ touchEventsSupported: true,
+ domSupported: false
+ };
+}
+else if (typeof document === 'undefined' && typeof self !== 'undefined') {
+ // In worker
+ env = {
+ browser: {},
+ os: {},
+ node: false,
+ worker: true,
+ canvasSupported: true,
+ domSupported: false
+ };
+}
+else if (typeof navigator === 'undefined') {
+ // In node
+ env = {
+ browser: {},
+ os: {},
+ node: true,
+ worker: false,
+ // Assume canvas is supported
+ canvasSupported: true,
+ svgSupported: true,
+ domSupported: false
+ };
+}
+else {
+ env = detect(navigator.userAgent);
+}
+
+var env$1 = env;
+
+// Zepto.js
+// (c) 2010-2013 Thomas Fuchs
+// Zepto.js may be freely distributed under the MIT license.
+
+function detect(ua) {
+ var os = {};
+ var browser = {};
+ // var webkit = ua.match(/Web[kK]it[\/]{0,1}([\d.]+)/);
+ // var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/);
+ // var ipad = ua.match(/(iPad).*OS\s([\d_]+)/);
+ // var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/);
+ // var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/);
+ // var webos = ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/);
+ // var touchpad = webos && ua.match(/TouchPad/);
+ // var kindle = ua.match(/Kindle\/([\d.]+)/);
+ // var silk = ua.match(/Silk\/([\d._]+)/);
+ // var blackberry = ua.match(/(BlackBerry).*Version\/([\d.]+)/);
+ // var bb10 = ua.match(/(BB10).*Version\/([\d.]+)/);
+ // var rimtabletos = ua.match(/(RIM\sTablet\sOS)\s([\d.]+)/);
+ // var playbook = ua.match(/PlayBook/);
+ // var chrome = ua.match(/Chrome\/([\d.]+)/) || ua.match(/CriOS\/([\d.]+)/);
+ var firefox = ua.match(/Firefox\/([\d.]+)/);
+ // var safari = webkit && ua.match(/Mobile\//) && !chrome;
+ // var webview = ua.match(/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/) && !chrome;
+ var ie = ua.match(/MSIE\s([\d.]+)/)
+ // IE 11 Trident/7.0; rv:11.0
+ || ua.match(/Trident\/.+?rv:(([\d.]+))/);
+ var edge = ua.match(/Edge\/([\d.]+)/); // IE 12 and 12+
+
+ var weChat = (/micromessenger/i).test(ua);
+
+ // Todo: clean this up with a better OS/browser seperation:
+ // - discern (more) between multiple browsers on android
+ // - decide if kindle fire in silk mode is android or not
+ // - Firefox on Android doesn't specify the Android version
+ // - possibly devide in os, device and browser hashes
+
+ // if (browser.webkit = !!webkit) browser.version = webkit[1];
+
+ // if (android) os.android = true, os.version = android[2];
+ // if (iphone && !ipod) os.ios = os.iphone = true, os.version = iphone[2].replace(/_/g, '.');
+ // if (ipad) os.ios = os.ipad = true, os.version = ipad[2].replace(/_/g, '.');
+ // if (ipod) os.ios = os.ipod = true, os.version = ipod[3] ? ipod[3].replace(/_/g, '.') : null;
+ // if (webos) os.webos = true, os.version = webos[2];
+ // if (touchpad) os.touchpad = true;
+ // if (blackberry) os.blackberry = true, os.version = blackberry[2];
+ // if (bb10) os.bb10 = true, os.version = bb10[2];
+ // if (rimtabletos) os.rimtabletos = true, os.version = rimtabletos[2];
+ // if (playbook) browser.playbook = true;
+ // if (kindle) os.kindle = true, os.version = kindle[1];
+ // if (silk) browser.silk = true, browser.version = silk[1];
+ // if (!silk && os.android && ua.match(/Kindle Fire/)) browser.silk = true;
+ // if (chrome) browser.chrome = true, browser.version = chrome[1];
+ if (firefox) {
+ browser.firefox = true;
+ browser.version = firefox[1];
+ }
+ // if (safari && (ua.match(/Safari/) || !!os.ios)) browser.safari = true;
+ // if (webview) browser.webview = true;
+
+ if (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
+ // not be set on win phone. So we do not consider Win Phone.
+ if (weChat) {
+ browser.weChat = true;
+ }
+
+ // os.tablet = !!(ipad || playbook || (android && !ua.match(/Mobile/)) ||
+ // (firefox && ua.match(/Tablet/)) || (ie && !ua.match(/Phone/) && ua.match(/Touch/)));
+ // os.phone = !!(!os.tablet && !os.ipod && (android || iphone || webos ||
+ // (chrome && ua.match(/Android/)) || (chrome && ua.match(/CriOS\/([\d.]+)/)) ||
+ // (firefox && ua.match(/Mobile/)) || (ie && ua.match(/Touch/))));
+
+ return {
+ browser: browser,
+ os: os,
+ node: false,
+ // 原生canvas支持,改极端点了
+ // canvasSupported : !(browser.ie && parseFloat(browser.version) < 9)
+ canvasSupported: !!document.createElement('canvas').getContext,
+ svgSupported: typeof SVGRect !== 'undefined',
+ // works on most browsers
+ // IE10/11 does not support touch event, and MS Edge supports them but not by
+ // default, so we dont check navigator.maxTouchPoints for them here.
+ touchEventsSupported: 'ontouchstart' in window && !browser.ie && !browser.edge,
+ //
.
+ pointerEventsSupported:
+ // (1) Firefox supports pointer but not by default, only MS browsers are reliable on pointer
+ // events currently. So we dont use that on other browsers unless tested sufficiently.
+ // For example, in iOS 13 Mobile Chromium 78, if the touching behavior starts page
+ // scroll, the `pointermove` event can not be fired any more. That will break some
+ // features like "pan horizontally to move something and pan vertically to page scroll".
+ // The horizontal pan probably be interrupted by the casually triggered page scroll.
+ // (2) Although IE 10 supports pointer event, it use old style and is different from the
+ // standard. So we exclude that. (IE 10 is hardly used on touch device)
+ 'onpointerdown' in window
+ && (browser.edge || (browser.ie && browser.version >= 11)),
+ // passiveSupported: detectPassiveSupport()
+ domSupported: typeof document !== 'undefined'
+ };
+}
+
+// See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
+// function detectPassiveSupport() {
+// // Test via a getter in the options object to see if the passive property is accessed
+// var supportsPassive = false;
+// try {
+// var opts = Object.defineProperty({}, 'passive', {
+// get: function() {
+// supportsPassive = true;
+// }
+// });
+// window.addEventListener('testPassive', function() {}, opts);
+// } catch (e) {
+// }
+// return supportsPassive;
+// }
+
+/**
+ * @module zrender/core/util
+ */
+
+// 用于处理merge时无法遍历Date等对象的问题
+var BUILTIN_OBJECT = {
+ '[object Function]': 1,
+ '[object RegExp]': 1,
+ '[object Date]': 1,
+ '[object Error]': 1,
+ '[object CanvasGradient]': 1,
+ '[object CanvasPattern]': 1,
+ // For node-canvas
+ '[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.
+var methods = {};
+
+function $override(name, fn) {
+ // Clear ctx instance for different environment
+ if (name === 'createCanvas') {
+ _ctx = null;
+ }
+
+ methods[name] = fn;
+}
+
+/**
+ * Those data types can be cloned:
+ * Plain object, Array, TypedArray, number, string, null, undefined.
+ * Those data types will be assgined using the orginal data:
+ * BUILTIN_OBJECT
+ * Instance of user defined class will be cloned to a plain object, without
+ * properties in prototype.
+ * Other data types is not supported (not sure what will happen).
+ *
+ * Caution: do not support clone Date, for performance consideration.
+ * (There might be a large number of date in `series.data`).
+ * So date should not be modified in and out of echarts.
+ *
+ * @param {*} source
+ * @return {*} new
+ */
+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 < len; i++) {
+ result[i] = clone(source[i]);
+ }
+ }
+ }
+ else if (TYPED_ARRAY[typeStr]) {
+ if (!isPrimitive(source)) {
+ var Ctor = source.constructor;
+ if (source.constructor.from) {
+ result = Ctor.from(source);
+ }
+ else {
+ result = new Ctor(source.length);
+ for (var i = 0, len = source.length; i < len; i++) {
+ result[i] = clone(source[i]);
+ }
+ }
+ }
+ }
+ else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) {
+ result = {};
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ result[key] = clone(source[key]);
+ }
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} target
+ * @param {*} source
+ * @param {boolean} [overwrite=false]
+ */
+function merge(target, source, overwrite) {
+ // We should escapse that source is string
+ // and enter for ... in ...
+ if (!isObject$1(source) || !isObject$1(target)) {
+ return overwrite ? clone(source) : target;
+ }
+
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ var targetProp = target[key];
+ var sourceProp = source[key];
+
+ if (isObject$1(sourceProp)
+ && isObject$1(targetProp)
+ && !isArray(sourceProp)
+ && !isArray(targetProp)
+ && !isDom(sourceProp)
+ && !isDom(targetProp)
+ && !isBuiltInObject(sourceProp)
+ && !isBuiltInObject(targetProp)
+ && !isPrimitive(sourceProp)
+ && !isPrimitive(targetProp)
+ ) {
+ // 如果需要递归覆盖,就递归调用merge
+ merge(targetProp, sourceProp, overwrite);
+ }
+ else if (overwrite || !(key in target)) {
+ // 否则只处理overwrite为true,或者在目标对象中没有此属性的情况
+ // NOTE,在 target[key] 不存在的时候也是直接覆盖
+ target[key] = clone(source[key], true);
+ }
+ }
+ }
+
+ return target;
+}
+
+/**
+ * @param {Array} targetAndSources The first item is target, and the rests are source.
+ * @param {boolean} [overwrite=false]
+ * @return {*} target
+ */
+function mergeAll(targetAndSources, overwrite) {
+ var result = targetAndSources[0];
+ for (var i = 1, len = targetAndSources.length; i < len; i++) {
+ result = merge(result, targetAndSources[i], overwrite);
+ }
+ return result;
+}
+
+/**
+ * @param {*} target
+ * @param {*} source
+ * @memberOf module:zrender/core/util
+ */
+function extend(target, source) {
+ for (var key in source) {
+ if (source.hasOwnProperty(key)) {
+ target[key] = source[key];
+ }
+ }
+ return target;
+}
+
+/**
+ * @param {*} target
+ * @param {*} source
+ * @param {boolean} [overlay=false]
+ * @memberOf module:zrender/core/util
+ */
+function defaults(target, source, overlay) {
+ for (var key in source) {
+ if (source.hasOwnProperty(key)
+ && (overlay ? source[key] != null : target[key] == null)
+ ) {
+ target[key] = source[key];
+ }
+ }
+ return target;
+}
+
+var createCanvas = function () {
+ return methods.createCanvas();
+};
+
+methods.createCanvas = function () {
+ return document.createElement('canvas');
+};
+
+// FIXME
+var _ctx;
+
+function getContext() {
+ if (!_ctx) {
+ // Use util.createCanvas instead of createCanvas
+ // because createCanvas may be overwritten in different environment
+ _ctx = createCanvas().getContext('2d');
+ }
+ return _ctx;
+}
+
+/**
+ * 查询数组中元素的index
+ * @memberOf module:zrender/core/util
+ */
+function indexOf(array, value) {
+ if (array) {
+ if (array.indexOf) {
+ return array.indexOf(value);
+ }
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (array[i] === value) {
+ return i;
+ }
+ }
+ }
+ return -1;
+}
+
+/**
+ * 构造类继承关系
+ *
+ * @memberOf module:zrender/core/util
+ * @param {Function} clazz 源类
+ * @param {Function} baseClazz 基类
+ */
+function inherits(clazz, baseClazz) {
+ var clazzPrototype = clazz.prototype;
+ function F() {}
+ F.prototype = baseClazz.prototype;
+ clazz.prototype = new F();
+
+ for (var prop in clazzPrototype) {
+ if (clazzPrototype.hasOwnProperty(prop)) {
+ clazz.prototype[prop] = clazzPrototype[prop];
+ }
+ }
+ clazz.prototype.constructor = clazz;
+ clazz.superClass = baseClazz;
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Object|Function} target
+ * @param {Object|Function} sorce
+ * @param {boolean} overlay
+ */
+function mixin(target, source, overlay) {
+ target = 'prototype' in target ? target.prototype : target;
+ source = 'prototype' in source ? source.prototype : source;
+
+ defaults(target, source, overlay);
+}
+
+/**
+ * Consider typed array.
+ * @param {Array|TypedArray} data
+ */
+function isArrayLike(data) {
+ if (!data) {
+ return;
+ }
+ if (typeof data === 'string') {
+ return false;
+ }
+ return typeof data.length === 'number';
+}
+
+/**
+ * 数组或对象遍历
+ * @memberOf module:zrender/core/util
+ * @param {Object|Array} obj
+ * @param {Function} cb
+ * @param {*} [context]
+ */
+function each$1(obj, cb, context) {
+ if (!(obj && cb)) {
+ return;
+ }
+ if (obj.forEach && obj.forEach === nativeForEach) {
+ obj.forEach(cb, context);
+ }
+ else if (obj.length === +obj.length) {
+ for (var i = 0, len = obj.length; i < len; i++) {
+ cb.call(context, obj[i], i, obj);
+ }
+ }
+ else {
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ cb.call(context, obj[key], key, obj);
+ }
+ }
+ }
+}
+
+/**
+ * 数组映射
+ * @memberOf module:zrender/core/util
+ * @param {Array} obj
+ * @param {Function} cb
+ * @param {*} [context]
+ * @return {Array}
+ */
+function map(obj, cb, context) {
+ if (!(obj && cb)) {
+ return;
+ }
+ if (obj.map && obj.map === nativeMap) {
+ return obj.map(cb, context);
+ }
+ else {
+ var result = [];
+ for (var i = 0, len = obj.length; i < len; i++) {
+ result.push(cb.call(context, obj[i], i, obj));
+ }
+ return result;
+ }
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Array} obj
+ * @param {Function} cb
+ * @param {Object} [memo]
+ * @param {*} [context]
+ * @return {Array}
+ */
+function reduce(obj, cb, memo, context) {
+ if (!(obj && cb)) {
+ return;
+ }
+ if (obj.reduce && obj.reduce === nativeReduce) {
+ return obj.reduce(cb, memo, context);
+ }
+ else {
+ for (var i = 0, len = obj.length; i < len; i++) {
+ memo = cb.call(context, memo, obj[i], i, obj);
+ }
+ return memo;
+ }
+}
+
+/**
+ * 数组过滤
+ * @memberOf module:zrender/core/util
+ * @param {Array} obj
+ * @param {Function} cb
+ * @param {*} [context]
+ * @return {Array}
+ */
+function filter(obj, cb, context) {
+ if (!(obj && cb)) {
+ return;
+ }
+ if (obj.filter && obj.filter === nativeFilter) {
+ return obj.filter(cb, context);
+ }
+ else {
+ var result = [];
+ for (var i = 0, len = obj.length; i < len; i++) {
+ if (cb.call(context, obj[i], i, obj)) {
+ result.push(obj[i]);
+ }
+ }
+ return result;
+ }
+}
+
+/**
+ * 数组项查找
+ * @memberOf module:zrender/core/util
+ * @param {Array} obj
+ * @param {Function} cb
+ * @param {*} [context]
+ * @return {*}
+ */
+function find(obj, cb, context) {
+ if (!(obj && cb)) {
+ return;
+ }
+ for (var i = 0, len = obj.length; i < len; i++) {
+ if (cb.call(context, obj[i], i, obj)) {
+ return obj[i];
+ }
+ }
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Function} func
+ * @param {*} context
+ * @return {Function}
+ */
+function bind(func, context) {
+ var args = nativeSlice.call(arguments, 2);
+ return function () {
+ return func.apply(context, args.concat(nativeSlice.call(arguments)));
+ };
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Function} func
+ * @return {Function}
+ */
+function curry(func) {
+ var args = nativeSlice.call(arguments, 1);
+ return function () {
+ return func.apply(this, args.concat(nativeSlice.call(arguments)));
+ };
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isArray(value) {
+ return objToString.call(value) === '[object Array]';
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isFunction$1(value) {
+ return typeof value === 'function';
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isString(value) {
+ return objToString.call(value) === '[object String]';
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isObject$1(value) {
+ // Avoid a V8 JIT bug in Chrome 19-20.
+ // See https://code.google.com/p/v8/issues/detail?id=2291 for more details.
+ var type = typeof value;
+ return type === 'function' || (!!value && type === 'object');
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isBuiltInObject(value) {
+ return !!BUILTIN_OBJECT[objToString.call(value)];
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isTypedArray(value) {
+ return !!TYPED_ARRAY[objToString.call(value)];
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {*} value
+ * @return {boolean}
+ */
+function isDom(value) {
+ return typeof value === 'object'
+ && typeof value.nodeType === 'number'
+ && typeof value.ownerDocument === 'object';
+}
+
+/**
+ * Whether is exactly NaN. Notice isNaN('a') returns true.
+ * @param {*} value
+ * @return {boolean}
+ */
+function eqNaN(value) {
+ /* eslint-disable-next-line no-self-compare */
+ return value !== value;
+}
+
+/**
+ * If value1 is not null, then return value1, otherwise judget rest of values.
+ * Low performance.
+ * @memberOf module:zrender/core/util
+ * @return {*} Final value
+ */
+function retrieve(values) {
+ for (var i = 0, len = arguments.length; i < len; i++) {
+ if (arguments[i] != null) {
+ return arguments[i];
+ }
+ }
+}
+
+function retrieve2(value0, value1) {
+ return value0 != null
+ ? value0
+ : value1;
+}
+
+function retrieve3(value0, value1, value2) {
+ return value0 != null
+ ? value0
+ : value1 != null
+ ? value1
+ : value2;
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {Array} arr
+ * @param {number} startIndex
+ * @param {number} endIndex
+ * @return {Array}
+ */
+function slice() {
+ return Function.call.apply(nativeSlice, arguments);
+}
+
+/**
+ * Normalize css liked array configuration
+ * e.g.
+ * 3 => [3, 3, 3, 3]
+ * [4, 2] => [4, 2, 4, 2]
+ * [4, 3, 2] => [4, 3, 2, 3]
+ * @param {number|Array.} val
+ * @return {Array.}
+ */
+function normalizeCssArray(val) {
+ if (typeof (val) === 'number') {
+ return [val, val, val, val];
+ }
+ var len = val.length;
+ if (len === 2) {
+ // vertical | horizontal
+ return [val[0], val[1], val[0], val[1]];
+ }
+ else if (len === 3) {
+ // top | horizontal | bottom
+ return [val[0], val[1], val[2], val[1]];
+ }
+ return val;
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {boolean} condition
+ * @param {string} message
+ */
+function assert$1(condition, message) {
+ if (!condition) {
+ throw new Error(message);
+ }
+}
+
+/**
+ * @memberOf module:zrender/core/util
+ * @param {string} str string to be trimed
+ * @return {string} trimed string
+ */
+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__';
+/**
+ * Set an object as primitive to be ignored traversing children in clone or merge
+ */
+function setAsPrimitive(obj) {
+ obj[primitiveKey] = true;
+}
+
+function isPrimitive(obj) {
+ return obj[primitiveKey];
+}
+
+/**
+ * @constructor
+ * @param {Object} obj Only apply `ownProperty`.
+ */
+function HashMap(obj) {
+ var isArr = isArray(obj);
+ // Key should not be set on this, otherwise
+ // methods get/set/... may be overrided.
+ this.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`.
+ // (We usually treat `null` and `undefined` as the same, different
+ // from ES6 Map).
+ get: function (key) {
+ return this.data.hasOwnProperty(key) ? this.data[key] : null;
+ },
+ set: function (key, value) {
+ // Comparing with invocation chaining, `return value` is more commonly
+ // used in this case: `var someVal = map.set('a', genVal());`
+ return (this.data[key] = value);
+ },
+ // Although util.each can be performed on this hashMap directly, user
+ // should not use the exposed keys, who are prefixed.
+ each: function (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.
+ removeKey: function (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; i < a.length; i++) {
+ newArray[i] = a[i];
+ }
+ var offset = a.length;
+ for (i = 0; i < b.length; i++) {
+ newArray[i + offset] = b[i];
+ }
+ return newArray;
+}
+
+
+function noop() {}
+
+
+var zrUtil = (Object.freeze || Object)({
+ $override: $override,
+ clone: clone,
+ merge: merge,
+ mergeAll: mergeAll,
+ extend: extend,
+ defaults: defaults,
+ createCanvas: createCanvas,
+ getContext: getContext,
+ indexOf: indexOf,
+ inherits: inherits,
+ mixin: mixin,
+ isArrayLike: isArrayLike,
+ each: each$1,
+ map: map,
+ reduce: reduce,
+ filter: filter,
+ find: find,
+ bind: bind,
+ curry: curry,
+ isArray: isArray,
+ isFunction: isFunction$1,
+ isString: isString,
+ isObject: isObject$1,
+ isBuiltInObject: isBuiltInObject,
+ isTypedArray: isTypedArray,
+ isDom: isDom,
+ eqNaN: eqNaN,
+ retrieve: retrieve,
+ retrieve2: retrieve2,
+ retrieve3: retrieve3,
+ slice: slice,
+ normalizeCssArray: normalizeCssArray,
+ assert: assert$1,
+ trim: trim,
+ setAsPrimitive: setAsPrimitive,
+ isPrimitive: isPrimitive,
+ createHashMap: createHashMap,
+ concatArray: concatArray,
+ noop: noop
+});
+
+/* global Float32Array */
+
+var ArrayCtor = typeof Float32Array === 'undefined'
+ ? Array
+ : Float32Array;
+
+/**
+ * 创建一个向量
+ * @param {number} [x=0]
+ * @param {number} [y=0]
+ * @return {Vector2}
+ */
+function create(x, y) {
+ var out = new ArrayCtor(2);
+ if (x == null) {
+ x = 0;
+ }
+ if (y == null) {
+ y = 0;
+ }
+ out[0] = x;
+ out[1] = y;
+ return out;
+}
+
+/**
+ * 复制向量数据
+ * @param {Vector2} out
+ * @param {Vector2} v
+ * @return {Vector2}
+ */
+function copy(out, v) {
+ out[0] = v[0];
+ out[1] = v[1];
+ return out;
+}
+
+/**
+ * 克隆一个向量
+ * @param {Vector2} v
+ * @return {Vector2}
+ */
+function clone$1(v) {
+ var out = new ArrayCtor(2);
+ out[0] = v[0];
+ out[1] = v[1];
+ return out;
+}
+
+/**
+ * 设置向量的两个项
+ * @param {Vector2} out
+ * @param {number} a
+ * @param {number} b
+ * @return {Vector2} 结果
+ */
+function set(out, a, b) {
+ out[0] = a;
+ out[1] = b;
+ return out;
+}
+
+/**
+ * 向量相加
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function add(out, v1, v2) {
+ out[0] = v1[0] + v2[0];
+ out[1] = v1[1] + v2[1];
+ return out;
+}
+
+/**
+ * 向量缩放后相加
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @param {number} a
+ */
+function scaleAndAdd(out, v1, v2, a) {
+ out[0] = v1[0] + v2[0] * a;
+ out[1] = v1[1] + v2[1] * a;
+ return out;
+}
+
+/**
+ * 向量相减
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function sub(out, v1, v2) {
+ out[0] = v1[0] - v2[0];
+ out[1] = v1[1] - v2[1];
+ return out;
+}
+
+/**
+ * 向量长度
+ * @param {Vector2} v
+ * @return {number}
+ */
+function len(v) {
+ return Math.sqrt(lenSquare(v));
+}
+var length = len; // jshint ignore:line
+
+/**
+ * 向量长度平方
+ * @param {Vector2} v
+ * @return {number}
+ */
+function lenSquare(v) {
+ return v[0] * v[0] + v[1] * v[1];
+}
+var lengthSquare = lenSquare;
+
+/**
+ * 向量乘法
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function mul(out, v1, v2) {
+ out[0] = v1[0] * v2[0];
+ out[1] = v1[1] * v2[1];
+ return out;
+}
+
+/**
+ * 向量除法
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function div(out, v1, v2) {
+ out[0] = v1[0] / v2[0];
+ out[1] = v1[1] / v2[1];
+ return out;
+}
+
+/**
+ * 向量点乘
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @return {number}
+ */
+function dot(v1, v2) {
+ return v1[0] * v2[0] + v1[1] * v2[1];
+}
+
+/**
+ * 向量缩放
+ * @param {Vector2} out
+ * @param {Vector2} v
+ * @param {number} s
+ */
+function scale(out, v, s) {
+ out[0] = v[0] * s;
+ out[1] = v[1] * s;
+ return out;
+}
+
+/**
+ * 向量归一化
+ * @param {Vector2} out
+ * @param {Vector2} v
+ */
+function normalize(out, v) {
+ var d = len(v);
+ if (d === 0) {
+ out[0] = 0;
+ out[1] = 0;
+ }
+ else {
+ out[0] = v[0] / d;
+ out[1] = v[1] / d;
+ }
+ return out;
+}
+
+/**
+ * 计算向量间距离
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @return {number}
+ */
+function distance(v1, v2) {
+ return Math.sqrt(
+ (v1[0] - v2[0]) * (v1[0] - v2[0])
+ + (v1[1] - v2[1]) * (v1[1] - v2[1])
+ );
+}
+var dist = distance;
+
+/**
+ * 向量距离平方
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @return {number}
+ */
+function distanceSquare(v1, v2) {
+ return (v1[0] - v2[0]) * (v1[0] - v2[0])
+ + (v1[1] - v2[1]) * (v1[1] - v2[1]);
+}
+var distSquare = distanceSquare;
+
+/**
+ * 求负向量
+ * @param {Vector2} out
+ * @param {Vector2} v
+ */
+function negate(out, v) {
+ out[0] = -v[0];
+ out[1] = -v[1];
+ return out;
+}
+
+/**
+ * 插值两个点
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ * @param {number} t
+ */
+function lerp(out, v1, v2, t) {
+ out[0] = v1[0] + t * (v2[0] - v1[0]);
+ out[1] = v1[1] + t * (v2[1] - v1[1]);
+ return out;
+}
+
+/**
+ * 矩阵左乘向量
+ * @param {Vector2} out
+ * @param {Vector2} v
+ * @param {Vector2} m
+ */
+function applyTransform(out, v, m) {
+ var x = v[0];
+ var y = v[1];
+ out[0] = m[0] * x + m[2] * y + m[4];
+ out[1] = m[1] * x + m[3] * y + m[5];
+ return out;
+}
+
+/**
+ * 求两个向量最小值
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function min(out, v1, v2) {
+ out[0] = Math.min(v1[0], v2[0]);
+ out[1] = Math.min(v1[1], v2[1]);
+ return out;
+}
+
+/**
+ * 求两个向量最大值
+ * @param {Vector2} out
+ * @param {Vector2} v1
+ * @param {Vector2} v2
+ */
+function max(out, v1, v2) {
+ out[0] = Math.max(v1[0], v2[0]);
+ out[1] = Math.max(v1[1], v2[1]);
+ return out;
+}
+
+
+var vector = (Object.freeze || Object)({
+ create: create,
+ copy: copy,
+ clone: clone$1,
+ set: set,
+ add: add,
+ scaleAndAdd: scaleAndAdd,
+ sub: sub,
+ len: len,
+ length: length,
+ lenSquare: lenSquare,
+ lengthSquare: lengthSquare,
+ mul: mul,
+ div: div,
+ dot: dot,
+ scale: scale,
+ normalize: normalize,
+ distance: distance,
+ dist: dist,
+ distanceSquare: distanceSquare,
+ distSquare: distSquare,
+ negate: negate,
+ lerp: lerp,
+ applyTransform: applyTransform,
+ min: min,
+ max: max
+});
+
+// TODO Draggable for group
+// FIXME Draggable on element which has parent rotation or scale
+function Draggable() {
+
+ this.on('mousedown', this._dragStart, this);
+ this.on('mousemove', this._drag, this);
+ this.on('mouseup', this._dragEnd, this);
+ // `mosuemove` and `mouseup` can be continue to fire when dragging.
+ // See [Drag outside] in `Handler.js`. So we do not need to trigger
+ // `_dragEnd` when globalout. That would brings better user experience.
+ // this.on('globalout', this._dragEnd, this);
+
+ // this._dropTarget = null;
+ // this._draggingTarget = null;
+
+ // this._x = 0;
+ // this._y = 0;
+}
+
+Draggable.prototype = {
+
+ constructor: Draggable,
+
+ _dragStart: function (e) {
+ var draggingTarget = e.target;
+ // Find if there is draggable in the ancestor
+ while (draggingTarget && !draggingTarget.draggable) {
+ draggingTarget = draggingTarget.parent;
+ }
+ if (draggingTarget) {
+ this._draggingTarget = draggingTarget;
+ draggingTarget.dragging = true;
+ this._x = e.offsetX;
+ this._y = e.offsetY;
+
+ this.dispatchToElement(param(draggingTarget, e), 'dragstart', e.event);
+ }
+ },
+
+ _drag: function (e) {
+ var draggingTarget = this._draggingTarget;
+ if (draggingTarget) {
+
+ var x = e.offsetX;
+ var y = e.offsetY;
+
+ var dx = x - this._x;
+ var dy = y - this._y;
+ this._x = x;
+ this._y = y;
+
+ draggingTarget.drift(dx, dy, e);
+ this.dispatchToElement(param(draggingTarget, e), 'drag', e.event);
+
+ var dropTarget = this.findHover(x, y, draggingTarget).target;
+ var lastDropTarget = this._dropTarget;
+ this._dropTarget = dropTarget;
+
+ if (draggingTarget !== dropTarget) {
+ if (lastDropTarget && dropTarget !== lastDropTarget) {
+ this.dispatchToElement(param(lastDropTarget, e), 'dragleave', e.event);
+ }
+ if (dropTarget && dropTarget !== lastDropTarget) {
+ this.dispatchToElement(param(dropTarget, e), 'dragenter', e.event);
+ }
+ }
+ }
+ },
+
+ _dragEnd: function (e) {
+ var draggingTarget = this._draggingTarget;
+
+ if (draggingTarget) {
+ draggingTarget.dragging = false;
+ }
+
+ this.dispatchToElement(param(draggingTarget, e), 'dragend', e.event);
+
+ if (this._dropTarget) {
+ this.dispatchToElement(param(this._dropTarget, e), 'drop', e.event);
+ }
+
+ this._draggingTarget = null;
+ this._dropTarget = null;
+ }
+
+};
+
+function param(target, e) {
+ return {target: target, topTarget: e && e.topTarget};
+}
+
+/**
+ * Event Mixin
+ * @module zrender/mixin/Eventful
+ * @author Kener (@Kener-林峰, kener.linfeng@gmail.com)
+ * pissang (https://www.github.com/pissang)
+ */
+
+var arrySlice = Array.prototype.slice;
+
+/**
+ * Event dispatcher.
+ *
+ * @alias module:zrender/mixin/Eventful
+ * @constructor
+ * @param {Object} [eventProcessor] The object eventProcessor is the scope when
+ * `eventProcessor.xxx` called.
+ * @param {Function} [eventProcessor.normalizeQuery]
+ * param: {string|Object} Raw query.
+ * return: {string|Object} Normalized query.
+ * @param {Function} [eventProcessor.filter] Event will be dispatched only
+ * if it returns `true`.
+ * param: {string} eventType
+ * param: {string|Object} query
+ * return: {boolean}
+ * @param {Function} [eventProcessor.afterTrigger] Called after all handlers called.
+ * param: {string} eventType
+ */
+var Eventful = function (eventProcessor) {
+ this._$handlers = {};
+ this._$eventProcessor = eventProcessor;
+};
+
+Eventful.prototype = {
+
+ constructor: Eventful,
+
+ /**
+ * The handler can only be triggered once, then removed.
+ *
+ * @param {string} event The event name.
+ * @param {string|Object} [query] Condition used on event filter.
+ * @param {Function} handler The event handler.
+ * @param {Object} context
+ */
+ one: function (event, query, handler, context) {
+ return on(this, event, query, handler, context, true);
+ },
+
+ /**
+ * Bind a handler.
+ *
+ * @param {string} event The event name.
+ * @param {string|Object} [query] Condition used on event filter.
+ * @param {Function} handler The event handler.
+ * @param {Object} [context]
+ */
+ on: function (event, query, handler, context) {
+ return on(this, event, query, handler, context, false);
+ },
+
+ /**
+ * Whether any handler has bound.
+ *
+ * @param {string} event
+ * @return {boolean}
+ */
+ isSilent: function (event) {
+ var _h = this._$handlers;
+ return !_h[event] || !_h[event].length;
+ },
+
+ /**
+ * Unbind a event.
+ *
+ * @param {string} [event] The event name.
+ * If no `event` input, "off" all listeners.
+ * @param {Function} [handler] The event handler.
+ * If no `handler` input, "off" all listeners of the `event`.
+ */
+ off: function (event, handler) {
+ var _h = this._$handlers;
+
+ if (!event) {
+ this._$handlers = {};
+ return this;
+ }
+
+ if (handler) {
+ if (_h[event]) {
+ var newList = [];
+ for (var i = 0, l = _h[event].length; i < l; i++) {
+ if (_h[event][i].h !== handler) {
+ newList.push(_h[event][i]);
+ }
+ }
+ _h[event] = newList;
+ }
+
+ if (_h[event] && _h[event].length === 0) {
+ delete _h[event];
+ }
+ }
+ else {
+ delete _h[event];
+ }
+
+ return this;
+ },
+
+ /**
+ * Dispatch a event.
+ *
+ * @param {string} type The event name.
+ */
+ trigger: function (type) {
+ var _h = this._$handlers[type];
+ var eventProcessor = this._$eventProcessor;
+
+ if (_h) {
+ var args = arguments;
+ var argLen = args.length;
+
+ if (argLen > 3) {
+ args = arrySlice.call(args, 1);
+ }
+
+ var len = _h.length;
+ for (var i = 0; i < len;) {
+ var hItem = _h[i];
+ if (eventProcessor
+ && eventProcessor.filter
+ && hItem.query != null
+ && !eventProcessor.filter(type, hItem.query)
+ ) {
+ i++;
+ continue;
+ }
+
+ // Optimize advise from backbone
+ switch (argLen) {
+ case 1:
+ hItem.h.call(hItem.ctx);
+ break;
+ case 2:
+ hItem.h.call(hItem.ctx, args[1]);
+ break;
+ case 3:
+ hItem.h.call(hItem.ctx, args[1], args[2]);
+ break;
+ default:
+ // have more than 2 given arguments
+ hItem.h.apply(hItem.ctx, args);
+ break;
+ }
+
+ if (hItem.one) {
+ _h.splice(i, 1);
+ len--;
+ }
+ else {
+ i++;
+ }
+ }
+ }
+
+ eventProcessor && eventProcessor.afterTrigger
+ && eventProcessor.afterTrigger(type);
+
+ return this;
+ },
+
+ /**
+ * Dispatch a event with context, which is specified at the last parameter.
+ *
+ * @param {string} type The event name.
+ */
+ triggerWithContext: function (type) {
+ var _h = this._$handlers[type];
+ var eventProcessor = this._$eventProcessor;
+
+ if (_h) {
+ var args = arguments;
+ var argLen = args.length;
+
+ if (argLen > 4) {
+ args = arrySlice.call(args, 1, args.length - 1);
+ }
+ var ctx = args[args.length - 1];
+
+ var len = _h.length;
+ for (var i = 0; i < len;) {
+ var hItem = _h[i];
+ if (eventProcessor
+ && eventProcessor.filter
+ && hItem.query != null
+ && !eventProcessor.filter(type, hItem.query)
+ ) {
+ i++;
+ continue;
+ }
+
+ // Optimize advise from backbone
+ switch (argLen) {
+ case 1:
+ hItem.h.call(ctx);
+ break;
+ case 2:
+ hItem.h.call(ctx, args[1]);
+ break;
+ case 3:
+ hItem.h.call(ctx, args[1], args[2]);
+ break;
+ default:
+ // have more than 2 given arguments
+ hItem.h.apply(ctx, args);
+ break;
+ }
+
+ if (hItem.one) {
+ _h.splice(i, 1);
+ len--;
+ }
+ else {
+ i++;
+ }
+ }
+ }
+
+ eventProcessor && eventProcessor.afterTrigger
+ && eventProcessor.afterTrigger(type);
+
+ return this;
+ }
+};
+
+
+function normalizeQuery(host, query) {
+ var eventProcessor = host._$eventProcessor;
+ if (query != null && eventProcessor && eventProcessor.normalizeQuery) {
+ query = eventProcessor.normalizeQuery(query);
+ }
+ return query;
+}
+
+function on(eventful, event, query, handler, context, isOnce) {
+ var _h = eventful._$handlers;
+
+ if (typeof query === 'function') {
+ context = handler;
+ handler = query;
+ query = null;
+ }
+
+ if (!handler || !event) {
+ return eventful;
+ }
+
+ query = normalizeQuery(eventful, query);
+
+ if (!_h[event]) {
+ _h[event] = [];
+ }
+
+ for (var i = 0; i < _h[event].length; i++) {
+ if (_h[event][i].h === handler) {
+ return eventful;
+ }
+ }
+
+ var wrap = {
+ h: handler,
+ one: isOnce,
+ query: query,
+ ctx: context || eventful,
+ // FIXME
+ // Do not publish this feature util it is proved that it makes sense.
+ callAtLast: handler.zrEventfulCallAtLast
+ };
+
+ var lastIndex = _h[event].length - 1;
+ var lastWrap = _h[event][lastIndex];
+ (lastWrap && lastWrap.callAtLast)
+ ? _h[event].splice(lastIndex, 0, wrap)
+ : _h[event].push(wrap);
+
+ return eventful;
+}
+
+/**
+ * The algoritm is learnt from
+ * https://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/
+ * And we made some optimization for matrix inversion.
+ * Other similar approaches:
+ * "cv::getPerspectiveTransform", "Direct Linear Transformation".
+ */
+
+var LN2 = Math.log(2);
+
+function determinant(rows, rank, rowStart, rowMask, colMask, detCache) {
+ var cacheKey = rowMask + '-' + colMask;
+ var fullRank = rows.length;
+
+ if (detCache.hasOwnProperty(cacheKey)) {
+ return detCache[cacheKey];
+ }
+
+ if (rank === 1) {
+ // In this case the colMask must be like: `11101111`. We can find the place of `0`.
+ var colStart = Math.round(Math.log(((1 << fullRank) - 1) & ~colMask) / LN2);
+ return rows[rowStart][colStart];
+ }
+
+ var subRowMask = rowMask | (1 << rowStart);
+ var subRowStart = rowStart + 1;
+ while (rowMask & (1 << subRowStart)) {
+ subRowStart++;
+ }
+
+ var sum = 0;
+ for (var j = 0, colLocalIdx = 0; j < fullRank; j++) {
+ var colTag = 1 << j;
+ if (!(colTag & colMask)) {
+ sum += (colLocalIdx % 2 ? -1 : 1) * rows[rowStart][j]
+ // det(subMatrix(0, j))
+ * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache);
+ colLocalIdx++;
+ }
+ }
+
+ detCache[cacheKey] = sum;
+
+ return sum;
+}
+
+/**
+ * Usage:
+ * ```js
+ * var transformer = buildTransformer(
+ * [10, 44, 100, 44, 100, 300, 10, 300],
+ * [50, 54, 130, 14, 140, 330, 14, 220]
+ * );
+ * var out = [];
+ * transformer && transformer([11, 33], out);
+ * ```
+ *
+ * Notice: `buildTransformer` may take more than 10ms in some Android device.
+ *
+ * @param {Array.} src source four points, [x0, y0, x1, y1, x2, y2, x3, y3]
+ * @param {Array.} dest destination four points, [x0, y0, x1, y1, x2, y2, x3, y3]
+ * @return {Function} transformer If fail, return null/undefined.
+ */
+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
+ // any three of the markers are collinear.
+ return;
+ }
+
+ // `invert(mA) * dest`, that is, `adj(mA) / det * dest`.
+ var 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))
+ * determinant(mA, 7, i === 0 ? 1 : 0, 1 << i, 1 << j, detCache)
+ / det * dest[i];
+ }
+ }
+
+ return function (out, srcPointX, srcPointY) {
+ var pk = srcPointX * vh[6] + srcPointY * vh[7] + 1;
+ out[0] = (srcPointX * vh[0] + srcPointY * vh[1] + vh[2]) / pk;
+ out[1] = (srcPointX * vh[3] + srcPointY * vh[4] + vh[5]) / pk;
+ };
+}
+
+var EVENT_SAVED_PROP = '___zrEVENTSAVED';
+var _calcOut$1 = [];
+
+/**
+ * Transform "local coord" from `elFrom` to `elTarget`.
+ * "local coord": the coord based on the input `el`. The origin point is at
+ * the position of "left: 0; top: 0;" in the `el`.
+ *
+ * Support when CSS transform is used.
+ *
+ * Having the `out` (that is, `[outX, outY]`), we can create an DOM element
+ * and set the CSS style as "left: outX; top: outY;" and append it to `elTarge`
+ * to locate the element.
+ *
+ * For example, this code below positions a child of `document.body` on the event
+ * point, no matter whether `body` has `margin`/`paddin`/`transfrom`/... :
+ * ```js
+ * transformLocalCoord(out, container, document.body, event.offsetX, event.offsetY);
+ * if (!eqNaN(out[0])) {
+ * // Then locate the tip element on the event point.
+ * var tipEl = document.createElement('div');
+ * tipEl.style.cssText = 'position: absolute; left:' + out[0] + ';top:' + out[1] + ';';
+ * document.body.appendChild(tipEl);
+ * }
+ * ```
+ *
+ * Notice: In some env this method is not supported. If called, `out` will be `[NaN, NaN]`.
+ *
+ * @param {Array.} out [inX: number, inY: number] The output..
+ * If can not transform, `out` will not be modified but return `false`.
+ * @param {HTMLElement} elFrom The `[inX, inY]` is based on elFrom.
+ * @param {HTMLElement} elTarget The `out` is based on elTarget.
+ * @param {number} inX
+ * @param {number} inY
+ * @return {boolean} Whether transform successfully.
+ */
+function transformLocalCoord(out, elFrom, elTarget, inX, inY) {
+ return transformCoordWithViewport(_calcOut$1, elFrom, inX, inY, true)
+ && transformCoordWithViewport(out, elTarget, _calcOut$1[0], _calcOut$1[1]);
+}
+
+/**
+ * Transform between a "viewport coord" and a "local coord".
+ * "viewport coord": the coord based on the left-top corner of the viewport
+ * of the browser.
+ * "local coord": the coord based on the input `el`. The origin point is at
+ * the position of "left: 0; top: 0;" in the `el`.
+ *
+ * Support the case when CSS transform is used on el.
+ *
+ * @param {Array.} out [inX: number, inY: number] The output. If `inverse: false`,
+ * it represents "local coord", otherwise "vireport coord".
+ * If can not transform, `out` will not be modified but return `false`.
+ * @param {HTMLElement} el The "local coord" is based on the `el`, see comment above.
+ * @param {number} inX If `inverse: false`,
+ * it represents "vireport coord", otherwise "local coord".
+ * @param {number} inY If `inverse: false`,
+ * it represents "vireport coord", otherwise "local coord".
+ * @param {boolean} [inverse=false]
+ * `true`: from "viewport coord" to "local coord".
+ * `false`: from "local coord" to "viewport coord".
+ * @return {boolean} Whether transform successfully.
+ */
+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',
+ // 'height: 5px',
+ propLR[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`.
+ return (oldCoordTheSame && transformer)
+ ? transformer
+ : (
+ saved.srcCoords = srcCoords,
+ saved[transformerName] = inverse
+ ? buildTransformer(destCoords, srcCoords)
+ : buildTransformer(srcCoords, destCoords)
+ );
+}
+
+function isCanvasEl(el) {
+ return el.nodeName.toUpperCase() === 'CANVAS';
+}
+
+/**
+ * Utilities for mouse or touch events.
+ */
+
+var isDomLevel2 = (typeof window !== 'undefined') && !!window.addEventListener;
+
+var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/;
+var _calcOut = [];
+
+/**
+ * Get the `zrX` and `zrY`, which are relative to the top-left of
+ * the input `el`.
+ * CSS transform (2D & 3D) is supported.
+ *
+ * The strategy to fetch the coords:
+ * + If `calculate` is not set as `true`, users of this method should
+ * ensure that `el` is the same or the same size & location as `e.target`.
+ * Otherwise the result coords are probably not expected. Because we
+ * firstly try to get coords from e.offsetX/e.offsetY.
+ * + If `calculate` is set as `true`, the input `el` can be any element
+ * and we force to calculate the coords based on `el`.
+ * + The input `el` should be positionable (not position:static).
+ *
+ * The force `calculate` can be used in case like:
+ * When mousemove event triggered on ec tooltip, `e.target` is not `el`(zr painter.dom).
+ *
+ * @param {HTMLElement} el DOM element.
+ * @param {Event} e Mouse event or touch event.
+ * @param {Object} out Get `out.zrX` and `out.zrY` as the result.
+ * @param {boolean} [calculate=false] Whether to force calculate
+ * the coordinates but not use ones provided by browser.
+ */
+function clientToLocal(el, e, out, calculate) {
+ out = out || {};
+
+ // According to the W3C Working Draft, offsetX and offsetY should be relative
+ // to the padding edge of the target element. The only browser using this convention
+ // is IE. Webkit uses the border edge, Opera uses the content edge, and FireFox does
+ // not support the properties.
+ // (see http://www.jacklmoore.com/notes/mouse-position/)
+ // In zr painter.dom, padding edge equals to border edge.
+
+ if (calculate || !env$1.canvasSupported) {
+ calculateZrXY(el, e, out);
+ }
+ // Caution: In FireFox, layerX/layerY Mouse position relative to the closest positioned
+ // ancestor element, so we should make sure el is positioned (e.g., not position:static).
+ // BTW1, Webkit don't return the same results as FF in non-simple cases (like add
+ // zoom-factor, overflow / opacity layers, transforms ...)
+ // BTW2, (ev.offsetY || ev.pageY - $(ev.target).offset().top) is not correct in preserve-3d.
+ //
+ // BTW3, In ff, offsetX/offsetY is always 0.
+ else 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?)
+ else if (e.offsetX != null) {
+ out.zrX = e.offsetX;
+ out.zrY = e.offsetY;
+ }
+ // For some other device, e.g., IOS safari.
+ else {
+ calculateZrXY(el, e, out);
+ }
+
+ return out;
+}
+
+function calculateZrXY(el, e, out) {
+ // BlackBerry 5, iOS 3 (original iPhone) don't have getBoundingRect.
+ if (env$1.domSupported && el.getBoundingClientRect) {
+ var ex = e.clientX;
+ var ey = e.clientY;
+
+ if (isCanvasEl(el)) {
+ // Original approach, which do not support CSS transform.
+ // marker can not be locationed in a canvas container
+ // (getBoundingClientRect is always 0). We do not support
+ // that input a pre-created canvas to zr while using css
+ // transform in iOS.
+ var 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;
+}
+
+/**
+ * Find native event compat for legency IE.
+ * Should be called at the begining of a native event listener.
+ *
+ * @param {Event} [e] Mouse event or touch event or pointer event.
+ * For lagency IE, we use `window.event` is used.
+ * @return {Event} The native event.
+ */
+function getNativeEvent(e) {
+ return e || window.event;
+}
+
+/**
+ * Normalize the coordinates of the input event.
+ *
+ * Get the `e.zrX` and `e.zrY`, which are relative to the top-left of
+ * the input `el`.
+ * Get `e.zrDelta` if using mouse wheel.
+ * Get `e.which`, see the comment inside this function.
+ *
+ * Do not calculate repeatly if `zrX` and `zrY` already exist.
+ *
+ * Notice: see comments in `clientToLocal`. check the relationship
+ * between the result coords and the parameters `el` and `calculate`.
+ *
+ * @param {HTMLElement} el DOM element.
+ * @param {Event} [e] See `getNativeEvent`.
+ * @param {boolean} [calculate=false] Whether to force calculate
+ * the coordinates but not use ones provided by browser.
+ * @return {UIEvent} The normalized native UIEvent.
+ */
+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;
+ // See jQuery: https://github.com/jquery/jquery/blob/master/src/event.js
+ // If e.which has been defined, it may be readonly,
+ // see: https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/which
+ var 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,
+ // when press left button and `mousemove (pointermove)` in Edge, the `e.which`
+ // is 65536 and the `e.button` is -1. But the `mouseup (pointerup)` and
+ // `mousedown (pointerdown)` is the same as Chrome does.
+
+ return e;
+}
+
+/**
+ * @param {HTMLElement} el
+ * @param {string} name
+ * @param {Function} handler
+ * @param {Object|boolean} opt If boolean, means `opt.capture`
+ * @param {boolean} [opt.capture=false]
+ * @param {boolean} [opt.passive=false]
+ */
+function addEventListener(el, name, handler, opt) {
+ if (isDomLevel2) {
+ // Reproduct the console warning:
+ // [Violation] Added non-passive event listener to a scroll-blocking event.
+ // Consider marking event handler as 'passive' to make the page more responsive.
+ // Just set console log level: verbose in chrome dev tool.
+ // then the warning log will be printed when addEventListener called.
+ // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
+ // We have not yet found a neat way to using passive. Because in zrender the dom event
+ // listener delegate all of the upper events of element. Some of those events need
+ // to prevent default. For example, the feature `preventDefaultMouseMove` of echarts.
+ // Before passive can be adopted, these issues should be considered:
+ // (1) Whether and how a zrender user specifies an event listener passive. And by default,
+ // passive or not.
+ // (2) How to tread that some zrender event listener is passive, and some is not. If
+ // we use other way but not preventDefault of mousewheel and touchmove, browser
+ // compatibility should be handled.
+
+ // var opts = (env.passiveSupported && name === 'mousewheel')
+ // ? {passive: true}
+ // // By default, the third param of el.addEventListener is `capture: false`.
+ // : void 0;
+ // el.addEventListener(name, handler /* , opts */);
+ el.addEventListener(name, handler, opt);
+ }
+ else {
+ // For simplicity, do not implement `setCapture` for IE9-.
+ el.attachEvent('on' + name, handler);
+ }
+}
+
+/**
+ * Parameter are the same as `addEventListener`.
+ *
+ * Notice that if a listener is registered twice, one with capture and one without,
+ * remove each one separately. Removal of a capturing listener does not affect a
+ * non-capturing version of the same listener, and vice versa.
+ */
+function removeEventListener(el, name, handler, opt) {
+ if (isDomLevel2) {
+ el.removeEventListener(name, handler, opt);
+ }
+ else {
+ el.detachEvent('on' + name, handler);
+ }
+}
+
+/**
+ * preventDefault and stopPropagation.
+ * Notice: do not use this method in zrender. It can only be
+ * used by upper applications if necessary.
+ *
+ * @param {Event} e A mouse or touch event.
+ */
+var stop = isDomLevel2
+ ? function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ e.cancelBubble = true;
+ }
+ : function (e) {
+ e.returnValue = false;
+ e.cancelBubble = true;
+ };
+
+/**
+ * This method only works for mouseup and mousedown. The functionality is restricted
+ * for fault tolerance, See the `e.which` compatibility above.
+ *
+ * @param {MouseEvent} e
+ * @return {boolean}
+ */
+function isMiddleOrRightButtonOnMouseUpDown(e) {
+ return e.which === 2 || e.which === 3;
+}
+
+/**
+ * To be removed.
+ * @deprecated
+ */
+
+/**
+ * Only implements needed gestures for mobile.
+ */
+
+var GestureMgr = function () {
+
+ /**
+ * @private
+ * @type {Array.