| |
| /* |
| * 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. |
| */ |
| |
| (function (global, factory) { |
| typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : |
| typeof define === 'function' && define.amd ? define(['exports'], factory) : |
| (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.echarts = {})); |
| }(this, (function (exports) { 'use strict'; |
| |
| /*! *****************************************************************************
|
| Copyright (c) Microsoft Corporation.
|
|
|
| Permission to use, copy, modify, and/or distribute this software for any
|
| purpose with or without fee is hereby granted.
|
|
|
| THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
| REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
| AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
| INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
| LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
| OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
| PERFORMANCE OF THIS SOFTWARE.
|
| ***************************************************************************** */
|
| /* global Reflect, Promise */
|
|
|
| var extendStatics = function(d, b) {
|
| extendStatics = Object.setPrototypeOf ||
|
| ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
| function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
| return extendStatics(d, b);
|
| };
|
|
|
| function __extends(d, b) {
|
| if (typeof b !== "function" && b !== null)
|
| throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
| extendStatics(d, b);
|
| function __() { this.constructor = d; }
|
| d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
| } |
| |
| var Browser = (function () { |
| function Browser() { |
| this.firefox = false; |
| this.ie = false; |
| this.edge = false; |
| this.newEdge = false; |
| this.weChat = false; |
| } |
| return Browser; |
| }()); |
| var Env = (function () { |
| function Env() { |
| this.browser = new Browser(); |
| this.node = false; |
| this.wxa = false; |
| this.worker = false; |
| this.svgSupported = false; |
| this.touchEventsSupported = false; |
| this.pointerEventsSupported = false; |
| this.domSupported = false; |
| this.transformSupported = false; |
| this.transform3dSupported = false; |
| this.hasGlobalWindow = typeof window !== 'undefined'; |
| } |
| return Env; |
| }()); |
| var env = new Env(); |
| if (typeof wx === 'object' && typeof wx.getSystemInfoSync === 'function') { |
| env.wxa = true; |
| env.touchEventsSupported = true; |
| } |
| else if (typeof document === 'undefined' && typeof self !== 'undefined') { |
| env.worker = true; |
| } |
| else if (typeof navigator === 'undefined') { |
| env.node = true; |
| env.svgSupported = true; |
| } |
| else { |
| detect(navigator.userAgent, env); |
| } |
| function detect(ua, env) { |
| var browser = env.browser; |
| var firefox = ua.match(/Firefox\/([\d.]+)/); |
| var ie = ua.match(/MSIE\s([\d.]+)/) |
| || ua.match(/Trident\/.+?rv:(([\d.]+))/); |
| var edge = ua.match(/Edge?\/([\d.]+)/); |
| var weChat = (/micromessenger/i).test(ua); |
| if (firefox) { |
| browser.firefox = true; |
| browser.version = firefox[1]; |
| } |
| if (ie) { |
| browser.ie = true; |
| browser.version = ie[1]; |
| } |
| if (edge) { |
| browser.edge = true; |
| browser.version = edge[1]; |
| browser.newEdge = +edge[1].split('.')[0] > 18; |
| } |
| if (weChat) { |
| browser.weChat = true; |
| } |
| env.svgSupported = typeof SVGRect !== 'undefined'; |
| env.touchEventsSupported = 'ontouchstart' in window && !browser.ie && !browser.edge; |
| env.pointerEventsSupported = 'onpointerdown' in window |
| && (browser.edge || (browser.ie && +browser.version >= 11)); |
| env.domSupported = typeof document !== 'undefined'; |
| var style = document.documentElement.style; |
| env.transform3dSupported = ((browser.ie && 'transition' in style) |
| || browser.edge |
| || (('WebKitCSSMatrix' in window) && ('m11' in new WebKitCSSMatrix())) |
| || 'MozPerspective' in style) |
| && !('OTransition' in style); |
| env.transformSupported = env.transform3dSupported |
| || (browser.ie && +browser.version >= 9); |
| } |
| |
| var DEFAULT_FONT_SIZE = 12; |
| var DEFAULT_FONT_FAMILY = 'sans-serif'; |
| var DEFAULT_FONT = DEFAULT_FONT_SIZE + "px " + DEFAULT_FONT_FAMILY; |
| var OFFSET = 20; |
| var SCALE = 100; |
| var defaultWidthMapStr = "007LLmW'55;N0500LLLLLLLLLL00NNNLzWW\\\\WQb\\0FWLg\\bWb\\WQ\\WrWWQ000CL5LLFLL0LL**F*gLLLL5F0LF\\FFF5.5N"; |
| function getTextWidthMap(mapStr) { |
| var map = {}; |
| if (typeof JSON === 'undefined') { |
| return map; |
| } |
| for (var i = 0; i < mapStr.length; i++) { |
| var char = String.fromCharCode(i + 32); |
| var size = (mapStr.charCodeAt(i) - OFFSET) / SCALE; |
| map[char] = size; |
| } |
| return map; |
| } |
| var DEFAULT_TEXT_WIDTH_MAP = getTextWidthMap(defaultWidthMapStr); |
| var platformApi = { |
| createCanvas: function () { |
| return typeof document !== 'undefined' |
| && document.createElement('canvas'); |
| }, |
| measureText: (function () { |
| var _ctx; |
| var _cachedFont; |
| return function (text, font) { |
| if (!_ctx) { |
| var canvas = platformApi.createCanvas(); |
| _ctx = canvas && canvas.getContext('2d'); |
| } |
| if (_ctx) { |
| if (_cachedFont !== font) { |
| _cachedFont = _ctx.font = font || DEFAULT_FONT; |
| } |
| return _ctx.measureText(text); |
| } |
| else { |
| text = text || ''; |
| font = font || DEFAULT_FONT; |
| var res = /^([0-9]*?)px$/.exec(font); |
| var fontSize = +(res && res[1]) || DEFAULT_FONT_SIZE; |
| var width = 0; |
| if (font.indexOf('mono') >= 0) { |
| width = fontSize * text.length; |
| } |
| else { |
| for (var i = 0; i < text.length; i++) { |
| var preCalcWidth = DEFAULT_TEXT_WIDTH_MAP[text[i]]; |
| width += preCalcWidth == null ? fontSize : (preCalcWidth * fontSize); |
| } |
| } |
| return { width: width }; |
| } |
| }; |
| })(), |
| loadImage: function (src, onload, onerror) { |
| var image = new Image(); |
| image.onload = onload; |
| image.onerror = onerror; |
| image.src = src; |
| return image; |
| } |
| }; |
| function setPlatformAPI(newPlatformApis) { |
| for (var key in platformApi) { |
| if (newPlatformApis[key]) { |
| platformApi[key] = newPlatformApis[key]; |
| } |
| } |
| } |
| |
| var BUILTIN_OBJECT = reduce([ |
| 'Function', |
| 'RegExp', |
| 'Date', |
| 'Error', |
| 'CanvasGradient', |
| 'CanvasPattern', |
| 'Image', |
| 'Canvas' |
| ], function (obj, val) { |
| obj['[object ' + val + ']'] = true; |
| return obj; |
| }, {}); |
| var TYPED_ARRAY = reduce([ |
| 'Int8', |
| 'Uint8', |
| 'Uint8Clamped', |
| 'Int16', |
| 'Uint16', |
| 'Int32', |
| 'Uint32', |
| 'Float32', |
| 'Float64' |
| ], function (obj, val) { |
| obj['[object ' + val + 'Array]'] = true; |
| return obj; |
| }, {}); |
| 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 ctorFunction = function () { }.constructor; |
| var protoFunction = ctorFunction ? ctorFunction.prototype : null; |
| var protoKey = '__proto__'; |
| var idStart = 0x0907; |
| function guid() { |
| return idStart++; |
| } |
| function logError() { |
| var args = []; |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args[_i] = arguments[_i]; |
| } |
| if (typeof console !== 'undefined') { |
| console.error.apply(console, args); |
| } |
| } |
| 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 (Ctor.from) { |
| result = Ctor.from(source); |
| } |
| else { |
| result = new Ctor(source.length); |
| for (var i = 0, len = source.length; i < len; i++) { |
| result[i] = source[i]; |
| } |
| } |
| } |
| } |
| else if (!BUILTIN_OBJECT[typeStr] && !isPrimitive(source) && !isDom(source)) { |
| result = {}; |
| for (var key in source) { |
| if (source.hasOwnProperty(key) && key !== protoKey) { |
| result[key] = clone(source[key]); |
| } |
| } |
| } |
| return result; |
| } |
| function merge(target, source, overwrite) { |
| if (!isObject(source) || !isObject(target)) { |
| return overwrite ? clone(source) : target; |
| } |
| for (var key in source) { |
| if (source.hasOwnProperty(key) && key !== protoKey) { |
| var targetProp = target[key]; |
| var sourceProp = source[key]; |
| if (isObject(sourceProp) |
| && isObject(targetProp) |
| && !isArray(sourceProp) |
| && !isArray(targetProp) |
| && !isDom(sourceProp) |
| && !isDom(targetProp) |
| && !isBuiltInObject(sourceProp) |
| && !isBuiltInObject(targetProp) |
| && !isPrimitive(sourceProp) |
| && !isPrimitive(targetProp)) { |
| merge(targetProp, sourceProp, overwrite); |
| } |
| else if (overwrite || !(key in target)) { |
| target[key] = clone(source[key]); |
| } |
| } |
| } |
| 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; |
| } |
| function extend(target, source) { |
| if (Object.assign) { |
| Object.assign(target, source); |
| } |
| else { |
| for (var key in source) { |
| if (source.hasOwnProperty(key) && key !== protoKey) { |
| target[key] = source[key]; |
| } |
| } |
| } |
| return target; |
| } |
| function defaults(target, source, overlay) { |
| var keysArr = keys(source); |
| for (var i = 0; i < keysArr.length; i++) { |
| var key = keysArr[i]; |
| if ((overlay ? source[key] != null : target[key] == null)) { |
| target[key] = source[key]; |
| } |
| } |
| return target; |
| } |
| var createCanvas = platformApi.createCanvas; |
| 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; |
| } |
| 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; |
| } |
| function mixin(target, source, override) { |
| target = 'prototype' in target ? target.prototype : target; |
| source = 'prototype' in source ? source.prototype : source; |
| if (Object.getOwnPropertyNames) { |
| var keyList = Object.getOwnPropertyNames(source); |
| for (var i = 0; i < keyList.length; i++) { |
| var key = keyList[i]; |
| if (key !== 'constructor') { |
| if ((override ? source[key] != null : target[key] == null)) { |
| target[key] = source[key]; |
| } |
| } |
| } |
| } |
| else { |
| defaults(target, source, override); |
| } |
| } |
| function isArrayLike(data) { |
| if (!data) { |
| return false; |
| } |
| if (typeof data === 'string') { |
| return false; |
| } |
| return typeof data.length === 'number'; |
| } |
| function each(arr, cb, context) { |
| if (!(arr && cb)) { |
| return; |
| } |
| if (arr.forEach && arr.forEach === nativeForEach) { |
| arr.forEach(cb, context); |
| } |
| else if (arr.length === +arr.length) { |
| for (var i = 0, len = arr.length; i < len; i++) { |
| cb.call(context, arr[i], i, arr); |
| } |
| } |
| else { |
| for (var key in arr) { |
| if (arr.hasOwnProperty(key)) { |
| cb.call(context, arr[key], key, arr); |
| } |
| } |
| } |
| } |
| function map(arr, cb, context) { |
| if (!arr) { |
| return []; |
| } |
| if (!cb) { |
| return slice(arr); |
| } |
| if (arr.map && arr.map === nativeMap) { |
| return arr.map(cb, context); |
| } |
| else { |
| var result = []; |
| for (var i = 0, len = arr.length; i < len; i++) { |
| result.push(cb.call(context, arr[i], i, arr)); |
| } |
| return result; |
| } |
| } |
| function reduce(arr, cb, memo, context) { |
| if (!(arr && cb)) { |
| return; |
| } |
| for (var i = 0, len = arr.length; i < len; i++) { |
| memo = cb.call(context, memo, arr[i], i, arr); |
| } |
| return memo; |
| } |
| function filter(arr, cb, context) { |
| if (!arr) { |
| return []; |
| } |
| if (!cb) { |
| return slice(arr); |
| } |
| if (arr.filter && arr.filter === nativeFilter) { |
| return arr.filter(cb, context); |
| } |
| else { |
| var result = []; |
| for (var i = 0, len = arr.length; i < len; i++) { |
| if (cb.call(context, arr[i], i, arr)) { |
| result.push(arr[i]); |
| } |
| } |
| return result; |
| } |
| } |
| function find(arr, cb, context) { |
| if (!(arr && cb)) { |
| return; |
| } |
| for (var i = 0, len = arr.length; i < len; i++) { |
| if (cb.call(context, arr[i], i, arr)) { |
| return arr[i]; |
| } |
| } |
| } |
| function keys(obj) { |
| if (!obj) { |
| return []; |
| } |
| if (Object.keys) { |
| return Object.keys(obj); |
| } |
| var keyList = []; |
| for (var key in obj) { |
| if (obj.hasOwnProperty(key)) { |
| keyList.push(key); |
| } |
| } |
| return keyList; |
| } |
| function bindPolyfill(func, context) { |
| var args = []; |
| for (var _i = 2; _i < arguments.length; _i++) { |
| args[_i - 2] = arguments[_i]; |
| } |
| return function () { |
| return func.apply(context, args.concat(nativeSlice.call(arguments))); |
| }; |
| } |
| var bind = (protoFunction && isFunction(protoFunction.bind)) |
| ? protoFunction.call.bind(protoFunction.bind) |
| : bindPolyfill; |
| function curry(func) { |
| var args = []; |
| for (var _i = 1; _i < arguments.length; _i++) { |
| args[_i - 1] = arguments[_i]; |
| } |
| return function () { |
| return func.apply(this, args.concat(nativeSlice.call(arguments))); |
| }; |
| } |
| function isArray(value) { |
| if (Array.isArray) { |
| return Array.isArray(value); |
| } |
| return objToString.call(value) === '[object Array]'; |
| } |
| function isFunction(value) { |
| return typeof value === 'function'; |
| } |
| function isString(value) { |
| return typeof value === 'string'; |
| } |
| function isStringSafe(value) { |
| return objToString.call(value) === '[object String]'; |
| } |
| function isNumber(value) { |
| return typeof value === 'number'; |
| } |
| function isObject(value) { |
| var type = typeof value; |
| return type === 'function' || (!!value && type === 'object'); |
| } |
| function isBuiltInObject(value) { |
| return !!BUILTIN_OBJECT[objToString.call(value)]; |
| } |
| function isTypedArray(value) { |
| return !!TYPED_ARRAY[objToString.call(value)]; |
| } |
| function isDom(value) { |
| return typeof value === 'object' |
| && typeof value.nodeType === 'number' |
| && typeof value.ownerDocument === 'object'; |
| } |
| function isGradientObject(value) { |
| return value.colorStops != null; |
| } |
| function isImagePatternObject(value) { |
| return value.image != null; |
| } |
| function isRegExp(value) { |
| return objToString.call(value) === '[object RegExp]'; |
| } |
| function eqNaN(value) { |
| return value !== value; |
| } |
| function retrieve() { |
| var args = []; |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args[_i] = arguments[_i]; |
| } |
| for (var i = 0, len = args.length; i < len; i++) { |
| if (args[i] != null) { |
| return args[i]; |
| } |
| } |
| } |
| function retrieve2(value0, value1) { |
| return value0 != null |
| ? value0 |
| : value1; |
| } |
| function retrieve3(value0, value1, value2) { |
| return value0 != null |
| ? value0 |
| : value1 != null |
| ? value1 |
| : value2; |
| } |
| function slice(arr) { |
| var args = []; |
| for (var _i = 1; _i < arguments.length; _i++) { |
| args[_i - 1] = arguments[_i]; |
| } |
| return nativeSlice.apply(arr, args); |
| } |
| function normalizeCssArray(val) { |
| if (typeof (val) === 'number') { |
| return [val, val, val, val]; |
| } |
| var len = val.length; |
| if (len === 2) { |
| return [val[0], val[1], val[0], val[1]]; |
| } |
| else if (len === 3) { |
| return [val[0], val[1], val[2], val[1]]; |
| } |
| return val; |
| } |
| function assert(condition, message) { |
| if (!condition) { |
| throw new Error(message); |
| } |
| } |
| 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__'; |
| function setAsPrimitive(obj) { |
| obj[primitiveKey] = true; |
| } |
| function isPrimitive(obj) { |
| return obj[primitiveKey]; |
| } |
| var MapPolyfill = (function () { |
| function MapPolyfill() { |
| this.data = {}; |
| } |
| MapPolyfill.prototype["delete"] = function (key) { |
| var existed = this.has(key); |
| if (existed) { |
| delete this.data[key]; |
| } |
| return existed; |
| }; |
| MapPolyfill.prototype.has = function (key) { |
| return this.data.hasOwnProperty(key); |
| }; |
| MapPolyfill.prototype.get = function (key) { |
| return this.data[key]; |
| }; |
| MapPolyfill.prototype.set = function (key, value) { |
| this.data[key] = value; |
| return this; |
| }; |
| MapPolyfill.prototype.keys = function () { |
| return keys(this.data); |
| }; |
| MapPolyfill.prototype.forEach = function (callback) { |
| var data = this.data; |
| for (var key in data) { |
| if (data.hasOwnProperty(key)) { |
| callback(data[key], key); |
| } |
| } |
| }; |
| return MapPolyfill; |
| }()); |
| var isNativeMapSupported = typeof Map === 'function'; |
| function maybeNativeMap() { |
| return (isNativeMapSupported ? new Map() : new MapPolyfill()); |
| } |
| var HashMap = (function () { |
| function HashMap(obj) { |
| var isArr = isArray(obj); |
| this.data = maybeNativeMap(); |
| var thisMap = this; |
| (obj instanceof HashMap) |
| ? obj.each(visit) |
| : (obj && each(obj, visit)); |
| function visit(value, key) { |
| isArr ? thisMap.set(value, key) : thisMap.set(key, value); |
| } |
| } |
| HashMap.prototype.hasKey = function (key) { |
| return this.data.has(key); |
| }; |
| HashMap.prototype.get = function (key) { |
| return this.data.get(key); |
| }; |
| HashMap.prototype.set = function (key, value) { |
| this.data.set(key, value); |
| return value; |
| }; |
| HashMap.prototype.each = function (cb, context) { |
| this.data.forEach(function (value, key) { |
| cb.call(context, value, key); |
| }); |
| }; |
| HashMap.prototype.keys = function () { |
| var keys = this.data.keys(); |
| return isNativeMapSupported |
| ? Array.from(keys) |
| : keys; |
| }; |
| HashMap.prototype.removeKey = function (key) { |
| this.data["delete"](key); |
| }; |
| return HashMap; |
| }()); |
| 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 (var i = 0; i < b.length; i++) { |
| newArray[i + offset] = b[i]; |
| } |
| return newArray; |
| } |
| function createObject(proto, properties) { |
| var obj; |
| if (Object.create) { |
| obj = Object.create(proto); |
| } |
| else { |
| var StyleCtor = function () { }; |
| StyleCtor.prototype = proto; |
| obj = new StyleCtor(); |
| } |
| if (properties) { |
| extend(obj, properties); |
| } |
| return obj; |
| } |
| function disableUserSelect(dom) { |
| var domStyle = dom.style; |
| domStyle.webkitUserSelect = 'none'; |
| domStyle.userSelect = 'none'; |
| domStyle.webkitTapHighlightColor = 'rgba(0,0,0,0)'; |
| domStyle['-webkit-touch-callout'] = 'none'; |
| } |
| function hasOwn(own, prop) { |
| return own.hasOwnProperty(prop); |
| } |
| function noop() { } |
| var RADIAN_TO_DEGREE = 180 / Math.PI; |
| |
| var util = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| guid: guid, |
| logError: logError, |
| clone: clone, |
| merge: merge, |
| mergeAll: mergeAll, |
| extend: extend, |
| defaults: defaults, |
| createCanvas: createCanvas, |
| indexOf: indexOf, |
| inherits: inherits, |
| mixin: mixin, |
| isArrayLike: isArrayLike, |
| each: each, |
| map: map, |
| reduce: reduce, |
| filter: filter, |
| find: find, |
| keys: keys, |
| bind: bind, |
| curry: curry, |
| isArray: isArray, |
| isFunction: isFunction, |
| isString: isString, |
| isStringSafe: isStringSafe, |
| isNumber: isNumber, |
| isObject: isObject, |
| isBuiltInObject: isBuiltInObject, |
| isTypedArray: isTypedArray, |
| isDom: isDom, |
| isGradientObject: isGradientObject, |
| isImagePatternObject: isImagePatternObject, |
| isRegExp: isRegExp, |
| eqNaN: eqNaN, |
| retrieve: retrieve, |
| retrieve2: retrieve2, |
| retrieve3: retrieve3, |
| slice: slice, |
| normalizeCssArray: normalizeCssArray, |
| assert: assert, |
| trim: trim, |
| setAsPrimitive: setAsPrimitive, |
| isPrimitive: isPrimitive, |
| HashMap: HashMap, |
| createHashMap: createHashMap, |
| concatArray: concatArray, |
| createObject: createObject, |
| disableUserSelect: disableUserSelect, |
| hasOwn: hasOwn, |
| noop: noop, |
| RADIAN_TO_DEGREE: RADIAN_TO_DEGREE |
| }); |
| |
| function create(x, y) { |
| if (x == null) { |
| x = 0; |
| } |
| if (y == null) { |
| y = 0; |
| } |
| return [x, y]; |
| } |
| function copy(out, v) { |
| out[0] = v[0]; |
| out[1] = v[1]; |
| return out; |
| } |
| function clone$1(v) { |
| return [v[0], v[1]]; |
| } |
| function set(out, a, b) { |
| out[0] = a; |
| out[1] = b; |
| return out; |
| } |
| function add(out, v1, v2) { |
| out[0] = v1[0] + v2[0]; |
| out[1] = v1[1] + v2[1]; |
| return out; |
| } |
| function scaleAndAdd(out, v1, v2, a) { |
| out[0] = v1[0] + v2[0] * a; |
| out[1] = v1[1] + v2[1] * a; |
| return out; |
| } |
| function sub(out, v1, v2) { |
| out[0] = v1[0] - v2[0]; |
| out[1] = v1[1] - v2[1]; |
| return out; |
| } |
| function len(v) { |
| return Math.sqrt(lenSquare(v)); |
| } |
| var length = len; |
| function lenSquare(v) { |
| return v[0] * v[0] + v[1] * v[1]; |
| } |
| var lengthSquare = lenSquare; |
| function mul(out, v1, v2) { |
| out[0] = v1[0] * v2[0]; |
| out[1] = v1[1] * v2[1]; |
| return out; |
| } |
| function div(out, v1, v2) { |
| out[0] = v1[0] / v2[0]; |
| out[1] = v1[1] / v2[1]; |
| return out; |
| } |
| function dot(v1, v2) { |
| return v1[0] * v2[0] + v1[1] * v2[1]; |
| } |
| function scale(out, v, s) { |
| out[0] = v[0] * s; |
| out[1] = v[1] * s; |
| return out; |
| } |
| 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; |
| } |
| 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; |
| function distanceSquare(v1, v2) { |
| return (v1[0] - v2[0]) * (v1[0] - v2[0]) |
| + (v1[1] - v2[1]) * (v1[1] - v2[1]); |
| } |
| var distSquare = distanceSquare; |
| function negate(out, v) { |
| out[0] = -v[0]; |
| out[1] = -v[1]; |
| return out; |
| } |
| 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; |
| } |
| 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; |
| } |
| function min(out, v1, v2) { |
| out[0] = Math.min(v1[0], v2[0]); |
| out[1] = Math.min(v1[1], v2[1]); |
| return out; |
| } |
| 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 = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| 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 |
| }); |
| |
| var Param = (function () { |
| function Param(target, e) { |
| this.target = target; |
| this.topTarget = e && e.topTarget; |
| } |
| return Param; |
| }()); |
| var Draggable = (function () { |
| function Draggable(handler) { |
| this.handler = handler; |
| handler.on('mousedown', this._dragStart, this); |
| handler.on('mousemove', this._drag, this); |
| handler.on('mouseup', this._dragEnd, this); |
| } |
| Draggable.prototype._dragStart = function (e) { |
| var draggingTarget = e.target; |
| while (draggingTarget && !draggingTarget.draggable) { |
| draggingTarget = draggingTarget.parent || draggingTarget.__hostTarget; |
| } |
| if (draggingTarget) { |
| this._draggingTarget = draggingTarget; |
| draggingTarget.dragging = true; |
| this._x = e.offsetX; |
| this._y = e.offsetY; |
| this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragstart', e.event); |
| } |
| }; |
| Draggable.prototype._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.handler.dispatchToElement(new Param(draggingTarget, e), 'drag', e.event); |
| var dropTarget = this.handler.findHover(x, y, draggingTarget).target; |
| var lastDropTarget = this._dropTarget; |
| this._dropTarget = dropTarget; |
| if (draggingTarget !== dropTarget) { |
| if (lastDropTarget && dropTarget !== lastDropTarget) { |
| this.handler.dispatchToElement(new Param(lastDropTarget, e), 'dragleave', e.event); |
| } |
| if (dropTarget && dropTarget !== lastDropTarget) { |
| this.handler.dispatchToElement(new Param(dropTarget, e), 'dragenter', e.event); |
| } |
| } |
| } |
| }; |
| Draggable.prototype._dragEnd = function (e) { |
| var draggingTarget = this._draggingTarget; |
| if (draggingTarget) { |
| draggingTarget.dragging = false; |
| } |
| this.handler.dispatchToElement(new Param(draggingTarget, e), 'dragend', e.event); |
| if (this._dropTarget) { |
| this.handler.dispatchToElement(new Param(this._dropTarget, e), 'drop', e.event); |
| } |
| this._draggingTarget = null; |
| this._dropTarget = null; |
| }; |
| return Draggable; |
| }()); |
| |
| var Eventful = (function () { |
| function Eventful(eventProcessors) { |
| if (eventProcessors) { |
| this._$eventProcessor = eventProcessors; |
| } |
| } |
| Eventful.prototype.on = function (event, query, handler, context) { |
| if (!this._$handlers) { |
| this._$handlers = {}; |
| } |
| var _h = this._$handlers; |
| if (typeof query === 'function') { |
| context = handler; |
| handler = query; |
| query = null; |
| } |
| if (!handler || !event) { |
| return this; |
| } |
| var eventProcessor = this._$eventProcessor; |
| if (query != null && eventProcessor && eventProcessor.normalizeQuery) { |
| query = eventProcessor.normalizeQuery(query); |
| } |
| if (!_h[event]) { |
| _h[event] = []; |
| } |
| for (var i = 0; i < _h[event].length; i++) { |
| if (_h[event][i].h === handler) { |
| return this; |
| } |
| } |
| var wrap = { |
| h: handler, |
| query: query, |
| ctx: (context || this), |
| 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 this; |
| }; |
| Eventful.prototype.isSilent = function (eventName) { |
| var _h = this._$handlers; |
| return !_h || !_h[eventName] || !_h[eventName].length; |
| }; |
| Eventful.prototype.off = function (eventType, handler) { |
| var _h = this._$handlers; |
| if (!_h) { |
| return this; |
| } |
| if (!eventType) { |
| this._$handlers = {}; |
| return this; |
| } |
| if (handler) { |
| if (_h[eventType]) { |
| var newList = []; |
| for (var i = 0, l = _h[eventType].length; i < l; i++) { |
| if (_h[eventType][i].h !== handler) { |
| newList.push(_h[eventType][i]); |
| } |
| } |
| _h[eventType] = newList; |
| } |
| if (_h[eventType] && _h[eventType].length === 0) { |
| delete _h[eventType]; |
| } |
| } |
| else { |
| delete _h[eventType]; |
| } |
| return this; |
| }; |
| Eventful.prototype.trigger = function (eventType) { |
| var args = []; |
| for (var _i = 1; _i < arguments.length; _i++) { |
| args[_i - 1] = arguments[_i]; |
| } |
| if (!this._$handlers) { |
| return this; |
| } |
| var _h = this._$handlers[eventType]; |
| var eventProcessor = this._$eventProcessor; |
| if (_h) { |
| var argLen = args.length; |
| var len = _h.length; |
| for (var i = 0; i < len; i++) { |
| var hItem = _h[i]; |
| if (eventProcessor |
| && eventProcessor.filter |
| && hItem.query != null |
| && !eventProcessor.filter(eventType, hItem.query)) { |
| continue; |
| } |
| switch (argLen) { |
| case 0: |
| hItem.h.call(hItem.ctx); |
| break; |
| case 1: |
| hItem.h.call(hItem.ctx, args[0]); |
| break; |
| case 2: |
| hItem.h.call(hItem.ctx, args[0], args[1]); |
| break; |
| default: |
| hItem.h.apply(hItem.ctx, args); |
| break; |
| } |
| } |
| } |
| eventProcessor && eventProcessor.afterTrigger |
| && eventProcessor.afterTrigger(eventType); |
| return this; |
| }; |
| Eventful.prototype.triggerWithContext = function (type) { |
| var args = []; |
| for (var _i = 1; _i < arguments.length; _i++) { |
| args[_i - 1] = arguments[_i]; |
| } |
| if (!this._$handlers) { |
| return this; |
| } |
| var _h = this._$handlers[type]; |
| var eventProcessor = this._$eventProcessor; |
| if (_h) { |
| var argLen = args.length; |
| var ctx = args[argLen - 1]; |
| var len = _h.length; |
| for (var i = 0; i < len; i++) { |
| var hItem = _h[i]; |
| if (eventProcessor |
| && eventProcessor.filter |
| && hItem.query != null |
| && !eventProcessor.filter(type, hItem.query)) { |
| continue; |
| } |
| switch (argLen) { |
| case 0: |
| hItem.h.call(ctx); |
| break; |
| case 1: |
| hItem.h.call(ctx, args[0]); |
| break; |
| case 2: |
| hItem.h.call(ctx, args[0], args[1]); |
| break; |
| default: |
| hItem.h.apply(ctx, args.slice(1, argLen - 1)); |
| break; |
| } |
| } |
| } |
| eventProcessor && eventProcessor.afterTrigger |
| && eventProcessor.afterTrigger(type); |
| return this; |
| }; |
| return Eventful; |
| }()); |
| |
| 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) { |
| 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] |
| * determinant(rows, rank - 1, subRowStart, subRowMask, colMask | colTag, detCache); |
| colLocalIdx++; |
| } |
| } |
| detCache[cacheKey] = sum; |
| return sum; |
| } |
| 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) { |
| return; |
| } |
| 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) |
| * 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'; |
| function transformCoordWithViewport(out, el, inX, inY, inverse) { |
| if (el.getBoundingClientRect && env.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', |
| 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 srcCoords = []; |
| var destCoords = []; |
| var oldCoordTheSame = true; |
| 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); |
| } |
| return (oldCoordTheSame && transformer) |
| ? transformer |
| : (saved.srcCoords = srcCoords, |
| saved[transformerName] = inverse |
| ? buildTransformer(destCoords, srcCoords) |
| : buildTransformer(srcCoords, destCoords)); |
| } |
| function isCanvasEl(el) { |
| return el.nodeName.toUpperCase() === 'CANVAS'; |
| } |
| var replaceReg = /([&<>"'])/g; |
| var replaceMap = { |
| '&': '&', |
| '<': '<', |
| '>': '>', |
| '"': '"', |
| '\'': ''' |
| }; |
| function encodeHTML(source) { |
| return source == null |
| ? '' |
| : (source + '').replace(replaceReg, function (str, c) { |
| return replaceMap[c]; |
| }); |
| } |
| |
| var MOUSE_EVENT_REG = /^(?:mouse|pointer|contextmenu|drag|drop)|click/; |
| var _calcOut = []; |
| var firefoxNotSupportOffsetXY = env.browser.firefox |
| && +env.browser.version.split('.')[0] < 39; |
| function clientToLocal(el, e, out, calculate) { |
| out = out || {}; |
| if (calculate) { |
| calculateZrXY(el, e, out); |
| } |
| else if (firefoxNotSupportOffsetXY |
| && e.layerX != null |
| && e.layerX !== e.offsetX) { |
| out.zrX = e.layerX; |
| out.zrY = e.layerY; |
| } |
| else if (e.offsetX != null) { |
| out.zrX = e.offsetX; |
| out.zrY = e.offsetY; |
| } |
| else { |
| calculateZrXY(el, e, out); |
| } |
| return out; |
| } |
| function calculateZrXY(el, e, out) { |
| if (env.domSupported && el.getBoundingClientRect) { |
| var ex = e.clientX; |
| var ey = e.clientY; |
| if (isCanvasEl(el)) { |
| 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; |
| } |
| function getNativeEvent(e) { |
| return e |
| || window.event; |
| } |
| 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); |
| var wheelDelta = getWheelDeltaMayPolyfill(e); |
| e.zrDelta = wheelDelta ? wheelDelta / 120 : -(e.detail || 0) / 3; |
| } |
| else { |
| var touch = eventType !== 'touchend' |
| ? e.targetTouches[0] |
| : e.changedTouches[0]; |
| touch && clientToLocal(el, touch, e, calculate); |
| } |
| 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))); |
| } |
| return e; |
| } |
| function getWheelDeltaMayPolyfill(e) { |
| var rawWheelDelta = e.wheelDelta; |
| if (rawWheelDelta) { |
| return rawWheelDelta; |
| } |
| var deltaX = e.deltaX; |
| var deltaY = e.deltaY; |
| if (deltaX == null || deltaY == null) { |
| return rawWheelDelta; |
| } |
| var delta = deltaY !== 0 ? Math.abs(deltaY) : Math.abs(deltaX); |
| var sign = deltaY > 0 ? -1 |
| : deltaY < 0 ? 1 |
| : deltaX > 0 ? -1 |
| : 1; |
| return 3 * delta * sign; |
| } |
| function addEventListener(el, name, handler, opt) { |
| el.addEventListener(name, handler, opt); |
| } |
| function removeEventListener(el, name, handler, opt) { |
| el.removeEventListener(name, handler, opt); |
| } |
| var stop = function (e) { |
| e.preventDefault(); |
| e.stopPropagation(); |
| e.cancelBubble = true; |
| }; |
| |
| var GestureMgr = (function () { |
| function GestureMgr() { |
| this._track = []; |
| } |
| GestureMgr.prototype.recognize = function (event, target, root) { |
| this._doTrack(event, target, root); |
| return this._recognize(event); |
| }; |
| GestureMgr.prototype.clear = function () { |
| this._track.length = 0; |
| return this; |
| }; |
| GestureMgr.prototype._doTrack = function (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; i < len; i++) { |
| var touch = touches[i]; |
| var pos = clientToLocal(root, touch, {}); |
| trackItem.points.push([pos.zrX, pos.zrY]); |
| trackItem.touches.push(touch); |
| } |
| this._track.push(trackItem); |
| }; |
| GestureMgr.prototype._recognize = function (event) { |
| for (var eventName in recognizers) { |
| if (recognizers.hasOwnProperty(eventName)) { |
| var gestureInfo = recognizers[eventName](this._track, event); |
| if (gestureInfo) { |
| return gestureInfo; |
| } |
| } |
| } |
| }; |
| return GestureMgr; |
| }()); |
| function dist$1(pointPair) { |
| var dx = pointPair[1][0] - pointPair[0][0]; |
| var dy = pointPair[1][1] - pointPair[0][1]; |
| return Math.sqrt(dx * dx + dy * dy); |
| } |
| function center(pointPair) { |
| return [ |
| (pointPair[0][0] + pointPair[1][0]) / 2, |
| (pointPair[0][1] + pointPair[1][1]) / 2 |
| ]; |
| } |
| var recognizers = { |
| pinch: function (tracks, event) { |
| var trackLen = tracks.length; |
| if (!trackLen) { |
| return; |
| } |
| var pinchEnd = (tracks[trackLen - 1] || {}).points; |
| var pinchPre = (tracks[trackLen - 2] || {}).points || pinchEnd; |
| if (pinchPre |
| && pinchPre.length > 1 |
| && 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: tracks[0].target, |
| event: event |
| }; |
| } |
| } |
| }; |
| |
| function create$1() { |
| return [1, 0, 0, 1, 0, 0]; |
| } |
| function identity(out) { |
| out[0] = 1; |
| out[1] = 0; |
| out[2] = 0; |
| out[3] = 1; |
| out[4] = 0; |
| out[5] = 0; |
| return out; |
| } |
| 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; |
| } |
| function mul$1(out, m1, m2) { |
| var 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; |
| } |
| 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; |
| } |
| 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; |
| } |
| 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; |
| } |
| 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; |
| } |
| function clone$2(a) { |
| var b = create$1(); |
| copy$1(b, a); |
| return b; |
| } |
| |
| var matrix = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| create: create$1, |
| identity: identity, |
| copy: copy$1, |
| mul: mul$1, |
| translate: translate, |
| rotate: rotate, |
| scale: scale$1, |
| invert: invert, |
| clone: clone$2 |
| }); |
| |
| var Point = (function () { |
| function Point(x, y) { |
| this.x = x || 0; |
| this.y = y || 0; |
| } |
| Point.prototype.copy = function (other) { |
| this.x = other.x; |
| this.y = other.y; |
| return this; |
| }; |
| Point.prototype.clone = function () { |
| return new Point(this.x, this.y); |
| }; |
| Point.prototype.set = function (x, y) { |
| this.x = x; |
| this.y = y; |
| return this; |
| }; |
| Point.prototype.equal = function (other) { |
| return other.x === this.x && other.y === this.y; |
| }; |
| Point.prototype.add = function (other) { |
| this.x += other.x; |
| this.y += other.y; |
| return this; |
| }; |
| Point.prototype.scale = function (scalar) { |
| this.x *= scalar; |
| this.y *= scalar; |
| }; |
| Point.prototype.scaleAndAdd = function (other, scalar) { |
| this.x += other.x * scalar; |
| this.y += other.y * scalar; |
| }; |
| Point.prototype.sub = function (other) { |
| this.x -= other.x; |
| this.y -= other.y; |
| return this; |
| }; |
| Point.prototype.dot = function (other) { |
| return this.x * other.x + this.y * other.y; |
| }; |
| Point.prototype.len = function () { |
| return Math.sqrt(this.x * this.x + this.y * this.y); |
| }; |
| Point.prototype.lenSquare = function () { |
| return this.x * this.x + this.y * this.y; |
| }; |
| Point.prototype.normalize = function () { |
| var len = this.len(); |
| this.x /= len; |
| this.y /= len; |
| return this; |
| }; |
| Point.prototype.distance = function (other) { |
| var dx = this.x - other.x; |
| var dy = this.y - other.y; |
| return Math.sqrt(dx * dx + dy * dy); |
| }; |
| Point.prototype.distanceSquare = function (other) { |
| var dx = this.x - other.x; |
| var dy = this.y - other.y; |
| return dx * dx + dy * dy; |
| }; |
| Point.prototype.negate = function () { |
| this.x = -this.x; |
| this.y = -this.y; |
| return this; |
| }; |
| Point.prototype.transform = function (m) { |
| if (!m) { |
| return; |
| } |
| var x = this.x; |
| var y = this.y; |
| this.x = m[0] * x + m[2] * y + m[4]; |
| this.y = m[1] * x + m[3] * y + m[5]; |
| return this; |
| }; |
| Point.prototype.toArray = function (out) { |
| out[0] = this.x; |
| out[1] = this.y; |
| return out; |
| }; |
| Point.prototype.fromArray = function (input) { |
| this.x = input[0]; |
| this.y = input[1]; |
| }; |
| Point.set = function (p, x, y) { |
| p.x = x; |
| p.y = y; |
| }; |
| Point.copy = function (p, p2) { |
| p.x = p2.x; |
| p.y = p2.y; |
| }; |
| Point.len = function (p) { |
| return Math.sqrt(p.x * p.x + p.y * p.y); |
| }; |
| Point.lenSquare = function (p) { |
| return p.x * p.x + p.y * p.y; |
| }; |
| Point.dot = function (p0, p1) { |
| return p0.x * p1.x + p0.y * p1.y; |
| }; |
| Point.add = function (out, p0, p1) { |
| out.x = p0.x + p1.x; |
| out.y = p0.y + p1.y; |
| }; |
| Point.sub = function (out, p0, p1) { |
| out.x = p0.x - p1.x; |
| out.y = p0.y - p1.y; |
| }; |
| Point.scale = function (out, p0, scalar) { |
| out.x = p0.x * scalar; |
| out.y = p0.y * scalar; |
| }; |
| Point.scaleAndAdd = function (out, p0, p1, scalar) { |
| out.x = p0.x + p1.x * scalar; |
| out.y = p0.y + p1.y * scalar; |
| }; |
| Point.lerp = function (out, p0, p1, t) { |
| var onet = 1 - t; |
| out.x = onet * p0.x + t * p1.x; |
| out.y = onet * p0.y + t * p1.y; |
| }; |
| return Point; |
| }()); |
| |
| var mathMin = Math.min; |
| var mathMax = Math.max; |
| var lt = new Point(); |
| var rb = new Point(); |
| var lb = new Point(); |
| var rt = new Point(); |
| var minTv = new Point(); |
| var maxTv = new Point(); |
| var BoundingRect = (function () { |
| function BoundingRect(x, y, width, height) { |
| if (width < 0) { |
| x = x + width; |
| width = -width; |
| } |
| if (height < 0) { |
| y = y + height; |
| height = -height; |
| } |
| this.x = x; |
| this.y = y; |
| this.width = width; |
| this.height = height; |
| } |
| BoundingRect.prototype.union = function (other) { |
| var x = mathMin(other.x, this.x); |
| var y = mathMin(other.y, this.y); |
| if (isFinite(this.x) && isFinite(this.width)) { |
| this.width = mathMax(other.x + other.width, this.x + this.width) - x; |
| } |
| else { |
| this.width = other.width; |
| } |
| if (isFinite(this.y) && isFinite(this.height)) { |
| this.height = mathMax(other.y + other.height, this.y + this.height) - y; |
| } |
| else { |
| this.height = other.height; |
| } |
| this.x = x; |
| this.y = y; |
| }; |
| BoundingRect.prototype.applyTransform = function (m) { |
| BoundingRect.applyTransform(this, this, m); |
| }; |
| BoundingRect.prototype.calculateTransform = function (b) { |
| var a = this; |
| var sx = b.width / a.width; |
| var sy = b.height / a.height; |
| var m = create$1(); |
| translate(m, m, [-a.x, -a.y]); |
| scale$1(m, m, [sx, sy]); |
| translate(m, m, [b.x, b.y]); |
| return m; |
| }; |
| BoundingRect.prototype.intersect = function (b, mtv) { |
| if (!b) { |
| return false; |
| } |
| if (!(b instanceof BoundingRect)) { |
| b = 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; |
| var overlap = !(ax1 < bx0 || bx1 < ax0 || ay1 < by0 || by1 < ay0); |
| if (mtv) { |
| var dMin = Infinity; |
| var dMax = 0; |
| var d0 = Math.abs(ax1 - bx0); |
| var d1 = Math.abs(bx1 - ax0); |
| var d2 = Math.abs(ay1 - by0); |
| var d3 = Math.abs(by1 - ay0); |
| var dx = Math.min(d0, d1); |
| var dy = Math.min(d2, d3); |
| if (ax1 < bx0 || bx1 < ax0) { |
| if (dx > dMax) { |
| dMax = dx; |
| if (d0 < d1) { |
| Point.set(maxTv, -d0, 0); |
| } |
| else { |
| Point.set(maxTv, d1, 0); |
| } |
| } |
| } |
| else { |
| if (dx < dMin) { |
| dMin = dx; |
| if (d0 < d1) { |
| Point.set(minTv, d0, 0); |
| } |
| else { |
| Point.set(minTv, -d1, 0); |
| } |
| } |
| } |
| if (ay1 < by0 || by1 < ay0) { |
| if (dy > dMax) { |
| dMax = dy; |
| if (d2 < d3) { |
| Point.set(maxTv, 0, -d2); |
| } |
| else { |
| Point.set(maxTv, 0, d3); |
| } |
| } |
| } |
| else { |
| if (dx < dMin) { |
| dMin = dx; |
| if (d2 < d3) { |
| Point.set(minTv, 0, d2); |
| } |
| else { |
| Point.set(minTv, 0, -d3); |
| } |
| } |
| } |
| } |
| if (mtv) { |
| Point.copy(mtv, overlap ? minTv : maxTv); |
| } |
| return overlap; |
| }; |
| BoundingRect.prototype.contain = function (x, y) { |
| var rect = this; |
| return x >= rect.x |
| && x <= (rect.x + rect.width) |
| && y >= rect.y |
| && y <= (rect.y + rect.height); |
| }; |
| BoundingRect.prototype.clone = function () { |
| return new BoundingRect(this.x, this.y, this.width, this.height); |
| }; |
| BoundingRect.prototype.copy = function (other) { |
| BoundingRect.copy(this, other); |
| }; |
| BoundingRect.prototype.plain = function () { |
| return { |
| x: this.x, |
| y: this.y, |
| width: this.width, |
| height: this.height |
| }; |
| }; |
| BoundingRect.prototype.isFinite = function () { |
| return isFinite(this.x) |
| && isFinite(this.y) |
| && isFinite(this.width) |
| && isFinite(this.height); |
| }; |
| BoundingRect.prototype.isZero = function () { |
| return this.width === 0 || this.height === 0; |
| }; |
| BoundingRect.create = function (rect) { |
| return new BoundingRect(rect.x, rect.y, rect.width, rect.height); |
| }; |
| BoundingRect.copy = function (target, source) { |
| target.x = source.x; |
| target.y = source.y; |
| target.width = source.width; |
| target.height = source.height; |
| }; |
| BoundingRect.applyTransform = function (target, source, m) { |
| if (!m) { |
| if (target !== source) { |
| BoundingRect.copy(target, source); |
| } |
| return; |
| } |
| if (m[1] < 1e-5 && m[1] > -1e-5 && m[2] < 1e-5 && m[2] > -1e-5) { |
| var sx = m[0]; |
| var sy = m[3]; |
| var tx = m[4]; |
| var ty = m[5]; |
| target.x = source.x * sx + tx; |
| target.y = source.y * sy + ty; |
| target.width = source.width * sx; |
| target.height = source.height * sy; |
| if (target.width < 0) { |
| target.x += target.width; |
| target.width = -target.width; |
| } |
| if (target.height < 0) { |
| target.y += target.height; |
| target.height = -target.height; |
| } |
| return; |
| } |
| lt.x = lb.x = source.x; |
| lt.y = rt.y = source.y; |
| rb.x = rt.x = source.x + source.width; |
| rb.y = lb.y = source.y + source.height; |
| lt.transform(m); |
| rt.transform(m); |
| rb.transform(m); |
| lb.transform(m); |
| target.x = mathMin(lt.x, rb.x, lb.x, rt.x); |
| target.y = mathMin(lt.y, rb.y, lb.y, rt.y); |
| var maxX = mathMax(lt.x, rb.x, lb.x, rt.x); |
| var maxY = mathMax(lt.y, rb.y, lb.y, rt.y); |
| target.width = maxX - target.x; |
| target.height = maxY - target.y; |
| }; |
| return BoundingRect; |
| }()); |
| |
| var SILENT = 'silent'; |
| function makeEventPacket(eveType, targetInfo, event) { |
| return { |
| type: eveType, |
| event: event, |
| target: targetInfo.target, |
| topTarget: 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); |
| } |
| var EmptyProxy = (function (_super) { |
| __extends(EmptyProxy, _super); |
| function EmptyProxy() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| _this.handler = null; |
| return _this; |
| } |
| EmptyProxy.prototype.dispose = function () { }; |
| EmptyProxy.prototype.setCursor = function () { }; |
| return EmptyProxy; |
| }(Eventful)); |
| var HoveredResult = (function () { |
| function HoveredResult(x, y) { |
| this.x = x; |
| this.y = y; |
| } |
| return HoveredResult; |
| }()); |
| var handlerNames = [ |
| 'click', 'dblclick', 'mousewheel', 'mouseout', |
| 'mouseup', 'mousedown', 'mousemove', 'contextmenu' |
| ]; |
| var tmpRect = new BoundingRect(0, 0, 0, 0); |
| var Handler = (function (_super) { |
| __extends(Handler, _super); |
| function Handler(storage, painter, proxy, painterRoot, pointerSize) { |
| var _this = _super.call(this) || this; |
| _this._hovered = new HoveredResult(0, 0); |
| _this.storage = storage; |
| _this.painter = painter; |
| _this.painterRoot = painterRoot; |
| _this._pointerSize = pointerSize; |
| proxy = proxy || new EmptyProxy(); |
| _this.proxy = null; |
| _this.setHandlerProxy(proxy); |
| _this._draggingMgr = new Draggable(_this); |
| return _this; |
| } |
| Handler.prototype.setHandlerProxy = function (proxy) { |
| if (this.proxy) { |
| this.proxy.dispose(); |
| } |
| if (proxy) { |
| each(handlerNames, function (name) { |
| proxy.on && proxy.on(name, this[name], this); |
| }, this); |
| proxy.handler = this; |
| } |
| this.proxy = proxy; |
| }; |
| Handler.prototype.mousemove = function (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 && !lastHoveredTarget.__zr) { |
| lastHovered = this.findHover(lastHovered.x, lastHovered.y); |
| lastHoveredTarget = lastHovered.target; |
| } |
| var hovered = this._hovered = isOutside ? new HoveredResult(x, y) : this.findHover(x, y); |
| var hoveredTarget = hovered.target; |
| var proxy = this.proxy; |
| proxy.setCursor && proxy.setCursor(hoveredTarget ? hoveredTarget.cursor : 'default'); |
| if (lastHoveredTarget && hoveredTarget !== lastHoveredTarget) { |
| this.dispatchToElement(lastHovered, 'mouseout', event); |
| } |
| this.dispatchToElement(hovered, 'mousemove', event); |
| if (hoveredTarget && hoveredTarget !== lastHoveredTarget) { |
| this.dispatchToElement(hovered, 'mouseover', event); |
| } |
| }; |
| Handler.prototype.mouseout = function (event) { |
| var eventControl = event.zrEventControl; |
| if (eventControl !== 'only_globalout') { |
| this.dispatchToElement(this._hovered, 'mouseout', event); |
| } |
| if (eventControl !== 'no_globalout') { |
| this.trigger('globalout', { type: 'globalout', event: event }); |
| } |
| }; |
| Handler.prototype.resize = function () { |
| this._hovered = new HoveredResult(0, 0); |
| }; |
| Handler.prototype.dispatch = function (eventName, eventArgs) { |
| var handler = this[eventName]; |
| handler && handler.call(this, eventArgs); |
| }; |
| Handler.prototype.dispose = function () { |
| this.proxy.dispose(); |
| this.storage = null; |
| this.proxy = null; |
| this.painter = null; |
| }; |
| Handler.prototype.setCursorStyle = function (cursorStyle) { |
| var proxy = this.proxy; |
| proxy.setCursor && proxy.setCursor(cursorStyle); |
| }; |
| Handler.prototype.dispatchToElement = function (targetInfo, eventName, event) { |
| targetInfo = targetInfo || {}; |
| var el = targetInfo.target; |
| if (el && el.silent) { |
| return; |
| } |
| var eventKey = ('on' + eventName); |
| var eventPacket = makeEventPacket(eventName, targetInfo, event); |
| while (el) { |
| el[eventKey] |
| && (eventPacket.cancelBubble = !!el[eventKey].call(el, eventPacket)); |
| el.trigger(eventName, eventPacket); |
| el = el.__hostTarget ? el.__hostTarget : el.parent; |
| if (eventPacket.cancelBubble) { |
| break; |
| } |
| } |
| if (!eventPacket.cancelBubble) { |
| this.trigger(eventName, eventPacket); |
| if (this.painter && this.painter.eachOtherLayer) { |
| this.painter.eachOtherLayer(function (layer) { |
| if (typeof (layer[eventKey]) === 'function') { |
| layer[eventKey].call(layer, eventPacket); |
| } |
| if (layer.trigger) { |
| layer.trigger(eventName, eventPacket); |
| } |
| }); |
| } |
| } |
| }; |
| Handler.prototype.findHover = function (x, y, exclude) { |
| var list = this.storage.getDisplayList(); |
| var out = new HoveredResult(x, y); |
| setHoverTarget(list, out, x, y, exclude); |
| if (this._pointerSize && !out.target) { |
| var candidates = []; |
| var pointerSize = this._pointerSize; |
| var targetSizeHalf = pointerSize / 2; |
| var pointerRect = new BoundingRect(x - targetSizeHalf, y - targetSizeHalf, pointerSize, pointerSize); |
| for (var i = list.length - 1; i >= 0; i--) { |
| var el = list[i]; |
| if (el !== exclude |
| && !el.ignore |
| && !el.ignoreCoarsePointer |
| && (!el.parent || !el.parent.ignoreCoarsePointer)) { |
| tmpRect.copy(el.getBoundingRect()); |
| if (el.transform) { |
| tmpRect.applyTransform(el.transform); |
| } |
| if (tmpRect.intersect(pointerRect)) { |
| candidates.push(el); |
| } |
| } |
| } |
| if (candidates.length) { |
| var rStep = 4; |
| var thetaStep = Math.PI / 12; |
| var PI2 = Math.PI * 2; |
| for (var r = 0; r < targetSizeHalf; r += rStep) { |
| for (var theta = 0; theta < PI2; theta += thetaStep) { |
| var x1 = x + r * Math.cos(theta); |
| var y1 = y + r * Math.sin(theta); |
| setHoverTarget(candidates, out, x1, y1, exclude); |
| if (out.target) { |
| return out; |
| } |
| } |
| } |
| } |
| } |
| return out; |
| }; |
| Handler.prototype.processGesture = function (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(); |
| if (gestureInfo) { |
| var type = gestureInfo.type; |
| event.gestureEvent = type; |
| var res = new HoveredResult(); |
| res.target = gestureInfo.target; |
| this.dispatchToElement(res, type, gestureInfo.event); |
| } |
| }; |
| return Handler; |
| }(Eventful)); |
| each(['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) { |
| hovered = this.findHover(x, y); |
| hoveredTarget = hovered.target; |
| } |
| if (name === 'mousedown') { |
| this._downEl = hoveredTarget; |
| this._downPoint = [event.zrX, event.zrY]; |
| this._upEl = hoveredTarget; |
| } |
| else if (name === 'mouseup') { |
| this._upEl = hoveredTarget; |
| } |
| else if (name === 'click') { |
| if (this._downEl !== this._upEl |
| || !this._downPoint |
| || 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 = void 0; |
| var ignoreClip = false; |
| while (el) { |
| if (el.ignoreClip) { |
| ignoreClip = true; |
| } |
| if (!ignoreClip) { |
| var clipPath = el.getClipPath(); |
| if (clipPath && !clipPath.contain(x, y)) { |
| return false; |
| } |
| if (el.silent) { |
| isSilent = true; |
| } |
| } |
| var hostEl = el.__hostTarget; |
| el = hostEl ? hostEl : el.parent; |
| } |
| return isSilent ? SILENT : true; |
| } |
| return false; |
| } |
| function setHoverTarget(list, out, x, y, exclude) { |
| for (var i = list.length - 1; i >= 0; i--) { |
| var el = list[i]; |
| var hoverCheckResult = void 0; |
| if (el !== exclude |
| && !el.ignore |
| && (hoverCheckResult = isHover(el, x, y))) { |
| !out.topTarget && (out.topTarget = el); |
| if (hoverCheckResult !== SILENT) { |
| out.target = el; |
| break; |
| } |
| } |
| } |
| } |
| function isOutsideBoundary(handlerInstance, x, y) { |
| var painter = handlerInstance.painter; |
| return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight(); |
| } |
| |
| var DEFAULT_MIN_MERGE = 32; |
| var DEFAULT_MIN_GALLOPING = 7; |
| function minRunLength(n) { |
| var r = 0; |
| while (n >= 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 < hi && compare(array[runHi], array[runHi - 1]) < 0) { |
| runHi++; |
| } |
| reverseRun(array, lo, runHi); |
| } |
| else { |
| while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) { |
| runHi++; |
| } |
| } |
| return runHi - lo; |
| } |
| function reverseRun(array, lo, hi) { |
| hi--; |
| while (lo < hi) { |
| var t = array[lo]; |
| array[lo++] = array[hi]; |
| array[hi--] = t; |
| } |
| } |
| function binaryInsertionSort(array, lo, hi, start, compare) { |
| if (start === lo) { |
| start++; |
| } |
| for (; start < hi; start++) { |
| var pivot = array[start]; |
| var left = lo; |
| var right = start; |
| var mid; |
| while (left < right) { |
| mid = left + right >>> 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 (offset < maxOffset && compare(value, array[start + hint + offset]) > 0) { |
| 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 (offset < maxOffset && compare(value, array[start + hint - offset]) <= 0) { |
| lastOffset = offset; |
| offset = (offset << 1) + 1; |
| if (offset <= 0) { |
| offset = maxOffset; |
| } |
| } |
| if (offset > maxOffset) { |
| offset = maxOffset; |
| } |
| var tmp = lastOffset; |
| lastOffset = hint - offset; |
| offset = hint - tmp; |
| } |
| lastOffset++; |
| while (lastOffset < offset) { |
| var m = lastOffset + (offset - 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 (offset < maxOffset && compare(value, array[start + hint - offset]) < 0) { |
| lastOffset = offset; |
| offset = (offset << 1) + 1; |
| if (offset <= 0) { |
| offset = maxOffset; |
| } |
| } |
| if (offset > maxOffset) { |
| offset = maxOffset; |
| } |
| var tmp = lastOffset; |
| lastOffset = hint - offset; |
| offset = hint - tmp; |
| } |
| else { |
| maxOffset = length - hint; |
| while (offset < maxOffset && compare(value, array[start + hint + 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 < offset) { |
| var m = lastOffset + (offset - 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 length = 0; |
| var runStart; |
| var runLength; |
| var stackSize = 0; |
| length = array.length; |
| 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]) { |
| n--; |
| } |
| } |
| else if (runLength[n] > runLength[n + 1]) { |
| break; |
| } |
| mergeAt(n); |
| } |
| } |
| function forceMergeRuns() { |
| while (stackSize > 1) { |
| var n = stackSize - 2; |
| if (n > 0 && runLength[n - 1] < runLength[n + 1]) { |
| n--; |
| } |
| mergeAt(n); |
| } |
| } |
| function mergeAt(i) { |
| var start1 = runStart[i]; |
| var length1 = runLength[i]; |
| var start2 = runStart[i + 1]; |
| var length2 = runLength[i + 1]; |
| runLength[i] = length1 + length2; |
| if (i === stackSize - 3) { |
| runStart[i + 1] = runStart[i + 2]; |
| runLength[i + 1] = runLength[i + 2]; |
| } |
| stackSize--; |
| var k = gallopRight(array[start2], array, start1, length1, 0, compare); |
| start1 += k; |
| length1 -= k; |
| if (length1 === 0) { |
| return; |
| } |
| length2 = gallopLeft(array[start1 + length1 - 1], array, start2, length2, length2 - 1, compare); |
| if (length2 === 0) { |
| return; |
| } |
| if (length1 <= length2) { |
| mergeLow(start1, length1, start2, length2); |
| } |
| else { |
| mergeHigh(start1, length1, start2, length2); |
| } |
| } |
| function mergeLow(start1, length1, start2, length2) { |
| var i = 0; |
| for (i = 0; i < length1; i++) { |
| tmp[i] = array[start1 + i]; |
| } |
| var cursor1 = 0; |
| var cursor2 = start2; |
| var dest = start1; |
| array[dest++] = array[cursor2++]; |
| if (--length2 === 0) { |
| for (i = 0; i < length1; i++) { |
| array[dest + i] = tmp[cursor1 + i]; |
| } |
| return; |
| } |
| if (length1 === 1) { |
| for (i = 0; i < length2; i++) { |
| array[dest + i] = array[cursor2 + i]; |
| } |
| array[dest + length2] = tmp[cursor1]; |
| return; |
| } |
| var _minGallop = minGallop; |
| var count1; |
| var count2; |
| var exit; |
| while (1) { |
| count1 = 0; |
| count2 = 0; |
| exit = false; |
| do { |
| if (compare(array[cursor2], tmp[cursor1]) < 0) { |
| array[dest++] = array[cursor2++]; |
| count2++; |
| count1 = 0; |
| if (--length2 === 0) { |
| exit = true; |
| break; |
| } |
| } |
| else { |
| array[dest++] = tmp[cursor1++]; |
| count1++; |
| count2 = 0; |
| if (--length1 === 1) { |
| exit = true; |
| break; |
| } |
| } |
| } while ((count1 | count2) < _minGallop); |
| if (exit) { |
| break; |
| } |
| do { |
| count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare); |
| if (count1 !== 0) { |
| for (i = 0; i < count1; i++) { |
| array[dest + i] = tmp[cursor1 + i]; |
| } |
| dest += count1; |
| cursor1 += count1; |
| length1 -= count1; |
| if (length1 <= 1) { |
| exit = true; |
| break; |
| } |
| } |
| array[dest++] = array[cursor2++]; |
| if (--length2 === 0) { |
| exit = true; |
| break; |
| } |
| count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare); |
| if (count2 !== 0) { |
| for (i = 0; i < count2; i++) { |
| array[dest + i] = array[cursor2 + i]; |
| } |
| dest += count2; |
| cursor2 += count2; |
| length2 -= count2; |
| if (length2 === 0) { |
| exit = true; |
| break; |
| } |
| } |
| array[dest++] = tmp[cursor1++]; |
| if (--length1 === 1) { |
| exit = true; |
| break; |
| } |
| _minGallop--; |
| } while (count1 >= 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 < length2; i++) { |
| array[dest + i] = array[cursor2 + i]; |
| } |
| array[dest + length2] = tmp[cursor1]; |
| } |
| else if (length1 === 0) { |
| throw new Error(); |
| } |
| else { |
| for (i = 0; i < length1; i++) { |
| array[dest + i] = tmp[cursor1 + i]; |
| } |
| } |
| } |
| function mergeHigh(start1, length1, start2, length2) { |
| var i = 0; |
| for (i = 0; i < length2; i++) { |
| tmp[i] = array[start2 + i]; |
| } |
| var cursor1 = start1 + length1 - 1; |
| var cursor2 = length2 - 1; |
| var dest = start2 + length2 - 1; |
| var customCursor = 0; |
| var customDest = 0; |
| array[dest--] = array[cursor1--]; |
| if (--length1 === 0) { |
| customCursor = dest - (length2 - 1); |
| for (i = 0; i < length2; i++) { |
| array[customCursor + i] = tmp[i]; |
| } |
| return; |
| } |
| 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]; |
| 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 < count2; i++) { |
| array[customDest + i] = tmp[customCursor + i]; |
| } |
| if (length2 <= 1) { |
| exit = true; |
| break; |
| } |
| } |
| array[dest--] = array[cursor1--]; |
| if (--length1 === 0) { |
| exit = true; |
| break; |
| } |
| _minGallop--; |
| } while (count1 >= 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(); |
| } |
| else { |
| customCursor = dest - (length2 - 1); |
| for (i = 0; i < length2; i++) { |
| array[customCursor + i] = tmp[i]; |
| } |
| } |
| } |
| return { |
| mergeRuns: mergeRuns, |
| forceMergeRuns: forceMergeRuns, |
| pushRun: pushRun |
| }; |
| } |
| function sort(array, compare, lo, hi) { |
| if (!lo) { |
| lo = 0; |
| } |
| if (!hi) { |
| hi = array.length; |
| } |
| var remaining = hi - lo; |
| if (remaining < 2) { |
| return; |
| } |
| var runLength = 0; |
| if (remaining < DEFAULT_MIN_MERGE) { |
| runLength = makeAscendingRun(array, lo, hi, compare); |
| binaryInsertionSort(array, lo, hi, lo + runLength, compare); |
| return; |
| } |
| var ts = TimSort(array, compare); |
| var minRun = minRunLength(remaining); |
| do { |
| runLength = makeAscendingRun(array, lo, hi, compare); |
| if (runLength < minRun) { |
| var force = remaining; |
| if (force > minRun) { |
| 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(); |
| } |
| |
| var REDRAW_BIT = 1; |
| var STYLE_CHANGED_BIT = 2; |
| var SHAPE_CHANGED_BIT = 4; |
| |
| var invalidZErrorLogged = false; |
| function logInvalidZError() { |
| if (invalidZErrorLogged) { |
| return; |
| } |
| invalidZErrorLogged = true; |
| console.warn('z / z2 / zlevel of displayable is invalid, which may cause unexpected errors'); |
| } |
| function shapeCompareFunc(a, b) { |
| if (a.zlevel === b.zlevel) { |
| if (a.z === b.z) { |
| return a.z2 - b.z2; |
| } |
| return a.z - b.z; |
| } |
| return a.zlevel - b.zlevel; |
| } |
| var Storage = (function () { |
| function Storage() { |
| this._roots = []; |
| this._displayList = []; |
| this._displayListLen = 0; |
| this.displayableSortFunc = shapeCompareFunc; |
| } |
| Storage.prototype.traverse = function (cb, context) { |
| for (var i = 0; i < this._roots.length; i++) { |
| this._roots[i].traverse(cb, context); |
| } |
| }; |
| Storage.prototype.getDisplayList = function (update, includeIgnore) { |
| includeIgnore = includeIgnore || false; |
| var displayList = this._displayList; |
| if (update || !displayList.length) { |
| this.updateDisplayList(includeIgnore); |
| } |
| return displayList; |
| }; |
| Storage.prototype.updateDisplayList = function (includeIgnore) { |
| this._displayListLen = 0; |
| var roots = this._roots; |
| var displayList = this._displayList; |
| for (var i = 0, len = roots.length; i < len; i++) { |
| this._updateAndAddDisplayable(roots[i], null, includeIgnore); |
| } |
| displayList.length = this._displayListLen; |
| sort(displayList, shapeCompareFunc); |
| }; |
| Storage.prototype._updateAndAddDisplayable = function (el, clipPaths, includeIgnore) { |
| if (el.ignore && !includeIgnore) { |
| return; |
| } |
| el.beforeUpdate(); |
| el.update(); |
| el.afterUpdate(); |
| var userSetClipPath = el.getClipPath(); |
| if (el.ignoreClip) { |
| clipPaths = null; |
| } |
| else if (userSetClipPath) { |
| if (clipPaths) { |
| clipPaths = clipPaths.slice(); |
| } |
| else { |
| clipPaths = []; |
| } |
| var currentClipPath = userSetClipPath; |
| var parentClipPath = el; |
| while (currentClipPath) { |
| currentClipPath.parent = parentClipPath; |
| currentClipPath.updateTransform(); |
| clipPaths.push(currentClipPath); |
| parentClipPath = currentClipPath; |
| currentClipPath = currentClipPath.getClipPath(); |
| } |
| } |
| if (el.childrenRef) { |
| var children = el.childrenRef(); |
| for (var i = 0; i < children.length; i++) { |
| var child = children[i]; |
| if (el.__dirty) { |
| child.__dirty |= REDRAW_BIT; |
| } |
| this._updateAndAddDisplayable(child, clipPaths, includeIgnore); |
| } |
| el.__dirty = 0; |
| } |
| else { |
| var disp = el; |
| if (clipPaths && clipPaths.length) { |
| disp.__clipPaths = clipPaths; |
| } |
| else if (disp.__clipPaths && disp.__clipPaths.length > 0) { |
| disp.__clipPaths = []; |
| } |
| if (isNaN(disp.z)) { |
| logInvalidZError(); |
| disp.z = 0; |
| } |
| if (isNaN(disp.z2)) { |
| logInvalidZError(); |
| disp.z2 = 0; |
| } |
| if (isNaN(disp.zlevel)) { |
| logInvalidZError(); |
| disp.zlevel = 0; |
| } |
| this._displayList[this._displayListLen++] = disp; |
| } |
| var decalEl = el.getDecalElement && el.getDecalElement(); |
| if (decalEl) { |
| this._updateAndAddDisplayable(decalEl, clipPaths, includeIgnore); |
| } |
| var textGuide = el.getTextGuideLine(); |
| if (textGuide) { |
| this._updateAndAddDisplayable(textGuide, clipPaths, includeIgnore); |
| } |
| var textEl = el.getTextContent(); |
| if (textEl) { |
| this._updateAndAddDisplayable(textEl, clipPaths, includeIgnore); |
| } |
| }; |
| Storage.prototype.addRoot = function (el) { |
| if (el.__zr && el.__zr.storage === this) { |
| return; |
| } |
| this._roots.push(el); |
| }; |
| Storage.prototype.delRoot = function (el) { |
| if (el instanceof Array) { |
| for (var i = 0, l = el.length; i < l; i++) { |
| this.delRoot(el[i]); |
| } |
| return; |
| } |
| var idx = indexOf(this._roots, el); |
| if (idx >= 0) { |
| this._roots.splice(idx, 1); |
| } |
| }; |
| Storage.prototype.delAllRoots = function () { |
| this._roots = []; |
| this._displayList = []; |
| this._displayListLen = 0; |
| return; |
| }; |
| Storage.prototype.getRoots = function () { |
| return this._roots; |
| }; |
| Storage.prototype.dispose = function () { |
| this._displayList = null; |
| this._roots = null; |
| }; |
| return Storage; |
| }()); |
| |
| var requestAnimationFrame; |
| requestAnimationFrame = (env.hasGlobalWindow |
| && ((window.requestAnimationFrame && window.requestAnimationFrame.bind(window)) |
| || (window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window)) |
| || window.mozRequestAnimationFrame |
| || window.webkitRequestAnimationFrame)) || function (func) { |
| return setTimeout(func, 16); |
| }; |
| var requestAnimationFrame$1 = requestAnimationFrame; |
| |
| var easingFuncs = { |
| linear: function (k) { |
| return k; |
| }, |
| quadraticIn: function (k) { |
| return k * k; |
| }, |
| quadraticOut: function (k) { |
| return k * (2 - k); |
| }, |
| quadraticInOut: function (k) { |
| if ((k *= 2) < 1) { |
| return 0.5 * k * k; |
| } |
| return -0.5 * (--k * (k - 2) - 1); |
| }, |
| cubicIn: function (k) { |
| return k * k * k; |
| }, |
| cubicOut: function (k) { |
| return --k * k * k + 1; |
| }, |
| cubicInOut: function (k) { |
| if ((k *= 2) < 1) { |
| return 0.5 * k * k * k; |
| } |
| return 0.5 * ((k -= 2) * k * k + 2); |
| }, |
| quarticIn: function (k) { |
| return k * k * k * k; |
| }, |
| quarticOut: function (k) { |
| return 1 - (--k * k * k * k); |
| }, |
| quarticInOut: function (k) { |
| if ((k *= 2) < 1) { |
| return 0.5 * k * k * k * k; |
| } |
| return -0.5 * ((k -= 2) * k * k * k - 2); |
| }, |
| quinticIn: function (k) { |
| return k * k * k * k * k; |
| }, |
| quinticOut: function (k) { |
| return --k * k * k * k * k + 1; |
| }, |
| quinticInOut: function (k) { |
| if ((k *= 2) < 1) { |
| return 0.5 * k * k * k * k * k; |
| } |
| return 0.5 * ((k -= 2) * k * k * k * k + 2); |
| }, |
| sinusoidalIn: function (k) { |
| return 1 - Math.cos(k * Math.PI / 2); |
| }, |
| sinusoidalOut: function (k) { |
| return Math.sin(k * Math.PI / 2); |
| }, |
| sinusoidalInOut: function (k) { |
| return 0.5 * (1 - Math.cos(Math.PI * k)); |
| }, |
| exponentialIn: function (k) { |
| return k === 0 ? 0 : Math.pow(1024, k - 1); |
| }, |
| exponentialOut: function (k) { |
| return k === 1 ? 1 : 1 - Math.pow(2, -10 * k); |
| }, |
| exponentialInOut: function (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); |
| }, |
| circularIn: function (k) { |
| return 1 - Math.sqrt(1 - k * k); |
| }, |
| circularOut: function (k) { |
| return Math.sqrt(1 - (--k * k)); |
| }, |
| circularInOut: function (k) { |
| if ((k *= 2) < 1) { |
| return -0.5 * (Math.sqrt(1 - k * k) - 1); |
| } |
| return 0.5 * (Math.sqrt(1 - (k -= 2) * k) + 1); |
| }, |
| elasticIn: function (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)); |
| }, |
| elasticOut: function (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); |
| }, |
| elasticInOut: function (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; |
| }, |
| backIn: function (k) { |
| var s = 1.70158; |
| return k * k * ((s + 1) * k - s); |
| }, |
| backOut: function (k) { |
| var s = 1.70158; |
| return --k * k * ((s + 1) * k + s) + 1; |
| }, |
| backInOut: function (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); |
| }, |
| bounceIn: function (k) { |
| return 1 - easingFuncs.bounceOut(1 - k); |
| }, |
| bounceOut: function (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; |
| } |
| }, |
| bounceInOut: function (k) { |
| if (k < 0.5) { |
| return easingFuncs.bounceIn(k * 2) * 0.5; |
| } |
| return easingFuncs.bounceOut(k * 2 - 1) * 0.5 + 0.5; |
| } |
| }; |
| |
| var mathPow = Math.pow; |
| var mathSqrt = Math.sqrt; |
| var EPSILON = 1e-8; |
| var EPSILON_NUMERIC = 1e-4; |
| var THREE_SQRT = mathSqrt(3); |
| var ONE_THIRD = 1 / 3; |
| var _v0 = create(); |
| var _v1 = create(); |
| var _v2 = create(); |
| function isAroundZero(val) { |
| return val > -EPSILON && val < EPSILON; |
| } |
| function isNotAroundZero(val) { |
| return val > EPSILON || val < -EPSILON; |
| } |
| 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); |
| } |
| 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); |
| } |
| function cubicRootAt(p0, p1, p2, p3, val, roots) { |
| var 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; |
| if (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; |
| var t2 = -K / 2; |
| if (t1 >= 0 && t1 <= 1) { |
| roots[n++] = t1; |
| } |
| if (t2 >= 0 && t2 <= 1) { |
| roots[n++] = t2; |
| } |
| } |
| else if (disc > 0) { |
| var discSqrt = mathSqrt(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(A * A * A)); |
| var theta = Math.acos(T) / 3; |
| var ASqrt = mathSqrt(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; |
| } |
| 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(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(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; |
| } |
| 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; |
| out[0] = p0; |
| out[1] = p01; |
| out[2] = p012; |
| out[3] = p0123; |
| out[4] = p0123; |
| out[5] = p123; |
| out[6] = p23; |
| out[7] = p3; |
| } |
| function cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, out) { |
| var t; |
| var interval = 0.005; |
| var d = Infinity; |
| var prev; |
| var next; |
| var d1; |
| var d2; |
| _v0[0] = x; |
| _v0[1] = y; |
| for (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 < d) { |
| t = _t; |
| d = d1; |
| } |
| } |
| d = Infinity; |
| for (var i = 0; i < 32; i++) { |
| if (interval < EPSILON_NUMERIC) { |
| break; |
| } |
| prev = t - interval; |
| next = t + interval; |
| _v1[0] = cubicAt(x0, x1, x2, x3, prev); |
| _v1[1] = cubicAt(y0, y1, y2, y3, prev); |
| d1 = distSquare(_v1, _v0); |
| if (prev >= 0 && d1 < d) { |
| t = prev; |
| d = d1; |
| } |
| else { |
| _v2[0] = cubicAt(x0, x1, x2, x3, next); |
| _v2[1] = cubicAt(y0, y1, y2, y3, next); |
| d2 = distSquare(_v2, _v0); |
| if (next <= 1 && d2 < d) { |
| t = next; |
| d = d2; |
| } |
| else { |
| interval *= 0.5; |
| } |
| } |
| } |
| if (out) { |
| out[0] = cubicAt(x0, x1, x2, x3, t); |
| out[1] = cubicAt(y0, y1, y2, y3, t); |
| } |
| return mathSqrt(d); |
| } |
| function cubicLength(x0, y0, x1, y1, x2, y2, x3, y3, iteration) { |
| var px = x0; |
| var py = y0; |
| var d = 0; |
| var step = 1 / iteration; |
| for (var i = 1; i <= iteration; i++) { |
| var t = i * step; |
| var x = cubicAt(x0, x1, x2, x3, t); |
| var y = cubicAt(y0, y1, y2, y3, t); |
| var dx = x - px; |
| var dy = y - py; |
| d += Math.sqrt(dx * dx + dy * dy); |
| px = x; |
| py = y; |
| } |
| return d; |
| } |
| function quadraticAt(p0, p1, p2, t) { |
| var onet = 1 - t; |
| return onet * (onet * p0 + 2 * t * p1) + t * t * p2; |
| } |
| function quadraticDerivativeAt(p0, p1, p2, t) { |
| return 2 * ((1 - t) * (p1 - p0) + t * (p2 - p1)); |
| } |
| 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(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(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; |
| } |
| function quadraticExtremum(p0, p1, p2) { |
| var divider = p0 + p2 - 2 * p1; |
| if (divider === 0) { |
| return 0.5; |
| } |
| else { |
| return (p0 - p1) / divider; |
| } |
| } |
| 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; |
| out[0] = p0; |
| out[1] = p01; |
| out[2] = p012; |
| out[3] = p012; |
| out[4] = p12; |
| out[5] = p2; |
| } |
| function quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, out) { |
| var t; |
| var interval = 0.005; |
| var d = Infinity; |
| _v0[0] = x; |
| _v0[1] = y; |
| for (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 < d) { |
| t = _t; |
| d = d1; |
| } |
| } |
| d = Infinity; |
| for (var i = 0; i < 32; i++) { |
| if (interval < EPSILON_NUMERIC) { |
| break; |
| } |
| var prev = t - interval; |
| var next = t + interval; |
| _v1[0] = quadraticAt(x0, x1, x2, prev); |
| _v1[1] = quadraticAt(y0, y1, y2, prev); |
| var d1 = distSquare(_v1, _v0); |
| if (prev >= 0 && d1 < d) { |
| t = prev; |
| d = d1; |
| } |
| else { |
| _v2[0] = quadraticAt(x0, x1, x2, next); |
| _v2[1] = quadraticAt(y0, y1, y2, next); |
| var d2 = distSquare(_v2, _v0); |
| if (next <= 1 && d2 < d) { |
| t = next; |
| d = d2; |
| } |
| else { |
| interval *= 0.5; |
| } |
| } |
| } |
| if (out) { |
| out[0] = quadraticAt(x0, x1, x2, t); |
| out[1] = quadraticAt(y0, y1, y2, t); |
| } |
| return mathSqrt(d); |
| } |
| function quadraticLength(x0, y0, x1, y1, x2, y2, iteration) { |
| var px = x0; |
| var py = y0; |
| var d = 0; |
| var step = 1 / iteration; |
| for (var i = 1; i <= iteration; i++) { |
| var t = i * step; |
| var x = quadraticAt(x0, x1, x2, t); |
| var y = quadraticAt(y0, y1, y2, t); |
| var dx = x - px; |
| var dy = y - py; |
| d += Math.sqrt(dx * dx + dy * dy); |
| px = x; |
| py = y; |
| } |
| return d; |
| } |
| |
| var regexp = /cubic-bezier\(([0-9,\.e ]+)\)/; |
| function createCubicEasingFunc(cubicEasingStr) { |
| var cubic = cubicEasingStr && regexp.exec(cubicEasingStr); |
| if (cubic) { |
| var points = cubic[1].split(','); |
| var a_1 = +trim(points[0]); |
| var b_1 = +trim(points[1]); |
| var c_1 = +trim(points[2]); |
| var d_1 = +trim(points[3]); |
| if (isNaN(a_1 + b_1 + c_1 + d_1)) { |
| return; |
| } |
| var roots_1 = []; |
| return function (p) { |
| return p <= 0 |
| ? 0 : p >= 1 |
| ? 1 |
| : cubicRootAt(0, a_1, c_1, 1, p, roots_1) && cubicAt(0, b_1, d_1, 1, roots_1[0]); |
| }; |
| } |
| } |
| |
| var Clip = (function () { |
| function Clip(opts) { |
| this._inited = false; |
| this._startTime = 0; |
| this._pausedTime = 0; |
| this._paused = false; |
| this._life = opts.life || 1000; |
| this._delay = opts.delay || 0; |
| this.loop = opts.loop || false; |
| this.onframe = opts.onframe || noop; |
| this.ondestroy = opts.ondestroy || noop; |
| this.onrestart = opts.onrestart || noop; |
| opts.easing && this.setEasing(opts.easing); |
| } |
| Clip.prototype.step = function (globalTime, deltaTime) { |
| if (!this._inited) { |
| this._startTime = globalTime + this._delay; |
| this._inited = true; |
| } |
| if (this._paused) { |
| this._pausedTime += deltaTime; |
| return; |
| } |
| var life = this._life; |
| var elapsedTime = globalTime - this._startTime - this._pausedTime; |
| var percent = elapsedTime / life; |
| if (percent < 0) { |
| percent = 0; |
| } |
| percent = Math.min(percent, 1); |
| var easingFunc = this.easingFunc; |
| var schedule = easingFunc ? easingFunc(percent) : percent; |
| this.onframe(schedule); |
| if (percent === 1) { |
| if (this.loop) { |
| var remainder = elapsedTime % life; |
| this._startTime = globalTime - remainder; |
| this._pausedTime = 0; |
| this.onrestart(); |
| } |
| else { |
| return true; |
| } |
| } |
| return false; |
| }; |
| Clip.prototype.pause = function () { |
| this._paused = true; |
| }; |
| Clip.prototype.resume = function () { |
| this._paused = false; |
| }; |
| Clip.prototype.setEasing = function (easing) { |
| this.easing = easing; |
| this.easingFunc = isFunction(easing) |
| ? easing |
| : easingFuncs[easing] || createCubicEasingFunc(easing); |
| }; |
| return Clip; |
| }()); |
| |
| var Entry = (function () { |
| function Entry(val) { |
| this.value = val; |
| } |
| return Entry; |
| }()); |
| var LinkedList = (function () { |
| function LinkedList() { |
| this._len = 0; |
| } |
| LinkedList.prototype.insert = function (val) { |
| var entry = new Entry(val); |
| this.insertEntry(entry); |
| return entry; |
| }; |
| LinkedList.prototype.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++; |
| }; |
| LinkedList.prototype.remove = function (entry) { |
| var prev = entry.prev; |
| var next = entry.next; |
| if (prev) { |
| prev.next = next; |
| } |
| else { |
| this.head = next; |
| } |
| if (next) { |
| next.prev = prev; |
| } |
| else { |
| this.tail = prev; |
| } |
| entry.next = entry.prev = null; |
| this._len--; |
| }; |
| LinkedList.prototype.len = function () { |
| return this._len; |
| }; |
| LinkedList.prototype.clear = function () { |
| this.head = this.tail = null; |
| this._len = 0; |
| }; |
| return LinkedList; |
| }()); |
| var LRU = (function () { |
| function LRU(maxSize) { |
| this._list = new LinkedList(); |
| this._maxSize = 10; |
| this._map = {}; |
| this._maxSize = maxSize; |
| } |
| LRU.prototype.put = function (key, value) { |
| var list = this._list; |
| var map = this._map; |
| var removed = null; |
| if (map[key] == null) { |
| var len = list.len(); |
| var entry = this._lastRemovedEntry; |
| if (len >= this._maxSize && len > 0) { |
| var 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; |
| }; |
| LRU.prototype.get = function (key) { |
| var entry = this._map[key]; |
| var list = this._list; |
| if (entry != null) { |
| if (entry !== list.tail) { |
| list.remove(entry); |
| list.insertEntry(entry); |
| } |
| return entry.value; |
| } |
| }; |
| LRU.prototype.clear = function () { |
| this._list.clear(); |
| this._map = {}; |
| }; |
| LRU.prototype.len = function () { |
| return this._list.len(); |
| }; |
| return LRU; |
| }()); |
| |
| 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) { |
| i = Math.round(i); |
| return i < 0 ? 0 : i > 255 ? 255 : i; |
| } |
| function clampCssAngle(i) { |
| i = Math.round(i); |
| return i < 0 ? 0 : i > 360 ? 360 : i; |
| } |
| function clampCssFloat(f) { |
| return f < 0 ? 0 : f > 1 ? 1 : f; |
| } |
| function parseCssInt(val) { |
| var str = val; |
| if (str.length && str.charAt(str.length - 1) === '%') { |
| return clampCssByte(parseFloat(str) / 100 * 255); |
| } |
| return clampCssByte(parseInt(str, 10)); |
| } |
| function parseCssFloat(val) { |
| var str = val; |
| if (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) { |
| if (lastRemovedArr) { |
| copyRgba(lastRemovedArr, rgbaArr); |
| } |
| lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice())); |
| } |
| function parse(colorStr, rgbaArr) { |
| if (!colorStr) { |
| return; |
| } |
| rgbaArr = rgbaArr || []; |
| var cached = colorCache.get(colorStr); |
| if (cached) { |
| return copyRgba(rgbaArr, cached); |
| } |
| colorStr = colorStr + ''; |
| var str = colorStr.replace(/ /g, '').toLowerCase(); |
| if (str in kCSSColorTable) { |
| copyRgba(rgbaArr, kCSSColorTable[str]); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } |
| var strLen = str.length; |
| if (str.charAt(0) === '#') { |
| if (strLen === 4 || strLen === 5) { |
| var iv = parseInt(str.slice(1, 4), 16); |
| if (!(iv >= 0 && iv <= 0xfff)) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| setRgba(rgbaArr, ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8), (iv & 0xf0) | ((iv & 0xf0) >> 4), (iv & 0xf) | ((iv & 0xf) << 4), strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } |
| else if (strLen === 7 || strLen === 9) { |
| var iv = parseInt(str.slice(1, 7), 16); |
| if (!(iv >= 0 && iv <= 0xffffff)) { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| setRgba(rgbaArr, (iv & 0xff0000) >> 16, (iv & 0xff00) >> 8, iv & 0xff, strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } |
| return; |
| } |
| var op = str.indexOf('('); |
| var ep = str.indexOf(')'); |
| if (op !== -1 && ep + 1 === strLen) { |
| var fname = str.substr(0, op); |
| var params = str.substr(op + 1, ep - (op + 1)).split(','); |
| var alpha = 1; |
| switch (fname) { |
| case 'rgba': |
| if (params.length !== 4) { |
| return params.length === 3 |
| ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1) |
| : setRgba(rgbaArr, 0, 0, 0, 1); |
| } |
| alpha = parseCssFloat(params.pop()); |
| case 'rgb': |
| if (params.length >= 3) { |
| setRgba(rgbaArr, parseCssInt(params[0]), parseCssInt(params[1]), parseCssInt(params[2]), params.length === 3 ? alpha : parseCssFloat(params[3])); |
| putToCache(colorStr, rgbaArr); |
| return rgbaArr; |
| } |
| else { |
| setRgba(rgbaArr, 0, 0, 0, 1); |
| return; |
| } |
| 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; |
| } |
| function hsla2rgba(hsla, rgba) { |
| var h = (((parseFloat(hsla[0]) % 360) + 360) % 360) / 360; |
| var 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; |
| } |
| function rgba2hsla(rgba) { |
| if (!rgba) { |
| return; |
| } |
| var R = rgba[0] / 255; |
| var G = rgba[1] / 255; |
| var B = rgba[2] / 255; |
| var vMin = Math.min(R, G, B); |
| var vMax = Math.max(R, G, B); |
| var delta = vMax - vMin; |
| var L = (vMax + vMin) / 2; |
| var H; |
| var S; |
| if (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; |
| } |
| 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 (colorArr[i] < 0) { |
| colorArr[i] = 0; |
| } |
| } |
| return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb'); |
| } |
| } |
| function toHex(color) { |
| var colorArr = parse(color); |
| if (colorArr) { |
| return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1); |
| } |
| } |
| 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; |
| } |
| var fastMapToColor = fastLerp; |
| 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; |
| } |
| var mapToColor = lerp$1; |
| function modifyHSL(color, h, s, l) { |
| var colorArr = parse(color); |
| if (color) { |
| colorArr = rgba2hsla(colorArr); |
| h != null && (colorArr[0] = clampCssAngle(h)); |
| s != null && (colorArr[1] = parseCssFloat(s)); |
| l != null && (colorArr[2] = parseCssFloat(l)); |
| return stringify(hsla2rgba(colorArr), 'rgba'); |
| } |
| } |
| function modifyAlpha(color, alpha) { |
| var colorArr = parse(color); |
| if (colorArr && alpha != null) { |
| colorArr[3] = clampCssFloat(alpha); |
| return stringify(colorArr, 'rgba'); |
| } |
| } |
| 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 + ')'; |
| } |
| function lum(color, backgroundLum) { |
| var arr = parse(color); |
| return arr |
| ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255 |
| + (1 - arr[3]) * backgroundLum |
| : 0; |
| } |
| function random() { |
| return stringify([ |
| Math.round(Math.random() * 255), |
| Math.round(Math.random() * 255), |
| Math.round(Math.random() * 255) |
| ], 'rgb'); |
| } |
| |
| var color = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| parse: parse, |
| lift: lift, |
| toHex: toHex, |
| fastLerp: fastLerp, |
| fastMapToColor: fastMapToColor, |
| lerp: lerp$1, |
| mapToColor: mapToColor, |
| modifyHSL: modifyHSL, |
| modifyAlpha: modifyAlpha, |
| stringify: stringify, |
| lum: lum, |
| random: random |
| }); |
| |
| function isLinearGradient(val) { |
| return val.type === 'linear'; |
| } |
| function isRadialGradient(val) { |
| return val.type === 'radial'; |
| } |
| |
| var arraySlice = Array.prototype.slice; |
| function interpolateNumber(p0, p1, percent) { |
| return (p1 - p0) * percent + p0; |
| } |
| function interpolate1DArray(out, p0, p1, percent) { |
| var len = p0.length; |
| for (var i = 0; i < len; i++) { |
| out[i] = interpolateNumber(p0[i], p1[i], percent); |
| } |
| return out; |
| } |
| function interpolate2DArray(out, p0, p1, percent) { |
| var len = p0.length; |
| var len2 = len && p0[0].length; |
| for (var i = 0; i < len; i++) { |
| if (!out[i]) { |
| out[i] = []; |
| } |
| for (var j = 0; j < len2; j++) { |
| out[i][j] = interpolateNumber(p0[i][j], p1[i][j], percent); |
| } |
| } |
| return out; |
| } |
| function add1DArray(out, p0, p1, sign) { |
| var len = p0.length; |
| for (var i = 0; i < len; i++) { |
| out[i] = p0[i] + p1[i] * sign; |
| } |
| return out; |
| } |
| function add2DArray(out, p0, p1, sign) { |
| var len = p0.length; |
| var len2 = len && p0[0].length; |
| for (var i = 0; i < len; i++) { |
| if (!out[i]) { |
| out[i] = []; |
| } |
| for (var j = 0; j < len2; j++) { |
| out[i][j] = p0[i][j] + p1[i][j] * sign; |
| } |
| } |
| return out; |
| } |
| function fillColorStops(val0, val1) { |
| var len0 = val0.length; |
| var len1 = val1.length; |
| var shorterArr = len0 > len1 ? val1 : val0; |
| var shorterLen = Math.min(len0, len1); |
| var last = shorterArr[shorterLen - 1] || { color: [0, 0, 0, 0], offset: 0 }; |
| for (var i = shorterLen; i < Math.max(len0, len1); i++) { |
| shorterArr.push({ |
| offset: last.offset, |
| color: last.color.slice() |
| }); |
| } |
| } |
| function fillArray(val0, val1, arrDim) { |
| var arr0 = val0; |
| var arr1 = val1; |
| if (!arr0.push || !arr1.push) { |
| return; |
| } |
| var arr0Len = arr0.length; |
| var arr1Len = arr1.length; |
| if (arr0Len !== arr1Len) { |
| var isPreviousLarger = arr0Len > arr1Len; |
| if (isPreviousLarger) { |
| arr0.length = arr1Len; |
| } |
| else { |
| for (var i = arr0Len; i < arr1Len; i++) { |
| arr0.push(arrDim === 1 ? arr1[i] : arraySlice.call(arr1[i])); |
| } |
| } |
| } |
| var len2 = arr0[0] && arr0[0].length; |
| for (var i = 0; i < arr0.length; i++) { |
| if (arrDim === 1) { |
| if (isNaN(arr0[i])) { |
| arr0[i] = arr1[i]; |
| } |
| } |
| else { |
| for (var j = 0; j < len2; j++) { |
| if (isNaN(arr0[i][j])) { |
| arr0[i][j] = arr1[i][j]; |
| } |
| } |
| } |
| } |
| } |
| function cloneValue(value) { |
| if (isArrayLike(value)) { |
| var len = value.length; |
| if (isArrayLike(value[0])) { |
| var ret = []; |
| for (var i = 0; i < len; i++) { |
| ret.push(arraySlice.call(value[i])); |
| } |
| return ret; |
| } |
| return arraySlice.call(value); |
| } |
| return value; |
| } |
| function rgba2String(rgba) { |
| rgba[0] = Math.floor(rgba[0]) || 0; |
| rgba[1] = Math.floor(rgba[1]) || 0; |
| rgba[2] = Math.floor(rgba[2]) || 0; |
| rgba[3] = rgba[3] == null ? 1 : rgba[3]; |
| return 'rgba(' + rgba.join(',') + ')'; |
| } |
| function guessArrayDim(value) { |
| return isArrayLike(value && value[0]) ? 2 : 1; |
| } |
| var VALUE_TYPE_NUMBER = 0; |
| var VALUE_TYPE_1D_ARRAY = 1; |
| var VALUE_TYPE_2D_ARRAY = 2; |
| var VALUE_TYPE_COLOR = 3; |
| var VALUE_TYPE_LINEAR_GRADIENT = 4; |
| var VALUE_TYPE_RADIAL_GRADIENT = 5; |
| var VALUE_TYPE_UNKOWN = 6; |
| function isGradientValueType(valType) { |
| return valType === VALUE_TYPE_LINEAR_GRADIENT || valType === VALUE_TYPE_RADIAL_GRADIENT; |
| } |
| function isArrayValueType(valType) { |
| return valType === VALUE_TYPE_1D_ARRAY || valType === VALUE_TYPE_2D_ARRAY; |
| } |
| var tmpRgba = [0, 0, 0, 0]; |
| var Track = (function () { |
| function Track(propName) { |
| this.keyframes = []; |
| this.discrete = false; |
| this._invalid = false; |
| this._needsSort = false; |
| this._lastFr = 0; |
| this._lastFrP = 0; |
| this.propName = propName; |
| } |
| Track.prototype.isFinished = function () { |
| return this._finished; |
| }; |
| Track.prototype.setFinished = function () { |
| this._finished = true; |
| if (this._additiveTrack) { |
| this._additiveTrack.setFinished(); |
| } |
| }; |
| Track.prototype.needsAnimate = function () { |
| return this.keyframes.length >= 1; |
| }; |
| Track.prototype.getAdditiveTrack = function () { |
| return this._additiveTrack; |
| }; |
| Track.prototype.addKeyframe = function (time, rawValue, easing) { |
| this._needsSort = true; |
| var keyframes = this.keyframes; |
| var len = keyframes.length; |
| var discrete = false; |
| var valType = VALUE_TYPE_UNKOWN; |
| var value = rawValue; |
| if (isArrayLike(rawValue)) { |
| var arrayDim = guessArrayDim(rawValue); |
| valType = arrayDim; |
| if (arrayDim === 1 && !isNumber(rawValue[0]) |
| || arrayDim === 2 && !isNumber(rawValue[0][0])) { |
| discrete = true; |
| } |
| } |
| else { |
| if (isNumber(rawValue) && !eqNaN(rawValue)) { |
| valType = VALUE_TYPE_NUMBER; |
| } |
| else if (isString(rawValue)) { |
| if (!isNaN(+rawValue)) { |
| valType = VALUE_TYPE_NUMBER; |
| } |
| else { |
| var colorArray = parse(rawValue); |
| if (colorArray) { |
| value = colorArray; |
| valType = VALUE_TYPE_COLOR; |
| } |
| } |
| } |
| else if (isGradientObject(rawValue)) { |
| var parsedGradient = extend({}, value); |
| parsedGradient.colorStops = map(rawValue.colorStops, function (colorStop) { return ({ |
| offset: colorStop.offset, |
| color: parse(colorStop.color) |
| }); }); |
| if (isLinearGradient(rawValue)) { |
| valType = VALUE_TYPE_LINEAR_GRADIENT; |
| } |
| else if (isRadialGradient(rawValue)) { |
| valType = VALUE_TYPE_RADIAL_GRADIENT; |
| } |
| value = parsedGradient; |
| } |
| } |
| if (len === 0) { |
| this.valType = valType; |
| } |
| else if (valType !== this.valType || valType === VALUE_TYPE_UNKOWN) { |
| discrete = true; |
| } |
| this.discrete = this.discrete || discrete; |
| var kf = { |
| time: time, |
| value: value, |
| rawValue: rawValue, |
| percent: 0 |
| }; |
| if (easing) { |
| kf.easing = easing; |
| kf.easingFunc = isFunction(easing) |
| ? easing |
| : easingFuncs[easing] || createCubicEasingFunc(easing); |
| } |
| keyframes.push(kf); |
| return kf; |
| }; |
| Track.prototype.prepare = function (maxTime, additiveTrack) { |
| var kfs = this.keyframes; |
| if (this._needsSort) { |
| kfs.sort(function (a, b) { |
| return a.time - b.time; |
| }); |
| } |
| var valType = this.valType; |
| var kfsLen = kfs.length; |
| var lastKf = kfs[kfsLen - 1]; |
| var isDiscrete = this.discrete; |
| var isArr = isArrayValueType(valType); |
| var isGradient = isGradientValueType(valType); |
| for (var i = 0; i < kfsLen; i++) { |
| var kf = kfs[i]; |
| var value = kf.value; |
| var lastValue = lastKf.value; |
| kf.percent = kf.time / maxTime; |
| if (!isDiscrete) { |
| if (isArr && i !== kfsLen - 1) { |
| fillArray(value, lastValue, valType); |
| } |
| else if (isGradient) { |
| fillColorStops(value.colorStops, lastValue.colorStops); |
| } |
| } |
| } |
| if (!isDiscrete |
| && valType !== VALUE_TYPE_RADIAL_GRADIENT |
| && additiveTrack |
| && this.needsAnimate() |
| && additiveTrack.needsAnimate() |
| && valType === additiveTrack.valType |
| && !additiveTrack._finished) { |
| this._additiveTrack = additiveTrack; |
| var startValue = kfs[0].value; |
| for (var i = 0; i < kfsLen; i++) { |
| if (valType === VALUE_TYPE_NUMBER) { |
| kfs[i].additiveValue = kfs[i].value - startValue; |
| } |
| else if (valType === VALUE_TYPE_COLOR) { |
| kfs[i].additiveValue = |
| add1DArray([], kfs[i].value, startValue, -1); |
| } |
| else if (isArrayValueType(valType)) { |
| kfs[i].additiveValue = valType === VALUE_TYPE_1D_ARRAY |
| ? add1DArray([], kfs[i].value, startValue, -1) |
| : add2DArray([], kfs[i].value, startValue, -1); |
| } |
| } |
| } |
| }; |
| Track.prototype.step = function (target, percent) { |
| if (this._finished) { |
| return; |
| } |
| if (this._additiveTrack && this._additiveTrack._finished) { |
| this._additiveTrack = null; |
| } |
| var isAdditive = this._additiveTrack != null; |
| var valueKey = isAdditive ? 'additiveValue' : 'value'; |
| var valType = this.valType; |
| var keyframes = this.keyframes; |
| var kfsNum = keyframes.length; |
| var propName = this.propName; |
| var isValueColor = valType === VALUE_TYPE_COLOR; |
| var frameIdx; |
| var lastFrame = this._lastFr; |
| var mathMin = Math.min; |
| var frame; |
| var nextFrame; |
| if (kfsNum === 1) { |
| frame = nextFrame = keyframes[0]; |
| } |
| else { |
| if (percent < 0) { |
| frameIdx = 0; |
| } |
| else if (percent < this._lastFrP) { |
| var start = mathMin(lastFrame + 1, kfsNum - 1); |
| for (frameIdx = start; frameIdx >= 0; frameIdx--) { |
| if (keyframes[frameIdx].percent <= percent) { |
| break; |
| } |
| } |
| frameIdx = mathMin(frameIdx, kfsNum - 2); |
| } |
| else { |
| for (frameIdx = lastFrame; frameIdx < kfsNum; frameIdx++) { |
| if (keyframes[frameIdx].percent > percent) { |
| break; |
| } |
| } |
| frameIdx = mathMin(frameIdx - 1, kfsNum - 2); |
| } |
| nextFrame = keyframes[frameIdx + 1]; |
| frame = keyframes[frameIdx]; |
| } |
| if (!(frame && nextFrame)) { |
| return; |
| } |
| this._lastFr = frameIdx; |
| this._lastFrP = percent; |
| var interval = (nextFrame.percent - frame.percent); |
| var w = interval === 0 ? 1 : mathMin((percent - frame.percent) / interval, 1); |
| if (nextFrame.easingFunc) { |
| w = nextFrame.easingFunc(w); |
| } |
| var targetArr = isAdditive ? this._additiveValue |
| : (isValueColor ? tmpRgba : target[propName]); |
| if ((isArrayValueType(valType) || isValueColor) && !targetArr) { |
| targetArr = this._additiveValue = []; |
| } |
| if (this.discrete) { |
| target[propName] = w < 1 ? frame.rawValue : nextFrame.rawValue; |
| } |
| else if (isArrayValueType(valType)) { |
| valType === VALUE_TYPE_1D_ARRAY |
| ? interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w) |
| : interpolate2DArray(targetArr, frame[valueKey], nextFrame[valueKey], w); |
| } |
| else if (isGradientValueType(valType)) { |
| var val = frame[valueKey]; |
| var nextVal_1 = nextFrame[valueKey]; |
| var isLinearGradient_1 = valType === VALUE_TYPE_LINEAR_GRADIENT; |
| target[propName] = { |
| type: isLinearGradient_1 ? 'linear' : 'radial', |
| x: interpolateNumber(val.x, nextVal_1.x, w), |
| y: interpolateNumber(val.y, nextVal_1.y, w), |
| colorStops: map(val.colorStops, function (colorStop, idx) { |
| var nextColorStop = nextVal_1.colorStops[idx]; |
| return { |
| offset: interpolateNumber(colorStop.offset, nextColorStop.offset, w), |
| color: rgba2String(interpolate1DArray([], colorStop.color, nextColorStop.color, w)) |
| }; |
| }), |
| global: nextVal_1.global |
| }; |
| if (isLinearGradient_1) { |
| target[propName].x2 = interpolateNumber(val.x2, nextVal_1.x2, w); |
| target[propName].y2 = interpolateNumber(val.y2, nextVal_1.y2, w); |
| } |
| else { |
| target[propName].r = interpolateNumber(val.r, nextVal_1.r, w); |
| } |
| } |
| else if (isValueColor) { |
| interpolate1DArray(targetArr, frame[valueKey], nextFrame[valueKey], w); |
| if (!isAdditive) { |
| target[propName] = rgba2String(targetArr); |
| } |
| } |
| else { |
| var value = interpolateNumber(frame[valueKey], nextFrame[valueKey], w); |
| if (isAdditive) { |
| this._additiveValue = value; |
| } |
| else { |
| target[propName] = value; |
| } |
| } |
| if (isAdditive) { |
| this._addToTarget(target); |
| } |
| }; |
| Track.prototype._addToTarget = function (target) { |
| var valType = this.valType; |
| var propName = this.propName; |
| var additiveValue = this._additiveValue; |
| if (valType === VALUE_TYPE_NUMBER) { |
| target[propName] = target[propName] + additiveValue; |
| } |
| else if (valType === VALUE_TYPE_COLOR) { |
| parse(target[propName], tmpRgba); |
| add1DArray(tmpRgba, tmpRgba, additiveValue, 1); |
| target[propName] = rgba2String(tmpRgba); |
| } |
| else if (valType === VALUE_TYPE_1D_ARRAY) { |
| add1DArray(target[propName], target[propName], additiveValue, 1); |
| } |
| else if (valType === VALUE_TYPE_2D_ARRAY) { |
| add2DArray(target[propName], target[propName], additiveValue, 1); |
| } |
| }; |
| return Track; |
| }()); |
| var Animator = (function () { |
| function Animator(target, loop, allowDiscreteAnimation, additiveTo) { |
| this._tracks = {}; |
| this._trackKeys = []; |
| this._maxTime = 0; |
| this._started = 0; |
| this._clip = null; |
| this._target = target; |
| this._loop = loop; |
| if (loop && additiveTo) { |
| logError('Can\' use additive animation on looped animation.'); |
| return; |
| } |
| this._additiveAnimators = additiveTo; |
| this._allowDiscrete = allowDiscreteAnimation; |
| } |
| Animator.prototype.getMaxTime = function () { |
| return this._maxTime; |
| }; |
| Animator.prototype.getDelay = function () { |
| return this._delay; |
| }; |
| Animator.prototype.getLoop = function () { |
| return this._loop; |
| }; |
| Animator.prototype.getTarget = function () { |
| return this._target; |
| }; |
| Animator.prototype.changeTarget = function (target) { |
| this._target = target; |
| }; |
| Animator.prototype.when = function (time, props, easing) { |
| return this.whenWithKeys(time, props, keys(props), easing); |
| }; |
| Animator.prototype.whenWithKeys = function (time, props, propNames, easing) { |
| var tracks = this._tracks; |
| for (var i = 0; i < propNames.length; i++) { |
| var propName = propNames[i]; |
| var track = tracks[propName]; |
| if (!track) { |
| track = tracks[propName] = new Track(propName); |
| var initialValue = void 0; |
| var additiveTrack = this._getAdditiveTrack(propName); |
| if (additiveTrack) { |
| var addtiveTrackKfs = additiveTrack.keyframes; |
| var lastFinalKf = addtiveTrackKfs[addtiveTrackKfs.length - 1]; |
| initialValue = lastFinalKf && lastFinalKf.value; |
| if (additiveTrack.valType === VALUE_TYPE_COLOR && initialValue) { |
| initialValue = rgba2String(initialValue); |
| } |
| } |
| else { |
| initialValue = this._target[propName]; |
| } |
| if (initialValue == null) { |
| continue; |
| } |
| if (time > 0) { |
| track.addKeyframe(0, cloneValue(initialValue), easing); |
| } |
| this._trackKeys.push(propName); |
| } |
| track.addKeyframe(time, cloneValue(props[propName]), easing); |
| } |
| this._maxTime = Math.max(this._maxTime, time); |
| return this; |
| }; |
| Animator.prototype.pause = function () { |
| this._clip.pause(); |
| this._paused = true; |
| }; |
| Animator.prototype.resume = function () { |
| this._clip.resume(); |
| this._paused = false; |
| }; |
| Animator.prototype.isPaused = function () { |
| return !!this._paused; |
| }; |
| Animator.prototype.duration = function (duration) { |
| this._maxTime = duration; |
| this._force = true; |
| return this; |
| }; |
| Animator.prototype._doneCallback = function () { |
| this._setTracksFinished(); |
| this._clip = null; |
| var doneList = this._doneCbs; |
| if (doneList) { |
| var len = doneList.length; |
| for (var i = 0; i < len; i++) { |
| doneList[i].call(this); |
| } |
| } |
| }; |
| Animator.prototype._abortedCallback = function () { |
| this._setTracksFinished(); |
| var animation = this.animation; |
| var abortedList = this._abortedCbs; |
| if (animation) { |
| animation.removeClip(this._clip); |
| } |
| this._clip = null; |
| if (abortedList) { |
| for (var i = 0; i < abortedList.length; i++) { |
| abortedList[i].call(this); |
| } |
| } |
| }; |
| Animator.prototype._setTracksFinished = function () { |
| var tracks = this._tracks; |
| var tracksKeys = this._trackKeys; |
| for (var i = 0; i < tracksKeys.length; i++) { |
| tracks[tracksKeys[i]].setFinished(); |
| } |
| }; |
| Animator.prototype._getAdditiveTrack = function (trackName) { |
| var additiveTrack; |
| var additiveAnimators = this._additiveAnimators; |
| if (additiveAnimators) { |
| for (var i = 0; i < additiveAnimators.length; i++) { |
| var track = additiveAnimators[i].getTrack(trackName); |
| if (track) { |
| additiveTrack = track; |
| } |
| } |
| } |
| return additiveTrack; |
| }; |
| Animator.prototype.start = function (easing) { |
| if (this._started > 0) { |
| return; |
| } |
| this._started = 1; |
| var self = this; |
| var tracks = []; |
| var maxTime = this._maxTime || 0; |
| for (var i = 0; i < this._trackKeys.length; i++) { |
| var propName = this._trackKeys[i]; |
| var track = this._tracks[propName]; |
| var additiveTrack = this._getAdditiveTrack(propName); |
| var kfs = track.keyframes; |
| var kfsNum = kfs.length; |
| track.prepare(maxTime, additiveTrack); |
| if (track.needsAnimate()) { |
| if (!this._allowDiscrete && track.discrete) { |
| var lastKf = kfs[kfsNum - 1]; |
| if (lastKf) { |
| self._target[track.propName] = lastKf.rawValue; |
| } |
| track.setFinished(); |
| } |
| else { |
| tracks.push(track); |
| } |
| } |
| } |
| if (tracks.length || this._force) { |
| var clip = new Clip({ |
| life: maxTime, |
| loop: this._loop, |
| delay: this._delay || 0, |
| onframe: function (percent) { |
| self._started = 2; |
| var additiveAnimators = self._additiveAnimators; |
| if (additiveAnimators) { |
| var stillHasAdditiveAnimator = false; |
| for (var i = 0; i < additiveAnimators.length; i++) { |
| if (additiveAnimators[i]._clip) { |
| stillHasAdditiveAnimator = true; |
| break; |
| } |
| } |
| if (!stillHasAdditiveAnimator) { |
| self._additiveAnimators = null; |
| } |
| } |
| for (var i = 0; i < tracks.length; i++) { |
| tracks[i].step(self._target, percent); |
| } |
| var onframeList = self._onframeCbs; |
| if (onframeList) { |
| for (var i = 0; i < onframeList.length; i++) { |
| onframeList[i](self._target, percent); |
| } |
| } |
| }, |
| ondestroy: function () { |
| self._doneCallback(); |
| } |
| }); |
| this._clip = clip; |
| if (this.animation) { |
| this.animation.addClip(clip); |
| } |
| if (easing) { |
| clip.setEasing(easing); |
| } |
| } |
| else { |
| this._doneCallback(); |
| } |
| return this; |
| }; |
| Animator.prototype.stop = function (forwardToLast) { |
| if (!this._clip) { |
| return; |
| } |
| var clip = this._clip; |
| if (forwardToLast) { |
| clip.onframe(1); |
| } |
| this._abortedCallback(); |
| }; |
| Animator.prototype.delay = function (time) { |
| this._delay = time; |
| return this; |
| }; |
| Animator.prototype.during = function (cb) { |
| if (cb) { |
| if (!this._onframeCbs) { |
| this._onframeCbs = []; |
| } |
| this._onframeCbs.push(cb); |
| } |
| return this; |
| }; |
| Animator.prototype.done = function (cb) { |
| if (cb) { |
| if (!this._doneCbs) { |
| this._doneCbs = []; |
| } |
| this._doneCbs.push(cb); |
| } |
| return this; |
| }; |
| Animator.prototype.aborted = function (cb) { |
| if (cb) { |
| if (!this._abortedCbs) { |
| this._abortedCbs = []; |
| } |
| this._abortedCbs.push(cb); |
| } |
| return this; |
| }; |
| Animator.prototype.getClip = function () { |
| return this._clip; |
| }; |
| Animator.prototype.getTrack = function (propName) { |
| return this._tracks[propName]; |
| }; |
| Animator.prototype.getTracks = function () { |
| var _this = this; |
| return map(this._trackKeys, function (key) { return _this._tracks[key]; }); |
| }; |
| Animator.prototype.stopTracks = function (propNames, forwardToLast) { |
| if (!propNames.length || !this._clip) { |
| return true; |
| } |
| var tracks = this._tracks; |
| var tracksKeys = this._trackKeys; |
| for (var i = 0; i < propNames.length; i++) { |
| var track = tracks[propNames[i]]; |
| if (track && !track.isFinished()) { |
| if (forwardToLast) { |
| track.step(this._target, 1); |
| } |
| else if (this._started === 1) { |
| track.step(this._target, 0); |
| } |
| track.setFinished(); |
| } |
| } |
| var allAborted = true; |
| for (var i = 0; i < tracksKeys.length; i++) { |
| if (!tracks[tracksKeys[i]].isFinished()) { |
| allAborted = false; |
| break; |
| } |
| } |
| if (allAborted) { |
| this._abortedCallback(); |
| } |
| return allAborted; |
| }; |
| Animator.prototype.saveTo = function (target, trackKeys, firstOrLast) { |
| if (!target) { |
| return; |
| } |
| trackKeys = trackKeys || this._trackKeys; |
| for (var i = 0; i < trackKeys.length; i++) { |
| var propName = trackKeys[i]; |
| var track = this._tracks[propName]; |
| if (!track || track.isFinished()) { |
| continue; |
| } |
| var kfs = track.keyframes; |
| var kf = kfs[firstOrLast ? 0 : kfs.length - 1]; |
| if (kf) { |
| target[propName] = cloneValue(kf.rawValue); |
| } |
| } |
| }; |
| Animator.prototype.__changeFinalValue = function (finalProps, trackKeys) { |
| trackKeys = trackKeys || keys(finalProps); |
| for (var i = 0; i < trackKeys.length; i++) { |
| var propName = trackKeys[i]; |
| var track = this._tracks[propName]; |
| if (!track) { |
| continue; |
| } |
| var kfs = track.keyframes; |
| if (kfs.length > 1) { |
| var lastKf = kfs.pop(); |
| track.addKeyframe(lastKf.time, finalProps[propName]); |
| track.prepare(this._maxTime, track.getAdditiveTrack()); |
| } |
| } |
| }; |
| return Animator; |
| }()); |
| |
| function getTime() { |
| return new Date().getTime(); |
| } |
| var Animation = (function (_super) { |
| __extends(Animation, _super); |
| function Animation(opts) { |
| var _this = _super.call(this) || this; |
| _this._running = false; |
| _this._time = 0; |
| _this._pausedTime = 0; |
| _this._pauseStart = 0; |
| _this._paused = false; |
| opts = opts || {}; |
| _this.stage = opts.stage || {}; |
| return _this; |
| } |
| Animation.prototype.addClip = function (clip) { |
| if (clip.animation) { |
| this.removeClip(clip); |
| } |
| if (!this._head) { |
| this._head = this._tail = clip; |
| } |
| else { |
| this._tail.next = clip; |
| clip.prev = this._tail; |
| clip.next = null; |
| this._tail = clip; |
| } |
| clip.animation = this; |
| }; |
| Animation.prototype.addAnimator = function (animator) { |
| animator.animation = this; |
| var clip = animator.getClip(); |
| if (clip) { |
| this.addClip(clip); |
| } |
| }; |
| Animation.prototype.removeClip = function (clip) { |
| if (!clip.animation) { |
| return; |
| } |
| var prev = clip.prev; |
| var next = clip.next; |
| if (prev) { |
| prev.next = next; |
| } |
| else { |
| this._head = next; |
| } |
| if (next) { |
| next.prev = prev; |
| } |
| else { |
| this._tail = prev; |
| } |
| clip.next = clip.prev = clip.animation = null; |
| }; |
| Animation.prototype.removeAnimator = function (animator) { |
| var clip = animator.getClip(); |
| if (clip) { |
| this.removeClip(clip); |
| } |
| animator.animation = null; |
| }; |
| Animation.prototype.update = function (notTriggerFrameAndStageUpdate) { |
| var time = getTime() - this._pausedTime; |
| var delta = time - this._time; |
| var clip = this._head; |
| while (clip) { |
| var nextClip = clip.next; |
| var finished = clip.step(time, delta); |
| if (finished) { |
| clip.ondestroy(); |
| this.removeClip(clip); |
| clip = nextClip; |
| } |
| else { |
| clip = nextClip; |
| } |
| } |
| this._time = time; |
| if (!notTriggerFrameAndStageUpdate) { |
| this.trigger('frame', delta); |
| this.stage.update && this.stage.update(); |
| } |
| }; |
| Animation.prototype._startLoop = function () { |
| var self = this; |
| this._running = true; |
| function step() { |
| if (self._running) { |
| requestAnimationFrame$1(step); |
| !self._paused && self.update(); |
| } |
| } |
| requestAnimationFrame$1(step); |
| }; |
| Animation.prototype.start = function () { |
| if (this._running) { |
| return; |
| } |
| this._time = getTime(); |
| this._pausedTime = 0; |
| this._startLoop(); |
| }; |
| Animation.prototype.stop = function () { |
| this._running = false; |
| }; |
| Animation.prototype.pause = function () { |
| if (!this._paused) { |
| this._pauseStart = getTime(); |
| this._paused = true; |
| } |
| }; |
| Animation.prototype.resume = function () { |
| if (this._paused) { |
| this._pausedTime += getTime() - this._pauseStart; |
| this._paused = false; |
| } |
| }; |
| Animation.prototype.clear = function () { |
| var clip = this._head; |
| while (clip) { |
| var nextClip = clip.next; |
| clip.prev = clip.next = clip.animation = null; |
| clip = nextClip; |
| } |
| this._head = this._tail = null; |
| }; |
| Animation.prototype.isFinished = function () { |
| return this._head == null; |
| }; |
| Animation.prototype.animate = function (target, options) { |
| options = options || {}; |
| this.start(); |
| var animator = new Animator(target, options.loop); |
| this.addAnimator(animator); |
| return animator; |
| }; |
| return Animation; |
| }(Eventful)); |
| |
| var TOUCH_CLICK_DELAY = 300; |
| var globalEventSupported = env.domSupported; |
| var localNativeListenerNames = (function () { |
| var mouseHandlerNames = [ |
| 'click', 'dblclick', 'mousewheel', 'wheel', 'mouseout', |
| 'mouseup', 'mousedown', 'mousemove', 'contextmenu' |
| ]; |
| var touchHandlerNames = [ |
| 'touchstart', 'touchend', 'touchmove' |
| ]; |
| var pointerEventNameMap = { |
| pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 |
| }; |
| var pointerHandlerNames = map(mouseHandlerNames, function (name) { |
| var nm = name.replace('mouse', 'pointer'); |
| return pointerEventNameMap.hasOwnProperty(nm) ? nm : name; |
| }); |
| return { |
| mouse: mouseHandlerNames, |
| touch: touchHandlerNames, |
| pointer: pointerHandlerNames |
| }; |
| })(); |
| var globalNativeListenerNames = { |
| mouse: ['mousemove', 'mouseup'], |
| pointer: ['pointermove', 'pointerup'] |
| }; |
| var wheelEventSupported = false; |
| function isPointerFromTouch(event) { |
| var pointerType = event.pointerType; |
| return pointerType === 'pen' || pointerType === 'touch'; |
| } |
| 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); |
| } |
| function markTouch(event) { |
| event && (event.zrByTouch = true); |
| } |
| function normalizeGlobalEvent(instance, event) { |
| return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true); |
| } |
| 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; |
| } |
| var FakeGlobalEvent = (function () { |
| function FakeGlobalEvent(instance, event) { |
| this.stopPropagation = noop; |
| this.stopImmediatePropagation = noop; |
| this.preventDefault = noop; |
| this.type = event.type; |
| this.target = this.currentTarget = instance.dom; |
| this.pointerType = event.pointerType; |
| this.clientX = event.clientX; |
| this.clientY = event.clientY; |
| } |
| return FakeGlobalEvent; |
| }()); |
| var localDOMHandlers = { |
| mousedown: function (event) { |
| event = normalizeEvent(this.dom, event); |
| this.__mayPointerCapture = [event.zrX, event.zrY]; |
| this.trigger('mousedown', event); |
| }, |
| mousemove: function (event) { |
| event = normalizeEvent(this.dom, event); |
| var downPoint = this.__mayPointerCapture; |
| if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) { |
| this.__togglePointerCapture(true); |
| } |
| this.trigger('mousemove', event); |
| }, |
| mouseup: function (event) { |
| event = normalizeEvent(this.dom, event); |
| this.__togglePointerCapture(false); |
| this.trigger('mouseup', event); |
| }, |
| mouseout: function (event) { |
| event = normalizeEvent(this.dom, event); |
| var element = event.toElement || event.relatedTarget; |
| if (!isLocalEl(this, element)) { |
| if (this.__pointerCapturing) { |
| event.zrEventControl = 'no_globalout'; |
| } |
| this.trigger('mouseout', event); |
| } |
| }, |
| wheel: function (event) { |
| wheelEventSupported = true; |
| event = normalizeEvent(this.dom, event); |
| this.trigger('mousewheel', event); |
| }, |
| mousewheel: function (event) { |
| if (wheelEventSupported) { |
| return; |
| } |
| event = normalizeEvent(this.dom, event); |
| this.trigger('mousewheel', event); |
| }, |
| touchstart: function (event) { |
| event = normalizeEvent(this.dom, event); |
| markTouch(event); |
| this.__lastTouchMoment = new Date(); |
| this.handler.processGesture(event, 'start'); |
| localDOMHandlers.mousemove.call(this, event); |
| localDOMHandlers.mousedown.call(this, event); |
| }, |
| touchmove: function (event) { |
| event = normalizeEvent(this.dom, event); |
| markTouch(event); |
| this.handler.processGesture(event, 'change'); |
| localDOMHandlers.mousemove.call(this, event); |
| }, |
| touchend: function (event) { |
| event = normalizeEvent(this.dom, event); |
| markTouch(event); |
| this.handler.processGesture(event, 'end'); |
| localDOMHandlers.mouseup.call(this, event); |
| if (+new Date() - (+this.__lastTouchMoment) < TOUCH_CLICK_DELAY) { |
| localDOMHandlers.click.call(this, event); |
| } |
| }, |
| pointerdown: function (event) { |
| localDOMHandlers.mousedown.call(this, event); |
| }, |
| pointermove: function (event) { |
| if (!isPointerFromTouch(event)) { |
| localDOMHandlers.mousemove.call(this, event); |
| } |
| }, |
| pointerup: function (event) { |
| localDOMHandlers.mouseup.call(this, event); |
| }, |
| pointerout: function (event) { |
| if (!isPointerFromTouch(event)) { |
| localDOMHandlers.mouseout.call(this, event); |
| } |
| } |
| }; |
| each(['click', 'dblclick', 'contextmenu'], function (name) { |
| localDOMHandlers[name] = function (event) { |
| event = normalizeEvent(this.dom, event); |
| this.trigger(name, event); |
| }; |
| }); |
| var globalDOMHandlers = { |
| pointermove: function (event) { |
| if (!isPointerFromTouch(event)) { |
| globalDOMHandlers.mousemove.call(this, event); |
| } |
| }, |
| pointerup: function (event) { |
| globalDOMHandlers.mouseup.call(this, event); |
| }, |
| mousemove: function (event) { |
| this.trigger('mousemove', event); |
| }, |
| mouseup: function (event) { |
| var pointerCaptureReleasing = this.__pointerCapturing; |
| this.__togglePointerCapture(false); |
| this.trigger('mouseup', event); |
| if (pointerCaptureReleasing) { |
| event.zrEventControl = 'only_globalout'; |
| this.trigger('mouseout', event); |
| } |
| } |
| }; |
| function mountLocalDOMEventListeners(instance, scope) { |
| var domHandlers = scope.domHandlers; |
| if (env.pointerEventsSupported) { |
| each(localNativeListenerNames.pointer, function (nativeEventName) { |
| mountSingleDOMEventListener(scope, nativeEventName, function (event) { |
| domHandlers[nativeEventName].call(instance, event); |
| }); |
| }); |
| } |
| else { |
| if (env.touchEventsSupported) { |
| each(localNativeListenerNames.touch, function (nativeEventName) { |
| mountSingleDOMEventListener(scope, nativeEventName, function (event) { |
| domHandlers[nativeEventName].call(instance, event); |
| setTouchTimer(scope); |
| }); |
| }); |
| } |
| each(localNativeListenerNames.mouse, function (nativeEventName) { |
| mountSingleDOMEventListener(scope, nativeEventName, function (event) { |
| event = getNativeEvent(event); |
| if (!scope.touching) { |
| domHandlers[nativeEventName].call(instance, event); |
| } |
| }); |
| }); |
| } |
| } |
| function mountGlobalDOMEventListeners(instance, scope) { |
| if (env.pointerEventsSupported) { |
| each(globalNativeListenerNames.pointer, mount); |
| } |
| else if (!env.touchEventsSupported) { |
| each(globalNativeListenerNames.mouse, mount); |
| } |
| function mount(nativeEventName) { |
| function nativeEventListener(event) { |
| event = getNativeEvent(event); |
| if (!isLocalEl(instance, event.target)) { |
| event = normalizeGlobalEvent(instance, event); |
| scope.domHandlers[nativeEventName].call(instance, event); |
| } |
| } |
| mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { capture: true }); |
| } |
| } |
| function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) { |
| scope.mounted[nativeEventName] = listener; |
| scope.listenerOpts[nativeEventName] = opt; |
| addEventListener(scope.domTarget, nativeEventName, listener, opt); |
| } |
| function unmountDOMEventListeners(scope) { |
| var mounted = scope.mounted; |
| for (var nativeEventName in mounted) { |
| if (mounted.hasOwnProperty(nativeEventName)) { |
| removeEventListener(scope.domTarget, nativeEventName, mounted[nativeEventName], scope.listenerOpts[nativeEventName]); |
| } |
| } |
| scope.mounted = {}; |
| } |
| var DOMHandlerScope = (function () { |
| function DOMHandlerScope(domTarget, domHandlers) { |
| this.mounted = {}; |
| this.listenerOpts = {}; |
| this.touching = false; |
| this.domTarget = domTarget; |
| this.domHandlers = domHandlers; |
| } |
| return DOMHandlerScope; |
| }()); |
| var HandlerDomProxy = (function (_super) { |
| __extends(HandlerDomProxy, _super); |
| function HandlerDomProxy(dom, painterRoot) { |
| var _this = _super.call(this) || this; |
| _this.__pointerCapturing = false; |
| _this.dom = dom; |
| _this.painterRoot = painterRoot; |
| _this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers); |
| if (globalEventSupported) { |
| _this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers); |
| } |
| mountLocalDOMEventListeners(_this, _this._localHandlerScope); |
| return _this; |
| } |
| HandlerDomProxy.prototype.dispose = function () { |
| unmountDOMEventListeners(this._localHandlerScope); |
| if (globalEventSupported) { |
| unmountDOMEventListeners(this._globalHandlerScope); |
| } |
| }; |
| HandlerDomProxy.prototype.setCursor = function (cursorStyle) { |
| this.dom.style && (this.dom.style.cursor = cursorStyle || 'default'); |
| }; |
| HandlerDomProxy.prototype.__togglePointerCapture = function (isPointerCapturing) { |
| this.__mayPointerCapture = null; |
| if (globalEventSupported |
| && ((+this.__pointerCapturing) ^ (+isPointerCapturing))) { |
| this.__pointerCapturing = isPointerCapturing; |
| var globalHandlerScope = this._globalHandlerScope; |
| isPointerCapturing |
| ? mountGlobalDOMEventListeners(this, globalHandlerScope) |
| : unmountDOMEventListeners(globalHandlerScope); |
| } |
| }; |
| return HandlerDomProxy; |
| }(Eventful)); |
| |
| var dpr = 1; |
| if (env.hasGlobalWindow) { |
| dpr = Math.max(window.devicePixelRatio |
| || (window.screen && window.screen.deviceXDPI / window.screen.logicalXDPI) |
| || 1, 1); |
| } |
| var devicePixelRatio = dpr; |
| var DARK_MODE_THRESHOLD = 0.4; |
| var DARK_LABEL_COLOR = '#333'; |
| var LIGHT_LABEL_COLOR = '#ccc'; |
| var LIGHTER_LABEL_COLOR = '#eee'; |
| |
| var mIdentity = identity; |
| var EPSILON$1 = 5e-5; |
| function isNotAroundZero$1(val) { |
| return val > EPSILON$1 || val < -EPSILON$1; |
| } |
| var scaleTmp = []; |
| var tmpTransform = []; |
| var originTransform = create$1(); |
| var abs = Math.abs; |
| var Transformable = (function () { |
| function Transformable() { |
| } |
| Transformable.prototype.getLocalTransform = function (m) { |
| return Transformable.getLocalTransform(this, m); |
| }; |
| Transformable.prototype.setPosition = function (arr) { |
| this.x = arr[0]; |
| this.y = arr[1]; |
| }; |
| Transformable.prototype.setScale = function (arr) { |
| this.scaleX = arr[0]; |
| this.scaleY = arr[1]; |
| }; |
| Transformable.prototype.setSkew = function (arr) { |
| this.skewX = arr[0]; |
| this.skewY = arr[1]; |
| }; |
| Transformable.prototype.setOrigin = function (arr) { |
| this.originX = arr[0]; |
| this.originY = arr[1]; |
| }; |
| Transformable.prototype.needLocalTransform = function () { |
| return isNotAroundZero$1(this.rotation) |
| || isNotAroundZero$1(this.x) |
| || isNotAroundZero$1(this.y) |
| || isNotAroundZero$1(this.scaleX - 1) |
| || isNotAroundZero$1(this.scaleY - 1) |
| || isNotAroundZero$1(this.skewX) |
| || isNotAroundZero$1(this.skewY); |
| }; |
| Transformable.prototype.updateTransform = function () { |
| var parentTransform = this.parent && this.parent.transform; |
| var needLocalTransform = this.needLocalTransform(); |
| var m = this.transform; |
| if (!(needLocalTransform || parentTransform)) { |
| m && mIdentity(m); |
| return; |
| } |
| m = m || create$1(); |
| if (needLocalTransform) { |
| this.getLocalTransform(m); |
| } |
| else { |
| mIdentity(m); |
| } |
| if (parentTransform) { |
| if (needLocalTransform) { |
| mul$1(m, parentTransform, m); |
| } |
| else { |
| copy$1(m, parentTransform); |
| } |
| } |
| this.transform = m; |
| this._resolveGlobalScaleRatio(m); |
| }; |
| Transformable.prototype._resolveGlobalScaleRatio = function (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); |
| }; |
| Transformable.prototype.getComputedTransform = function () { |
| var transformNode = this; |
| var ancestors = []; |
| while (transformNode) { |
| ancestors.push(transformNode); |
| transformNode = transformNode.parent; |
| } |
| while (transformNode = ancestors.pop()) { |
| transformNode.updateTransform(); |
| } |
| return this.transform; |
| }; |
| Transformable.prototype.setLocalTransform = function (m) { |
| if (!m) { |
| return; |
| } |
| var sx = m[0] * m[0] + m[1] * m[1]; |
| var sy = m[2] * m[2] + m[3] * m[3]; |
| var rotation = Math.atan2(m[1], m[0]); |
| var shearX = Math.PI / 2 + rotation - Math.atan2(m[3], m[2]); |
| sy = Math.sqrt(sy) * Math.cos(shearX); |
| sx = Math.sqrt(sx); |
| this.skewX = shearX; |
| this.skewY = 0; |
| this.rotation = -rotation; |
| this.x = +m[4]; |
| this.y = +m[5]; |
| this.scaleX = sx; |
| this.scaleY = sy; |
| this.originX = 0; |
| this.originY = 0; |
| }; |
| Transformable.prototype.decomposeTransform = function () { |
| if (!this.transform) { |
| return; |
| } |
| var parent = this.parent; |
| var m = this.transform; |
| if (parent && parent.transform) { |
| mul$1(tmpTransform, parent.invTransform, m); |
| m = tmpTransform; |
| } |
| var ox = this.originX; |
| var oy = this.originY; |
| if (ox || oy) { |
| originTransform[4] = ox; |
| originTransform[5] = oy; |
| mul$1(tmpTransform, m, originTransform); |
| tmpTransform[4] -= ox; |
| tmpTransform[5] -= oy; |
| m = tmpTransform; |
| } |
| this.setLocalTransform(m); |
| }; |
| Transformable.prototype.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; |
| }; |
| Transformable.prototype.transformCoordToLocal = function (x, y) { |
| var v2 = [x, y]; |
| var invTransform = this.invTransform; |
| if (invTransform) { |
| applyTransform(v2, v2, invTransform); |
| } |
| return v2; |
| }; |
| Transformable.prototype.transformCoordToGlobal = function (x, y) { |
| var v2 = [x, y]; |
| var transform = this.transform; |
| if (transform) { |
| applyTransform(v2, v2, transform); |
| } |
| return v2; |
| }; |
| Transformable.prototype.getLineScale = function () { |
| var m = this.transform; |
| return 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; |
| }; |
| Transformable.prototype.copyTransform = function (source) { |
| copyTransform(this, source); |
| }; |
| Transformable.getLocalTransform = function (target, m) { |
| m = m || []; |
| var ox = target.originX || 0; |
| var oy = target.originY || 0; |
| var sx = target.scaleX; |
| var sy = target.scaleY; |
| var ax = target.anchorX; |
| var ay = target.anchorY; |
| var rotation = target.rotation || 0; |
| var x = target.x; |
| var y = target.y; |
| var skewX = target.skewX ? Math.tan(target.skewX) : 0; |
| var skewY = target.skewY ? Math.tan(-target.skewY) : 0; |
| if (ox || oy || ax || ay) { |
| var dx = ox + ax; |
| var dy = oy + ay; |
| m[4] = -dx * sx - skewX * dy * sy; |
| m[5] = -dy * sy - skewY * dx * sx; |
| } |
| else { |
| m[4] = m[5] = 0; |
| } |
| m[0] = sx; |
| m[3] = sy; |
| m[1] = skewY * sx; |
| m[2] = skewX * sy; |
| rotation && rotate(m, m, rotation); |
| m[4] += ox + x; |
| m[5] += oy + y; |
| return m; |
| }; |
| Transformable.initDefaultProps = (function () { |
| var proto = Transformable.prototype; |
| proto.scaleX = |
| proto.scaleY = |
| proto.globalScaleRatio = 1; |
| proto.x = |
| proto.y = |
| proto.originX = |
| proto.originY = |
| proto.skewX = |
| proto.skewY = |
| proto.rotation = |
| proto.anchorX = |
| proto.anchorY = 0; |
| })(); |
| return Transformable; |
| }()); |
| var TRANSFORMABLE_PROPS = [ |
| 'x', 'y', 'originX', 'originY', 'anchorX', 'anchorY', 'rotation', 'scaleX', 'scaleY', 'skewX', 'skewY' |
| ]; |
| function copyTransform(target, source) { |
| for (var i = 0; i < TRANSFORMABLE_PROPS.length; i++) { |
| var propName = TRANSFORMABLE_PROPS[i]; |
| target[propName] = source[propName]; |
| } |
| } |
| |
| var textWidthCache = {}; |
| function getWidth(text, font) { |
| font = font || DEFAULT_FONT; |
| var cacheOfFont = textWidthCache[font]; |
| if (!cacheOfFont) { |
| cacheOfFont = textWidthCache[font] = new LRU(500); |
| } |
| var width = cacheOfFont.get(text); |
| if (width == null) { |
| width = platformApi.measureText(text, font).width; |
| cacheOfFont.put(text, width); |
| } |
| return width; |
| } |
| function innerGetBoundingRect(text, font, textAlign, textBaseline) { |
| var width = getWidth(text, font); |
| var height = getLineHeight(font); |
| var x = adjustTextX(0, width, textAlign); |
| var y = adjustTextY(0, height, textBaseline); |
| var rect = new BoundingRect(x, y, width, height); |
| return rect; |
| } |
| function getBoundingRect(text, font, textAlign, textBaseline) { |
| var textLines = ((text || '') + '').split('\n'); |
| var len = textLines.length; |
| if (len === 1) { |
| return innerGetBoundingRect(textLines[0], font, textAlign, textBaseline); |
| } |
| else { |
| var uniondRect = new BoundingRect(0, 0, 0, 0); |
| for (var i = 0; i < textLines.length; i++) { |
| var rect = innerGetBoundingRect(textLines[i], font, textAlign, textBaseline); |
| i === 0 ? uniondRect.copy(rect) : uniondRect.union(rect); |
| } |
| return uniondRect; |
| } |
| } |
| function adjustTextX(x, width, textAlign) { |
| if (textAlign === 'right') { |
| x -= width; |
| } |
| else if (textAlign === 'center') { |
| x -= width / 2; |
| } |
| return x; |
| } |
| function adjustTextY(y, height, verticalAlign) { |
| if (verticalAlign === 'middle') { |
| y -= height / 2; |
| } |
| else if (verticalAlign === 'bottom') { |
| y -= height; |
| } |
| return y; |
| } |
| function getLineHeight(font) { |
| return getWidth('国', font); |
| } |
| function parsePercent(value, maxValue) { |
| if (typeof value === 'string') { |
| if (value.lastIndexOf('%') >= 0) { |
| return parseFloat(value) / 100 * maxValue; |
| } |
| return parseFloat(value); |
| } |
| return value; |
| } |
| function calculateTextPosition(out, opts, rect) { |
| var textPosition = opts.position || 'inside'; |
| var distance = opts.distance != null ? opts.distance : 5; |
| var height = rect.height; |
| var width = rect.width; |
| var halfHeight = height / 2; |
| var x = rect.x; |
| var y = rect.y; |
| var textAlign = 'left'; |
| var textVerticalAlign = 'top'; |
| if (textPosition instanceof Array) { |
| x += parsePercent(textPosition[0], rect.width); |
| y += parsePercent(textPosition[1], rect.height); |
| textAlign = null; |
| textVerticalAlign = null; |
| } |
| else { |
| 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.align = textAlign; |
| out.verticalAlign = textVerticalAlign; |
| return out; |
| } |
| |
| var PRESERVED_NORMAL_STATE = '__zr_normal__'; |
| var PRIMARY_STATES_KEYS = TRANSFORMABLE_PROPS.concat(['ignore']); |
| var DEFAULT_ANIMATABLE_MAP = reduce(TRANSFORMABLE_PROPS, function (obj, key) { |
| obj[key] = true; |
| return obj; |
| }, { ignore: false }); |
| var tmpTextPosCalcRes = {}; |
| var tmpBoundingRect = new BoundingRect(0, 0, 0, 0); |
| var Element = (function () { |
| function Element(props) { |
| this.id = guid(); |
| this.animators = []; |
| this.currentStates = []; |
| this.states = {}; |
| this._init(props); |
| } |
| Element.prototype._init = function (props) { |
| this.attr(props); |
| }; |
| Element.prototype.drift = function (dx, dy, e) { |
| 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.markRedraw(); |
| }; |
| Element.prototype.beforeUpdate = function () { }; |
| Element.prototype.afterUpdate = function () { }; |
| Element.prototype.update = function () { |
| this.updateTransform(); |
| if (this.__dirty) { |
| this.updateInnerText(); |
| } |
| }; |
| Element.prototype.updateInnerText = function (forceUpdate) { |
| var textEl = this._textContent; |
| if (textEl && (!textEl.ignore || forceUpdate)) { |
| if (!this.textConfig) { |
| this.textConfig = {}; |
| } |
| var textConfig = this.textConfig; |
| var isLocal = textConfig.local; |
| var innerTransformable = textEl.innerTransformable; |
| var textAlign = void 0; |
| var textVerticalAlign = void 0; |
| var textStyleChanged = false; |
| innerTransformable.parent = isLocal ? this : null; |
| var innerOrigin = false; |
| innerTransformable.copyTransform(textEl); |
| if (textConfig.position != null) { |
| var layoutRect = tmpBoundingRect; |
| if (textConfig.layoutRect) { |
| layoutRect.copy(textConfig.layoutRect); |
| } |
| else { |
| layoutRect.copy(this.getBoundingRect()); |
| } |
| if (!isLocal) { |
| layoutRect.applyTransform(this.transform); |
| } |
| if (this.calculateTextPosition) { |
| this.calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect); |
| } |
| else { |
| calculateTextPosition(tmpTextPosCalcRes, textConfig, layoutRect); |
| } |
| innerTransformable.x = tmpTextPosCalcRes.x; |
| innerTransformable.y = tmpTextPosCalcRes.y; |
| textAlign = tmpTextPosCalcRes.align; |
| textVerticalAlign = tmpTextPosCalcRes.verticalAlign; |
| var textOrigin = textConfig.origin; |
| if (textOrigin && textConfig.rotation != null) { |
| var relOriginX = void 0; |
| var relOriginY = void 0; |
| if (textOrigin === 'center') { |
| relOriginX = layoutRect.width * 0.5; |
| relOriginY = layoutRect.height * 0.5; |
| } |
| else { |
| relOriginX = parsePercent(textOrigin[0], layoutRect.width); |
| relOriginY = parsePercent(textOrigin[1], layoutRect.height); |
| } |
| innerOrigin = true; |
| innerTransformable.originX = -innerTransformable.x + relOriginX + (isLocal ? 0 : layoutRect.x); |
| innerTransformable.originY = -innerTransformable.y + relOriginY + (isLocal ? 0 : layoutRect.y); |
| } |
| } |
| if (textConfig.rotation != null) { |
| innerTransformable.rotation = textConfig.rotation; |
| } |
| var textOffset = textConfig.offset; |
| if (textOffset) { |
| innerTransformable.x += textOffset[0]; |
| innerTransformable.y += textOffset[1]; |
| if (!innerOrigin) { |
| innerTransformable.originX = -textOffset[0]; |
| innerTransformable.originY = -textOffset[1]; |
| } |
| } |
| var isInside = textConfig.inside == null |
| ? (typeof textConfig.position === 'string' && textConfig.position.indexOf('inside') >= 0) |
| : textConfig.inside; |
| var innerTextDefaultStyle = this._innerTextDefaultStyle || (this._innerTextDefaultStyle = {}); |
| var textFill = void 0; |
| var textStroke = void 0; |
| var autoStroke = void 0; |
| if (isInside && this.canBeInsideText()) { |
| textFill = textConfig.insideFill; |
| textStroke = textConfig.insideStroke; |
| if (textFill == null || textFill === 'auto') { |
| textFill = this.getInsideTextFill(); |
| } |
| if (textStroke == null || textStroke === 'auto') { |
| textStroke = this.getInsideTextStroke(textFill); |
| autoStroke = true; |
| } |
| } |
| else { |
| textFill = textConfig.outsideFill; |
| textStroke = textConfig.outsideStroke; |
| if (textFill == null || textFill === 'auto') { |
| textFill = this.getOutsideFill(); |
| } |
| if (textStroke == null || textStroke === 'auto') { |
| textStroke = this.getOutsideStroke(textFill); |
| autoStroke = true; |
| } |
| } |
| textFill = textFill || '#000'; |
| if (textFill !== innerTextDefaultStyle.fill |
| || textStroke !== innerTextDefaultStyle.stroke |
| || autoStroke !== innerTextDefaultStyle.autoStroke |
| || textAlign !== innerTextDefaultStyle.align |
| || textVerticalAlign !== innerTextDefaultStyle.verticalAlign) { |
| textStyleChanged = true; |
| innerTextDefaultStyle.fill = textFill; |
| innerTextDefaultStyle.stroke = textStroke; |
| innerTextDefaultStyle.autoStroke = autoStroke; |
| innerTextDefaultStyle.align = textAlign; |
| innerTextDefaultStyle.verticalAlign = textVerticalAlign; |
| textEl.setDefaultTextStyle(innerTextDefaultStyle); |
| } |
| textEl.__dirty |= REDRAW_BIT; |
| if (textStyleChanged) { |
| textEl.dirtyStyle(true); |
| } |
| } |
| }; |
| Element.prototype.canBeInsideText = function () { |
| return true; |
| }; |
| Element.prototype.getInsideTextFill = function () { |
| return '#fff'; |
| }; |
| Element.prototype.getInsideTextStroke = function (textFill) { |
| return '#000'; |
| }; |
| Element.prototype.getOutsideFill = function () { |
| return this.__zr && this.__zr.isDarkMode() ? LIGHT_LABEL_COLOR : DARK_LABEL_COLOR; |
| }; |
| Element.prototype.getOutsideStroke = function (textFill) { |
| var backgroundColor = this.__zr && this.__zr.getBackgroundColor(); |
| var colorArr = typeof backgroundColor === 'string' && parse(backgroundColor); |
| if (!colorArr) { |
| colorArr = [255, 255, 255, 1]; |
| } |
| var alpha = colorArr[3]; |
| var isDark = this.__zr.isDarkMode(); |
| for (var i = 0; i < 3; i++) { |
| colorArr[i] = colorArr[i] * alpha + (isDark ? 0 : 255) * (1 - alpha); |
| } |
| colorArr[3] = 1; |
| return stringify(colorArr, 'rgba'); |
| }; |
| Element.prototype.traverse = function (cb, context) { }; |
| Element.prototype.attrKV = function (key, value) { |
| if (key === 'textConfig') { |
| this.setTextConfig(value); |
| } |
| else if (key === 'textContent') { |
| this.setTextContent(value); |
| } |
| else if (key === 'clipPath') { |
| this.setClipPath(value); |
| } |
| else if (key === 'extra') { |
| this.extra = this.extra || {}; |
| extend(this.extra, value); |
| } |
| else { |
| this[key] = value; |
| } |
| }; |
| Element.prototype.hide = function () { |
| this.ignore = true; |
| this.markRedraw(); |
| }; |
| Element.prototype.show = function () { |
| this.ignore = false; |
| this.markRedraw(); |
| }; |
| Element.prototype.attr = function (keyOrObj, value) { |
| if (typeof keyOrObj === 'string') { |
| this.attrKV(keyOrObj, value); |
| } |
| else if (isObject(keyOrObj)) { |
| var obj = keyOrObj; |
| var keysArr = keys(obj); |
| for (var i = 0; i < keysArr.length; i++) { |
| var key = keysArr[i]; |
| this.attrKV(key, keyOrObj[key]); |
| } |
| } |
| this.markRedraw(); |
| return this; |
| }; |
| Element.prototype.saveCurrentToNormalState = function (toState) { |
| this._innerSaveToNormal(toState); |
| var normalState = this._normalState; |
| for (var i = 0; i < this.animators.length; i++) { |
| var animator = this.animators[i]; |
| var fromStateTransition = animator.__fromStateTransition; |
| if (animator.getLoop() || fromStateTransition && fromStateTransition !== PRESERVED_NORMAL_STATE) { |
| continue; |
| } |
| var targetName = animator.targetName; |
| var target = targetName |
| ? normalState[targetName] : normalState; |
| animator.saveTo(target); |
| } |
| }; |
| Element.prototype._innerSaveToNormal = function (toState) { |
| var normalState = this._normalState; |
| if (!normalState) { |
| normalState = this._normalState = {}; |
| } |
| if (toState.textConfig && !normalState.textConfig) { |
| normalState.textConfig = this.textConfig; |
| } |
| this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS); |
| }; |
| Element.prototype._savePrimaryToNormal = function (toState, normalState, primaryKeys) { |
| for (var i = 0; i < primaryKeys.length; i++) { |
| var key = primaryKeys[i]; |
| if (toState[key] != null && !(key in normalState)) { |
| normalState[key] = this[key]; |
| } |
| } |
| }; |
| Element.prototype.hasState = function () { |
| return this.currentStates.length > 0; |
| }; |
| Element.prototype.getState = function (name) { |
| return this.states[name]; |
| }; |
| Element.prototype.ensureState = function (name) { |
| var states = this.states; |
| if (!states[name]) { |
| states[name] = {}; |
| } |
| return states[name]; |
| }; |
| Element.prototype.clearStates = function (noAnimation) { |
| this.useState(PRESERVED_NORMAL_STATE, false, noAnimation); |
| }; |
| Element.prototype.useState = function (stateName, keepCurrentStates, noAnimation, forceUseHoverLayer) { |
| var toNormalState = stateName === PRESERVED_NORMAL_STATE; |
| var hasStates = this.hasState(); |
| if (!hasStates && toNormalState) { |
| return; |
| } |
| var currentStates = this.currentStates; |
| var animationCfg = this.stateTransition; |
| if (indexOf(currentStates, stateName) >= 0 && (keepCurrentStates || currentStates.length === 1)) { |
| return; |
| } |
| var state; |
| if (this.stateProxy && !toNormalState) { |
| state = this.stateProxy(stateName); |
| } |
| if (!state) { |
| state = (this.states && this.states[stateName]); |
| } |
| if (!state && !toNormalState) { |
| logError("State " + stateName + " not exists."); |
| return; |
| } |
| if (!toNormalState) { |
| this.saveCurrentToNormalState(state); |
| } |
| var useHoverLayer = !!((state && state.hoverLayer) || forceUseHoverLayer); |
| if (useHoverLayer) { |
| this._toggleHoverLayerFlag(true); |
| } |
| this._applyStateObj(stateName, state, this._normalState, keepCurrentStates, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg); |
| var textContent = this._textContent; |
| var textGuide = this._textGuide; |
| if (textContent) { |
| textContent.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer); |
| } |
| if (textGuide) { |
| textGuide.useState(stateName, keepCurrentStates, noAnimation, useHoverLayer); |
| } |
| if (toNormalState) { |
| this.currentStates = []; |
| this._normalState = {}; |
| } |
| else { |
| if (!keepCurrentStates) { |
| this.currentStates = [stateName]; |
| } |
| else { |
| this.currentStates.push(stateName); |
| } |
| } |
| this._updateAnimationTargets(); |
| this.markRedraw(); |
| if (!useHoverLayer && this.__inHover) { |
| this._toggleHoverLayerFlag(false); |
| this.__dirty &= ~REDRAW_BIT; |
| } |
| return state; |
| }; |
| Element.prototype.useStates = function (states, noAnimation, forceUseHoverLayer) { |
| if (!states.length) { |
| this.clearStates(); |
| } |
| else { |
| var stateObjects = []; |
| var currentStates = this.currentStates; |
| var len = states.length; |
| var notChange = len === currentStates.length; |
| if (notChange) { |
| for (var i = 0; i < len; i++) { |
| if (states[i] !== currentStates[i]) { |
| notChange = false; |
| break; |
| } |
| } |
| } |
| if (notChange) { |
| return; |
| } |
| for (var i = 0; i < len; i++) { |
| var stateName = states[i]; |
| var stateObj = void 0; |
| if (this.stateProxy) { |
| stateObj = this.stateProxy(stateName, states); |
| } |
| if (!stateObj) { |
| stateObj = this.states[stateName]; |
| } |
| if (stateObj) { |
| stateObjects.push(stateObj); |
| } |
| } |
| var lastStateObj = stateObjects[len - 1]; |
| var useHoverLayer = !!((lastStateObj && lastStateObj.hoverLayer) || forceUseHoverLayer); |
| if (useHoverLayer) { |
| this._toggleHoverLayerFlag(true); |
| } |
| var mergedState = this._mergeStates(stateObjects); |
| var animationCfg = this.stateTransition; |
| this.saveCurrentToNormalState(mergedState); |
| this._applyStateObj(states.join(','), mergedState, this._normalState, false, !noAnimation && !this.__inHover && animationCfg && animationCfg.duration > 0, animationCfg); |
| var textContent = this._textContent; |
| var textGuide = this._textGuide; |
| if (textContent) { |
| textContent.useStates(states, noAnimation, useHoverLayer); |
| } |
| if (textGuide) { |
| textGuide.useStates(states, noAnimation, useHoverLayer); |
| } |
| this._updateAnimationTargets(); |
| this.currentStates = states.slice(); |
| this.markRedraw(); |
| if (!useHoverLayer && this.__inHover) { |
| this._toggleHoverLayerFlag(false); |
| this.__dirty &= ~REDRAW_BIT; |
| } |
| } |
| }; |
| Element.prototype._updateAnimationTargets = function () { |
| for (var i = 0; i < this.animators.length; i++) { |
| var animator = this.animators[i]; |
| if (animator.targetName) { |
| animator.changeTarget(this[animator.targetName]); |
| } |
| } |
| }; |
| Element.prototype.removeState = function (state) { |
| var idx = indexOf(this.currentStates, state); |
| if (idx >= 0) { |
| var currentStates = this.currentStates.slice(); |
| currentStates.splice(idx, 1); |
| this.useStates(currentStates); |
| } |
| }; |
| Element.prototype.replaceState = function (oldState, newState, forceAdd) { |
| var currentStates = this.currentStates.slice(); |
| var idx = indexOf(currentStates, oldState); |
| var newStateExists = indexOf(currentStates, newState) >= 0; |
| if (idx >= 0) { |
| if (!newStateExists) { |
| currentStates[idx] = newState; |
| } |
| else { |
| currentStates.splice(idx, 1); |
| } |
| } |
| else if (forceAdd && !newStateExists) { |
| currentStates.push(newState); |
| } |
| this.useStates(currentStates); |
| }; |
| Element.prototype.toggleState = function (state, enable) { |
| if (enable) { |
| this.useState(state, true); |
| } |
| else { |
| this.removeState(state); |
| } |
| }; |
| Element.prototype._mergeStates = function (states) { |
| var mergedState = {}; |
| var mergedTextConfig; |
| for (var i = 0; i < states.length; i++) { |
| var state = states[i]; |
| extend(mergedState, state); |
| if (state.textConfig) { |
| mergedTextConfig = mergedTextConfig || {}; |
| extend(mergedTextConfig, state.textConfig); |
| } |
| } |
| if (mergedTextConfig) { |
| mergedState.textConfig = mergedTextConfig; |
| } |
| return mergedState; |
| }; |
| Element.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { |
| var needsRestoreToNormal = !(state && keepCurrentStates); |
| if (state && state.textConfig) { |
| this.textConfig = extend({}, keepCurrentStates ? this.textConfig : normalState.textConfig); |
| extend(this.textConfig, state.textConfig); |
| } |
| else if (needsRestoreToNormal) { |
| if (normalState.textConfig) { |
| this.textConfig = normalState.textConfig; |
| } |
| } |
| var transitionTarget = {}; |
| var hasTransition = false; |
| for (var i = 0; i < PRIMARY_STATES_KEYS.length; i++) { |
| var key = PRIMARY_STATES_KEYS[i]; |
| var propNeedsTransition = transition && DEFAULT_ANIMATABLE_MAP[key]; |
| if (state && state[key] != null) { |
| if (propNeedsTransition) { |
| hasTransition = true; |
| transitionTarget[key] = state[key]; |
| } |
| else { |
| this[key] = state[key]; |
| } |
| } |
| else if (needsRestoreToNormal) { |
| if (normalState[key] != null) { |
| if (propNeedsTransition) { |
| hasTransition = true; |
| transitionTarget[key] = normalState[key]; |
| } |
| else { |
| this[key] = normalState[key]; |
| } |
| } |
| } |
| } |
| if (!transition) { |
| for (var i = 0; i < this.animators.length; i++) { |
| var animator = this.animators[i]; |
| var targetName = animator.targetName; |
| if (!animator.getLoop()) { |
| animator.__changeFinalValue(targetName |
| ? (state || normalState)[targetName] |
| : (state || normalState)); |
| } |
| } |
| } |
| if (hasTransition) { |
| this._transitionState(stateName, transitionTarget, animationCfg); |
| } |
| }; |
| Element.prototype._attachComponent = function (componentEl) { |
| if (componentEl.__zr && !componentEl.__hostTarget) { |
| if ("development" !== 'production') { |
| throw new Error('Text element has been added to zrender.'); |
| } |
| return; |
| } |
| if (componentEl === this) { |
| if ("development" !== 'production') { |
| throw new Error('Recursive component attachment.'); |
| } |
| return; |
| } |
| var zr = this.__zr; |
| if (zr) { |
| componentEl.addSelfToZr(zr); |
| } |
| componentEl.__zr = zr; |
| componentEl.__hostTarget = this; |
| }; |
| Element.prototype._detachComponent = function (componentEl) { |
| if (componentEl.__zr) { |
| componentEl.removeSelfFromZr(componentEl.__zr); |
| } |
| componentEl.__zr = null; |
| componentEl.__hostTarget = null; |
| }; |
| Element.prototype.getClipPath = function () { |
| return this._clipPath; |
| }; |
| Element.prototype.setClipPath = function (clipPath) { |
| if (this._clipPath && this._clipPath !== clipPath) { |
| this.removeClipPath(); |
| } |
| this._attachComponent(clipPath); |
| this._clipPath = clipPath; |
| this.markRedraw(); |
| }; |
| Element.prototype.removeClipPath = function () { |
| var clipPath = this._clipPath; |
| if (clipPath) { |
| this._detachComponent(clipPath); |
| this._clipPath = null; |
| this.markRedraw(); |
| } |
| }; |
| Element.prototype.getTextContent = function () { |
| return this._textContent; |
| }; |
| Element.prototype.setTextContent = function (textEl) { |
| var previousTextContent = this._textContent; |
| if (previousTextContent === textEl) { |
| return; |
| } |
| if (previousTextContent && previousTextContent !== textEl) { |
| this.removeTextContent(); |
| } |
| if ("development" !== 'production') { |
| if (textEl.__zr && !textEl.__hostTarget) { |
| throw new Error('Text element has been added to zrender.'); |
| } |
| } |
| textEl.innerTransformable = new Transformable(); |
| this._attachComponent(textEl); |
| this._textContent = textEl; |
| this.markRedraw(); |
| }; |
| Element.prototype.setTextConfig = function (cfg) { |
| if (!this.textConfig) { |
| this.textConfig = {}; |
| } |
| extend(this.textConfig, cfg); |
| this.markRedraw(); |
| }; |
| Element.prototype.removeTextConfig = function () { |
| this.textConfig = null; |
| this.markRedraw(); |
| }; |
| Element.prototype.removeTextContent = function () { |
| var textEl = this._textContent; |
| if (textEl) { |
| textEl.innerTransformable = null; |
| this._detachComponent(textEl); |
| this._textContent = null; |
| this._innerTextDefaultStyle = null; |
| this.markRedraw(); |
| } |
| }; |
| Element.prototype.getTextGuideLine = function () { |
| return this._textGuide; |
| }; |
| Element.prototype.setTextGuideLine = function (guideLine) { |
| if (this._textGuide && this._textGuide !== guideLine) { |
| this.removeTextGuideLine(); |
| } |
| this._attachComponent(guideLine); |
| this._textGuide = guideLine; |
| this.markRedraw(); |
| }; |
| Element.prototype.removeTextGuideLine = function () { |
| var textGuide = this._textGuide; |
| if (textGuide) { |
| this._detachComponent(textGuide); |
| this._textGuide = null; |
| this.markRedraw(); |
| } |
| }; |
| Element.prototype.markRedraw = function () { |
| this.__dirty |= REDRAW_BIT; |
| var zr = this.__zr; |
| if (zr) { |
| if (this.__inHover) { |
| zr.refreshHover(); |
| } |
| else { |
| zr.refresh(); |
| } |
| } |
| if (this.__hostTarget) { |
| this.__hostTarget.markRedraw(); |
| } |
| }; |
| Element.prototype.dirty = function () { |
| this.markRedraw(); |
| }; |
| Element.prototype._toggleHoverLayerFlag = function (inHover) { |
| this.__inHover = inHover; |
| var textContent = this._textContent; |
| var textGuide = this._textGuide; |
| if (textContent) { |
| textContent.__inHover = inHover; |
| } |
| if (textGuide) { |
| textGuide.__inHover = inHover; |
| } |
| }; |
| Element.prototype.addSelfToZr = function (zr) { |
| if (this.__zr === zr) { |
| return; |
| } |
| this.__zr = zr; |
| var animators = this.animators; |
| if (animators) { |
| for (var i = 0; i < animators.length; i++) { |
| zr.animation.addAnimator(animators[i]); |
| } |
| } |
| if (this._clipPath) { |
| this._clipPath.addSelfToZr(zr); |
| } |
| if (this._textContent) { |
| this._textContent.addSelfToZr(zr); |
| } |
| if (this._textGuide) { |
| this._textGuide.addSelfToZr(zr); |
| } |
| }; |
| Element.prototype.removeSelfFromZr = function (zr) { |
| if (!this.__zr) { |
| return; |
| } |
| this.__zr = null; |
| var animators = this.animators; |
| if (animators) { |
| for (var i = 0; i < animators.length; i++) { |
| zr.animation.removeAnimator(animators[i]); |
| } |
| } |
| if (this._clipPath) { |
| this._clipPath.removeSelfFromZr(zr); |
| } |
| if (this._textContent) { |
| this._textContent.removeSelfFromZr(zr); |
| } |
| if (this._textGuide) { |
| this._textGuide.removeSelfFromZr(zr); |
| } |
| }; |
| Element.prototype.animate = function (key, loop, allowDiscreteAnimation) { |
| var target = key ? this[key] : this; |
| if ("development" !== 'production') { |
| if (!target) { |
| logError('Property "' |
| + key |
| + '" is not existed in element ' |
| + this.id); |
| return; |
| } |
| } |
| var animator = new Animator(target, loop, allowDiscreteAnimation); |
| key && (animator.targetName = key); |
| this.addAnimator(animator, key); |
| return animator; |
| }; |
| Element.prototype.addAnimator = function (animator, key) { |
| var zr = this.__zr; |
| var el = this; |
| animator.during(function () { |
| el.updateDuringAnimation(key); |
| }).done(function () { |
| var animators = el.animators; |
| var idx = indexOf(animators, animator); |
| if (idx >= 0) { |
| animators.splice(idx, 1); |
| } |
| }); |
| this.animators.push(animator); |
| if (zr) { |
| zr.animation.addAnimator(animator); |
| } |
| zr && zr.wakeUp(); |
| }; |
| Element.prototype.updateDuringAnimation = function (key) { |
| this.markRedraw(); |
| }; |
| Element.prototype.stopAnimation = function (scope, forwardToLast) { |
| var animators = this.animators; |
| var len = animators.length; |
| var leftAnimators = []; |
| for (var i = 0; i < len; i++) { |
| var animator = animators[i]; |
| if (!scope || scope === animator.scope) { |
| animator.stop(forwardToLast); |
| } |
| else { |
| leftAnimators.push(animator); |
| } |
| } |
| this.animators = leftAnimators; |
| return this; |
| }; |
| Element.prototype.animateTo = function (target, cfg, animationProps) { |
| animateTo(this, target, cfg, animationProps); |
| }; |
| Element.prototype.animateFrom = function (target, cfg, animationProps) { |
| animateTo(this, target, cfg, animationProps, true); |
| }; |
| Element.prototype._transitionState = function (stateName, target, cfg, animationProps) { |
| var animators = animateTo(this, target, cfg, animationProps); |
| for (var i = 0; i < animators.length; i++) { |
| animators[i].__fromStateTransition = stateName; |
| } |
| }; |
| Element.prototype.getBoundingRect = function () { |
| return null; |
| }; |
| Element.prototype.getPaintRect = function () { |
| return null; |
| }; |
| Element.initDefaultProps = (function () { |
| var elProto = Element.prototype; |
| elProto.type = 'element'; |
| elProto.name = ''; |
| elProto.ignore = |
| elProto.silent = |
| elProto.isGroup = |
| elProto.draggable = |
| elProto.dragging = |
| elProto.ignoreClip = |
| elProto.__inHover = false; |
| elProto.__dirty = REDRAW_BIT; |
| var logs = {}; |
| function logDeprecatedError(key, xKey, yKey) { |
| if (!logs[key + xKey + yKey]) { |
| console.warn("DEPRECATED: '" + key + "' has been deprecated. use '" + xKey + "', '" + yKey + "' instead"); |
| logs[key + xKey + yKey] = true; |
| } |
| } |
| function createLegacyProperty(key, privateKey, xKey, yKey) { |
| Object.defineProperty(elProto, key, { |
| get: function () { |
| if ("development" !== 'production') { |
| logDeprecatedError(key, xKey, yKey); |
| } |
| if (!this[privateKey]) { |
| var pos = this[privateKey] = []; |
| enhanceArray(this, pos); |
| } |
| return this[privateKey]; |
| }, |
| set: function (pos) { |
| if ("development" !== 'production') { |
| logDeprecatedError(key, xKey, yKey); |
| } |
| this[xKey] = pos[0]; |
| this[yKey] = pos[1]; |
| this[privateKey] = pos; |
| enhanceArray(this, pos); |
| } |
| }); |
| function enhanceArray(self, pos) { |
| Object.defineProperty(pos, 0, { |
| get: function () { |
| return self[xKey]; |
| }, |
| set: function (val) { |
| self[xKey] = val; |
| } |
| }); |
| Object.defineProperty(pos, 1, { |
| get: function () { |
| return self[yKey]; |
| }, |
| set: function (val) { |
| self[yKey] = val; |
| } |
| }); |
| } |
| } |
| if (Object.defineProperty) { |
| createLegacyProperty('position', '_legacyPos', 'x', 'y'); |
| createLegacyProperty('scale', '_legacyScale', 'scaleX', 'scaleY'); |
| createLegacyProperty('origin', '_legacyOrigin', 'originX', 'originY'); |
| } |
| })(); |
| return Element; |
| }()); |
| mixin(Element, Eventful); |
| mixin(Element, Transformable); |
| function animateTo(animatable, target, cfg, animationProps, reverse) { |
| cfg = cfg || {}; |
| var animators = []; |
| animateToShallow(animatable, '', animatable, target, cfg, animationProps, animators, reverse); |
| var finishCount = animators.length; |
| var doneHappened = false; |
| var cfgDone = cfg.done; |
| var cfgAborted = cfg.aborted; |
| var doneCb = function () { |
| doneHappened = true; |
| finishCount--; |
| if (finishCount <= 0) { |
| doneHappened |
| ? (cfgDone && cfgDone()) |
| : (cfgAborted && cfgAborted()); |
| } |
| }; |
| var abortedCb = function () { |
| finishCount--; |
| if (finishCount <= 0) { |
| doneHappened |
| ? (cfgDone && cfgDone()) |
| : (cfgAborted && cfgAborted()); |
| } |
| }; |
| if (!finishCount) { |
| cfgDone && cfgDone(); |
| } |
| if (animators.length > 0 && cfg.during) { |
| animators[0].during(function (target, percent) { |
| cfg.during(percent); |
| }); |
| } |
| for (var i = 0; i < animators.length; i++) { |
| var animator = animators[i]; |
| if (doneCb) { |
| animator.done(doneCb); |
| } |
| if (abortedCb) { |
| animator.aborted(abortedCb); |
| } |
| if (cfg.force) { |
| animator.duration(cfg.duration); |
| } |
| animator.start(cfg.easing); |
| } |
| return animators; |
| } |
| function copyArrShallow(source, target, len) { |
| for (var i = 0; i < len; i++) { |
| source[i] = target[i]; |
| } |
| } |
| function is2DArray(value) { |
| return isArrayLike(value[0]); |
| } |
| function copyValue(target, source, key) { |
| if (isArrayLike(source[key])) { |
| if (!isArrayLike(target[key])) { |
| target[key] = []; |
| } |
| if (isTypedArray(source[key])) { |
| var len = source[key].length; |
| if (target[key].length !== len) { |
| target[key] = new (source[key].constructor)(len); |
| copyArrShallow(target[key], source[key], len); |
| } |
| } |
| else { |
| var sourceArr = source[key]; |
| var targetArr = target[key]; |
| var len0 = sourceArr.length; |
| if (is2DArray(sourceArr)) { |
| var len1 = sourceArr[0].length; |
| for (var i = 0; i < len0; i++) { |
| if (!targetArr[i]) { |
| targetArr[i] = Array.prototype.slice.call(sourceArr[i]); |
| } |
| else { |
| copyArrShallow(targetArr[i], sourceArr[i], len1); |
| } |
| } |
| } |
| else { |
| copyArrShallow(targetArr, sourceArr, len0); |
| } |
| targetArr.length = sourceArr.length; |
| } |
| } |
| else { |
| target[key] = source[key]; |
| } |
| } |
| function isValueSame(val1, val2) { |
| return val1 === val2 |
| || isArrayLike(val1) && isArrayLike(val2) && is1DArraySame(val1, val2); |
| } |
| function is1DArraySame(arr0, arr1) { |
| var len = arr0.length; |
| if (len !== arr1.length) { |
| return false; |
| } |
| for (var i = 0; i < len; i++) { |
| if (arr0[i] !== arr1[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| function animateToShallow(animatable, topKey, animateObj, target, cfg, animationProps, animators, reverse) { |
| var targetKeys = keys(target); |
| var duration = cfg.duration; |
| var delay = cfg.delay; |
| var additive = cfg.additive; |
| var setToFinal = cfg.setToFinal; |
| var animateAll = !isObject(animationProps); |
| var existsAnimators = animatable.animators; |
| var animationKeys = []; |
| for (var k = 0; k < targetKeys.length; k++) { |
| var innerKey = targetKeys[k]; |
| var targetVal = target[innerKey]; |
| if (targetVal != null && animateObj[innerKey] != null |
| && (animateAll || animationProps[innerKey])) { |
| if (isObject(targetVal) |
| && !isArrayLike(targetVal) |
| && !isGradientObject(targetVal)) { |
| if (topKey) { |
| if (!reverse) { |
| animateObj[innerKey] = targetVal; |
| animatable.updateDuringAnimation(topKey); |
| } |
| continue; |
| } |
| animateToShallow(animatable, innerKey, animateObj[innerKey], targetVal, cfg, animationProps && animationProps[innerKey], animators, reverse); |
| } |
| else { |
| animationKeys.push(innerKey); |
| } |
| } |
| else if (!reverse) { |
| animateObj[innerKey] = targetVal; |
| animatable.updateDuringAnimation(topKey); |
| animationKeys.push(innerKey); |
| } |
| } |
| var keyLen = animationKeys.length; |
| if (!additive && keyLen) { |
| for (var i = 0; i < existsAnimators.length; i++) { |
| var animator = existsAnimators[i]; |
| if (animator.targetName === topKey) { |
| var allAborted = animator.stopTracks(animationKeys); |
| if (allAborted) { |
| var idx = indexOf(existsAnimators, animator); |
| existsAnimators.splice(idx, 1); |
| } |
| } |
| } |
| } |
| if (!cfg.force) { |
| animationKeys = filter(animationKeys, function (key) { return !isValueSame(target[key], animateObj[key]); }); |
| keyLen = animationKeys.length; |
| } |
| if (keyLen > 0 |
| || (cfg.force && !animators.length)) { |
| var revertedSource = void 0; |
| var reversedTarget = void 0; |
| var sourceClone = void 0; |
| if (reverse) { |
| reversedTarget = {}; |
| if (setToFinal) { |
| revertedSource = {}; |
| } |
| for (var i = 0; i < keyLen; i++) { |
| var innerKey = animationKeys[i]; |
| reversedTarget[innerKey] = animateObj[innerKey]; |
| if (setToFinal) { |
| revertedSource[innerKey] = target[innerKey]; |
| } |
| else { |
| animateObj[innerKey] = target[innerKey]; |
| } |
| } |
| } |
| else if (setToFinal) { |
| sourceClone = {}; |
| for (var i = 0; i < keyLen; i++) { |
| var innerKey = animationKeys[i]; |
| sourceClone[innerKey] = cloneValue(animateObj[innerKey]); |
| copyValue(animateObj, target, innerKey); |
| } |
| } |
| var animator = new Animator(animateObj, false, false, additive ? filter(existsAnimators, function (animator) { return animator.targetName === topKey; }) : null); |
| animator.targetName = topKey; |
| if (cfg.scope) { |
| animator.scope = cfg.scope; |
| } |
| if (setToFinal && revertedSource) { |
| animator.whenWithKeys(0, revertedSource, animationKeys); |
| } |
| if (sourceClone) { |
| animator.whenWithKeys(0, sourceClone, animationKeys); |
| } |
| animator.whenWithKeys(duration == null ? 500 : duration, reverse ? reversedTarget : target, animationKeys).delay(delay || 0); |
| animatable.addAnimator(animator, topKey); |
| animators.push(animator); |
| } |
| } |
| |
| var Group = (function (_super) { |
| __extends(Group, _super); |
| function Group(opts) { |
| var _this = _super.call(this) || this; |
| _this.isGroup = true; |
| _this._children = []; |
| _this.attr(opts); |
| return _this; |
| } |
| Group.prototype.childrenRef = function () { |
| return this._children; |
| }; |
| Group.prototype.children = function () { |
| return this._children.slice(); |
| }; |
| Group.prototype.childAt = function (idx) { |
| return this._children[idx]; |
| }; |
| Group.prototype.childOfName = function (name) { |
| var children = this._children; |
| for (var i = 0; i < children.length; i++) { |
| if (children[i].name === name) { |
| return children[i]; |
| } |
| } |
| }; |
| Group.prototype.childCount = function () { |
| return this._children.length; |
| }; |
| Group.prototype.add = function (child) { |
| if (child) { |
| if (child !== this && child.parent !== this) { |
| this._children.push(child); |
| this._doAdd(child); |
| } |
| if ("development" !== 'production') { |
| if (child.__hostTarget) { |
| throw 'This elemenet has been used as an attachment'; |
| } |
| } |
| } |
| return this; |
| }; |
| Group.prototype.addBefore = function (child, nextSibling) { |
| if (child && child !== this && child.parent !== this |
| && nextSibling && nextSibling.parent === this) { |
| var children = this._children; |
| var idx = children.indexOf(nextSibling); |
| if (idx >= 0) { |
| children.splice(idx, 0, child); |
| this._doAdd(child); |
| } |
| } |
| return this; |
| }; |
| Group.prototype.replace = function (oldChild, newChild) { |
| var idx = indexOf(this._children, oldChild); |
| if (idx >= 0) { |
| this.replaceAt(newChild, idx); |
| } |
| return this; |
| }; |
| Group.prototype.replaceAt = function (child, index) { |
| var children = this._children; |
| var old = children[index]; |
| if (child && child !== this && child.parent !== this && child !== old) { |
| children[index] = child; |
| old.parent = null; |
| var zr = this.__zr; |
| if (zr) { |
| old.removeSelfFromZr(zr); |
| } |
| this._doAdd(child); |
| } |
| return this; |
| }; |
| Group.prototype._doAdd = function (child) { |
| if (child.parent) { |
| child.parent.remove(child); |
| } |
| child.parent = this; |
| var zr = this.__zr; |
| if (zr && zr !== child.__zr) { |
| child.addSelfToZr(zr); |
| } |
| zr && zr.refresh(); |
| }; |
| Group.prototype.remove = function (child) { |
| var zr = this.__zr; |
| var children = this._children; |
| var idx = indexOf(children, child); |
| if (idx < 0) { |
| return this; |
| } |
| children.splice(idx, 1); |
| child.parent = null; |
| if (zr) { |
| child.removeSelfFromZr(zr); |
| } |
| zr && zr.refresh(); |
| return this; |
| }; |
| Group.prototype.removeAll = function () { |
| var children = this._children; |
| var zr = this.__zr; |
| for (var i = 0; i < children.length; i++) { |
| var child = children[i]; |
| if (zr) { |
| child.removeSelfFromZr(zr); |
| } |
| child.parent = null; |
| } |
| children.length = 0; |
| return this; |
| }; |
| Group.prototype.eachChild = function (cb, context) { |
| var children = this._children; |
| for (var i = 0; i < children.length; i++) { |
| var child = children[i]; |
| cb.call(context, child, i); |
| } |
| return this; |
| }; |
| Group.prototype.traverse = function (cb, context) { |
| for (var i = 0; i < this._children.length; i++) { |
| var child = this._children[i]; |
| var stopped = cb.call(context, child); |
| if (child.isGroup && !stopped) { |
| child.traverse(cb, context); |
| } |
| } |
| return this; |
| }; |
| Group.prototype.addSelfToZr = function (zr) { |
| _super.prototype.addSelfToZr.call(this, zr); |
| for (var i = 0; i < this._children.length; i++) { |
| var child = this._children[i]; |
| child.addSelfToZr(zr); |
| } |
| }; |
| Group.prototype.removeSelfFromZr = function (zr) { |
| _super.prototype.removeSelfFromZr.call(this, zr); |
| for (var i = 0; i < this._children.length; i++) { |
| var child = this._children[i]; |
| child.removeSelfFromZr(zr); |
| } |
| }; |
| Group.prototype.getBoundingRect = function (includeChildren) { |
| var tmpRect = new BoundingRect(0, 0, 0, 0); |
| var children = includeChildren || this._children; |
| var tmpMat = []; |
| var rect = null; |
| for (var i = 0; i < children.length; i++) { |
| var child = children[i]; |
| if (child.ignore || child.invisible) { |
| continue; |
| } |
| var childRect = child.getBoundingRect(); |
| var transform = child.getLocalTransform(tmpMat); |
| if (transform) { |
| BoundingRect.applyTransform(tmpRect, childRect, transform); |
| rect = rect || tmpRect.clone(); |
| rect.union(tmpRect); |
| } |
| else { |
| rect = rect || childRect.clone(); |
| rect.union(childRect); |
| } |
| } |
| return rect || tmpRect; |
| }; |
| return Group; |
| }(Element)); |
| Group.prototype.type = 'group'; |
| |
| /*! |
| * ZRender, a high performance 2d drawing library. |
| * |
| * Copyright (c) 2013, Baidu Inc. |
| * All rights reserved. |
| * |
| * LICENSE |
| * https://github.com/ecomfe/zrender/blob/master/LICENSE.txt |
| */ |
| var painterCtors = {}; |
| var instances = {}; |
| function delInstance(id) { |
| delete instances[id]; |
| } |
| function isDarkMode(backgroundColor) { |
| if (!backgroundColor) { |
| return false; |
| } |
| if (typeof backgroundColor === 'string') { |
| return lum(backgroundColor, 1) < DARK_MODE_THRESHOLD; |
| } |
| else if (backgroundColor.colorStops) { |
| var colorStops = backgroundColor.colorStops; |
| var totalLum = 0; |
| var len = colorStops.length; |
| for (var i = 0; i < len; i++) { |
| totalLum += lum(colorStops[i].color, 1); |
| } |
| totalLum /= len; |
| return totalLum < DARK_MODE_THRESHOLD; |
| } |
| return false; |
| } |
| var ZRender = (function () { |
| function ZRender(id, dom, opts) { |
| var _this = this; |
| this._sleepAfterStill = 10; |
| this._stillFrameAccum = 0; |
| this._needsRefresh = true; |
| this._needsRefreshHover = true; |
| this._darkMode = false; |
| opts = opts || {}; |
| this.dom = dom; |
| this.id = id; |
| var storage = new Storage(); |
| var rendererType = opts.renderer || 'canvas'; |
| if (!painterCtors[rendererType]) { |
| rendererType = keys(painterCtors)[0]; |
| } |
| if ("development" !== 'production') { |
| if (!painterCtors[rendererType]) { |
| throw new Error("Renderer '" + rendererType + "' is not imported. Please import it first."); |
| } |
| } |
| opts.useDirtyRect = opts.useDirtyRect == null |
| ? false |
| : opts.useDirtyRect; |
| var painter = new painterCtors[rendererType](dom, storage, opts, id); |
| var ssrMode = opts.ssr || painter.ssrOnly; |
| this.storage = storage; |
| this.painter = painter; |
| var handerProxy = (!env.node && !env.worker && !ssrMode) |
| ? new HandlerDomProxy(painter.getViewportRoot(), painter.root) |
| : null; |
| var useCoarsePointer = opts.useCoarsePointer; |
| var usePointerSize = (useCoarsePointer == null || useCoarsePointer === 'auto') |
| ? env.touchEventsSupported |
| : !!useCoarsePointer; |
| var defaultPointerSize = 44; |
| var pointerSize; |
| if (usePointerSize) { |
| pointerSize = retrieve2(opts.pointerSize, defaultPointerSize); |
| } |
| this.handler = new Handler(storage, painter, handerProxy, painter.root, pointerSize); |
| this.animation = new Animation({ |
| stage: { |
| update: ssrMode ? null : function () { return _this._flush(true); } |
| } |
| }); |
| if (!ssrMode) { |
| this.animation.start(); |
| } |
| } |
| ZRender.prototype.add = function (el) { |
| if (!el) { |
| return; |
| } |
| this.storage.addRoot(el); |
| el.addSelfToZr(this); |
| this.refresh(); |
| }; |
| ZRender.prototype.remove = function (el) { |
| if (!el) { |
| return; |
| } |
| this.storage.delRoot(el); |
| el.removeSelfFromZr(this); |
| this.refresh(); |
| }; |
| ZRender.prototype.configLayer = function (zLevel, config) { |
| if (this.painter.configLayer) { |
| this.painter.configLayer(zLevel, config); |
| } |
| this.refresh(); |
| }; |
| ZRender.prototype.setBackgroundColor = function (backgroundColor) { |
| if (this.painter.setBackgroundColor) { |
| this.painter.setBackgroundColor(backgroundColor); |
| } |
| this.refresh(); |
| this._backgroundColor = backgroundColor; |
| this._darkMode = isDarkMode(backgroundColor); |
| }; |
| ZRender.prototype.getBackgroundColor = function () { |
| return this._backgroundColor; |
| }; |
| ZRender.prototype.setDarkMode = function (darkMode) { |
| this._darkMode = darkMode; |
| }; |
| ZRender.prototype.isDarkMode = function () { |
| return this._darkMode; |
| }; |
| ZRender.prototype.refreshImmediately = function (fromInside) { |
| if (!fromInside) { |
| this.animation.update(true); |
| } |
| this._needsRefresh = false; |
| this.painter.refresh(); |
| this._needsRefresh = false; |
| }; |
| ZRender.prototype.refresh = function () { |
| this._needsRefresh = true; |
| this.animation.start(); |
| }; |
| ZRender.prototype.flush = function () { |
| this._flush(false); |
| }; |
| ZRender.prototype._flush = function (fromInside) { |
| var triggerRendered; |
| var start = getTime(); |
| if (this._needsRefresh) { |
| triggerRendered = true; |
| this.refreshImmediately(fromInside); |
| } |
| if (this._needsRefreshHover) { |
| triggerRendered = true; |
| this.refreshHoverImmediately(); |
| } |
| var end = getTime(); |
| if (triggerRendered) { |
| this._stillFrameAccum = 0; |
| this.trigger('rendered', { |
| elapsedTime: end - start |
| }); |
| } |
| else if (this._sleepAfterStill > 0) { |
| this._stillFrameAccum++; |
| if (this._stillFrameAccum > this._sleepAfterStill) { |
| this.animation.stop(); |
| } |
| } |
| }; |
| ZRender.prototype.setSleepAfterStill = function (stillFramesCount) { |
| this._sleepAfterStill = stillFramesCount; |
| }; |
| ZRender.prototype.wakeUp = function () { |
| this.animation.start(); |
| this._stillFrameAccum = 0; |
| }; |
| ZRender.prototype.refreshHover = function () { |
| this._needsRefreshHover = true; |
| }; |
| ZRender.prototype.refreshHoverImmediately = function () { |
| this._needsRefreshHover = false; |
| if (this.painter.refreshHover && this.painter.getType() === 'canvas') { |
| this.painter.refreshHover(); |
| } |
| }; |
| ZRender.prototype.resize = function (opts) { |
| opts = opts || {}; |
| this.painter.resize(opts.width, opts.height); |
| this.handler.resize(); |
| }; |
| ZRender.prototype.clearAnimation = function () { |
| this.animation.clear(); |
| }; |
| ZRender.prototype.getWidth = function () { |
| return this.painter.getWidth(); |
| }; |
| ZRender.prototype.getHeight = function () { |
| return this.painter.getHeight(); |
| }; |
| ZRender.prototype.setCursorStyle = function (cursorStyle) { |
| this.handler.setCursorStyle(cursorStyle); |
| }; |
| ZRender.prototype.findHover = function (x, y) { |
| return this.handler.findHover(x, y); |
| }; |
| ZRender.prototype.on = function (eventName, eventHandler, context) { |
| this.handler.on(eventName, eventHandler, context); |
| return this; |
| }; |
| ZRender.prototype.off = function (eventName, eventHandler) { |
| this.handler.off(eventName, eventHandler); |
| }; |
| ZRender.prototype.trigger = function (eventName, event) { |
| this.handler.trigger(eventName, event); |
| }; |
| ZRender.prototype.clear = function () { |
| var roots = this.storage.getRoots(); |
| for (var i = 0; i < roots.length; i++) { |
| if (roots[i] instanceof Group) { |
| roots[i].removeSelfFromZr(this); |
| } |
| } |
| this.storage.delAllRoots(); |
| this.painter.clear(); |
| }; |
| ZRender.prototype.dispose = function () { |
| 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); |
| }; |
| return ZRender; |
| }()); |
| function init(dom, opts) { |
| var zr = new ZRender(guid(), dom, opts); |
| instances[zr.id] = zr; |
| return zr; |
| } |
| function dispose(zr) { |
| zr.dispose(); |
| } |
| function disposeAll() { |
| for (var key in instances) { |
| if (instances.hasOwnProperty(key)) { |
| instances[key].dispose(); |
| } |
| } |
| instances = {}; |
| } |
| function getInstance(id) { |
| return instances[id]; |
| } |
| function registerPainter(name, Ctor) { |
| painterCtors[name] = Ctor; |
| } |
| var version = '5.4.1-dev.20221104'; |
| |
| var zrender = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| init: init, |
| dispose: dispose, |
| disposeAll: disposeAll, |
| getInstance: getInstance, |
| registerPainter: registerPainter, |
| version: version |
| }); |
| |
| var RADIAN_EPSILON = 1e-4; // Although chrome already enlarge this number to 100 for `toFixed`, but |
| // we sill follow the spec for compatibility. |
| |
| var ROUND_SUPPORTED_PRECISION_MAX = 20; |
| |
| function _trim(str) { |
| return str.replace(/^\s+|\s+$/g, ''); |
| } |
| /** |
| * Linear mapping a value from domain to range |
| * @param val |
| * @param domain Domain extent domain[0] can be bigger than domain[1] |
| * @param range Range extent range[0] can be bigger than range[1] |
| * @param clamp Default to be false |
| */ |
| |
| |
| function linearMap(val, domain, range, clamp) { |
| var d0 = domain[0]; |
| var d1 = domain[1]; |
| var r0 = range[0]; |
| var r1 = range[1]; |
| var subDomain = d1 - d0; |
| var subRange = r1 - r0; |
| |
| if (subDomain === 0) { |
| return subRange === 0 ? r0 : (r0 + r1) / 2; |
| } // Avoid accuracy problem in edge, such as |
| // 146.39 - 62.83 === 83.55999999999999. |
| // See echarts/test/ut/spec/util/number.js#linearMap#accuracyError |
| // It is a little verbose for efficiency considering this method |
| // is a hotspot. |
| |
| |
| if (clamp) { |
| if (subDomain > 0) { |
| if (val <= d0) { |
| return r0; |
| } else if (val >= d1) { |
| return r1; |
| } |
| } else { |
| if (val >= d0) { |
| return r0; |
| } else if (val <= d1) { |
| return r1; |
| } |
| } |
| } else { |
| if (val === d0) { |
| return r0; |
| } |
| |
| if (val === d1) { |
| return r1; |
| } |
| } |
| |
| return (val - d0) / subDomain * subRange + r0; |
| } |
| /** |
| * Convert a percent string to absolute number. |
| * Returns NaN if percent is not a valid string or number |
| */ |
| |
| 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 (isString(percent)) { |
| if (_trim(percent).match(/%$/)) { |
| return parseFloat(percent) / 100 * all; |
| } |
| |
| return parseFloat(percent); |
| } |
| |
| return percent == null ? NaN : +percent; |
| } |
| function round(x, precision, returnStr) { |
| if (precision == null) { |
| precision = 10; |
| } // Avoid range error |
| |
| |
| precision = Math.min(Math.max(0, precision), ROUND_SUPPORTED_PRECISION_MAX); // PENDING: 1.005.toFixed(2) is '1.00' rather than '1.01' |
| |
| x = (+x).toFixed(precision); |
| return returnStr ? x : +x; |
| } |
| /** |
| * Inplacd asc sort arr. |
| * The input arr will be modified. |
| */ |
| |
| function asc(arr) { |
| arr.sort(function (a, b) { |
| return a - b; |
| }); |
| return arr; |
| } |
| /** |
| * Get precision. |
| */ |
| |
| function getPrecision(val) { |
| val = +val; |
| |
| if (isNaN(val)) { |
| return 0; |
| } // It is much faster than methods converting number to string as follows |
| // let tmp = val.toString(); |
| // return tmp.length - 1 - tmp.indexOf('.'); |
| // especially when precision is low |
| // Notice: |
| // (1) If the loop count is over about 20, it is slower than `getPrecisionSafe`. |
| // (see https://jsbench.me/2vkpcekkvw/1) |
| // (2) If the val is less than for example 1e-15, the result may be incorrect. |
| // (see test/ut/spec/util/number.test.ts `getPrecision_equal_random`) |
| |
| |
| if (val > 1e-14) { |
| var e = 1; |
| |
| for (var i = 0; i < 15; i++, e *= 10) { |
| if (Math.round(val * e) / e === val) { |
| return i; |
| } |
| } |
| } |
| |
| return getPrecisionSafe(val); |
| } |
| /** |
| * Get precision with slow but safe method |
| */ |
| |
| function getPrecisionSafe(val) { |
| // toLowerCase for: '3.4E-12' |
| var str = val.toString().toLowerCase(); // Consider scientific notation: '3.4e-12' '3.4e+12' |
| |
| var eIndex = str.indexOf('e'); |
| var exp = eIndex > 0 ? +str.slice(eIndex + 1) : 0; |
| var significandPartLen = eIndex > 0 ? eIndex : str.length; |
| var dotIndex = str.indexOf('.'); |
| var decimalPartLen = dotIndex < 0 ? 0 : significandPartLen - 1 - dotIndex; |
| return Math.max(0, decimalPartLen - exp); |
| } |
| /** |
| * Minimal dicernible data precisioin according to a single pixel. |
| */ |
| |
| 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. |
| |
| var precision = Math.min(Math.max(-dataQuantity + sizeQuantity, 0), 20); |
| return !isFinite(precision) ? 20 : precision; |
| } |
| /** |
| * Get a data of given precision, assuring the sum of percentages |
| * in valueList is 1. |
| * The largest remainder method is used. |
| * https://en.wikipedia.org/wiki/Largest_remainder_method |
| * |
| * @param valueList a list of all data |
| * @param idx index of the data to be processed in valueList |
| * @param precision integer number showing digits of precision |
| * @return percent ranging from 0 to 100 |
| */ |
| |
| function getPercentWithPrecision(valueList, idx, precision) { |
| if (!valueList[idx]) { |
| return 0; |
| } |
| |
| var seats = getPercentSeats(valueList, precision); |
| return seats[idx] || 0; |
| } |
| /** |
| * Get a data of given precision, assuring the sum of percentages |
| * in valueList is 1. |
| * The largest remainder method is used. |
| * https://en.wikipedia.org/wiki/Largest_remainder_method |
| * |
| * @param valueList a list of all data |
| * @param precision integer number showing digits of precision |
| * @return {Array<number>} |
| */ |
| |
| function getPercentSeats(valueList, precision) { |
| var sum = reduce(valueList, function (acc, val) { |
| return acc + (isNaN(val) ? 0 : val); |
| }, 0); |
| |
| if (sum === 0) { |
| return []; |
| } |
| |
| 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. |
| return 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. |
| |
| while (currentSum < targetSeats) { |
| // Find next largest remainder. |
| var max = Number.NEGATIVE_INFINITY; |
| var maxId = null; |
| |
| for (var i = 0, len = remainder.length; i < len; ++i) { |
| if (remainder[i] > max) { |
| max = remainder[i]; |
| maxId = i; |
| } |
| } // Add a vote to max remainder. |
| |
| |
| ++seats[maxId]; |
| remainder[maxId] = 0; |
| ++currentSum; |
| } |
| |
| return map(seats, function (seat) { |
| return seat / digits; |
| }); |
| } |
| /** |
| * Solve the floating point adding problem like 0.1 + 0.2 === 0.30000000000000004 |
| * See <http://0.30000000000000004.com/> |
| */ |
| |
| function addSafe(val0, val1) { |
| var maxPrecision = Math.max(getPrecision(val0), getPrecision(val1)); // const multiplier = Math.pow(10, maxPrecision); |
| // return (Math.round(val0 * multiplier) + Math.round(val1 * multiplier)) / multiplier; |
| |
| var sum = val0 + val1; // // PENDING: support more? |
| |
| return maxPrecision > ROUND_SUPPORTED_PRECISION_MAX ? sum : round(sum, maxPrecision); |
| } // Number.MAX_SAFE_INTEGER, ie do not support. |
| |
| var MAX_SAFE_INTEGER = 9007199254740991; |
| /** |
| * To 0 - 2 * PI, considering negative radian. |
| */ |
| |
| function remRadian(radian) { |
| var pi2 = Math.PI * 2; |
| return (radian % pi2 + pi2) % pi2; |
| } |
| /** |
| * @param {type} radian |
| * @return {boolean} |
| */ |
| |
| function isRadianAroundZero(val) { |
| return val > -RADIAN_EPSILON && val < RADIAN_EPSILON; |
| } // eslint-disable-next-line |
| |
| var TIME_REG = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d{1,2})(?::(\d{1,2})(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/; // jshint ignore:line |
| |
| /** |
| * @param value valid type: number | string | Date, otherwise return `new Date(NaN)` |
| * These values can be accepted: |
| * + An instance of Date, represent a time in its own time zone. |
| * + Or string in a subset of ISO 8601, only including: |
| * + only year, month, date: '2012-03', '2012-03-01', '2012-03-01 05', '2012-03-01 05:06', |
| * + separated with T or space: '2012-03-01T12:22:33.123', '2012-03-01 12:22:33.123', |
| * + time zone: '2012-03-01T12:22:33Z', '2012-03-01T12:22:33+8000', '2012-03-01T12:22:33-05:00', |
| * all of which will be treated as local time if time zone is not specified |
| * (see <https://momentjs.com/>). |
| * + Or other string format, including (all of which will be treated as local time): |
| * '2012', '2012-3-1', '2012/3/1', '2012/03/01', |
| * '2009/6/12 2:00', '2009/6/12 2:05:08', '2009/6/12 2:05:08.123' |
| * + a timestamp, which represent a time in UTC. |
| * @return date Never be null/undefined. If invalid, return `new Date(NaN)`. |
| */ |
| |
| function parseDate(value) { |
| if (value instanceof Date) { |
| return value; |
| } else if (isString(value)) { |
| // Different browsers parse date in different way, so we parse it manually. |
| // Some other issues: |
| // new Date('1970-01-01') is UTC, |
| // new Date('1970/01/01') and new Date('1970-1-01') is local. |
| // See issue #3623 |
| var match = TIME_REG.exec(value); |
| |
| if (!match) { |
| // return Invalid Date. |
| return new Date(NaN); |
| } // Use local time when no timezone offset is specified. |
| |
| |
| if (!match[8]) { |
| // match[n] can only be string or undefined. |
| // But take care of '12' + 1 => '121'. |
| return new Date(+match[1], +(match[2] || 1) - 1, +match[3] || 1, +match[4] || 0, +(match[5] || 0), +match[6] || 0, match[7] ? +match[7].substring(0, 3) : 0); |
| } // Timezoneoffset of Javascript Date has considered DST (Daylight Saving Time, |
| // https://tc39.github.io/ecma262/#sec-daylight-saving-time-adjustment). |
| // For example, system timezone is set as "Time Zone: America/Toronto", |
| // then these code will get different result: |
| // `new Date(1478411999999).getTimezoneOffset(); // get 240` |
| // `new Date(1478412000000).getTimezoneOffset(); // get 300` |
| // So we should not use `new Date`, but use `Date.UTC`. |
| else { |
| 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] ? +match[7].substring(0, 3) : 0)); |
| } |
| } else if (value == null) { |
| return new Date(NaN); |
| } |
| |
| return new Date(Math.round(value)); |
| } |
| /** |
| * Quantity of a number. e.g. 0.1, 1, 10, 100 |
| * |
| * @param val |
| * @return |
| */ |
| |
| function quantity(val) { |
| return Math.pow(10, quantityExponent(val)); |
| } |
| /** |
| * Exponent of the quantity of a number |
| * e.g., 1234 equals to 1.234*10^3, so quantityExponent(1234) is 3 |
| * |
| * @param val non-negative value |
| * @return |
| */ |
| |
| function quantityExponent(val) { |
| if (val === 0) { |
| return 0; |
| } |
| |
| var exp = Math.floor(Math.log(val) / Math.LN10); |
| /** |
| * exp is expected to be the rounded-down result of the base-10 log of val. |
| * But due to the precision loss with Math.log(val), we need to restore it |
| * using 10^exp to make sure we can get val back from exp. #11249 |
| */ |
| |
| if (val / Math.pow(10, exp) >= 10) { |
| exp++; |
| } |
| |
| return exp; |
| } |
| /** |
| * find a “nice” number approximately equal to x. Round the number if round = true, |
| * take ceiling if round = false. The primary observation is that the “nicest” |
| * numbers in decimal are 1, 2, and 5, and all power-of-ten multiples of these numbers. |
| * |
| * See "Nice Numbers for Graph Labels" of Graphic Gems. |
| * |
| * @param val Non-negative value. |
| * @param round |
| * @return Niced number |
| */ |
| |
| function nice(val, round) { |
| var exponent = quantityExponent(val); |
| var exp10 = Math.pow(10, exponent); |
| var f = val / exp10; // 1 <= f < 10 |
| |
| var 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). |
| // 20 is the uppper bound of toFixed. |
| |
| return exponent >= -20 ? +val.toFixed(exponent < 0 ? -exponent : 0) : val; |
| } |
| /** |
| * This code was copied from "d3.js" |
| * <https://github.com/d3/d3/blob/9cc9a875e636a1dcf36cc1e07bdf77e1ad6e2c74/src/arrays/quantile.js>. |
| * See the license statement at the head of this file. |
| * @param ascArr |
| */ |
| |
| 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; |
| } |
| /** |
| * Order intervals asc, and split them when overlap. |
| * expect(numberUtil.reformIntervals([ |
| * {interval: [18, 62], close: [1, 1]}, |
| * {interval: [-Infinity, -70], close: [0, 0]}, |
| * {interval: [-70, -26], close: [1, 1]}, |
| * {interval: [-26, 18], close: [1, 1]}, |
| * {interval: [62, 150], close: [1, 1]}, |
| * {interval: [106, 150], close: [1, 1]}, |
| * {interval: [150, Infinity], close: [0, 0]} |
| * ])).toEqual([ |
| * {interval: [-Infinity, -70], close: [0, 0]}, |
| * {interval: [-70, -26], close: [1, 1]}, |
| * {interval: [-26, 18], close: [0, 1]}, |
| * {interval: [18, 62], close: [0, 1]}, |
| * {interval: [62, 150], close: [0, 1]}, |
| * {interval: [150, Infinity], close: [0, 0]} |
| * ]); |
| * @param list, where `close` mean open or close |
| * of the interval, and Infinity can be used. |
| * @return The origin list, which has been reformed. |
| */ |
| |
| 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 < list.length;) { |
| var interval = list[i].interval; |
| var close_1 = list[i].close; |
| |
| for (var lg = 0; lg < 2; lg++) { |
| if (interval[lg] <= curr) { |
| interval[lg] = curr; |
| close_1[lg] = !lg ? 1 - currClose : 1; |
| } |
| |
| curr = interval[lg]; |
| currClose = close_1[lg]; |
| } |
| |
| if (interval[0] === interval[1] && close_1[0] * close_1[1] !== 1) { |
| list.splice(i, 1); |
| } else { |
| i++; |
| } |
| } |
| |
| return list; |
| |
| function littleThan(a, b, lg) { |
| return a.interval[lg] < b.interval[lg] || a.interval[lg] === b.interval[lg] && (a.close[lg] - b.close[lg] === (!lg ? 1 : -1) || !lg && littleThan(a, b, 1)); |
| } |
| } |
| /** |
| * [Numeric is defined as]: |
| * `parseFloat(val) == val` |
| * For example: |
| * numeric: |
| * typeof number except NaN, '-123', '123', '2e3', '-2e3', '011', 'Infinity', Infinity, |
| * and they rounded by white-spaces or line-terminal like ' -123 \n ' (see es spec) |
| * not-numeric: |
| * null, undefined, [], {}, true, false, 'NaN', NaN, '123ab', |
| * empty string, string with only white-spaces or line-terminal (see es spec), |
| * 0x12, '0x12', '-0x12', 012, '012', '-012', |
| * non-string, ... |
| * |
| * @test See full test cases in `test/ut/spec/util/number.js`. |
| * @return Must be a typeof number. If not numeric, return NaN. |
| */ |
| |
| function numericToNumber(val) { |
| var valFloat = parseFloat(val); |
| return valFloat == val // eslint-disable-line eqeqeq |
| && (valFloat !== 0 || !isString(val) || val.indexOf('x') <= 0) // For case ' 0x0 '. |
| ? valFloat : NaN; |
| } |
| /** |
| * Definition of "numeric": see `numericToNumber`. |
| */ |
| |
| function isNumeric(val) { |
| return !isNaN(numericToNumber(val)); |
| } |
| /** |
| * Use random base to prevent users hard code depending on |
| * this auto generated marker id. |
| * @return An positive integer. |
| */ |
| |
| function getRandomIdBase() { |
| return Math.round(Math.random() * 9); |
| } |
| /** |
| * Get the greatest common divisor. |
| * |
| * @param {number} a one number |
| * @param {number} b the other number |
| */ |
| |
| function getGreatestCommonDividor(a, b) { |
| if (b === 0) { |
| return a; |
| } |
| |
| return getGreatestCommonDividor(b, a % b); |
| } |
| /** |
| * Get the least common multiple. |
| * |
| * @param {number} a one number |
| * @param {number} b the other number |
| */ |
| |
| function getLeastCommonMultiple(a, b) { |
| if (a == null) { |
| return b; |
| } |
| |
| if (b == null) { |
| return a; |
| } |
| |
| return a * b / getGreatestCommonDividor(a, b); |
| } |
| |
| var ECHARTS_PREFIX = '[ECharts] '; |
| var storedLogs = {}; |
| var hasConsole = typeof console !== 'undefined' // eslint-disable-next-line |
| && console.warn && console.log; |
| |
| function outputLog(type, str, onlyOnce) { |
| if (hasConsole) { |
| if (onlyOnce) { |
| if (storedLogs[str]) { |
| return; |
| } |
| |
| storedLogs[str] = true; |
| } // eslint-disable-next-line |
| |
| |
| console[type](ECHARTS_PREFIX + str); |
| } |
| } |
| |
| function log(str, onlyOnce) { |
| outputLog('log', str, onlyOnce); |
| } |
| function warn(str, onlyOnce) { |
| outputLog('warn', str, onlyOnce); |
| } |
| function error(str, onlyOnce) { |
| outputLog('error', str, onlyOnce); |
| } |
| function deprecateLog(str) { |
| if ("development" !== 'production') { |
| // Not display duplicate message. |
| outputLog('warn', 'DEPRECATED: ' + str, true); |
| } |
| } |
| function deprecateReplaceLog(oldOpt, newOpt, scope) { |
| if ("development" !== 'production') { |
| deprecateLog((scope ? "[" + scope + "]" : '') + (oldOpt + " is deprecated, use " + newOpt + " instead.")); |
| } |
| } |
| /** |
| * If in __DEV__ environment, get console printable message for users hint. |
| * Parameters are separated by ' '. |
| * @usage |
| * makePrintable('This is an error on', someVar, someObj); |
| * |
| * @param hintInfo anything about the current execution context to hint users. |
| * @throws Error |
| */ |
| |
| function makePrintable() { |
| var hintInfo = []; |
| |
| for (var _i = 0; _i < arguments.length; _i++) { |
| hintInfo[_i] = arguments[_i]; |
| } |
| |
| var msg = ''; |
| |
| if ("development" !== 'production') { |
| // Fuzzy stringify for print. |
| // This code only exist in dev environment. |
| var makePrintableStringIfPossible_1 = function (val) { |
| return val === void 0 ? 'undefined' : val === Infinity ? 'Infinity' : val === -Infinity ? '-Infinity' : eqNaN(val) ? 'NaN' : val instanceof Date ? 'Date(' + val.toISOString() + ')' : isFunction(val) ? 'function () { ... }' : isRegExp(val) ? val + '' : null; |
| }; |
| |
| msg = map(hintInfo, function (arg) { |
| if (isString(arg)) { |
| // Print without quotation mark for some statement. |
| return arg; |
| } else { |
| var printableStr = makePrintableStringIfPossible_1(arg); |
| |
| if (printableStr != null) { |
| return printableStr; |
| } else if (typeof JSON !== 'undefined' && JSON.stringify) { |
| try { |
| return JSON.stringify(arg, function (n, val) { |
| var printableStr = makePrintableStringIfPossible_1(val); |
| return printableStr == null ? val : printableStr; |
| }); // In most cases the info object is small, so do not line break. |
| } catch (err) { |
| return '?'; |
| } |
| } else { |
| return '?'; |
| } |
| } |
| }).join(' '); |
| } |
| |
| return msg; |
| } |
| /** |
| * @throws Error |
| */ |
| |
| function throwError(msg) { |
| throw new Error(msg); |
| } |
| |
| function interpolateNumber$1(p0, p1, percent) { |
| return (p1 - p0) * percent + p0; |
| } |
| /** |
| * Make the name displayable. But we should |
| * make sure it is not duplicated with user |
| * specified name, so use '\0'; |
| */ |
| |
| |
| var DUMMY_COMPONENT_NAME_PREFIX = 'series\0'; |
| var INTERNAL_COMPONENT_ID_PREFIX = '\0_ec_\0'; |
| /** |
| * If value is not array, then translate it to array. |
| * @param {*} value |
| * @return {Array} [value] or value |
| */ |
| |
| function normalizeToArray(value) { |
| return value instanceof Array ? value : value == null ? [] : [value]; |
| } |
| /** |
| * Sync default option between normal and emphasis like `position` and `show` |
| * In case some one will write code like |
| * label: { |
| * show: false, |
| * position: 'outside', |
| * fontSize: 18 |
| * }, |
| * emphasis: { |
| * label: { show: true } |
| * } |
| */ |
| |
| function defaultEmphasis(opt, key, subOpts) { |
| // Caution: performance sensitive. |
| if (opt) { |
| opt[key] = opt[key] || {}; |
| opt.emphasis = opt.emphasis || {}; |
| opt.emphasis[key] = opt.emphasis[key] || {}; // Default emphasis option from normal |
| |
| for (var i = 0, len = subOpts.length; i < len; i++) { |
| var subOptName = subOpts[i]; |
| |
| if (!opt.emphasis[key].hasOwnProperty(subOptName) && opt[key].hasOwnProperty(subOptName)) { |
| opt.emphasis[key][subOptName] = opt[key][subOptName]; |
| } |
| } |
| } |
| } |
| var TEXT_STYLE_OPTIONS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'rich', 'tag', 'color', 'textBorderColor', 'textBorderWidth', 'width', 'height', 'lineHeight', 'align', 'verticalAlign', 'baseline', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY', 'backgroundColor', 'borderColor', 'borderWidth', 'borderRadius', 'padding']; // modelUtil.LABEL_OPTIONS = modelUtil.TEXT_STYLE_OPTIONS.concat([ |
| // 'position', 'offset', 'rotate', 'origin', 'show', 'distance', 'formatter', |
| // 'fontStyle', 'fontWeight', 'fontSize', 'fontFamily', |
| // // FIXME: deprecated, check and remove it. |
| // 'textStyle' |
| // ]); |
| |
| /** |
| * The method does not ensure performance. |
| * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] |
| * This helper method retrieves value from data. |
| */ |
| |
| function getDataItemValue(dataItem) { |
| return isObject(dataItem) && !isArray(dataItem) && !(dataItem instanceof Date) ? dataItem.value : dataItem; |
| } |
| /** |
| * data could be [12, 2323, {value: 223}, [1221, 23], {value: [2, 23]}] |
| * This helper method determine if dataItem has extra option besides value |
| */ |
| |
| function isDataItemOption(dataItem) { |
| return isObject(dataItem) && !(dataItem instanceof Array); // // markLine data can be array |
| // && !(dataItem[0] && isObject(dataItem[0]) && !(dataItem[0] instanceof Array)); |
| } |
| /** |
| * Mapping to existings for merge. |
| * |
| * Mode "normalMege": |
| * The mapping result (merge result) will keep the order of the existing |
| * component, rather than the order of new option. Because we should ensure |
| * some specified index reference (like xAxisIndex) keep work. |
| * And in most cases, "merge option" is used to update partial option but not |
| * be expected to change the order. |
| * |
| * Mode "replaceMege": |
| * (1) Only the id mapped components will be merged. |
| * (2) Other existing components (except internal components) will be removed. |
| * (3) Other new options will be used to create new component. |
| * (4) The index of the existing components will not be modified. |
| * That means their might be "hole" after the removal. |
| * The new components are created first at those available index. |
| * |
| * Mode "replaceAll": |
| * This mode try to support that reproduce an echarts instance from another |
| * echarts instance (via `getOption`) in some simple cases. |
| * In this scenario, the `result` index are exactly the consistent with the `newCmptOptions`, |
| * which ensures the component index referring (like `xAxisIndex: ?`) corrent. That is, |
| * the "hole" in `newCmptOptions` will also be kept. |
| * On the contrary, other modes try best to eliminate holes. |
| * PENDING: This is an experimental mode yet. |
| * |
| * @return See the comment of <MappingResult>. |
| */ |
| |
| function mappingToExists(existings, newCmptOptions, mode) { |
| var isNormalMergeMode = mode === 'normalMerge'; |
| var isReplaceMergeMode = mode === 'replaceMerge'; |
| var isReplaceAllMode = mode === 'replaceAll'; |
| existings = existings || []; |
| newCmptOptions = (newCmptOptions || []).slice(); |
| var existingIdIdxMap = createHashMap(); // Validate id and name on user input option. |
| |
| each(newCmptOptions, function (cmptOption, index) { |
| if (!isObject(cmptOption)) { |
| newCmptOptions[index] = null; |
| return; |
| } |
| |
| if ("development" !== 'production') { |
| // There is some legacy case that name is set as `false`. |
| // But should work normally rather than throw error. |
| if (cmptOption.id != null && !isValidIdOrName(cmptOption.id)) { |
| warnInvalidateIdOrName(cmptOption.id); |
| } |
| |
| if (cmptOption.name != null && !isValidIdOrName(cmptOption.name)) { |
| warnInvalidateIdOrName(cmptOption.name); |
| } |
| } |
| }); |
| var result = prepareResult(existings, existingIdIdxMap, mode); |
| |
| if (isNormalMergeMode || isReplaceMergeMode) { |
| mappingById(result, existings, existingIdIdxMap, newCmptOptions); |
| } |
| |
| if (isNormalMergeMode) { |
| mappingByName(result, newCmptOptions); |
| } |
| |
| if (isNormalMergeMode || isReplaceMergeMode) { |
| mappingByIndex(result, newCmptOptions, isReplaceMergeMode); |
| } else if (isReplaceAllMode) { |
| mappingInReplaceAllMode(result, newCmptOptions); |
| } |
| |
| makeIdAndName(result); // The array `result` MUST NOT contain elided items, otherwise the |
| // forEach will omit those items and result in incorrect result. |
| |
| return result; |
| } |
| |
| function prepareResult(existings, existingIdIdxMap, mode) { |
| var result = []; |
| |
| if (mode === 'replaceAll') { |
| return result; |
| } // Do not use native `map` to in case that the array `existings` |
| // contains elided items, which will be omitted. |
| |
| |
| for (var index = 0; index < existings.length; index++) { |
| var existing = existings[index]; // Because of replaceMerge, `existing` may be null/undefined. |
| |
| if (existing && existing.id != null) { |
| existingIdIdxMap.set(existing.id, index); |
| } // For non-internal-componnets: |
| // Mode "normalMerge": all existings kept. |
| // Mode "replaceMerge": all existing removed unless mapped by id. |
| // For internal-components: |
| // go with "replaceMerge" approach in both mode. |
| |
| |
| result.push({ |
| existing: mode === 'replaceMerge' || isComponentIdInternal(existing) ? null : existing, |
| newOption: null, |
| keyInfo: null, |
| brandNew: null |
| }); |
| } |
| |
| return result; |
| } |
| |
| function mappingById(result, existings, existingIdIdxMap, newCmptOptions) { |
| // Mapping by id if specified. |
| each(newCmptOptions, function (cmptOption, index) { |
| if (!cmptOption || cmptOption.id == null) { |
| return; |
| } |
| |
| var optionId = makeComparableKey(cmptOption.id); |
| var existingIdx = existingIdIdxMap.get(optionId); |
| |
| if (existingIdx != null) { |
| var resultItem = result[existingIdx]; |
| assert(!resultItem.newOption, 'Duplicated option on id "' + optionId + '".'); |
| resultItem.newOption = cmptOption; // In both mode, if id matched, new option will be merged to |
| // the existings rather than creating new component model. |
| |
| resultItem.existing = existings[existingIdx]; |
| newCmptOptions[index] = null; |
| } |
| }); |
| } |
| |
| function mappingByName(result, newCmptOptions) { |
| // Mapping by name if specified. |
| each(newCmptOptions, function (cmptOption, index) { |
| if (!cmptOption || cmptOption.name == null) { |
| return; |
| } |
| |
| for (var i = 0; i < result.length; i++) { |
| var existing = result[i].existing; |
| |
| if (!result[i].newOption // Consider name: two map to one. |
| // Can not match when both ids existing but different. |
| && existing && (existing.id == null || cmptOption.id == null) && !isComponentIdInternal(cmptOption) && !isComponentIdInternal(existing) && keyExistAndEqual('name', existing, cmptOption)) { |
| result[i].newOption = cmptOption; |
| newCmptOptions[index] = null; |
| return; |
| } |
| } |
| }); |
| } |
| |
| function mappingByIndex(result, newCmptOptions, brandNew) { |
| each(newCmptOptions, function (cmptOption) { |
| if (!cmptOption) { |
| return; |
| } // Find the first place that not mapped by id and not internal component (consider the "hole"). |
| |
| |
| var resultItem; |
| var nextIdx = 0; |
| |
| while ( // Be `!resultItem` only when `nextIdx >= result.length`. |
| (resultItem = result[nextIdx]) && ( // (1) Existing models that already have id should be able to mapped to. Because |
| // after mapping performed, model will always be assigned with an id if user not given. |
| // After that all models have id. |
| // (2) If new option has id, it can only set to a hole or append to the last. It should |
| // not be merged to the existings with different id. Because id should not be overwritten. |
| // (3) Name can be overwritten, because axis use name as 'show label text'. |
| resultItem.newOption || isComponentIdInternal(resultItem.existing) || // In mode "replaceMerge", here no not-mapped-non-internal-existing. |
| resultItem.existing && cmptOption.id != null && !keyExistAndEqual('id', cmptOption, resultItem.existing))) { |
| nextIdx++; |
| } |
| |
| if (resultItem) { |
| resultItem.newOption = cmptOption; |
| resultItem.brandNew = brandNew; |
| } else { |
| result.push({ |
| newOption: cmptOption, |
| brandNew: brandNew, |
| existing: null, |
| keyInfo: null |
| }); |
| } |
| |
| nextIdx++; |
| }); |
| } |
| |
| function mappingInReplaceAllMode(result, newCmptOptions) { |
| each(newCmptOptions, function (cmptOption) { |
| // The feature "reproduce" requires "hole" will also reproduced |
| // in case that component index referring are broken. |
| result.push({ |
| newOption: cmptOption, |
| brandNew: true, |
| existing: null, |
| keyInfo: null |
| }); |
| }); |
| } |
| /** |
| * Make id and name for mapping result (result of mappingToExists) |
| * into `keyInfo` field. |
| */ |
| |
| |
| function makeIdAndName(mapResult) { |
| // We use this id to hash component models and view instances |
| // in echarts. id can be specified by user, or auto generated. |
| // The id generation rule ensures new view instance are able |
| // to mapped to old instance when setOption are called in |
| // no-merge mode. So we generate model id by name and plus |
| // type in view id. |
| // name can be duplicated among components, which is convenient |
| // to specify multi components (like series) by one name. |
| // Ensure that each id is distinct. |
| var idMap = createHashMap(); |
| each(mapResult, function (item) { |
| var existing = item.existing; |
| existing && idMap.set(existing.id, item); |
| }); |
| each(mapResult, function (item) { |
| var opt = item.newOption; // Force ensure id not duplicated. |
| |
| assert(!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. |
| |
| each(mapResult, function (item, index) { |
| var existing = item.existing; |
| var opt = item.newOption; |
| var keyInfo = item.keyInfo; |
| |
| if (!isObject(opt)) { |
| return; |
| } // Name can be overwritten. Consider case: axis.name = '20km'. |
| // But id generated by name will not be changed, which affect |
| // only in that case: setOption with 'not merge mode' and view |
| // instance will be recreated, which can be accepted. |
| |
| |
| keyInfo.name = opt.name != null ? makeComparableKey(opt.name) : existing ? existing.name // Avoid that different series has the same name, |
| // because name may be used like in color pallet. |
| : DUMMY_COMPONENT_NAME_PREFIX + index; |
| |
| if (existing) { |
| keyInfo.id = makeComparableKey(existing.id); |
| } else if (opt.id != null) { |
| keyInfo.id = makeComparableKey(opt.id); |
| } else { |
| // Consider this situatoin: |
| // optionA: [{name: 'a'}, {name: 'a'}, {..}] |
| // optionB [{..}, {name: 'a'}, {name: 'a'}] |
| // Series with the same name between optionA and optionB |
| // should be mapped. |
| var idNum = 0; |
| |
| do { |
| keyInfo.id = '\0' + keyInfo.name + '\0' + idNum++; |
| } while (idMap.get(keyInfo.id)); |
| } |
| |
| idMap.set(keyInfo.id, item); |
| }); |
| } |
| |
| function keyExistAndEqual(attr, obj1, obj2) { |
| var key1 = convertOptionIdName(obj1[attr], null); |
| var key2 = convertOptionIdName(obj2[attr], null); // See `MappingExistingItem`. `id` and `name` trade string equals to number. |
| |
| return key1 != null && key2 != null && key1 === key2; |
| } |
| /** |
| * @return return null if not exist. |
| */ |
| |
| |
| function makeComparableKey(val) { |
| if ("development" !== 'production') { |
| if (val == null) { |
| throw new Error(); |
| } |
| } |
| |
| return convertOptionIdName(val, ''); |
| } |
| |
| function convertOptionIdName(idOrName, defaultValue) { |
| if (idOrName == null) { |
| return defaultValue; |
| } |
| |
| return isString(idOrName) ? idOrName : isNumber(idOrName) || isStringSafe(idOrName) ? idOrName + '' : defaultValue; |
| } |
| |
| function warnInvalidateIdOrName(idOrName) { |
| if ("development" !== 'production') { |
| warn('`' + idOrName + '` is invalid id or name. Must be a string or number.'); |
| } |
| } |
| |
| function isValidIdOrName(idOrName) { |
| return isStringSafe(idOrName) || isNumeric(idOrName); |
| } |
| |
| function isNameSpecified(componentModel) { |
| var name = componentModel.name; // Is specified when `indexOf` get -1 or > 0. |
| |
| return !!(name && name.indexOf(DUMMY_COMPONENT_NAME_PREFIX)); |
| } |
| /** |
| * @public |
| * @param {Object} cmptOption |
| * @return {boolean} |
| */ |
| |
| function isComponentIdInternal(cmptOption) { |
| return cmptOption && cmptOption.id != null && makeComparableKey(cmptOption.id).indexOf(INTERNAL_COMPONENT_ID_PREFIX) === 0; |
| } |
| function setComponentTypeToKeyInfo(mappingResult, mainType, componentModelCtor) { |
| // Set mainType and complete subType. |
| each(mappingResult, function (item) { |
| var newOption = item.newOption; |
| |
| if (isObject(newOption)) { |
| item.keyInfo.mainType = mainType; |
| item.keyInfo.subType = determineSubType(mainType, newOption, item.existing, componentModelCtor); |
| } |
| }); |
| } |
| |
| function determineSubType(mainType, newCmptOption, existComponent, componentModelCtor) { |
| var subType = newCmptOption.type ? newCmptOption.type : existComponent ? existComponent.subType // Use determineSubType only when there is no existComponent. |
| : componentModelCtor.determineSubType(mainType, newCmptOption); // tooltip, markline, markpoint may always has no subType |
| |
| return subType; |
| } |
| /** |
| * @param payload Contains dataIndex (means rawIndex) / dataIndexInside / name |
| * each of which can be Array or primary type. |
| * @return dataIndex If not found, return undefined/null. |
| */ |
| |
| 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); |
| } |
| } |
| /** |
| * Enable property storage to any host object. |
| * Notice: Serialization is not supported. |
| * |
| * For example: |
| * let inner = zrUitl.makeInner(); |
| * |
| * function some1(hostObj) { |
| * inner(hostObj).someProperty = 1212; |
| * ... |
| * } |
| * function some2() { |
| * let fields = inner(this); |
| * fields.someProperty1 = 1212; |
| * fields.someProperty2 = 'xx'; |
| * ... |
| * } |
| * |
| * @return {Function} |
| */ |
| |
| function makeInner() { |
| var key = '__ec_inner_' + innerUniqueIndex++; |
| return function (hostObj) { |
| return hostObj[key] || (hostObj[key] = {}); |
| }; |
| } |
| var innerUniqueIndex = getRandomIdBase(); |
| /** |
| * The same behavior as `component.getReferringComponents`. |
| */ |
| |
| function parseFinder(ecModel, finderInput, opt) { |
| var _a = preParseFinder(finderInput, opt), |
| mainTypeSpecified = _a.mainTypeSpecified, |
| queryOptionMap = _a.queryOptionMap, |
| others = _a.others; |
| |
| var result = others; |
| var defaultMainType = opt ? opt.defaultMainType : null; |
| |
| if (!mainTypeSpecified && defaultMainType) { |
| queryOptionMap.set(defaultMainType, {}); |
| } |
| |
| queryOptionMap.each(function (queryOption, mainType) { |
| var queryResult = queryReferringComponents(ecModel, mainType, queryOption, { |
| useDefault: defaultMainType === mainType, |
| enableAll: opt && opt.enableAll != null ? opt.enableAll : true, |
| enableNone: opt && opt.enableNone != null ? opt.enableNone : true |
| }); |
| result[mainType + 'Models'] = queryResult.models; |
| result[mainType + 'Model'] = queryResult.models[0]; |
| }); |
| return result; |
| } |
| function preParseFinder(finderInput, opt) { |
| var finder; |
| |
| if (isString(finderInput)) { |
| var obj = {}; |
| obj[finderInput + 'Index'] = 0; |
| finder = obj; |
| } else { |
| finder = finderInput; |
| } |
| |
| var queryOptionMap = createHashMap(); |
| var others = {}; |
| var mainTypeSpecified = false; |
| each(finder, function (value, key) { |
| // Exclude 'dataIndex' and other illgal keys. |
| if (key === 'dataIndex' || key === 'dataIndexInside') { |
| others[key] = value; |
| return; |
| } |
| |
| var parsedKey = key.match(/^(\w+)(Index|Id|Name)$/) || []; |
| var mainType = parsedKey[1]; |
| var queryType = (parsedKey[2] || '').toLowerCase(); |
| |
| if (!mainType || !queryType || opt && opt.includeMainTypes && indexOf(opt.includeMainTypes, mainType) < 0) { |
| return; |
| } |
| |
| mainTypeSpecified = mainTypeSpecified || !!mainType; |
| var queryOption = queryOptionMap.get(mainType) || queryOptionMap.set(mainType, {}); |
| queryOption[queryType] = value; |
| }); |
| return { |
| mainTypeSpecified: mainTypeSpecified, |
| queryOptionMap: queryOptionMap, |
| others: others |
| }; |
| } |
| var SINGLE_REFERRING = { |
| useDefault: true, |
| enableAll: false, |
| enableNone: false |
| }; |
| function queryReferringComponents(ecModel, mainType, userOption, opt) { |
| opt = opt || SINGLE_REFERRING; |
| var indexOption = userOption.index; |
| var idOption = userOption.id; |
| var nameOption = userOption.name; |
| var result = { |
| models: null, |
| specified: indexOption != null || idOption != null || nameOption != null |
| }; |
| |
| if (!result.specified) { |
| // Use the first as default if `useDefault`. |
| var firstCmpt = void 0; |
| result.models = opt.useDefault && (firstCmpt = ecModel.getComponent(mainType)) ? [firstCmpt] : []; |
| return result; |
| } |
| |
| if (indexOption === 'none' || indexOption === false) { |
| assert(opt.enableNone, '`"none"` or `false` is not a valid value on index option.'); |
| result.models = []; |
| return result; |
| } // `queryComponents` will return all components if |
| // both all of index/id/name are null/undefined. |
| |
| |
| if (indexOption === 'all') { |
| assert(opt.enableAll, '`"all"` is not a valid value on index option.'); |
| indexOption = idOption = nameOption = null; |
| } |
| |
| result.models = ecModel.queryComponents({ |
| mainType: mainType, |
| index: indexOption, |
| id: idOption, |
| name: nameOption |
| }); |
| return result; |
| } |
| 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]; |
| } |
| /** |
| * Interpolate raw values of a series with percent |
| * |
| * @param data data |
| * @param labelModel label model of the text element |
| * @param sourceValue start value. May be null/undefined when init. |
| * @param targetValue end value |
| * @param percent 0~1 percentage; 0 uses start value while 1 uses end value |
| * @return interpolated values |
| * If `sourceValue` and `targetValue` are `number`, return `number`. |
| * If `sourceValue` and `targetValue` are `string`, return `string`. |
| * If `sourceValue` and `targetValue` are `(string | number)[]`, return `(string | number)[]`. |
| * Other cases do not supported. |
| */ |
| |
| function interpolateRawValues(data, precision, sourceValue, targetValue, percent) { |
| var isAutoPrecision = precision == null || precision === 'auto'; |
| |
| if (targetValue == null) { |
| return targetValue; |
| } |
| |
| if (isNumber(targetValue)) { |
| var value = interpolateNumber$1(sourceValue || 0, targetValue, percent); |
| return round(value, isAutoPrecision ? Math.max(getPrecision(sourceValue || 0), getPrecision(targetValue)) : precision); |
| } else if (isString(targetValue)) { |
| return percent < 1 ? sourceValue : targetValue; |
| } else { |
| var interpolated = []; |
| var leftArr = sourceValue; |
| var rightArr = targetValue; |
| var length_1 = Math.max(leftArr ? leftArr.length : 0, rightArr.length); |
| |
| for (var i = 0; i < length_1; ++i) { |
| var info = data.getDimensionInfo(i); // Don't interpolate ordinal dims |
| |
| if (info && info.type === 'ordinal') { |
| // In init, there is no `sourceValue`, but should better not to get undefined result. |
| interpolated[i] = (percent < 1 && leftArr ? leftArr : rightArr)[i]; |
| } else { |
| var leftVal = leftArr && leftArr[i] ? leftArr[i] : 0; |
| var rightVal = rightArr[i]; |
| var value = interpolateNumber$1(leftVal, rightVal, percent); |
| interpolated[i] = round(value, isAutoPrecision ? Math.max(getPrecision(leftVal), getPrecision(rightVal)) : precision); |
| } |
| } |
| |
| return interpolated; |
| } |
| } |
| |
| var TYPE_DELIMITER = '.'; |
| var IS_CONTAINER = '___EC__COMPONENT__CONTAINER___'; |
| var IS_EXTENDED_CLASS = '___EC__EXTENDED_CLASS___'; |
| /** |
| * Notice, parseClassType('') should returns {main: '', sub: ''} |
| * @public |
| */ |
| |
| function parseClassType(componentType) { |
| var ret = { |
| main: '', |
| sub: '' |
| }; |
| |
| if (componentType) { |
| var typeArr = componentType.split(TYPE_DELIMITER); |
| ret.main = typeArr[0] || ''; |
| ret.sub = typeArr[1] || ''; |
| } |
| |
| return ret; |
| } |
| /** |
| * @public |
| */ |
| |
| function checkClassType(componentType) { |
| assert(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(componentType), 'componentType "' + componentType + '" illegal'); |
| } |
| |
| function isExtendedClass(clz) { |
| return !!(clz && clz[IS_EXTENDED_CLASS]); |
| } |
| /** |
| * Implements `ExtendableConstructor` for `rootClz`. |
| * |
| * @usage |
| * ```ts |
| * class Xxx {} |
| * type XxxConstructor = typeof Xxx & ExtendableConstructor |
| * enableClassExtend(Xxx as XxxConstructor); |
| * ``` |
| */ |
| |
| function enableClassExtend(rootClz, mandatoryMethods) { |
| rootClz.$constructor = rootClz; // FIXME: not necessary? |
| |
| rootClz.extend = function (proto) { |
| if ("development" !== 'production') { |
| each(mandatoryMethods, function (method) { |
| if (!proto[method]) { |
| console.warn('Method `' + method + '` should be implemented' + (proto.type ? ' in ' + proto.type : '') + '.'); |
| } |
| }); |
| } |
| |
| var superClass = this; |
| var ExtendedClass; |
| |
| if (isESClass(superClass)) { |
| ExtendedClass = |
| /** @class */ |
| function (_super) { |
| __extends(class_1, _super); |
| |
| function class_1() { |
| return _super.apply(this, arguments) || this; |
| } |
| |
| return class_1; |
| }(superClass); |
| } else { |
| // For backward compat, we both support ts class inheritance and this |
| // "extend" approach. |
| // The constructor should keep the same behavior as ts class inheritance: |
| // If this constructor/$constructor is not declared, auto invoke the super |
| // constructor. |
| // If this constructor/$constructor is declared, it is responsible for |
| // calling the super constructor. |
| ExtendedClass = function () { |
| (proto.$constructor || superClass).apply(this, arguments); |
| }; |
| |
| inherits(ExtendedClass, this); |
| } |
| |
| extend(ExtendedClass.prototype, proto); |
| ExtendedClass[IS_EXTENDED_CLASS] = true; |
| ExtendedClass.extend = this.extend; |
| ExtendedClass.superCall = superCall; |
| ExtendedClass.superApply = superApply; |
| ExtendedClass.superClass = superClass; |
| return ExtendedClass; |
| }; |
| } |
| |
| function isESClass(fn) { |
| return isFunction(fn) && /^class\s/.test(Function.prototype.toString.call(fn)); |
| } |
| /** |
| * A work around to both support ts extend and this extend mechanism. |
| * on sub-class. |
| * @usage |
| * ```ts |
| * class Component { ... } |
| * classUtil.enableClassExtend(Component); |
| * classUtil.enableClassManagement(Component, {registerWhenExtend: true}); |
| * |
| * class Series extends Component { ... } |
| * // Without calling `markExtend`, `registerWhenExtend` will not work. |
| * Component.markExtend(Series); |
| * ``` |
| */ |
| |
| |
| function mountExtend(SubClz, SupperClz) { |
| SubClz.extend = SupperClz.extend; |
| } // A random offset. |
| |
| var classBase = Math.round(Math.random() * 10); |
| /** |
| * Implements `CheckableConstructor` for `target`. |
| * Can not use instanceof, consider different scope by |
| * cross domain or es module import in ec extensions. |
| * Mount a method "isInstance()" to Clz. |
| * |
| * @usage |
| * ```ts |
| * class Xxx {} |
| * type XxxConstructor = typeof Xxx & CheckableConstructor; |
| * enableClassCheck(Xxx as XxxConstructor) |
| * ``` |
| */ |
| |
| function enableClassCheck(target) { |
| var classAttr = ['__\0is_clz', classBase++].join('_'); |
| target.prototype[classAttr] = true; |
| |
| if ("development" !== 'production') { |
| assert(!target.isInstance, 'The method "is" can not be defined.'); |
| } |
| |
| target.isInstance = function (obj) { |
| return !!(obj && obj[classAttr]); |
| }; |
| } // superCall should have class info, which can not be fetched from 'this'. |
| // Consider this case: |
| // class A has method f, |
| // class B inherits class A, overrides method f, f call superApply('f'), |
| // class C inherits class B, does not override method f, |
| // then when method of class C is called, dead loop occurred. |
| |
| function superCall(context, methodName) { |
| var args = []; |
| |
| for (var _i = 2; _i < arguments.length; _i++) { |
| args[_i - 2] = arguments[_i]; |
| } |
| |
| return this.superClass.prototype[methodName].apply(context, args); |
| } |
| |
| function superApply(context, methodName, args) { |
| return this.superClass.prototype[methodName].apply(context, args); |
| } |
| /** |
| * Implements `ClassManager` for `target` |
| * |
| * @usage |
| * ```ts |
| * class Xxx {} |
| * type XxxConstructor = typeof Xxx & ClassManager |
| * enableClassManagement(Xxx as XxxConstructor); |
| * ``` |
| */ |
| |
| |
| function enableClassManagement(target) { |
| /** |
| * Component model classes |
| * key: componentType, |
| * value: |
| * componentClass, when componentType is 'a' |
| * or Object.<subKey, componentClass>, when componentType is 'a.b' |
| */ |
| var storage = {}; |
| |
| target.registerClass = function (clz) { |
| // `type` should not be a "instance member". |
| // If using TS class, should better declared as `static type = 'series.pie'`. |
| // otherwise users have to mount `type` on prototype manually. |
| // For backward compat and enable instance visit type via `this.type`, |
| // we still support fetch `type` from prototype. |
| var componentFullType = clz.type || clz.prototype.type; |
| |
| if (componentFullType) { |
| checkClassType(componentFullType); // If only static type declared, we assign it to prototype mandatorily. |
| |
| clz.prototype.type = componentFullType; |
| var componentTypeInfo = parseClassType(componentFullType); |
| |
| if (!componentTypeInfo.sub) { |
| if ("development" !== 'production') { |
| if (storage[componentTypeInfo.main]) { |
| console.warn(componentTypeInfo.main + ' exists.'); |
| } |
| } |
| |
| storage[componentTypeInfo.main] = clz; |
| } else if (componentTypeInfo.sub !== IS_CONTAINER) { |
| var container = makeContainer(componentTypeInfo); |
| container[componentTypeInfo.sub] = clz; |
| } |
| } |
| |
| return clz; |
| }; |
| |
| target.getClass = function (mainType, subType, throwWhenNotFound) { |
| var clz = storage[mainType]; |
| |
| if (clz && clz[IS_CONTAINER]) { |
| clz = subType ? clz[subType] : null; |
| } |
| |
| if (throwWhenNotFound && !clz) { |
| throw new Error(!subType ? mainType + '.' + 'type should be specified.' : 'Component ' + mainType + '.' + (subType || '') + ' is used but not imported.'); |
| } |
| |
| return clz; |
| }; |
| |
| target.getClassesByMainType = function (componentType) { |
| var componentTypeInfo = parseClassType(componentType); |
| var result = []; |
| var obj = storage[componentTypeInfo.main]; |
| |
| if (obj && obj[IS_CONTAINER]) { |
| each(obj, function (o, type) { |
| type !== IS_CONTAINER && result.push(o); |
| }); |
| } else { |
| result.push(obj); |
| } |
| |
| return result; |
| }; |
| |
| target.hasClass = function (componentType) { |
| // Just consider componentType.main. |
| var componentTypeInfo = parseClassType(componentType); |
| return !!storage[componentTypeInfo.main]; |
| }; |
| /** |
| * @return Like ['aa', 'bb'], but can not be ['aa.xx'] |
| */ |
| |
| |
| target.getAllClassMainTypes = function () { |
| var types = []; |
| each(storage, function (obj, type) { |
| types.push(type); |
| }); |
| return types; |
| }; |
| /** |
| * If a main type is container and has sub types |
| */ |
| |
| |
| target.hasSubTypes = function (componentType) { |
| var componentTypeInfo = parseClassType(componentType); |
| var obj = storage[componentTypeInfo.main]; |
| return obj && obj[IS_CONTAINER]; |
| }; |
| |
| function makeContainer(componentTypeInfo) { |
| var container = storage[componentTypeInfo.main]; |
| |
| if (!container || !container[IS_CONTAINER]) { |
| container = storage[componentTypeInfo.main] = {}; |
| container[IS_CONTAINER] = true; |
| } |
| |
| return container; |
| } |
| } // /** |
| // * @param {string|Array.<string>} properties |
| // */ |
| // export function setReadOnly(obj, properties) { |
| // FIXME It seems broken in IE8 simulation of IE11 |
| // if (!zrUtil.isArray(properties)) { |
| // properties = properties != null ? [properties] : []; |
| // } |
| // zrUtil.each(properties, function (prop) { |
| // let value = obj[prop]; |
| // Object.defineProperty |
| // && Object.defineProperty(obj, prop, { |
| // value: value, writable: false |
| // }); |
| // zrUtil.isArray(obj[prop]) |
| // && Object.freeze |
| // && Object.freeze(obj[prop]); |
| // }); |
| // } |
| |
| function makeStyleMapper(properties, ignoreParent) { |
| // Normalize |
| for (var i = 0; i < properties.length; i++) { |
| if (!properties[i][1]) { |
| properties[i][1] = properties[i][0]; |
| } |
| } |
| |
| ignoreParent = ignoreParent || false; |
| return function (model, excludes, includes) { |
| var style = {}; |
| |
| for (var i = 0; i < properties.length; i++) { |
| var propName = properties[i][1]; |
| |
| if (excludes && indexOf(excludes, propName) >= 0 || includes && indexOf(includes, propName) < 0) { |
| continue; |
| } |
| |
| var val = model.getShallow(propName, ignoreParent); |
| |
| if (val != null) { |
| style[properties[i][0]] = val; |
| } |
| } // TODO Text or image? |
| |
| |
| return style; |
| }; |
| } |
| |
| var AREA_STYLE_KEY_MAP = [['fill', 'color'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['opacity'], ['shadowColor'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`. |
| // So do not transfer decal directly. |
| ]; |
| var getAreaStyle = makeStyleMapper(AREA_STYLE_KEY_MAP); |
| |
| var AreaStyleMixin = |
| /** @class */ |
| function () { |
| function AreaStyleMixin() {} |
| |
| AreaStyleMixin.prototype.getAreaStyle = function (excludes, includes) { |
| return getAreaStyle(this, excludes, includes); |
| }; |
| |
| return AreaStyleMixin; |
| }(); |
| |
| var globalImageCache = new LRU(50); |
| function findExistImage(newImageOrSrc) { |
| if (typeof newImageOrSrc === 'string') { |
| var cachedImgObj = globalImageCache.get(newImageOrSrc); |
| return cachedImgObj && cachedImgObj.image; |
| } |
| else { |
| return newImageOrSrc; |
| } |
| } |
| function createOrUpdateImage(newImageOrSrc, image, hostEl, onload, cbPayload) { |
| if (!newImageOrSrc) { |
| return image; |
| } |
| else if (typeof newImageOrSrc === 'string') { |
| if ((image && image.__zrImageSrc === newImageOrSrc) || !hostEl) { |
| return image; |
| } |
| var cachedImgObj = globalImageCache.get(newImageOrSrc); |
| var pendingWrap = { hostEl: hostEl, cb: onload, cbPayload: cbPayload }; |
| if (cachedImgObj) { |
| image = cachedImgObj.image; |
| !isImageReady(image) && cachedImgObj.pending.push(pendingWrap); |
| } |
| else { |
| image = platformApi.loadImage(newImageOrSrc, imageOnLoad, imageOnLoad); |
| image.__zrImageSrc = newImageOrSrc; |
| globalImageCache.put(newImageOrSrc, image.__cachedImgObj = { |
| image: image, |
| pending: [pendingWrap] |
| }); |
| } |
| return image; |
| } |
| else { |
| return newImageOrSrc; |
| } |
| } |
| function imageOnLoad() { |
| var cachedImgObj = this.__cachedImgObj; |
| this.onload = this.onerror = this.__cachedImgObj = null; |
| for (var i = 0; i < cachedImgObj.pending.length; i++) { |
| var pendingWrap = cachedImgObj.pending[i]; |
| var cb = pendingWrap.cb; |
| cb && cb(this, pendingWrap.cbPayload); |
| pendingWrap.hostEl.dirty(); |
| } |
| cachedImgObj.pending.length = 0; |
| } |
| function isImageReady(image) { |
| return image && image.width && image.height; |
| } |
| |
| var STYLE_REG = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g; |
| function truncateText(text, containerWidth, font, ellipsis, options) { |
| if (!containerWidth) { |
| return ''; |
| } |
| var textLines = (text + '').split('\n'); |
| options = prepareTruncateOptions(containerWidth, font, ellipsis, options); |
| for (var i = 0, len = textLines.length; i < len; i++) { |
| textLines[i] = truncateSingleLine(textLines[i], options); |
| } |
| return textLines.join('\n'); |
| } |
| function prepareTruncateOptions(containerWidth, font, ellipsis, options) { |
| options = options || {}; |
| var preparedOpts = extend({}, options); |
| preparedOpts.font = font; |
| ellipsis = retrieve2(ellipsis, '...'); |
| preparedOpts.maxIterations = retrieve2(options.maxIterations, 2); |
| var minChar = preparedOpts.minChar = retrieve2(options.minChar, 0); |
| preparedOpts.cnCharWidth = getWidth('国', font); |
| var ascCharWidth = preparedOpts.ascCharWidth = getWidth('a', font); |
| preparedOpts.placeholder = retrieve2(options.placeholder, ''); |
| var contentWidth = containerWidth = Math.max(0, containerWidth - 1); |
| for (var i = 0; i < minChar && contentWidth >= ascCharWidth; i++) { |
| contentWidth -= ascCharWidth; |
| } |
| var ellipsisWidth = getWidth(ellipsis, font); |
| if (ellipsisWidth > contentWidth) { |
| ellipsis = ''; |
| ellipsisWidth = 0; |
| } |
| contentWidth = containerWidth - ellipsisWidth; |
| preparedOpts.ellipsis = ellipsis; |
| preparedOpts.ellipsisWidth = ellipsisWidth; |
| preparedOpts.contentWidth = contentWidth; |
| preparedOpts.containerWidth = containerWidth; |
| return preparedOpts; |
| } |
| 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; i < len && width < contentWidth; i++) { |
| var charCode = text.charCodeAt(i); |
| width += (0 <= charCode && charCode <= 127) ? ascCharWidth : cnCharWidth; |
| } |
| return i; |
| } |
| function parsePlainText(text, style) { |
| text != null && (text += ''); |
| var overflow = style.overflow; |
| var padding = style.padding; |
| var font = style.font; |
| var truncate = overflow === 'truncate'; |
| var calculatedLineHeight = getLineHeight(font); |
| var lineHeight = retrieve2(style.lineHeight, calculatedLineHeight); |
| var bgColorDrawn = !!(style.backgroundColor); |
| var truncateLineOverflow = style.lineOverflow === 'truncate'; |
| var width = style.width; |
| var lines; |
| if (width != null && (overflow === 'break' || overflow === 'breakAll')) { |
| lines = text ? wrapText(text, style.font, width, overflow === 'breakAll', 0).lines : []; |
| } |
| else { |
| lines = text ? text.split('\n') : []; |
| } |
| var contentHeight = lines.length * lineHeight; |
| var height = retrieve2(style.height, contentHeight); |
| if (contentHeight > height && truncateLineOverflow) { |
| var lineCount = Math.floor(height / lineHeight); |
| lines = lines.slice(0, lineCount); |
| } |
| if (text && truncate && width != null) { |
| var options = prepareTruncateOptions(width, font, style.ellipsis, { |
| minChar: style.truncateMinChar, |
| placeholder: style.placeholder |
| }); |
| for (var i = 0; i < lines.length; i++) { |
| lines[i] = truncateSingleLine(lines[i], options); |
| } |
| } |
| var outerHeight = height; |
| var contentWidth = 0; |
| for (var i = 0; i < lines.length; i++) { |
| contentWidth = Math.max(getWidth(lines[i], font), contentWidth); |
| } |
| if (width == null) { |
| width = contentWidth; |
| } |
| var outerWidth = contentWidth; |
| if (padding) { |
| outerHeight += padding[0] + padding[2]; |
| outerWidth += padding[1] + padding[3]; |
| width += padding[1] + padding[3]; |
| } |
| if (bgColorDrawn) { |
| outerWidth = width; |
| } |
| return { |
| lines: lines, |
| height: height, |
| outerWidth: outerWidth, |
| outerHeight: outerHeight, |
| lineHeight: lineHeight, |
| calculatedLineHeight: calculatedLineHeight, |
| contentWidth: contentWidth, |
| contentHeight: contentHeight, |
| width: width |
| }; |
| } |
| var RichTextToken = (function () { |
| function RichTextToken() { |
| } |
| return RichTextToken; |
| }()); |
| var RichTextLine = (function () { |
| function RichTextLine(tokens) { |
| this.tokens = []; |
| if (tokens) { |
| this.tokens = tokens; |
| } |
| } |
| return RichTextLine; |
| }()); |
| var RichTextContentBlock = (function () { |
| function RichTextContentBlock() { |
| this.width = 0; |
| this.height = 0; |
| this.contentWidth = 0; |
| this.contentHeight = 0; |
| this.outerWidth = 0; |
| this.outerHeight = 0; |
| this.lines = []; |
| } |
| return RichTextContentBlock; |
| }()); |
| function parseRichText(text, style) { |
| var contentBlock = new RichTextContentBlock(); |
| text != null && (text += ''); |
| if (!text) { |
| return contentBlock; |
| } |
| var topWidth = style.width; |
| var topHeight = style.height; |
| var overflow = style.overflow; |
| var wrapInfo = (overflow === 'break' || overflow === 'breakAll') && topWidth != null |
| ? { width: topWidth, accumWidth: 0, breakAll: overflow === 'breakAll' } |
| : null; |
| var lastIndex = STYLE_REG.lastIndex = 0; |
| var result; |
| while ((result = STYLE_REG.exec(text)) != null) { |
| var matchedIndex = result.index; |
| if (matchedIndex > lastIndex) { |
| pushTokens(contentBlock, text.substring(lastIndex, matchedIndex), style, wrapInfo); |
| } |
| pushTokens(contentBlock, result[2], style, wrapInfo, result[1]); |
| lastIndex = STYLE_REG.lastIndex; |
| } |
| if (lastIndex < text.length) { |
| pushTokens(contentBlock, text.substring(lastIndex, text.length), style, wrapInfo); |
| } |
| var pendingList = []; |
| var calculatedHeight = 0; |
| var calculatedWidth = 0; |
| var stlPadding = style.padding; |
| var truncate = overflow === 'truncate'; |
| var truncateLine = style.lineOverflow === 'truncate'; |
| function finishLine(line, lineWidth, lineHeight) { |
| line.width = lineWidth; |
| line.lineHeight = lineHeight; |
| calculatedHeight += lineHeight; |
| calculatedWidth = Math.max(calculatedWidth, lineWidth); |
| } |
| outer: for (var i = 0; i < contentBlock.lines.length; i++) { |
| var line = contentBlock.lines[i]; |
| var lineHeight = 0; |
| var lineWidth = 0; |
| for (var j = 0; j < line.tokens.length; j++) { |
| var token = line.tokens[j]; |
| var tokenStyle = token.styleName && style.rich[token.styleName] || {}; |
| var textPadding = token.textPadding = tokenStyle.padding; |
| var paddingH = textPadding ? textPadding[1] + textPadding[3] : 0; |
| var font = token.font = tokenStyle.font || style.font; |
| token.contentHeight = getLineHeight(font); |
| var tokenHeight = retrieve2(tokenStyle.height, token.contentHeight); |
| token.innerHeight = tokenHeight; |
| textPadding && (tokenHeight += textPadding[0] + textPadding[2]); |
| token.height = tokenHeight; |
| token.lineHeight = retrieve3(tokenStyle.lineHeight, style.lineHeight, tokenHeight); |
| token.align = tokenStyle && tokenStyle.align || style.align; |
| token.verticalAlign = tokenStyle && tokenStyle.verticalAlign || 'middle'; |
| if (truncateLine && topHeight != null && calculatedHeight + token.lineHeight > topHeight) { |
| if (j > 0) { |
| line.tokens = line.tokens.slice(0, j); |
| finishLine(line, lineWidth, lineHeight); |
| contentBlock.lines = contentBlock.lines.slice(0, i + 1); |
| } |
| else { |
| contentBlock.lines = contentBlock.lines.slice(0, i); |
| } |
| break outer; |
| } |
| var styleTokenWidth = tokenStyle.width; |
| var tokenWidthNotSpecified = styleTokenWidth == null || styleTokenWidth === 'auto'; |
| if (typeof styleTokenWidth === 'string' && styleTokenWidth.charAt(styleTokenWidth.length - 1) === '%') { |
| token.percentWidth = styleTokenWidth; |
| pendingList.push(token); |
| token.contentWidth = getWidth(token.text, font); |
| } |
| else { |
| if (tokenWidthNotSpecified) { |
| var textBackgroundColor = tokenStyle.backgroundColor; |
| var bgImg = textBackgroundColor && textBackgroundColor.image; |
| if (bgImg) { |
| bgImg = findExistImage(bgImg); |
| if (isImageReady(bgImg)) { |
| token.width = Math.max(token.width, bgImg.width * tokenHeight / bgImg.height); |
| } |
| } |
| } |
| var remainTruncWidth = truncate && topWidth != null |
| ? topWidth - lineWidth : null; |
| if (remainTruncWidth != null && remainTruncWidth < token.width) { |
| if (!tokenWidthNotSpecified || remainTruncWidth < paddingH) { |
| token.text = ''; |
| token.width = token.contentWidth = 0; |
| } |
| else { |
| token.text = truncateText(token.text, remainTruncWidth - paddingH, font, style.ellipsis, { minChar: style.truncateMinChar }); |
| token.width = token.contentWidth = getWidth(token.text, font); |
| } |
| } |
| else { |
| token.contentWidth = getWidth(token.text, font); |
| } |
| } |
| token.width += paddingH; |
| lineWidth += token.width; |
| tokenStyle && (lineHeight = Math.max(lineHeight, token.lineHeight)); |
| } |
| finishLine(line, lineWidth, lineHeight); |
| } |
| contentBlock.outerWidth = contentBlock.width = retrieve2(topWidth, calculatedWidth); |
| contentBlock.outerHeight = contentBlock.height = retrieve2(topHeight, calculatedHeight); |
| contentBlock.contentHeight = calculatedHeight; |
| contentBlock.contentWidth = calculatedWidth; |
| if (stlPadding) { |
| contentBlock.outerWidth += stlPadding[1] + stlPadding[3]; |
| contentBlock.outerHeight += stlPadding[0] + stlPadding[2]; |
| } |
| for (var i = 0; i < pendingList.length; i++) { |
| var token = pendingList[i]; |
| var percentWidth = token.percentWidth; |
| token.width = parseInt(percentWidth, 10) / 100 * contentBlock.width; |
| } |
| return contentBlock; |
| } |
| function pushTokens(block, str, style, wrapInfo, styleName) { |
| var isEmptyStr = str === ''; |
| var tokenStyle = styleName && style.rich[styleName] || {}; |
| var lines = block.lines; |
| var font = tokenStyle.font || style.font; |
| var newLine = false; |
| var strLines; |
| var linesWidths; |
| if (wrapInfo) { |
| var tokenPadding = tokenStyle.padding; |
| var tokenPaddingH = tokenPadding ? tokenPadding[1] + tokenPadding[3] : 0; |
| if (tokenStyle.width != null && tokenStyle.width !== 'auto') { |
| var outerWidth_1 = parsePercent(tokenStyle.width, wrapInfo.width) + tokenPaddingH; |
| if (lines.length > 0) { |
| if (outerWidth_1 + wrapInfo.accumWidth > wrapInfo.width) { |
| strLines = str.split('\n'); |
| newLine = true; |
| } |
| } |
| wrapInfo.accumWidth = outerWidth_1; |
| } |
| else { |
| var res = wrapText(str, font, wrapInfo.width, wrapInfo.breakAll, wrapInfo.accumWidth); |
| wrapInfo.accumWidth = res.accumWidth + tokenPaddingH; |
| linesWidths = res.linesWidths; |
| strLines = res.lines; |
| } |
| } |
| else { |
| strLines = str.split('\n'); |
| } |
| for (var i = 0; i < strLines.length; i++) { |
| var text = strLines[i]; |
| var token = new RichTextToken(); |
| token.styleName = styleName; |
| token.text = text; |
| token.isLineHolder = !text && !isEmptyStr; |
| if (typeof tokenStyle.width === 'number') { |
| token.width = tokenStyle.width; |
| } |
| else { |
| token.width = linesWidths |
| ? linesWidths[i] |
| : getWidth(text, font); |
| } |
| if (!i && !newLine) { |
| var tokens = (lines[lines.length - 1] || (lines[0] = new RichTextLine())).tokens; |
| var tokensLen = tokens.length; |
| (tokensLen === 1 && tokens[0].isLineHolder) |
| ? (tokens[0] = token) |
| : ((text || !tokensLen || isEmptyStr) && tokens.push(token)); |
| } |
| else { |
| lines.push(new RichTextLine([token])); |
| } |
| } |
| } |
| function isLatin(ch) { |
| var code = ch.charCodeAt(0); |
| return code >= 0x21 && code <= 0x17F; |
| } |
| var breakCharMap = reduce(',&?/;] '.split(''), function (obj, ch) { |
| obj[ch] = true; |
| return obj; |
| }, {}); |
| function isWordBreakChar(ch) { |
| if (isLatin(ch)) { |
| if (breakCharMap[ch]) { |
| return true; |
| } |
| return false; |
| } |
| return true; |
| } |
| function wrapText(text, font, lineWidth, isBreakAll, lastAccumWidth) { |
| var lines = []; |
| var linesWidths = []; |
| var line = ''; |
| var currentWord = ''; |
| var currentWordWidth = 0; |
| var accumWidth = 0; |
| for (var i = 0; i < text.length; i++) { |
| var ch = text.charAt(i); |
| if (ch === '\n') { |
| if (currentWord) { |
| line += currentWord; |
| accumWidth += currentWordWidth; |
| } |
| lines.push(line); |
| linesWidths.push(accumWidth); |
| line = ''; |
| currentWord = ''; |
| currentWordWidth = 0; |
| accumWidth = 0; |
| continue; |
| } |
| var chWidth = getWidth(ch, font); |
| var inWord = isBreakAll ? false : !isWordBreakChar(ch); |
| if (!lines.length |
| ? lastAccumWidth + accumWidth + chWidth > lineWidth |
| : accumWidth + chWidth > lineWidth) { |
| if (!accumWidth) { |
| if (inWord) { |
| lines.push(currentWord); |
| linesWidths.push(currentWordWidth); |
| currentWord = ch; |
| currentWordWidth = chWidth; |
| } |
| else { |
| lines.push(ch); |
| linesWidths.push(chWidth); |
| } |
| } |
| else if (line || currentWord) { |
| if (inWord) { |
| if (!line) { |
| line = currentWord; |
| currentWord = ''; |
| currentWordWidth = 0; |
| accumWidth = currentWordWidth; |
| } |
| lines.push(line); |
| linesWidths.push(accumWidth - currentWordWidth); |
| currentWord += ch; |
| currentWordWidth += chWidth; |
| line = ''; |
| accumWidth = currentWordWidth; |
| } |
| else { |
| if (currentWord) { |
| line += currentWord; |
| currentWord = ''; |
| currentWordWidth = 0; |
| } |
| lines.push(line); |
| linesWidths.push(accumWidth); |
| line = ch; |
| accumWidth = chWidth; |
| } |
| } |
| continue; |
| } |
| accumWidth += chWidth; |
| if (inWord) { |
| currentWord += ch; |
| currentWordWidth += chWidth; |
| } |
| else { |
| if (currentWord) { |
| line += currentWord; |
| currentWord = ''; |
| currentWordWidth = 0; |
| } |
| line += ch; |
| } |
| } |
| if (!lines.length && !line) { |
| line = text; |
| currentWord = ''; |
| currentWordWidth = 0; |
| } |
| if (currentWord) { |
| line += currentWord; |
| } |
| if (line) { |
| lines.push(line); |
| linesWidths.push(accumWidth); |
| } |
| if (lines.length === 1) { |
| accumWidth += lastAccumWidth; |
| } |
| return { |
| accumWidth: accumWidth, |
| lines: lines, |
| linesWidths: linesWidths |
| }; |
| } |
| |
| var STYLE_MAGIC_KEY = '__zr_style_' + Math.round((Math.random() * 10)); |
| var DEFAULT_COMMON_STYLE = { |
| shadowBlur: 0, |
| shadowOffsetX: 0, |
| shadowOffsetY: 0, |
| shadowColor: '#000', |
| opacity: 1, |
| blend: 'source-over' |
| }; |
| var DEFAULT_COMMON_ANIMATION_PROPS = { |
| style: { |
| shadowBlur: true, |
| shadowOffsetX: true, |
| shadowOffsetY: true, |
| shadowColor: true, |
| opacity: true |
| } |
| }; |
| DEFAULT_COMMON_STYLE[STYLE_MAGIC_KEY] = true; |
| var PRIMARY_STATES_KEYS$1 = ['z', 'z2', 'invisible']; |
| var PRIMARY_STATES_KEYS_IN_HOVER_LAYER = ['invisible']; |
| var Displayable = (function (_super) { |
| __extends(Displayable, _super); |
| function Displayable(props) { |
| return _super.call(this, props) || this; |
| } |
| Displayable.prototype._init = function (props) { |
| var keysArr = keys(props); |
| for (var i = 0; i < keysArr.length; i++) { |
| var key = keysArr[i]; |
| if (key === 'style') { |
| this.useStyle(props[key]); |
| } |
| else { |
| _super.prototype.attrKV.call(this, key, props[key]); |
| } |
| } |
| if (!this.style) { |
| this.useStyle({}); |
| } |
| }; |
| Displayable.prototype.beforeBrush = function () { }; |
| Displayable.prototype.afterBrush = function () { }; |
| Displayable.prototype.innerBeforeBrush = function () { }; |
| Displayable.prototype.innerAfterBrush = function () { }; |
| Displayable.prototype.shouldBePainted = function (viewWidth, viewHeight, considerClipPath, considerAncestors) { |
| var m = this.transform; |
| if (this.ignore |
| || this.invisible |
| || this.style.opacity === 0 |
| || (this.culling |
| && isDisplayableCulled(this, viewWidth, viewHeight)) |
| || (m && !m[0] && !m[3])) { |
| return false; |
| } |
| if (considerClipPath && this.__clipPaths) { |
| for (var i = 0; i < this.__clipPaths.length; ++i) { |
| if (this.__clipPaths[i].isZeroArea()) { |
| return false; |
| } |
| } |
| } |
| if (considerAncestors && this.parent) { |
| var parent_1 = this.parent; |
| while (parent_1) { |
| if (parent_1.ignore) { |
| return false; |
| } |
| parent_1 = parent_1.parent; |
| } |
| } |
| return true; |
| }; |
| Displayable.prototype.contain = function (x, y) { |
| return this.rectContain(x, y); |
| }; |
| Displayable.prototype.traverse = function (cb, context) { |
| cb.call(context, this); |
| }; |
| Displayable.prototype.rectContain = function (x, y) { |
| var coord = this.transformCoordToLocal(x, y); |
| var rect = this.getBoundingRect(); |
| return rect.contain(coord[0], coord[1]); |
| }; |
| Displayable.prototype.getPaintRect = function () { |
| var rect = this._paintRect; |
| if (!this._paintRect || this.__dirty) { |
| var transform = this.transform; |
| var elRect = this.getBoundingRect(); |
| var style = this.style; |
| var shadowSize = style.shadowBlur || 0; |
| var shadowOffsetX = style.shadowOffsetX || 0; |
| var shadowOffsetY = style.shadowOffsetY || 0; |
| rect = this._paintRect || (this._paintRect = new BoundingRect(0, 0, 0, 0)); |
| if (transform) { |
| BoundingRect.applyTransform(rect, elRect, transform); |
| } |
| else { |
| rect.copy(elRect); |
| } |
| if (shadowSize || shadowOffsetX || shadowOffsetY) { |
| rect.width += shadowSize * 2 + Math.abs(shadowOffsetX); |
| rect.height += shadowSize * 2 + Math.abs(shadowOffsetY); |
| rect.x = Math.min(rect.x, rect.x + shadowOffsetX - shadowSize); |
| rect.y = Math.min(rect.y, rect.y + shadowOffsetY - shadowSize); |
| } |
| var tolerance = this.dirtyRectTolerance; |
| if (!rect.isZero()) { |
| rect.x = Math.floor(rect.x - tolerance); |
| rect.y = Math.floor(rect.y - tolerance); |
| rect.width = Math.ceil(rect.width + 1 + tolerance * 2); |
| rect.height = Math.ceil(rect.height + 1 + tolerance * 2); |
| } |
| } |
| return rect; |
| }; |
| Displayable.prototype.setPrevPaintRect = function (paintRect) { |
| if (paintRect) { |
| this._prevPaintRect = this._prevPaintRect || new BoundingRect(0, 0, 0, 0); |
| this._prevPaintRect.copy(paintRect); |
| } |
| else { |
| this._prevPaintRect = null; |
| } |
| }; |
| Displayable.prototype.getPrevPaintRect = function () { |
| return this._prevPaintRect; |
| }; |
| Displayable.prototype.animateStyle = function (loop) { |
| return this.animate('style', loop); |
| }; |
| Displayable.prototype.updateDuringAnimation = function (targetKey) { |
| if (targetKey === 'style') { |
| this.dirtyStyle(); |
| } |
| else { |
| this.markRedraw(); |
| } |
| }; |
| Displayable.prototype.attrKV = function (key, value) { |
| if (key !== 'style') { |
| _super.prototype.attrKV.call(this, key, value); |
| } |
| else { |
| if (!this.style) { |
| this.useStyle(value); |
| } |
| else { |
| this.setStyle(value); |
| } |
| } |
| }; |
| Displayable.prototype.setStyle = function (keyOrObj, value) { |
| if (typeof keyOrObj === 'string') { |
| this.style[keyOrObj] = value; |
| } |
| else { |
| extend(this.style, keyOrObj); |
| } |
| this.dirtyStyle(); |
| return this; |
| }; |
| Displayable.prototype.dirtyStyle = function (notRedraw) { |
| if (!notRedraw) { |
| this.markRedraw(); |
| } |
| this.__dirty |= STYLE_CHANGED_BIT; |
| if (this._rect) { |
| this._rect = null; |
| } |
| }; |
| Displayable.prototype.dirty = function () { |
| this.dirtyStyle(); |
| }; |
| Displayable.prototype.styleChanged = function () { |
| return !!(this.__dirty & STYLE_CHANGED_BIT); |
| }; |
| Displayable.prototype.styleUpdated = function () { |
| this.__dirty &= ~STYLE_CHANGED_BIT; |
| }; |
| Displayable.prototype.createStyle = function (obj) { |
| return createObject(DEFAULT_COMMON_STYLE, obj); |
| }; |
| Displayable.prototype.useStyle = function (obj) { |
| if (!obj[STYLE_MAGIC_KEY]) { |
| obj = this.createStyle(obj); |
| } |
| if (this.__inHover) { |
| this.__hoverStyle = obj; |
| } |
| else { |
| this.style = obj; |
| } |
| this.dirtyStyle(); |
| }; |
| Displayable.prototype.isStyleObject = function (obj) { |
| return obj[STYLE_MAGIC_KEY]; |
| }; |
| Displayable.prototype._innerSaveToNormal = function (toState) { |
| _super.prototype._innerSaveToNormal.call(this, toState); |
| var normalState = this._normalState; |
| if (toState.style && !normalState.style) { |
| normalState.style = this._mergeStyle(this.createStyle(), this.style); |
| } |
| this._savePrimaryToNormal(toState, normalState, PRIMARY_STATES_KEYS$1); |
| }; |
| Displayable.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { |
| _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg); |
| var needsRestoreToNormal = !(state && keepCurrentStates); |
| var targetStyle; |
| if (state && state.style) { |
| if (transition) { |
| if (keepCurrentStates) { |
| targetStyle = state.style; |
| } |
| else { |
| targetStyle = this._mergeStyle(this.createStyle(), normalState.style); |
| this._mergeStyle(targetStyle, state.style); |
| } |
| } |
| else { |
| targetStyle = this._mergeStyle(this.createStyle(), keepCurrentStates ? this.style : normalState.style); |
| this._mergeStyle(targetStyle, state.style); |
| } |
| } |
| else if (needsRestoreToNormal) { |
| targetStyle = normalState.style; |
| } |
| if (targetStyle) { |
| if (transition) { |
| var sourceStyle = this.style; |
| this.style = this.createStyle(needsRestoreToNormal ? {} : sourceStyle); |
| if (needsRestoreToNormal) { |
| var changedKeys = keys(sourceStyle); |
| for (var i = 0; i < changedKeys.length; i++) { |
| var key = changedKeys[i]; |
| if (key in targetStyle) { |
| targetStyle[key] = targetStyle[key]; |
| this.style[key] = sourceStyle[key]; |
| } |
| } |
| } |
| var targetKeys = keys(targetStyle); |
| for (var i = 0; i < targetKeys.length; i++) { |
| var key = targetKeys[i]; |
| this.style[key] = this.style[key]; |
| } |
| this._transitionState(stateName, { |
| style: targetStyle |
| }, animationCfg, this.getAnimationStyleProps()); |
| } |
| else { |
| this.useStyle(targetStyle); |
| } |
| } |
| var statesKeys = this.__inHover ? PRIMARY_STATES_KEYS_IN_HOVER_LAYER : PRIMARY_STATES_KEYS$1; |
| for (var i = 0; i < statesKeys.length; i++) { |
| var key = statesKeys[i]; |
| if (state && state[key] != null) { |
| this[key] = state[key]; |
| } |
| else if (needsRestoreToNormal) { |
| if (normalState[key] != null) { |
| this[key] = normalState[key]; |
| } |
| } |
| } |
| }; |
| Displayable.prototype._mergeStates = function (states) { |
| var mergedState = _super.prototype._mergeStates.call(this, states); |
| var mergedStyle; |
| for (var i = 0; i < states.length; i++) { |
| var state = states[i]; |
| if (state.style) { |
| mergedStyle = mergedStyle || {}; |
| this._mergeStyle(mergedStyle, state.style); |
| } |
| } |
| if (mergedStyle) { |
| mergedState.style = mergedStyle; |
| } |
| return mergedState; |
| }; |
| Displayable.prototype._mergeStyle = function (targetStyle, sourceStyle) { |
| extend(targetStyle, sourceStyle); |
| return targetStyle; |
| }; |
| Displayable.prototype.getAnimationStyleProps = function () { |
| return DEFAULT_COMMON_ANIMATION_PROPS; |
| }; |
| Displayable.initDefaultProps = (function () { |
| var dispProto = Displayable.prototype; |
| dispProto.type = 'displayable'; |
| dispProto.invisible = false; |
| dispProto.z = 0; |
| dispProto.z2 = 0; |
| dispProto.zlevel = 0; |
| dispProto.culling = false; |
| dispProto.cursor = 'pointer'; |
| dispProto.rectHover = false; |
| dispProto.incremental = false; |
| dispProto._rect = null; |
| dispProto.dirtyRectTolerance = 0; |
| dispProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT; |
| })(); |
| return Displayable; |
| }(Element)); |
| var tmpRect$1 = new BoundingRect(0, 0, 0, 0); |
| var viewRect = new BoundingRect(0, 0, 0, 0); |
| function isDisplayableCulled(el, width, height) { |
| tmpRect$1.copy(el.getBoundingRect()); |
| if (el.transform) { |
| tmpRect$1.applyTransform(el.transform); |
| } |
| viewRect.width = width; |
| viewRect.height = height; |
| return !tmpRect$1.intersect(viewRect); |
| } |
| |
| var mathMin$1 = Math.min; |
| var mathMax$1 = Math.max; |
| var mathSin = Math.sin; |
| var mathCos = Math.cos; |
| var PI2 = Math.PI * 2; |
| var start = create(); |
| var end = create(); |
| var extremity = create(); |
| function fromLine(x0, y0, x1, y1, min, max) { |
| min[0] = mathMin$1(x0, x1); |
| min[1] = mathMin$1(y0, y1); |
| max[0] = mathMax$1(x0, x1); |
| max[1] = mathMax$1(y0, y1); |
| } |
| var xDim = []; |
| var yDim = []; |
| function fromCubic(x0, y0, x1, y1, x2, y2, x3, y3, min, max) { |
| var cubicExtrema$1 = cubicExtrema; |
| var cubicAt$1 = cubicAt; |
| var n = cubicExtrema$1(x0, x1, x2, x3, xDim); |
| min[0] = Infinity; |
| min[1] = Infinity; |
| max[0] = -Infinity; |
| max[1] = -Infinity; |
| for (var i = 0; i < n; i++) { |
| var x = cubicAt$1(x0, x1, x2, x3, xDim[i]); |
| min[0] = mathMin$1(x, min[0]); |
| max[0] = mathMax$1(x, max[0]); |
| } |
| n = cubicExtrema$1(y0, y1, y2, y3, yDim); |
| for (var i = 0; i < n; i++) { |
| var y = cubicAt$1(y0, y1, y2, y3, yDim[i]); |
| min[1] = mathMin$1(y, min[1]); |
| max[1] = mathMax$1(y, max[1]); |
| } |
| min[0] = mathMin$1(x0, min[0]); |
| max[0] = mathMax$1(x0, max[0]); |
| min[0] = mathMin$1(x3, min[0]); |
| max[0] = mathMax$1(x3, max[0]); |
| min[1] = mathMin$1(y0, min[1]); |
| max[1] = mathMax$1(y0, max[1]); |
| min[1] = mathMin$1(y3, min[1]); |
| max[1] = mathMax$1(y3, max[1]); |
| } |
| function fromQuadratic(x0, y0, x1, y1, x2, y2, min, max) { |
| var quadraticExtremum$1 = quadraticExtremum; |
| var quadraticAt$1 = quadraticAt; |
| var tx = mathMax$1(mathMin$1(quadraticExtremum$1(x0, x1, x2), 1), 0); |
| var ty = mathMax$1(mathMin$1(quadraticExtremum$1(y0, y1, y2), 1), 0); |
| var x = quadraticAt$1(x0, x1, x2, tx); |
| var y = quadraticAt$1(y0, y1, y2, ty); |
| min[0] = mathMin$1(x0, x2, x); |
| min[1] = mathMin$1(y0, y2, y); |
| max[0] = mathMax$1(x0, x2, x); |
| max[1] = mathMax$1(y0, y2, y); |
| } |
| 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) { |
| min$1[0] = x - rx; |
| min$1[1] = y - ry; |
| max$1[0] = x + rx; |
| max$1[1] = y + ry; |
| return; |
| } |
| start[0] = mathCos(startAngle) * rx + x; |
| start[1] = mathSin(startAngle) * ry + y; |
| end[0] = mathCos(endAngle) * rx + x; |
| end[1] = mathSin(endAngle) * ry + y; |
| vec2Min(min$1, start, end); |
| vec2Max(max$1, start, end); |
| startAngle = 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 (startAngle < endAngle && anticlockwise) { |
| startAngle += PI2; |
| } |
| if (anticlockwise) { |
| var tmp = endAngle; |
| endAngle = startAngle; |
| startAngle = tmp; |
| } |
| for (var angle = 0; angle < endAngle; angle += Math.PI / 2) { |
| if (angle > startAngle) { |
| extremity[0] = mathCos(angle) * rx + x; |
| extremity[1] = mathSin(angle) * ry + y; |
| vec2Min(min$1, extremity, min$1); |
| vec2Max(max$1, extremity, max$1); |
| } |
| } |
| } |
| |
| var CMD = { |
| M: 1, |
| L: 2, |
| C: 3, |
| Q: 4, |
| A: 5, |
| Z: 6, |
| R: 7 |
| }; |
| var tmpOutX = []; |
| var tmpOutY = []; |
| var 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 mathAbs = Math.abs; |
| var PI = Math.PI; |
| var PI2$1 = PI * 2; |
| var hasTypedArray = typeof Float32Array !== 'undefined'; |
| var tmpAngles = []; |
| function modPI2(radian) { |
| var n = Math.round(radian / PI * 1e8) / 1e8; |
| return (n % 2) * PI; |
| } |
| function normalizeArcAngles(angles, anticlockwise) { |
| var newStartAngle = modPI2(angles[0]); |
| if (newStartAngle < 0) { |
| newStartAngle += PI2$1; |
| } |
| var delta = newStartAngle - angles[0]; |
| var newEndAngle = angles[1]; |
| newEndAngle += delta; |
| if (!anticlockwise && newEndAngle - newStartAngle >= PI2$1) { |
| newEndAngle = newStartAngle + PI2$1; |
| } |
| else if (anticlockwise && newStartAngle - newEndAngle >= PI2$1) { |
| newEndAngle = newStartAngle - PI2$1; |
| } |
| else if (!anticlockwise && newStartAngle > newEndAngle) { |
| newEndAngle = newStartAngle + (PI2$1 - modPI2(newStartAngle - newEndAngle)); |
| } |
| else if (anticlockwise && newStartAngle < newEndAngle) { |
| newEndAngle = newStartAngle - (PI2$1 - modPI2(newEndAngle - newStartAngle)); |
| } |
| angles[0] = newStartAngle; |
| angles[1] = newEndAngle; |
| } |
| var PathProxy = (function () { |
| function PathProxy(notSaveData) { |
| this.dpr = 1; |
| this._xi = 0; |
| this._yi = 0; |
| this._x0 = 0; |
| this._y0 = 0; |
| this._len = 0; |
| if (notSaveData) { |
| this._saveData = false; |
| } |
| if (this._saveData) { |
| this.data = []; |
| } |
| } |
| PathProxy.prototype.increaseVersion = function () { |
| this._version++; |
| }; |
| PathProxy.prototype.getVersion = function () { |
| return this._version; |
| }; |
| PathProxy.prototype.setScale = function (sx, sy, segmentIgnoreThreshold) { |
| segmentIgnoreThreshold = segmentIgnoreThreshold || 0; |
| if (segmentIgnoreThreshold > 0) { |
| this._ux = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sx) || 0; |
| this._uy = mathAbs(segmentIgnoreThreshold / devicePixelRatio / sy) || 0; |
| } |
| }; |
| PathProxy.prototype.setDPR = function (dpr) { |
| this.dpr = dpr; |
| }; |
| PathProxy.prototype.setContext = function (ctx) { |
| this._ctx = ctx; |
| }; |
| PathProxy.prototype.getContext = function () { |
| return this._ctx; |
| }; |
| PathProxy.prototype.beginPath = function () { |
| this._ctx && this._ctx.beginPath(); |
| this.reset(); |
| return this; |
| }; |
| PathProxy.prototype.reset = function () { |
| if (this._saveData) { |
| this._len = 0; |
| } |
| if (this._pathSegLen) { |
| this._pathSegLen = null; |
| this._pathLen = 0; |
| } |
| this._version++; |
| }; |
| PathProxy.prototype.moveTo = function (x, y) { |
| this._drawPendingPt(); |
| this.addData(CMD.M, x, y); |
| this._ctx && this._ctx.moveTo(x, y); |
| this._x0 = x; |
| this._y0 = y; |
| this._xi = x; |
| this._yi = y; |
| return this; |
| }; |
| PathProxy.prototype.lineTo = function (x, y) { |
| var dx = mathAbs(x - this._xi); |
| var dy = mathAbs(y - this._yi); |
| var exceedUnit = dx > this._ux || dy > this._uy; |
| this.addData(CMD.L, x, y); |
| if (this._ctx && exceedUnit) { |
| this._ctx.lineTo(x, y); |
| } |
| if (exceedUnit) { |
| this._xi = x; |
| this._yi = y; |
| this._pendingPtDist = 0; |
| } |
| else { |
| var d2 = dx * dx + dy * dy; |
| if (d2 > this._pendingPtDist) { |
| this._pendingPtX = x; |
| this._pendingPtY = y; |
| this._pendingPtDist = d2; |
| } |
| } |
| return this; |
| }; |
| PathProxy.prototype.bezierCurveTo = function (x1, y1, x2, y2, x3, y3) { |
| this._drawPendingPt(); |
| this.addData(CMD.C, x1, y1, x2, y2, x3, y3); |
| if (this._ctx) { |
| this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); |
| } |
| this._xi = x3; |
| this._yi = y3; |
| return this; |
| }; |
| PathProxy.prototype.quadraticCurveTo = function (x1, y1, x2, y2) { |
| this._drawPendingPt(); |
| this.addData(CMD.Q, x1, y1, x2, y2); |
| if (this._ctx) { |
| this._ctx.quadraticCurveTo(x1, y1, x2, y2); |
| } |
| this._xi = x2; |
| this._yi = y2; |
| return this; |
| }; |
| PathProxy.prototype.arc = function (cx, cy, r, startAngle, endAngle, anticlockwise) { |
| this._drawPendingPt(); |
| tmpAngles[0] = startAngle; |
| tmpAngles[1] = endAngle; |
| normalizeArcAngles(tmpAngles, anticlockwise); |
| startAngle = tmpAngles[0]; |
| endAngle = tmpAngles[1]; |
| var delta = endAngle - startAngle; |
| this.addData(CMD.A, cx, cy, r, r, startAngle, delta, 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; |
| }; |
| PathProxy.prototype.arcTo = function (x1, y1, x2, y2, radius) { |
| this._drawPendingPt(); |
| if (this._ctx) { |
| this._ctx.arcTo(x1, y1, x2, y2, radius); |
| } |
| return this; |
| }; |
| PathProxy.prototype.rect = function (x, y, w, h) { |
| this._drawPendingPt(); |
| this._ctx && this._ctx.rect(x, y, w, h); |
| this.addData(CMD.R, x, y, w, h); |
| return this; |
| }; |
| PathProxy.prototype.closePath = function () { |
| this._drawPendingPt(); |
| this.addData(CMD.Z); |
| var ctx = this._ctx; |
| var x0 = this._x0; |
| var y0 = this._y0; |
| if (ctx) { |
| ctx.closePath(); |
| } |
| this._xi = x0; |
| this._yi = y0; |
| return this; |
| }; |
| PathProxy.prototype.fill = function (ctx) { |
| ctx && ctx.fill(); |
| this.toStatic(); |
| }; |
| PathProxy.prototype.stroke = function (ctx) { |
| ctx && ctx.stroke(); |
| this.toStatic(); |
| }; |
| PathProxy.prototype.len = function () { |
| return this._len; |
| }; |
| PathProxy.prototype.setData = function (data) { |
| var len = data.length; |
| if (!(this.data && this.data.length === len) && hasTypedArray) { |
| this.data = new Float32Array(len); |
| } |
| for (var i = 0; i < len; i++) { |
| this.data[i] = data[i]; |
| } |
| this._len = len; |
| }; |
| PathProxy.prototype.appendPath = function (path) { |
| if (!(path instanceof Array)) { |
| path = [path]; |
| } |
| var len = path.length; |
| var appendSize = 0; |
| var offset = this._len; |
| for (var i = 0; i < len; i++) { |
| appendSize += path[i].len(); |
| } |
| if (hasTypedArray && (this.data instanceof Float32Array)) { |
| this.data = new Float32Array(offset + appendSize); |
| } |
| for (var i = 0; i < len; i++) { |
| var appendPathData = path[i].data; |
| for (var k = 0; k < appendPathData.length; k++) { |
| this.data[offset++] = appendPathData[k]; |
| } |
| } |
| this._len = offset; |
| }; |
| PathProxy.prototype.addData = function (cmd, a, b, c, d, e, f, g, h) { |
| if (!this._saveData) { |
| return; |
| } |
| var data = this.data; |
| if (this._len + arguments.length > data.length) { |
| this._expandData(); |
| data = this.data; |
| } |
| for (var i = 0; i < arguments.length; i++) { |
| data[this._len++] = arguments[i]; |
| } |
| }; |
| PathProxy.prototype._drawPendingPt = function () { |
| if (this._pendingPtDist > 0) { |
| this._ctx && this._ctx.lineTo(this._pendingPtX, this._pendingPtY); |
| this._pendingPtDist = 0; |
| } |
| }; |
| PathProxy.prototype._expandData = function () { |
| if (!(this.data instanceof Array)) { |
| var newData = []; |
| for (var i = 0; i < this._len; i++) { |
| newData[i] = this.data[i]; |
| } |
| this.data = newData; |
| } |
| }; |
| PathProxy.prototype.toStatic = function () { |
| if (!this._saveData) { |
| return; |
| } |
| this._drawPendingPt(); |
| var data = this.data; |
| if (data instanceof Array) { |
| data.length = this._len; |
| if (hasTypedArray && this._len > 11) { |
| this.data = new Float32Array(data); |
| } |
| } |
| }; |
| PathProxy.prototype.getBoundingRect = function () { |
| 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; |
| var i; |
| for (i = 0; i < this._len;) { |
| var cmd = data[i++]; |
| var isFirst = i === 1; |
| if (isFirst) { |
| xi = data[i]; |
| yi = data[i + 1]; |
| x0 = xi; |
| y0 = yi; |
| } |
| switch (cmd) { |
| case CMD.M: |
| xi = x0 = data[i++]; |
| yi = y0 = data[i++]; |
| min2[0] = x0; |
| min2[1] = y0; |
| max2[0] = x0; |
| max2[1] = y0; |
| break; |
| case CMD.L: |
| fromLine(xi, yi, data[i], data[i + 1], min2, max2); |
| xi = data[i++]; |
| yi = data[i++]; |
| break; |
| case CMD.C: |
| fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2); |
| xi = data[i++]; |
| yi = data[i++]; |
| break; |
| case CMD.Q: |
| fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2); |
| xi = data[i++]; |
| yi = data[i++]; |
| break; |
| case CMD.A: |
| var cx = data[i++]; |
| var cy = data[i++]; |
| var rx = data[i++]; |
| var ry = data[i++]; |
| var startAngle = data[i++]; |
| var endAngle = data[i++] + startAngle; |
| i += 1; |
| var anticlockwise = !data[i++]; |
| if (isFirst) { |
| x0 = mathCos$1(startAngle) * rx + cx; |
| y0 = mathSin$1(startAngle) * ry + cy; |
| } |
| fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2); |
| xi = mathCos$1(endAngle) * rx + cx; |
| yi = mathSin$1(endAngle) * ry + cy; |
| break; |
| case CMD.R: |
| x0 = xi = data[i++]; |
| y0 = yi = data[i++]; |
| var width = data[i++]; |
| var height = data[i++]; |
| fromLine(x0, y0, x0 + width, y0 + height, min2, max2); |
| break; |
| case CMD.Z: |
| xi = x0; |
| yi = y0; |
| break; |
| } |
| min(min$1, min$1, min2); |
| max(max$1, max$1, max2); |
| } |
| if (i === 0) { |
| min$1[0] = min$1[1] = max$1[0] = max$1[1] = 0; |
| } |
| return new BoundingRect(min$1[0], min$1[1], max$1[0] - min$1[0], max$1[1] - min$1[1]); |
| }; |
| PathProxy.prototype._calculateLength = function () { |
| var data = this.data; |
| var len = this._len; |
| var ux = this._ux; |
| var uy = this._uy; |
| var xi = 0; |
| var yi = 0; |
| var x0 = 0; |
| var y0 = 0; |
| if (!this._pathSegLen) { |
| this._pathSegLen = []; |
| } |
| var pathSegLen = this._pathSegLen; |
| var pathTotalLen = 0; |
| var segCount = 0; |
| for (var i = 0; i < len;) { |
| var cmd = data[i++]; |
| var isFirst = i === 1; |
| if (isFirst) { |
| xi = data[i]; |
| yi = data[i + 1]; |
| x0 = xi; |
| y0 = yi; |
| } |
| var l = -1; |
| switch (cmd) { |
| case CMD.M: |
| xi = x0 = data[i++]; |
| yi = y0 = data[i++]; |
| break; |
| case CMD.L: { |
| var x2 = data[i++]; |
| var y2 = data[i++]; |
| var dx = x2 - xi; |
| var dy = y2 - yi; |
| if (mathAbs(dx) > ux || mathAbs(dy) > uy || i === len - 1) { |
| l = Math.sqrt(dx * dx + dy * dy); |
| xi = x2; |
| yi = y2; |
| } |
| break; |
| } |
| case CMD.C: { |
| var x1 = data[i++]; |
| var y1 = data[i++]; |
| var x2 = data[i++]; |
| var y2 = data[i++]; |
| var x3 = data[i++]; |
| var y3 = data[i++]; |
| l = cubicLength(xi, yi, x1, y1, x2, y2, x3, y3, 10); |
| xi = x3; |
| yi = y3; |
| break; |
| } |
| case CMD.Q: { |
| var x1 = data[i++]; |
| var y1 = data[i++]; |
| var x2 = data[i++]; |
| var y2 = data[i++]; |
| l = quadraticLength(xi, yi, x1, y1, x2, y2, 10); |
| xi = x2; |
| yi = y2; |
| break; |
| } |
| case CMD.A: |
| var cx = data[i++]; |
| var cy = data[i++]; |
| var rx = data[i++]; |
| var ry = data[i++]; |
| var startAngle = data[i++]; |
| var delta = data[i++]; |
| var endAngle = delta + startAngle; |
| i += 1; |
| var anticlockwise = !data[i++]; |
| if (isFirst) { |
| x0 = mathCos$1(startAngle) * rx + cx; |
| y0 = mathSin$1(startAngle) * ry + cy; |
| } |
| l = mathMax$2(rx, ry) * mathMin$2(PI2$1, Math.abs(delta)); |
| xi = mathCos$1(endAngle) * rx + cx; |
| yi = mathSin$1(endAngle) * ry + cy; |
| break; |
| case CMD.R: { |
| x0 = xi = data[i++]; |
| y0 = yi = data[i++]; |
| var width = data[i++]; |
| var height = data[i++]; |
| l = width * 2 + height * 2; |
| break; |
| } |
| case CMD.Z: { |
| var dx = x0 - xi; |
| var dy = y0 - yi; |
| l = Math.sqrt(dx * dx + dy * dy); |
| xi = x0; |
| yi = y0; |
| break; |
| } |
| } |
| if (l >= 0) { |
| pathSegLen[segCount++] = l; |
| pathTotalLen += l; |
| } |
| } |
| this._pathLen = pathTotalLen; |
| return pathTotalLen; |
| }; |
| PathProxy.prototype.rebuildPath = function (ctx, percent) { |
| var d = this.data; |
| var ux = this._ux; |
| var uy = this._uy; |
| var len = this._len; |
| var x0; |
| var y0; |
| var xi; |
| var yi; |
| var x; |
| var y; |
| var drawPart = percent < 1; |
| var pathSegLen; |
| var pathTotalLen; |
| var accumLength = 0; |
| var segCount = 0; |
| var displayedLength; |
| var pendingPtDist = 0; |
| var pendingPtX; |
| var pendingPtY; |
| if (drawPart) { |
| if (!this._pathSegLen) { |
| this._calculateLength(); |
| } |
| pathSegLen = this._pathSegLen; |
| pathTotalLen = this._pathLen; |
| displayedLength = percent * pathTotalLen; |
| if (!displayedLength) { |
| return; |
| } |
| } |
| lo: for (var i = 0; i < len;) { |
| var cmd = d[i++]; |
| var isFirst = i === 1; |
| if (isFirst) { |
| xi = d[i]; |
| yi = d[i + 1]; |
| x0 = xi; |
| y0 = yi; |
| } |
| if (cmd !== CMD.L && pendingPtDist > 0) { |
| ctx.lineTo(pendingPtX, pendingPtY); |
| pendingPtDist = 0; |
| } |
| switch (cmd) { |
| case CMD.M: |
| x0 = xi = d[i++]; |
| y0 = yi = d[i++]; |
| ctx.moveTo(xi, yi); |
| break; |
| case CMD.L: { |
| x = d[i++]; |
| y = d[i++]; |
| var dx = mathAbs(x - xi); |
| var dy = mathAbs(y - yi); |
| if (dx > ux || dy > uy) { |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| var t = (displayedLength - accumLength) / l; |
| ctx.lineTo(xi * (1 - t) + x * t, yi * (1 - t) + y * t); |
| break lo; |
| } |
| accumLength += l; |
| } |
| ctx.lineTo(x, y); |
| xi = x; |
| yi = y; |
| pendingPtDist = 0; |
| } |
| else { |
| var d2 = dx * dx + dy * dy; |
| if (d2 > pendingPtDist) { |
| pendingPtX = x; |
| pendingPtY = y; |
| pendingPtDist = d2; |
| } |
| } |
| break; |
| } |
| case CMD.C: { |
| var x1 = d[i++]; |
| var y1 = d[i++]; |
| var x2 = d[i++]; |
| var y2 = d[i++]; |
| var x3 = d[i++]; |
| var y3 = d[i++]; |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| var t = (displayedLength - accumLength) / l; |
| cubicSubdivide(xi, x1, x2, x3, t, tmpOutX); |
| cubicSubdivide(yi, y1, y2, y3, t, tmpOutY); |
| ctx.bezierCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2], tmpOutX[3], tmpOutY[3]); |
| break lo; |
| } |
| accumLength += l; |
| } |
| ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3); |
| xi = x3; |
| yi = y3; |
| break; |
| } |
| case CMD.Q: { |
| var x1 = d[i++]; |
| var y1 = d[i++]; |
| var x2 = d[i++]; |
| var y2 = d[i++]; |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| var t = (displayedLength - accumLength) / l; |
| quadraticSubdivide(xi, x1, x2, t, tmpOutX); |
| quadraticSubdivide(yi, y1, y2, t, tmpOutY); |
| ctx.quadraticCurveTo(tmpOutX[1], tmpOutY[1], tmpOutX[2], tmpOutY[2]); |
| break lo; |
| } |
| accumLength += l; |
| } |
| ctx.quadraticCurveTo(x1, y1, x2, y2); |
| xi = x2; |
| yi = y2; |
| break; |
| } |
| case CMD.A: |
| var cx = d[i++]; |
| var cy = d[i++]; |
| var rx = d[i++]; |
| var ry = d[i++]; |
| var startAngle = d[i++]; |
| var delta = d[i++]; |
| var psi = d[i++]; |
| var anticlockwise = !d[i++]; |
| var r = (rx > ry) ? rx : ry; |
| var isEllipse = mathAbs(rx - ry) > 1e-3; |
| var endAngle = startAngle + delta; |
| var breakBuild = false; |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| endAngle = startAngle + delta * (displayedLength - accumLength) / l; |
| breakBuild = true; |
| } |
| accumLength += l; |
| } |
| if (isEllipse && ctx.ellipse) { |
| ctx.ellipse(cx, cy, rx, ry, psi, startAngle, endAngle, anticlockwise); |
| } |
| else { |
| ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise); |
| } |
| if (breakBuild) { |
| break lo; |
| } |
| if (isFirst) { |
| x0 = mathCos$1(startAngle) * rx + cx; |
| y0 = mathSin$1(startAngle) * 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]; |
| x = d[i++]; |
| y = d[i++]; |
| var width = d[i++]; |
| var height = d[i++]; |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| var d_1 = displayedLength - accumLength; |
| ctx.moveTo(x, y); |
| ctx.lineTo(x + mathMin$2(d_1, width), y); |
| d_1 -= width; |
| if (d_1 > 0) { |
| ctx.lineTo(x + width, y + mathMin$2(d_1, height)); |
| } |
| d_1 -= height; |
| if (d_1 > 0) { |
| ctx.lineTo(x + mathMax$2(width - d_1, 0), y + height); |
| } |
| d_1 -= width; |
| if (d_1 > 0) { |
| ctx.lineTo(x, y + mathMax$2(height - d_1, 0)); |
| } |
| break lo; |
| } |
| accumLength += l; |
| } |
| ctx.rect(x, y, width, height); |
| break; |
| case CMD.Z: |
| if (drawPart) { |
| var l = pathSegLen[segCount++]; |
| if (accumLength + l > displayedLength) { |
| var t = (displayedLength - accumLength) / l; |
| ctx.lineTo(xi * (1 - t) + x0 * t, yi * (1 - t) + y0 * t); |
| break lo; |
| } |
| accumLength += l; |
| } |
| ctx.closePath(); |
| xi = x0; |
| yi = y0; |
| } |
| } |
| }; |
| PathProxy.prototype.clone = function () { |
| var newProxy = new PathProxy(); |
| var data = this.data; |
| newProxy.data = data.slice ? data.slice() |
| : Array.prototype.slice.call(data); |
| newProxy._len = this._len; |
| return newProxy; |
| }; |
| PathProxy.CMD = CMD; |
| PathProxy.initDefaultProps = (function () { |
| var proto = PathProxy.prototype; |
| proto._saveData = true; |
| proto._ux = 0; |
| proto._uy = 0; |
| proto._pendingPtDist = 0; |
| proto._version = 0; |
| })(); |
| return PathProxy; |
| }()); |
| |
| function containStroke(x0, y0, x1, y1, lineWidth, x, y) { |
| if (lineWidth === 0) { |
| return false; |
| } |
| var _l = lineWidth; |
| var _a = 0; |
| var _b = x0; |
| if ((y > y0 + _l && y > y1 + _l) |
| || (y < y0 - _l && y < y1 - _l) |
| || (x > x0 + _l && x > x1 + _l) |
| || (x < x0 - _l && x < x1 - _l)) { |
| return false; |
| } |
| if (x0 !== x1) { |
| _a = (y0 - y1) / (x0 - x1); |
| _b = (x0 * y1 - x1 * y0) / (x0 - x1); |
| } |
| else { |
| return Math.abs(x - x0) <= _l / 2; |
| } |
| var tmp = _a * x - y + _b; |
| var _s = tmp * tmp / (_a * _a + 1); |
| return _s <= _l / 2 * _l / 2; |
| } |
| |
| function containStroke$1(x0, y0, x1, y1, x2, y2, x3, y3, lineWidth, x, y) { |
| if (lineWidth === 0) { |
| return false; |
| } |
| var _l = lineWidth; |
| if ((y > y0 + _l && y > y1 + _l && y > y2 + _l && y > y3 + _l) |
| || (y < y0 - _l && y < y1 - _l && y < y2 - _l && y < y3 - _l) |
| || (x > x0 + _l && x > x1 + _l && x > x2 + _l && x > x3 + _l) |
| || (x < x0 - _l && x < x1 - _l && x < x2 - _l && x < x3 - _l)) { |
| return false; |
| } |
| var d = cubicProjectPoint(x0, y0, x1, y1, x2, y2, x3, y3, x, y, null); |
| return d <= _l / 2; |
| } |
| |
| function containStroke$2(x0, y0, x1, y1, x2, y2, lineWidth, x, y) { |
| if (lineWidth === 0) { |
| return false; |
| } |
| var _l = lineWidth; |
| if ((y > y0 + _l && y > y1 + _l && y > y2 + _l) |
| || (y < y0 - _l && y < y1 - _l && y < y2 - _l) |
| || (x > x0 + _l && x > x1 + _l && x > x2 + _l) |
| || (x < x0 - _l && x < x1 - _l && x < x2 - _l)) { |
| return false; |
| } |
| var d = quadraticProjectPoint(x0, y0, x1, y1, x2, y2, x, y, null); |
| return d <= _l / 2; |
| } |
| |
| var PI2$2 = Math.PI * 2; |
| function normalizeRadian(angle) { |
| angle %= PI2$2; |
| if (angle < 0) { |
| angle += PI2$2; |
| } |
| return angle; |
| } |
| |
| var PI2$3 = Math.PI * 2; |
| function containStroke$3(cx, cy, r, startAngle, endAngle, anticlockwise, lineWidth, x, y) { |
| if (lineWidth === 0) { |
| return false; |
| } |
| var _l = lineWidth; |
| x -= cx; |
| y -= cy; |
| var d = Math.sqrt(x * x + y * y); |
| if ((d - _l > r) || (d + _l < r)) { |
| return false; |
| } |
| if (Math.abs(startAngle - endAngle) % PI2$3 < 1e-4) { |
| return true; |
| } |
| if (anticlockwise) { |
| var tmp = startAngle; |
| startAngle = normalizeRadian(endAngle); |
| endAngle = normalizeRadian(tmp); |
| } |
| else { |
| startAngle = normalizeRadian(startAngle); |
| endAngle = normalizeRadian(endAngle); |
| } |
| if (startAngle > endAngle) { |
| endAngle += PI2$3; |
| } |
| var angle = Math.atan2(y, x); |
| if (angle < 0) { |
| angle += PI2$3; |
| } |
| return (angle >= startAngle && angle <= endAngle) |
| || (angle + PI2$3 >= startAngle && angle + PI2$3 <= endAngle); |
| } |
| |
| function windingLine(x0, y0, x1, y1, x, y) { |
| if ((y > y0 && y > y1) || (y < y0 && y < y1)) { |
| return 0; |
| } |
| if (y1 === y0) { |
| return 0; |
| } |
| var t = (y - y0) / (y1 - y0); |
| var dir = y1 < y0 ? 1 : -1; |
| if (t === 1 || t === 0) { |
| dir = y1 < y0 ? 0.5 : -0.5; |
| } |
| var x_ = t * (x1 - x0) + x0; |
| return x_ === x ? Infinity : x_ > x ? dir : 0; |
| } |
| |
| var CMD$1 = PathProxy.CMD; |
| var PI2$4 = Math.PI * 2; |
| var EPSILON$2 = 1e-4; |
| function isAroundEqual(a, b) { |
| return Math.abs(a - b) < EPSILON$2; |
| } |
| var roots = [-1, -1, -1]; |
| var extrema = [-1, -1]; |
| function swapExtrema() { |
| var tmp = extrema[0]; |
| extrema[0] = extrema[1]; |
| extrema[1] = tmp; |
| } |
| function windingCubic(x0, y0, x1, y1, x2, y2, x3, y3, x, y) { |
| if ((y > y0 && y > y1 && y > y2 && y > y3) |
| || (y < y0 && y < y1 && y < y2 && y < y3)) { |
| return 0; |
| } |
| var nRoots = cubicRootAt(y0, y1, y2, y3, y, roots); |
| if (nRoots === 0) { |
| return 0; |
| } |
| else { |
| var w = 0; |
| var nExtrema = -1; |
| var y0_ = void 0; |
| var y1_ = void 0; |
| for (var i = 0; i < nRoots; i++) { |
| var t = roots[i]; |
| var unit = (t === 0 || t === 1) ? 0.5 : 1; |
| var x_ = cubicAt(x0, x1, x2, x3, t); |
| if (x_ < x) { |
| continue; |
| } |
| if (nExtrema < 0) { |
| nExtrema = cubicExtrema(y0, y1, y2, y3, extrema); |
| if (extrema[1] < extrema[0] && nExtrema > 1) { |
| swapExtrema(); |
| } |
| y0_ = cubicAt(y0, y1, y2, y3, extrema[0]); |
| if (nExtrema > 1) { |
| y1_ = cubicAt(y0, y1, y2, y3, extrema[1]); |
| } |
| } |
| if (nExtrema === 2) { |
| if (t < extrema[0]) { |
| w += y0_ < y0 ? unit : -unit; |
| } |
| else if (t < extrema[1]) { |
| w += y1_ < y0_ ? unit : -unit; |
| } |
| else { |
| w += y3 < y1_ ? unit : -unit; |
| } |
| } |
| else { |
| if (t < extrema[0]) { |
| w += y0_ < y0 ? unit : -unit; |
| } |
| else { |
| w += y3 < y0_ ? unit : -unit; |
| } |
| } |
| } |
| return w; |
| } |
| } |
| function windingQuadratic(x0, y0, x1, y1, x2, y2, x, y) { |
| if ((y > y0 && y > y1 && y > y2) |
| || (y < y0 && y < y1 && y < y2)) { |
| return 0; |
| } |
| var nRoots = quadraticRootAt(y0, y1, y2, y, roots); |
| if (nRoots === 0) { |
| return 0; |
| } |
| else { |
| var t = quadraticExtremum(y0, y1, y2); |
| if (t >= 0 && t <= 1) { |
| var w = 0; |
| var y_ = quadraticAt(y0, y1, y2, t); |
| for (var i = 0; i < nRoots; i++) { |
| var unit = (roots[i] === 0 || roots[i] === 1) ? 0.5 : 1; |
| var x_ = quadraticAt(x0, x1, x2, roots[i]); |
| if (x_ < x) { |
| continue; |
| } |
| if (roots[i] < t) { |
| w += y_ < y0 ? unit : -unit; |
| } |
| else { |
| w += y2 < y_ ? unit : -unit; |
| } |
| } |
| return w; |
| } |
| else { |
| var unit = (roots[0] === 0 || roots[0] === 1) ? 0.5 : 1; |
| var x_ = quadraticAt(x0, x1, x2, roots[0]); |
| if (x_ < x) { |
| return 0; |
| } |
| return y2 < y0 ? unit : -unit; |
| } |
| } |
| } |
| function windingArc(cx, cy, r, startAngle, endAngle, anticlockwise, x, y) { |
| y -= cy; |
| if (y > r || y < -r) { |
| return 0; |
| } |
| var tmp = Math.sqrt(r * r - y * y); |
| roots[0] = -tmp; |
| roots[1] = tmp; |
| var dTheta = Math.abs(startAngle - endAngle); |
| if (dTheta < 1e-4) { |
| return 0; |
| } |
| if (dTheta >= PI2$4 - 1e-4) { |
| startAngle = 0; |
| endAngle = PI2$4; |
| var dir = anticlockwise ? 1 : -1; |
| if (x >= roots[0] + cx && x <= roots[1] + cx) { |
| return dir; |
| } |
| else { |
| return 0; |
| } |
| } |
| if (startAngle > endAngle) { |
| var tmp_1 = startAngle; |
| startAngle = endAngle; |
| endAngle = tmp_1; |
| } |
| if (startAngle < 0) { |
| startAngle += PI2$4; |
| endAngle += PI2$4; |
| } |
| 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$4 + angle; |
| } |
| if ((angle >= startAngle && angle <= endAngle) |
| || (angle + PI2$4 >= startAngle && angle + PI2$4 <= endAngle)) { |
| if (angle > Math.PI / 2 && angle < Math.PI * 1.5) { |
| dir = -dir; |
| } |
| w += dir; |
| } |
| } |
| } |
| return w; |
| } |
| function containPath(path, lineWidth, isStroke, x, y) { |
| var data = path.data; |
| var len = path.len(); |
| var w = 0; |
| var xi = 0; |
| var yi = 0; |
| var x0 = 0; |
| var y0 = 0; |
| var x1; |
| var y1; |
| for (var i = 0; i < len;) { |
| var cmd = data[i++]; |
| var isFirst = i === 1; |
| if (cmd === CMD$1.M && i > 1) { |
| if (!isStroke) { |
| w += windingLine(xi, yi, x0, y0, x, y); |
| } |
| } |
| if (isFirst) { |
| xi = data[i]; |
| yi = data[i + 1]; |
| x0 = xi; |
| y0 = yi; |
| } |
| switch (cmd) { |
| case CMD$1.M: |
| x0 = data[i++]; |
| y0 = data[i++]; |
| xi = x0; |
| yi = y0; |
| break; |
| case CMD$1.L: |
| if (isStroke) { |
| if (containStroke(xi, yi, data[i], data[i + 1], lineWidth, x, y)) { |
| return true; |
| } |
| } |
| else { |
| w += 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$1(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$2(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: |
| var cx = data[i++]; |
| var cy = data[i++]; |
| var rx = data[i++]; |
| var ry = data[i++]; |
| var theta = data[i++]; |
| var dTheta = data[i++]; |
| i += 1; |
| var anticlockwise = !!(1 - data[i++]); |
| x1 = Math.cos(theta) * rx + cx; |
| y1 = Math.sin(theta) * ry + cy; |
| if (!isFirst) { |
| w += windingLine(xi, yi, x1, y1, x, y); |
| } |
| else { |
| x0 = x1; |
| y0 = y1; |
| } |
| var _x = (x - cx) * ry / rx + cx; |
| if (isStroke) { |
| if (containStroke$3(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++]; |
| x1 = x0 + width; |
| y1 = y0 + height; |
| if (isStroke) { |
| if (containStroke(x0, y0, x1, y0, lineWidth, x, y) |
| || containStroke(x1, y0, x1, y1, lineWidth, x, y) |
| || containStroke(x1, y1, x0, y1, lineWidth, x, y) |
| || containStroke(x0, y1, x0, y0, lineWidth, x, y)) { |
| return true; |
| } |
| } |
| else { |
| w += windingLine(x1, y0, x1, y1, x, y); |
| w += windingLine(x0, y1, x0, y0, x, y); |
| } |
| break; |
| case CMD$1.Z: |
| if (isStroke) { |
| if (containStroke(xi, yi, x0, y0, lineWidth, x, y)) { |
| return true; |
| } |
| } |
| else { |
| w += windingLine(xi, yi, x0, y0, x, y); |
| } |
| xi = x0; |
| yi = y0; |
| break; |
| } |
| } |
| if (!isStroke && !isAroundEqual(yi, y0)) { |
| w += windingLine(xi, yi, x0, y0, x, y) || 0; |
| } |
| return w !== 0; |
| } |
| function contain(pathProxy, x, y) { |
| return containPath(pathProxy, 0, false, x, y); |
| } |
| function containStroke$4(pathProxy, lineWidth, x, y) { |
| return containPath(pathProxy, lineWidth, true, x, y); |
| } |
| |
| var DEFAULT_PATH_STYLE = defaults({ |
| fill: '#000', |
| stroke: null, |
| strokePercent: 1, |
| fillOpacity: 1, |
| strokeOpacity: 1, |
| lineDashOffset: 0, |
| lineWidth: 1, |
| lineCap: 'butt', |
| miterLimit: 10, |
| strokeNoScale: false, |
| strokeFirst: false |
| }, DEFAULT_COMMON_STYLE); |
| var DEFAULT_PATH_ANIMATION_PROPS = { |
| style: defaults({ |
| fill: true, |
| stroke: true, |
| strokePercent: true, |
| fillOpacity: true, |
| strokeOpacity: true, |
| lineDashOffset: true, |
| lineWidth: true, |
| miterLimit: true |
| }, DEFAULT_COMMON_ANIMATION_PROPS.style) |
| }; |
| var pathCopyParams = TRANSFORMABLE_PROPS.concat(['invisible', |
| 'culling', 'z', 'z2', 'zlevel', 'parent' |
| ]); |
| var Path = (function (_super) { |
| __extends(Path, _super); |
| function Path(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Path.prototype.update = function () { |
| var _this = this; |
| _super.prototype.update.call(this); |
| var style = this.style; |
| if (style.decal) { |
| var decalEl = this._decalEl = this._decalEl || new Path(); |
| if (decalEl.buildPath === Path.prototype.buildPath) { |
| decalEl.buildPath = function (ctx) { |
| _this.buildPath(ctx, _this.shape); |
| }; |
| } |
| decalEl.silent = true; |
| var decalElStyle = decalEl.style; |
| for (var key in style) { |
| if (decalElStyle[key] !== style[key]) { |
| decalElStyle[key] = style[key]; |
| } |
| } |
| decalElStyle.fill = style.fill ? style.decal : null; |
| decalElStyle.decal = null; |
| decalElStyle.shadowColor = null; |
| style.strokeFirst && (decalElStyle.stroke = null); |
| for (var i = 0; i < pathCopyParams.length; ++i) { |
| decalEl[pathCopyParams[i]] = this[pathCopyParams[i]]; |
| } |
| decalEl.__dirty |= REDRAW_BIT; |
| } |
| else if (this._decalEl) { |
| this._decalEl = null; |
| } |
| }; |
| Path.prototype.getDecalElement = function () { |
| return this._decalEl; |
| }; |
| Path.prototype._init = function (props) { |
| var keysArr = keys(props); |
| this.shape = this.getDefaultShape(); |
| var defaultStyle = this.getDefaultStyle(); |
| if (defaultStyle) { |
| this.useStyle(defaultStyle); |
| } |
| for (var i = 0; i < keysArr.length; i++) { |
| var key = keysArr[i]; |
| var value = props[key]; |
| if (key === 'style') { |
| if (!this.style) { |
| this.useStyle(value); |
| } |
| else { |
| extend(this.style, value); |
| } |
| } |
| else if (key === 'shape') { |
| extend(this.shape, value); |
| } |
| else { |
| _super.prototype.attrKV.call(this, key, value); |
| } |
| } |
| if (!this.style) { |
| this.useStyle({}); |
| } |
| }; |
| Path.prototype.getDefaultStyle = function () { |
| return null; |
| }; |
| Path.prototype.getDefaultShape = function () { |
| return {}; |
| }; |
| Path.prototype.canBeInsideText = function () { |
| return this.hasFill(); |
| }; |
| Path.prototype.getInsideTextFill = function () { |
| var pathFill = this.style.fill; |
| if (pathFill !== 'none') { |
| if (isString(pathFill)) { |
| var fillLum = lum(pathFill, 0); |
| if (fillLum > 0.5) { |
| return DARK_LABEL_COLOR; |
| } |
| else if (fillLum > 0.2) { |
| return LIGHTER_LABEL_COLOR; |
| } |
| return LIGHT_LABEL_COLOR; |
| } |
| else if (pathFill) { |
| return LIGHT_LABEL_COLOR; |
| } |
| } |
| return DARK_LABEL_COLOR; |
| }; |
| Path.prototype.getInsideTextStroke = function (textFill) { |
| var pathFill = this.style.fill; |
| if (isString(pathFill)) { |
| var zr = this.__zr; |
| var isDarkMode = !!(zr && zr.isDarkMode()); |
| var isDarkLabel = lum(textFill, 0) < DARK_MODE_THRESHOLD; |
| if (isDarkMode === isDarkLabel) { |
| return pathFill; |
| } |
| } |
| }; |
| Path.prototype.buildPath = function (ctx, shapeCfg, inBatch) { }; |
| Path.prototype.pathUpdated = function () { |
| this.__dirty &= ~SHAPE_CHANGED_BIT; |
| }; |
| Path.prototype.getUpdatedPathProxy = function (inBatch) { |
| !this.path && this.createPathProxy(); |
| this.path.beginPath(); |
| this.buildPath(this.path, this.shape, inBatch); |
| return this.path; |
| }; |
| Path.prototype.createPathProxy = function () { |
| this.path = new PathProxy(false); |
| }; |
| Path.prototype.hasStroke = function () { |
| var style = this.style; |
| var stroke = style.stroke; |
| return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0)); |
| }; |
| Path.prototype.hasFill = function () { |
| var style = this.style; |
| var fill = style.fill; |
| return fill != null && fill !== 'none'; |
| }; |
| Path.prototype.getBoundingRect = function () { |
| var rect = this._rect; |
| var style = this.style; |
| var needsUpdateRect = !rect; |
| if (needsUpdateRect) { |
| var firstInvoke = false; |
| if (!this.path) { |
| firstInvoke = true; |
| this.createPathProxy(); |
| } |
| var path = this.path; |
| if (firstInvoke || (this.__dirty & SHAPE_CHANGED_BIT)) { |
| path.beginPath(); |
| this.buildPath(path, this.shape, false); |
| this.pathUpdated(); |
| } |
| rect = path.getBoundingRect(); |
| } |
| this._rect = rect; |
| if (this.hasStroke() && this.path && this.path.len() > 0) { |
| var rectStroke = this._rectStroke || (this._rectStroke = rect.clone()); |
| if (this.__dirty || needsUpdateRect) { |
| rectStroke.copy(rect); |
| var lineScale = style.strokeNoScale ? this.getLineScale() : 1; |
| var w = style.lineWidth; |
| if (!this.hasFill()) { |
| var strokeContainThreshold = this.strokeContainThreshold; |
| w = Math.max(w, strokeContainThreshold == null ? 4 : strokeContainThreshold); |
| } |
| if (lineScale > 1e-10) { |
| rectStroke.width += w / lineScale; |
| rectStroke.height += w / lineScale; |
| rectStroke.x -= w / lineScale / 2; |
| rectStroke.y -= w / lineScale / 2; |
| } |
| } |
| return rectStroke; |
| } |
| return rect; |
| }; |
| Path.prototype.contain = function (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 pathProxy = this.path; |
| if (this.hasStroke()) { |
| var lineWidth = style.lineWidth; |
| var lineScale = style.strokeNoScale ? this.getLineScale() : 1; |
| if (lineScale > 1e-10) { |
| if (!this.hasFill()) { |
| lineWidth = Math.max(lineWidth, this.strokeContainThreshold); |
| } |
| if (containStroke$4(pathProxy, lineWidth / lineScale, x, y)) { |
| return true; |
| } |
| } |
| } |
| if (this.hasFill()) { |
| return contain(pathProxy, x, y); |
| } |
| } |
| return false; |
| }; |
| Path.prototype.dirtyShape = function () { |
| this.__dirty |= SHAPE_CHANGED_BIT; |
| if (this._rect) { |
| this._rect = null; |
| } |
| if (this._decalEl) { |
| this._decalEl.dirtyShape(); |
| } |
| this.markRedraw(); |
| }; |
| Path.prototype.dirty = function () { |
| this.dirtyStyle(); |
| this.dirtyShape(); |
| }; |
| Path.prototype.animateShape = function (loop) { |
| return this.animate('shape', loop); |
| }; |
| Path.prototype.updateDuringAnimation = function (targetKey) { |
| if (targetKey === 'style') { |
| this.dirtyStyle(); |
| } |
| else if (targetKey === 'shape') { |
| this.dirtyShape(); |
| } |
| else { |
| this.markRedraw(); |
| } |
| }; |
| Path.prototype.attrKV = function (key, value) { |
| if (key === 'shape') { |
| this.setShape(value); |
| } |
| else { |
| _super.prototype.attrKV.call(this, key, value); |
| } |
| }; |
| Path.prototype.setShape = function (keyOrObj, value) { |
| var shape = this.shape; |
| if (!shape) { |
| shape = this.shape = {}; |
| } |
| if (typeof keyOrObj === 'string') { |
| shape[keyOrObj] = value; |
| } |
| else { |
| extend(shape, keyOrObj); |
| } |
| this.dirtyShape(); |
| return this; |
| }; |
| Path.prototype.shapeChanged = function () { |
| return !!(this.__dirty & SHAPE_CHANGED_BIT); |
| }; |
| Path.prototype.createStyle = function (obj) { |
| return createObject(DEFAULT_PATH_STYLE, obj); |
| }; |
| Path.prototype._innerSaveToNormal = function (toState) { |
| _super.prototype._innerSaveToNormal.call(this, toState); |
| var normalState = this._normalState; |
| if (toState.shape && !normalState.shape) { |
| normalState.shape = extend({}, this.shape); |
| } |
| }; |
| Path.prototype._applyStateObj = function (stateName, state, normalState, keepCurrentStates, transition, animationCfg) { |
| _super.prototype._applyStateObj.call(this, stateName, state, normalState, keepCurrentStates, transition, animationCfg); |
| var needsRestoreToNormal = !(state && keepCurrentStates); |
| var targetShape; |
| if (state && state.shape) { |
| if (transition) { |
| if (keepCurrentStates) { |
| targetShape = state.shape; |
| } |
| else { |
| targetShape = extend({}, normalState.shape); |
| extend(targetShape, state.shape); |
| } |
| } |
| else { |
| targetShape = extend({}, keepCurrentStates ? this.shape : normalState.shape); |
| extend(targetShape, state.shape); |
| } |
| } |
| else if (needsRestoreToNormal) { |
| targetShape = normalState.shape; |
| } |
| if (targetShape) { |
| if (transition) { |
| this.shape = extend({}, this.shape); |
| var targetShapePrimaryProps = {}; |
| var shapeKeys = keys(targetShape); |
| for (var i = 0; i < shapeKeys.length; i++) { |
| var key = shapeKeys[i]; |
| if (typeof targetShape[key] === 'object') { |
| this.shape[key] = targetShape[key]; |
| } |
| else { |
| targetShapePrimaryProps[key] = targetShape[key]; |
| } |
| } |
| this._transitionState(stateName, { |
| shape: targetShapePrimaryProps |
| }, animationCfg); |
| } |
| else { |
| this.shape = targetShape; |
| this.dirtyShape(); |
| } |
| } |
| }; |
| Path.prototype._mergeStates = function (states) { |
| var mergedState = _super.prototype._mergeStates.call(this, states); |
| var mergedShape; |
| for (var i = 0; i < states.length; i++) { |
| var state = states[i]; |
| if (state.shape) { |
| mergedShape = mergedShape || {}; |
| this._mergeStyle(mergedShape, state.shape); |
| } |
| } |
| if (mergedShape) { |
| mergedState.shape = mergedShape; |
| } |
| return mergedState; |
| }; |
| Path.prototype.getAnimationStyleProps = function () { |
| return DEFAULT_PATH_ANIMATION_PROPS; |
| }; |
| Path.prototype.isZeroArea = function () { |
| return false; |
| }; |
| Path.extend = function (defaultProps) { |
| var Sub = (function (_super) { |
| __extends(Sub, _super); |
| function Sub(opts) { |
| var _this = _super.call(this, opts) || this; |
| defaultProps.init && defaultProps.init.call(_this, opts); |
| return _this; |
| } |
| Sub.prototype.getDefaultStyle = function () { |
| return clone(defaultProps.style); |
| }; |
| Sub.prototype.getDefaultShape = function () { |
| return clone(defaultProps.shape); |
| }; |
| return Sub; |
| }(Path)); |
| for (var key in defaultProps) { |
| if (typeof defaultProps[key] === 'function') { |
| Sub.prototype[key] = defaultProps[key]; |
| } |
| } |
| return Sub; |
| }; |
| Path.initDefaultProps = (function () { |
| var pathProto = Path.prototype; |
| pathProto.type = 'path'; |
| pathProto.strokeContainThreshold = 5; |
| pathProto.segmentIgnoreThreshold = 0; |
| pathProto.subPixelOptimize = false; |
| pathProto.autoBatch = false; |
| pathProto.__dirty = REDRAW_BIT | STYLE_CHANGED_BIT | SHAPE_CHANGED_BIT; |
| })(); |
| return Path; |
| }(Displayable)); |
| |
| var DEFAULT_TSPAN_STYLE = defaults({ |
| strokeFirst: true, |
| font: DEFAULT_FONT, |
| x: 0, |
| y: 0, |
| textAlign: 'left', |
| textBaseline: 'top', |
| miterLimit: 2 |
| }, DEFAULT_PATH_STYLE); |
| var TSpan = (function (_super) { |
| __extends(TSpan, _super); |
| function TSpan() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| TSpan.prototype.hasStroke = function () { |
| var style = this.style; |
| var stroke = style.stroke; |
| return stroke != null && stroke !== 'none' && style.lineWidth > 0; |
| }; |
| TSpan.prototype.hasFill = function () { |
| var style = this.style; |
| var fill = style.fill; |
| return fill != null && fill !== 'none'; |
| }; |
| TSpan.prototype.createStyle = function (obj) { |
| return createObject(DEFAULT_TSPAN_STYLE, obj); |
| }; |
| TSpan.prototype.setBoundingRect = function (rect) { |
| this._rect = rect; |
| }; |
| TSpan.prototype.getBoundingRect = function () { |
| var style = this.style; |
| if (!this._rect) { |
| var text = style.text; |
| text != null ? (text += '') : (text = ''); |
| var rect = getBoundingRect(text, style.font, style.textAlign, style.textBaseline); |
| rect.x += style.x || 0; |
| rect.y += style.y || 0; |
| if (this.hasStroke()) { |
| var w = style.lineWidth; |
| rect.x -= w / 2; |
| rect.y -= w / 2; |
| rect.width += w; |
| rect.height += w; |
| } |
| this._rect = rect; |
| } |
| return this._rect; |
| }; |
| TSpan.initDefaultProps = (function () { |
| var tspanProto = TSpan.prototype; |
| tspanProto.dirtyRectTolerance = 10; |
| })(); |
| return TSpan; |
| }(Displayable)); |
| TSpan.prototype.type = 'tspan'; |
| |
| var DEFAULT_IMAGE_STYLE = defaults({ |
| x: 0, |
| y: 0 |
| }, DEFAULT_COMMON_STYLE); |
| var DEFAULT_IMAGE_ANIMATION_PROPS = { |
| style: defaults({ |
| x: true, |
| y: true, |
| width: true, |
| height: true, |
| sx: true, |
| sy: true, |
| sWidth: true, |
| sHeight: true |
| }, DEFAULT_COMMON_ANIMATION_PROPS.style) |
| }; |
| function isImageLike(source) { |
| return !!(source |
| && typeof source !== 'string' |
| && source.width && source.height); |
| } |
| var ZRImage = (function (_super) { |
| __extends(ZRImage, _super); |
| function ZRImage() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| ZRImage.prototype.createStyle = function (obj) { |
| return createObject(DEFAULT_IMAGE_STYLE, obj); |
| }; |
| ZRImage.prototype._getSize = function (dim) { |
| var style = this.style; |
| var size = style[dim]; |
| if (size != null) { |
| return size; |
| } |
| var imageSource = isImageLike(style.image) |
| ? style.image : this.__image; |
| if (!imageSource) { |
| return 0; |
| } |
| var otherDim = dim === 'width' ? 'height' : 'width'; |
| var otherDimSize = style[otherDim]; |
| if (otherDimSize == null) { |
| return imageSource[dim]; |
| } |
| else { |
| return imageSource[dim] / imageSource[otherDim] * otherDimSize; |
| } |
| }; |
| ZRImage.prototype.getWidth = function () { |
| return this._getSize('width'); |
| }; |
| ZRImage.prototype.getHeight = function () { |
| return this._getSize('height'); |
| }; |
| ZRImage.prototype.getAnimationStyleProps = function () { |
| return DEFAULT_IMAGE_ANIMATION_PROPS; |
| }; |
| ZRImage.prototype.getBoundingRect = function () { |
| var style = this.style; |
| if (!this._rect) { |
| this._rect = new BoundingRect(style.x || 0, style.y || 0, this.getWidth(), this.getHeight()); |
| } |
| return this._rect; |
| }; |
| return ZRImage; |
| }(Displayable)); |
| ZRImage.prototype.type = 'image'; |
| |
| 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; |
| if (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 round$1 = Math.round; |
| function subPixelOptimizeLine(outputShape, inputShape, style) { |
| if (!inputShape) { |
| return; |
| } |
| var x1 = inputShape.x1; |
| var x2 = inputShape.x2; |
| var y1 = inputShape.y1; |
| var y2 = inputShape.y2; |
| outputShape.x1 = x1; |
| outputShape.x2 = x2; |
| outputShape.y1 = y1; |
| outputShape.y2 = y2; |
| var lineWidth = style && style.lineWidth; |
| if (!lineWidth) { |
| return outputShape; |
| } |
| if (round$1(x1 * 2) === round$1(x2 * 2)) { |
| outputShape.x1 = outputShape.x2 = subPixelOptimize(x1, lineWidth, true); |
| } |
| if (round$1(y1 * 2) === round$1(y2 * 2)) { |
| outputShape.y1 = outputShape.y2 = subPixelOptimize(y1, lineWidth, true); |
| } |
| return outputShape; |
| } |
| function subPixelOptimizeRect(outputShape, inputShape, style) { |
| if (!inputShape) { |
| return; |
| } |
| var originX = inputShape.x; |
| var originY = inputShape.y; |
| var originWidth = inputShape.width; |
| var originHeight = inputShape.height; |
| outputShape.x = originX; |
| outputShape.y = originY; |
| outputShape.width = originWidth; |
| outputShape.height = originHeight; |
| var lineWidth = style && style.lineWidth; |
| if (!lineWidth) { |
| return outputShape; |
| } |
| outputShape.x = subPixelOptimize(originX, lineWidth, true); |
| outputShape.y = subPixelOptimize(originY, lineWidth, true); |
| outputShape.width = Math.max(subPixelOptimize(originX + originWidth, lineWidth, false) - outputShape.x, originWidth === 0 ? 0 : 1); |
| outputShape.height = Math.max(subPixelOptimize(originY + originHeight, lineWidth, false) - outputShape.y, originHeight === 0 ? 0 : 1); |
| return outputShape; |
| } |
| function subPixelOptimize(position, lineWidth, positiveOrNegative) { |
| if (!lineWidth) { |
| return position; |
| } |
| var doubledPosition = round$1(position * 2); |
| return (doubledPosition + round$1(lineWidth)) % 2 === 0 |
| ? doubledPosition / 2 |
| : (doubledPosition + (positiveOrNegative ? 1 : -1)) / 2; |
| } |
| |
| var RectShape = (function () { |
| function RectShape() { |
| this.x = 0; |
| this.y = 0; |
| this.width = 0; |
| this.height = 0; |
| } |
| return RectShape; |
| }()); |
| var subPixelOptimizeOutputShape = {}; |
| var Rect = (function (_super) { |
| __extends(Rect, _super); |
| function Rect(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Rect.prototype.getDefaultShape = function () { |
| return new RectShape(); |
| }; |
| Rect.prototype.buildPath = function (ctx, shape) { |
| var x; |
| var y; |
| var width; |
| var height; |
| if (this.subPixelOptimize) { |
| var optimizedShape = subPixelOptimizeRect(subPixelOptimizeOutputShape, shape, this.style); |
| x = optimizedShape.x; |
| y = optimizedShape.y; |
| width = optimizedShape.width; |
| height = optimizedShape.height; |
| optimizedShape.r = shape.r; |
| shape = optimizedShape; |
| } |
| else { |
| x = shape.x; |
| y = shape.y; |
| width = shape.width; |
| height = shape.height; |
| } |
| if (!shape.r) { |
| ctx.rect(x, y, width, height); |
| } |
| else { |
| buildPath(ctx, shape); |
| } |
| }; |
| Rect.prototype.isZeroArea = function () { |
| return !this.shape.width || !this.shape.height; |
| }; |
| return Rect; |
| }(Path)); |
| Rect.prototype.type = 'rect'; |
| |
| var DEFAULT_RICH_TEXT_COLOR = { |
| fill: '#000' |
| }; |
| var DEFAULT_STROKE_LINE_WIDTH = 2; |
| var DEFAULT_TEXT_ANIMATION_PROPS = { |
| style: defaults({ |
| fill: true, |
| stroke: true, |
| fillOpacity: true, |
| strokeOpacity: true, |
| lineWidth: true, |
| fontSize: true, |
| lineHeight: true, |
| width: true, |
| height: true, |
| textShadowColor: true, |
| textShadowBlur: true, |
| textShadowOffsetX: true, |
| textShadowOffsetY: true, |
| backgroundColor: true, |
| padding: true, |
| borderColor: true, |
| borderWidth: true, |
| borderRadius: true |
| }, DEFAULT_COMMON_ANIMATION_PROPS.style) |
| }; |
| var ZRText = (function (_super) { |
| __extends(ZRText, _super); |
| function ZRText(opts) { |
| var _this = _super.call(this) || this; |
| _this.type = 'text'; |
| _this._children = []; |
| _this._defaultStyle = DEFAULT_RICH_TEXT_COLOR; |
| _this.attr(opts); |
| return _this; |
| } |
| ZRText.prototype.childrenRef = function () { |
| return this._children; |
| }; |
| ZRText.prototype.update = function () { |
| _super.prototype.update.call(this); |
| if (this.styleChanged()) { |
| this._updateSubTexts(); |
| } |
| for (var i = 0; i < this._children.length; i++) { |
| var child = this._children[i]; |
| child.zlevel = this.zlevel; |
| child.z = this.z; |
| child.z2 = this.z2; |
| child.culling = this.culling; |
| child.cursor = this.cursor; |
| child.invisible = this.invisible; |
| } |
| }; |
| ZRText.prototype.updateTransform = function () { |
| var innerTransformable = this.innerTransformable; |
| if (innerTransformable) { |
| innerTransformable.updateTransform(); |
| if (innerTransformable.transform) { |
| this.transform = innerTransformable.transform; |
| } |
| } |
| else { |
| _super.prototype.updateTransform.call(this); |
| } |
| }; |
| ZRText.prototype.getLocalTransform = function (m) { |
| var innerTransformable = this.innerTransformable; |
| return innerTransformable |
| ? innerTransformable.getLocalTransform(m) |
| : _super.prototype.getLocalTransform.call(this, m); |
| }; |
| ZRText.prototype.getComputedTransform = function () { |
| if (this.__hostTarget) { |
| this.__hostTarget.getComputedTransform(); |
| this.__hostTarget.updateInnerText(true); |
| } |
| return _super.prototype.getComputedTransform.call(this); |
| }; |
| ZRText.prototype._updateSubTexts = function () { |
| this._childCursor = 0; |
| normalizeTextStyle(this.style); |
| this.style.rich |
| ? this._updateRichTexts() |
| : this._updatePlainTexts(); |
| this._children.length = this._childCursor; |
| this.styleUpdated(); |
| }; |
| ZRText.prototype.addSelfToZr = function (zr) { |
| _super.prototype.addSelfToZr.call(this, zr); |
| for (var i = 0; i < this._children.length; i++) { |
| this._children[i].__zr = zr; |
| } |
| }; |
| ZRText.prototype.removeSelfFromZr = function (zr) { |
| _super.prototype.removeSelfFromZr.call(this, zr); |
| for (var i = 0; i < this._children.length; i++) { |
| this._children[i].__zr = null; |
| } |
| }; |
| ZRText.prototype.getBoundingRect = function () { |
| if (this.styleChanged()) { |
| this._updateSubTexts(); |
| } |
| if (!this._rect) { |
| var tmpRect = new BoundingRect(0, 0, 0, 0); |
| var children = this._children; |
| var tmpMat = []; |
| var rect = null; |
| for (var i = 0; i < children.length; i++) { |
| var child = children[i]; |
| var childRect = child.getBoundingRect(); |
| var transform = child.getLocalTransform(tmpMat); |
| if (transform) { |
| tmpRect.copy(childRect); |
| tmpRect.applyTransform(transform); |
| rect = rect || tmpRect.clone(); |
| rect.union(tmpRect); |
| } |
| else { |
| rect = rect || childRect.clone(); |
| rect.union(childRect); |
| } |
| } |
| this._rect = rect || tmpRect; |
| } |
| return this._rect; |
| }; |
| ZRText.prototype.setDefaultTextStyle = function (defaultTextStyle) { |
| this._defaultStyle = defaultTextStyle || DEFAULT_RICH_TEXT_COLOR; |
| }; |
| ZRText.prototype.setTextContent = function (textContent) { |
| if ("development" !== 'production') { |
| throw new Error('Can\'t attach text on another text'); |
| } |
| }; |
| ZRText.prototype._mergeStyle = function (targetStyle, sourceStyle) { |
| if (!sourceStyle) { |
| return targetStyle; |
| } |
| var sourceRich = sourceStyle.rich; |
| var targetRich = targetStyle.rich || (sourceRich && {}); |
| extend(targetStyle, sourceStyle); |
| if (sourceRich && targetRich) { |
| this._mergeRich(targetRich, sourceRich); |
| targetStyle.rich = targetRich; |
| } |
| else if (targetRich) { |
| targetStyle.rich = targetRich; |
| } |
| return targetStyle; |
| }; |
| ZRText.prototype._mergeRich = function (targetRich, sourceRich) { |
| var richNames = keys(sourceRich); |
| for (var i = 0; i < richNames.length; i++) { |
| var richName = richNames[i]; |
| targetRich[richName] = targetRich[richName] || {}; |
| extend(targetRich[richName], sourceRich[richName]); |
| } |
| }; |
| ZRText.prototype.getAnimationStyleProps = function () { |
| return DEFAULT_TEXT_ANIMATION_PROPS; |
| }; |
| ZRText.prototype._getOrCreateChild = function (Ctor) { |
| var child = this._children[this._childCursor]; |
| if (!child || !(child instanceof Ctor)) { |
| child = new Ctor(); |
| } |
| this._children[this._childCursor++] = child; |
| child.__zr = this.__zr; |
| child.parent = this; |
| return child; |
| }; |
| ZRText.prototype._updatePlainTexts = function () { |
| var style = this.style; |
| var textFont = style.font || DEFAULT_FONT; |
| var textPadding = style.padding; |
| var text = getStyleText(style); |
| var contentBlock = parsePlainText(text, style); |
| var needDrawBg = needDrawBackground(style); |
| var bgColorDrawn = !!(style.backgroundColor); |
| var outerHeight = contentBlock.outerHeight; |
| var outerWidth = contentBlock.outerWidth; |
| var contentWidth = contentBlock.contentWidth; |
| var textLines = contentBlock.lines; |
| var lineHeight = contentBlock.lineHeight; |
| var defaultStyle = this._defaultStyle; |
| var baseX = style.x || 0; |
| var baseY = style.y || 0; |
| var textAlign = style.align || defaultStyle.align || 'left'; |
| var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign || 'top'; |
| var textX = baseX; |
| var textY = adjustTextY(baseY, contentBlock.contentHeight, verticalAlign); |
| if (needDrawBg || textPadding) { |
| var boxX = adjustTextX(baseX, outerWidth, textAlign); |
| var boxY = adjustTextY(baseY, outerHeight, verticalAlign); |
| needDrawBg && this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight); |
| } |
| textY += lineHeight / 2; |
| if (textPadding) { |
| textX = getTextXForPadding(baseX, textAlign, textPadding); |
| if (verticalAlign === 'top') { |
| textY += textPadding[0]; |
| } |
| else if (verticalAlign === 'bottom') { |
| textY -= textPadding[2]; |
| } |
| } |
| var defaultLineWidth = 0; |
| var useDefaultFill = false; |
| var textFill = getFill('fill' in style |
| ? style.fill |
| : (useDefaultFill = true, defaultStyle.fill)); |
| var textStroke = getStroke('stroke' in style |
| ? style.stroke |
| : (!bgColorDrawn |
| && (!defaultStyle.autoStroke || useDefaultFill)) |
| ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke) |
| : null); |
| var hasShadow = style.textShadowBlur > 0; |
| var fixedBoundingRect = style.width != null |
| && (style.overflow === 'truncate' || style.overflow === 'break' || style.overflow === 'breakAll'); |
| var calculatedLineHeight = contentBlock.calculatedLineHeight; |
| for (var i = 0; i < textLines.length; i++) { |
| var el = this._getOrCreateChild(TSpan); |
| var subElStyle = el.createStyle(); |
| el.useStyle(subElStyle); |
| subElStyle.text = textLines[i]; |
| subElStyle.x = textX; |
| subElStyle.y = textY; |
| if (textAlign) { |
| subElStyle.textAlign = textAlign; |
| } |
| subElStyle.textBaseline = 'middle'; |
| subElStyle.opacity = style.opacity; |
| subElStyle.strokeFirst = true; |
| if (hasShadow) { |
| subElStyle.shadowBlur = style.textShadowBlur || 0; |
| subElStyle.shadowColor = style.textShadowColor || 'transparent'; |
| subElStyle.shadowOffsetX = style.textShadowOffsetX || 0; |
| subElStyle.shadowOffsetY = style.textShadowOffsetY || 0; |
| } |
| subElStyle.stroke = textStroke; |
| subElStyle.fill = textFill; |
| if (textStroke) { |
| subElStyle.lineWidth = style.lineWidth || defaultLineWidth; |
| subElStyle.lineDash = style.lineDash; |
| subElStyle.lineDashOffset = style.lineDashOffset || 0; |
| } |
| subElStyle.font = textFont; |
| setSeparateFont(subElStyle, style); |
| textY += lineHeight; |
| if (fixedBoundingRect) { |
| el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, style.width, subElStyle.textAlign), adjustTextY(subElStyle.y, calculatedLineHeight, subElStyle.textBaseline), contentWidth, calculatedLineHeight)); |
| } |
| } |
| }; |
| ZRText.prototype._updateRichTexts = function () { |
| var style = this.style; |
| var text = getStyleText(style); |
| var contentBlock = parseRichText(text, style); |
| var contentWidth = contentBlock.width; |
| var outerWidth = contentBlock.outerWidth; |
| var outerHeight = contentBlock.outerHeight; |
| var textPadding = style.padding; |
| var baseX = style.x || 0; |
| var baseY = style.y || 0; |
| var defaultStyle = this._defaultStyle; |
| var textAlign = style.align || defaultStyle.align; |
| var verticalAlign = style.verticalAlign || defaultStyle.verticalAlign; |
| var boxX = adjustTextX(baseX, outerWidth, textAlign); |
| var boxY = adjustTextY(baseY, outerHeight, verticalAlign); |
| var xLeft = boxX; |
| var lineTop = boxY; |
| if (textPadding) { |
| xLeft += textPadding[3]; |
| lineTop += textPadding[0]; |
| } |
| var xRight = xLeft + contentWidth; |
| if (needDrawBackground(style)) { |
| this._renderBackground(style, style, boxX, boxY, outerWidth, outerHeight); |
| } |
| var bgColorDrawn = !!(style.backgroundColor); |
| for (var i = 0; i < contentBlock.lines.length; i++) { |
| var line = contentBlock.lines[i]; |
| var tokens = line.tokens; |
| var tokenCount = tokens.length; |
| var lineHeight = line.lineHeight; |
| var remainedWidth = line.width; |
| var leftIndex = 0; |
| var lineXLeft = xLeft; |
| var lineXRight = xRight; |
| var rightIndex = tokenCount - 1; |
| var token = void 0; |
| while (leftIndex < tokenCount |
| && (token = tokens[leftIndex], !token.align || token.align === 'left')) { |
| this._placeToken(token, style, lineHeight, lineTop, lineXLeft, 'left', bgColorDrawn); |
| remainedWidth -= token.width; |
| lineXLeft += token.width; |
| leftIndex++; |
| } |
| while (rightIndex >= 0 |
| && (token = tokens[rightIndex], token.align === 'right')) { |
| this._placeToken(token, style, lineHeight, lineTop, lineXRight, 'right', bgColorDrawn); |
| remainedWidth -= token.width; |
| lineXRight -= token.width; |
| rightIndex--; |
| } |
| lineXLeft += (contentWidth - (lineXLeft - xLeft) - (xRight - lineXRight) - remainedWidth) / 2; |
| while (leftIndex <= rightIndex) { |
| token = tokens[leftIndex]; |
| this._placeToken(token, style, lineHeight, lineTop, lineXLeft + token.width / 2, 'center', bgColorDrawn); |
| lineXLeft += token.width; |
| leftIndex++; |
| } |
| lineTop += lineHeight; |
| } |
| }; |
| ZRText.prototype._placeToken = function (token, style, lineHeight, lineTop, x, textAlign, parentBgColorDrawn) { |
| var tokenStyle = style.rich[token.styleName] || {}; |
| tokenStyle.text = token.text; |
| var verticalAlign = token.verticalAlign; |
| var y = lineTop + lineHeight / 2; |
| if (verticalAlign === 'top') { |
| y = lineTop + token.height / 2; |
| } |
| else if (verticalAlign === 'bottom') { |
| y = lineTop + lineHeight - token.height / 2; |
| } |
| var needDrawBg = !token.isLineHolder && needDrawBackground(tokenStyle); |
| needDrawBg && this._renderBackground(tokenStyle, style, textAlign === 'right' |
| ? x - token.width |
| : textAlign === 'center' |
| ? x - token.width / 2 |
| : x, y - token.height / 2, token.width, token.height); |
| var bgColorDrawn = !!tokenStyle.backgroundColor; |
| var textPadding = token.textPadding; |
| if (textPadding) { |
| x = getTextXForPadding(x, textAlign, textPadding); |
| y -= token.height / 2 - textPadding[0] - token.innerHeight / 2; |
| } |
| var el = this._getOrCreateChild(TSpan); |
| var subElStyle = el.createStyle(); |
| el.useStyle(subElStyle); |
| var defaultStyle = this._defaultStyle; |
| var useDefaultFill = false; |
| var defaultLineWidth = 0; |
| var textFill = getFill('fill' in tokenStyle ? tokenStyle.fill |
| : 'fill' in style ? style.fill |
| : (useDefaultFill = true, defaultStyle.fill)); |
| var textStroke = getStroke('stroke' in tokenStyle ? tokenStyle.stroke |
| : 'stroke' in style ? style.stroke |
| : (!bgColorDrawn |
| && !parentBgColorDrawn |
| && (!defaultStyle.autoStroke || useDefaultFill)) ? (defaultLineWidth = DEFAULT_STROKE_LINE_WIDTH, defaultStyle.stroke) |
| : null); |
| var hasShadow = tokenStyle.textShadowBlur > 0 |
| || style.textShadowBlur > 0; |
| subElStyle.text = token.text; |
| subElStyle.x = x; |
| subElStyle.y = y; |
| if (hasShadow) { |
| subElStyle.shadowBlur = tokenStyle.textShadowBlur || style.textShadowBlur || 0; |
| subElStyle.shadowColor = tokenStyle.textShadowColor || style.textShadowColor || 'transparent'; |
| subElStyle.shadowOffsetX = tokenStyle.textShadowOffsetX || style.textShadowOffsetX || 0; |
| subElStyle.shadowOffsetY = tokenStyle.textShadowOffsetY || style.textShadowOffsetY || 0; |
| } |
| subElStyle.textAlign = textAlign; |
| subElStyle.textBaseline = 'middle'; |
| subElStyle.font = token.font || DEFAULT_FONT; |
| subElStyle.opacity = retrieve3(tokenStyle.opacity, style.opacity, 1); |
| setSeparateFont(subElStyle, tokenStyle); |
| if (textStroke) { |
| subElStyle.lineWidth = retrieve3(tokenStyle.lineWidth, style.lineWidth, defaultLineWidth); |
| subElStyle.lineDash = retrieve2(tokenStyle.lineDash, style.lineDash); |
| subElStyle.lineDashOffset = style.lineDashOffset || 0; |
| subElStyle.stroke = textStroke; |
| } |
| if (textFill) { |
| subElStyle.fill = textFill; |
| } |
| var textWidth = token.contentWidth; |
| var textHeight = token.contentHeight; |
| el.setBoundingRect(new BoundingRect(adjustTextX(subElStyle.x, textWidth, subElStyle.textAlign), adjustTextY(subElStyle.y, textHeight, subElStyle.textBaseline), textWidth, textHeight)); |
| }; |
| ZRText.prototype._renderBackground = function (style, topStyle, x, y, width, height) { |
| var textBackgroundColor = style.backgroundColor; |
| var textBorderWidth = style.borderWidth; |
| var textBorderColor = style.borderColor; |
| var isImageBg = textBackgroundColor && textBackgroundColor.image; |
| var isPlainOrGradientBg = textBackgroundColor && !isImageBg; |
| var textBorderRadius = style.borderRadius; |
| var self = this; |
| var rectEl; |
| var imgEl; |
| if (isPlainOrGradientBg || style.lineHeight || (textBorderWidth && textBorderColor)) { |
| rectEl = this._getOrCreateChild(Rect); |
| rectEl.useStyle(rectEl.createStyle()); |
| rectEl.style.fill = null; |
| var rectShape = rectEl.shape; |
| rectShape.x = x; |
| rectShape.y = y; |
| rectShape.width = width; |
| rectShape.height = height; |
| rectShape.r = textBorderRadius; |
| rectEl.dirtyShape(); |
| } |
| if (isPlainOrGradientBg) { |
| var rectStyle = rectEl.style; |
| rectStyle.fill = textBackgroundColor || null; |
| rectStyle.fillOpacity = retrieve2(style.fillOpacity, 1); |
| } |
| else if (isImageBg) { |
| imgEl = this._getOrCreateChild(ZRImage); |
| imgEl.onload = function () { |
| self.dirtyStyle(); |
| }; |
| var imgStyle = imgEl.style; |
| imgStyle.image = textBackgroundColor.image; |
| imgStyle.x = x; |
| imgStyle.y = y; |
| imgStyle.width = width; |
| imgStyle.height = height; |
| } |
| if (textBorderWidth && textBorderColor) { |
| var rectStyle = rectEl.style; |
| rectStyle.lineWidth = textBorderWidth; |
| rectStyle.stroke = textBorderColor; |
| rectStyle.strokeOpacity = retrieve2(style.strokeOpacity, 1); |
| rectStyle.lineDash = style.borderDash; |
| rectStyle.lineDashOffset = style.borderDashOffset || 0; |
| rectEl.strokeContainThreshold = 0; |
| if (rectEl.hasFill() && rectEl.hasStroke()) { |
| rectStyle.strokeFirst = true; |
| rectStyle.lineWidth *= 2; |
| } |
| } |
| var commonStyle = (rectEl || imgEl).style; |
| commonStyle.shadowBlur = style.shadowBlur || 0; |
| commonStyle.shadowColor = style.shadowColor || 'transparent'; |
| commonStyle.shadowOffsetX = style.shadowOffsetX || 0; |
| commonStyle.shadowOffsetY = style.shadowOffsetY || 0; |
| commonStyle.opacity = retrieve3(style.opacity, topStyle.opacity, 1); |
| }; |
| ZRText.makeFont = function (style) { |
| var font = ''; |
| if (hasSeparateFont(style)) { |
| font = [ |
| style.fontStyle, |
| style.fontWeight, |
| parseFontSize(style.fontSize), |
| style.fontFamily || 'sans-serif' |
| ].join(' '); |
| } |
| return font && trim(font) || style.textFont || style.font; |
| }; |
| return ZRText; |
| }(Displayable)); |
| var VALID_TEXT_ALIGN = { left: true, right: 1, center: 1 }; |
| var VALID_TEXT_VERTICAL_ALIGN = { top: 1, bottom: 1, middle: 1 }; |
| var FONT_PARTS = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily']; |
| function parseFontSize(fontSize) { |
| if (typeof fontSize === 'string' |
| && (fontSize.indexOf('px') !== -1 |
| || fontSize.indexOf('rem') !== -1 |
| || fontSize.indexOf('em') !== -1)) { |
| return fontSize; |
| } |
| else if (!isNaN(+fontSize)) { |
| return fontSize + 'px'; |
| } |
| else { |
| return DEFAULT_FONT_SIZE + 'px'; |
| } |
| } |
| function setSeparateFont(targetStyle, sourceStyle) { |
| for (var i = 0; i < FONT_PARTS.length; i++) { |
| var fontProp = FONT_PARTS[i]; |
| var val = sourceStyle[fontProp]; |
| if (val != null) { |
| targetStyle[fontProp] = val; |
| } |
| } |
| } |
| function hasSeparateFont(style) { |
| return style.fontSize != null || style.fontFamily || style.fontWeight; |
| } |
| function normalizeTextStyle(style) { |
| normalizeStyle(style); |
| each(style.rich, normalizeStyle); |
| return style; |
| } |
| function normalizeStyle(style) { |
| if (style) { |
| style.font = ZRText.makeFont(style); |
| var textAlign = style.align; |
| textAlign === 'middle' && (textAlign = 'center'); |
| style.align = (textAlign == null || VALID_TEXT_ALIGN[textAlign]) ? textAlign : 'left'; |
| var verticalAlign = style.verticalAlign; |
| verticalAlign === 'center' && (verticalAlign = 'middle'); |
| style.verticalAlign = (verticalAlign == null || VALID_TEXT_VERTICAL_ALIGN[verticalAlign]) ? verticalAlign : 'top'; |
| var textPadding = style.padding; |
| if (textPadding) { |
| style.padding = normalizeCssArray(style.padding); |
| } |
| } |
| } |
| function getStroke(stroke, lineWidth) { |
| return (stroke == null || lineWidth <= 0 || stroke === 'transparent' || stroke === 'none') |
| ? null |
| : (stroke.image || stroke.colorStops) |
| ? '#000' |
| : stroke; |
| } |
| function getFill(fill) { |
| return (fill == null || fill === 'none') |
| ? null |
| : (fill.image || fill.colorStops) |
| ? '#000' |
| : fill; |
| } |
| function getTextXForPadding(x, textAlign, textPadding) { |
| return textAlign === 'right' |
| ? (x - textPadding[1]) |
| : textAlign === 'center' |
| ? (x + textPadding[3] / 2 - textPadding[1] / 2) |
| : (x + textPadding[3]); |
| } |
| function getStyleText(style) { |
| var text = style.text; |
| text != null && (text += ''); |
| return text; |
| } |
| function needDrawBackground(style) { |
| return !!(style.backgroundColor |
| || style.lineHeight |
| || (style.borderWidth && style.borderColor)); |
| } |
| |
| var getECData = makeInner(); |
| var setCommonECData = function (seriesIndex, dataType, dataIdx, el) { |
| if (el) { |
| var ecData = getECData(el); // Add data index and series index for indexing the data by element |
| // Useful in tooltip |
| |
| ecData.dataIndex = dataIdx; |
| ecData.dataType = dataType; |
| ecData.seriesIndex = seriesIndex; // TODO: not store dataIndex on children. |
| |
| if (el.type === 'group') { |
| el.traverse(function (child) { |
| var childECData = getECData(child); |
| childECData.seriesIndex = seriesIndex; |
| childECData.dataIndex = dataIdx; |
| childECData.dataType = dataType; |
| }); |
| } |
| } |
| }; |
| |
| var _highlightNextDigit = 1; |
| var _highlightKeyMap = {}; |
| var getSavedStates = makeInner(); |
| var getComponentStates = makeInner(); |
| var HOVER_STATE_NORMAL = 0; |
| var HOVER_STATE_BLUR = 1; |
| var HOVER_STATE_EMPHASIS = 2; |
| var SPECIAL_STATES = ['emphasis', 'blur', 'select']; |
| var DISPLAY_STATES = ['normal', 'emphasis', 'blur', 'select']; |
| var Z2_EMPHASIS_LIFT = 10; |
| var Z2_SELECT_LIFT = 9; |
| var HIGHLIGHT_ACTION_TYPE = 'highlight'; |
| var DOWNPLAY_ACTION_TYPE = 'downplay'; |
| var SELECT_ACTION_TYPE = 'select'; |
| var UNSELECT_ACTION_TYPE = 'unselect'; |
| var TOGGLE_SELECT_ACTION_TYPE = 'toggleSelect'; |
| |
| function hasFillOrStroke(fillOrStroke) { |
| return fillOrStroke != null && fillOrStroke !== 'none'; |
| } // Most lifted color are duplicated. |
| |
| |
| var liftedColorCache = new LRU(100); |
| |
| function liftColor(color$1) { |
| if (isString(color$1)) { |
| var liftedColor = liftedColorCache.get(color$1); |
| |
| if (!liftedColor) { |
| liftedColor = lift(color$1, -0.1); |
| liftedColorCache.put(color$1, liftedColor); |
| } |
| |
| return liftedColor; |
| } else if (isGradientObject(color$1)) { |
| var ret = extend({}, color$1); |
| ret.colorStops = map(color$1.colorStops, function (stop) { |
| return { |
| offset: stop.offset, |
| color: lift(stop.color, -0.1) |
| }; |
| }); |
| return ret; |
| } // Change nothing. |
| |
| |
| return color$1; |
| } |
| |
| function doChangeHoverState(el, stateName, hoverStateEnum) { |
| if (el.onHoverStateChange && (el.hoverState || 0) !== hoverStateEnum) { |
| el.onHoverStateChange(stateName); |
| } |
| |
| el.hoverState = hoverStateEnum; |
| } |
| |
| function singleEnterEmphasis(el) { |
| // Only mark the flag. |
| // States will be applied in the echarts.ts in next frame. |
| doChangeHoverState(el, 'emphasis', HOVER_STATE_EMPHASIS); |
| } |
| |
| function singleLeaveEmphasis(el) { |
| // Only mark the flag. |
| // States will be applied in the echarts.ts in next frame. |
| if (el.hoverState === HOVER_STATE_EMPHASIS) { |
| doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL); |
| } |
| } |
| |
| function singleEnterBlur(el) { |
| doChangeHoverState(el, 'blur', HOVER_STATE_BLUR); |
| } |
| |
| function singleLeaveBlur(el) { |
| if (el.hoverState === HOVER_STATE_BLUR) { |
| doChangeHoverState(el, 'normal', HOVER_STATE_NORMAL); |
| } |
| } |
| |
| function singleEnterSelect(el) { |
| el.selected = true; |
| } |
| |
| function singleLeaveSelect(el) { |
| el.selected = false; |
| } |
| |
| function updateElementState(el, updater, commonParam) { |
| updater(el, commonParam); |
| } |
| |
| function traverseUpdateState(el, updater, commonParam) { |
| updateElementState(el, updater, commonParam); |
| el.isGroup && el.traverse(function (child) { |
| updateElementState(child, updater, commonParam); |
| }); |
| } |
| |
| function setStatesFlag(el, stateName) { |
| switch (stateName) { |
| case 'emphasis': |
| el.hoverState = HOVER_STATE_EMPHASIS; |
| break; |
| |
| case 'normal': |
| el.hoverState = HOVER_STATE_NORMAL; |
| break; |
| |
| case 'blur': |
| el.hoverState = HOVER_STATE_BLUR; |
| break; |
| |
| case 'select': |
| el.selected = true; |
| } |
| } |
| |
| function getFromStateStyle(el, props, toStateName, defaultValue) { |
| var style = el.style; |
| var fromState = {}; |
| |
| for (var i = 0; i < props.length; i++) { |
| var propName = props[i]; |
| var val = style[propName]; |
| fromState[propName] = val == null ? defaultValue && defaultValue[propName] : val; |
| } |
| |
| for (var i = 0; i < el.animators.length; i++) { |
| var animator = el.animators[i]; |
| |
| if (animator.__fromStateTransition // Don't consider the animation to emphasis state. |
| && animator.__fromStateTransition.indexOf(toStateName) < 0 && animator.targetName === 'style') { |
| animator.saveTo(fromState, props); |
| } |
| } |
| |
| return fromState; |
| } |
| |
| function createEmphasisDefaultState(el, stateName, targetStates, state) { |
| var hasSelect = targetStates && indexOf(targetStates, 'select') >= 0; |
| var cloned = false; |
| |
| if (el instanceof Path) { |
| var store = getSavedStates(el); |
| var fromFill = hasSelect ? store.selectFill || store.normalFill : store.normalFill; |
| var fromStroke = hasSelect ? store.selectStroke || store.normalStroke : store.normalStroke; |
| |
| if (hasFillOrStroke(fromFill) || hasFillOrStroke(fromStroke)) { |
| state = state || {}; |
| var emphasisStyle = state.style || {}; // inherit case |
| |
| if (emphasisStyle.fill === 'inherit') { |
| cloned = true; |
| state = extend({}, state); |
| emphasisStyle = extend({}, emphasisStyle); |
| emphasisStyle.fill = fromFill; |
| } // Apply default color lift |
| else if (!hasFillOrStroke(emphasisStyle.fill) && hasFillOrStroke(fromFill)) { |
| cloned = true; // Not modify the original value. |
| |
| state = extend({}, state); |
| emphasisStyle = extend({}, emphasisStyle); // Already being applied 'emphasis'. DON'T lift color multiple times. |
| |
| emphasisStyle.fill = liftColor(fromFill); |
| } // Not highlight stroke if fill has been highlighted. |
| else if (!hasFillOrStroke(emphasisStyle.stroke) && hasFillOrStroke(fromStroke)) { |
| if (!cloned) { |
| state = extend({}, state); |
| emphasisStyle = extend({}, emphasisStyle); |
| } |
| |
| emphasisStyle.stroke = liftColor(fromStroke); |
| } |
| |
| state.style = emphasisStyle; |
| } |
| } |
| |
| if (state) { |
| // TODO Share with textContent? |
| if (state.z2 == null) { |
| if (!cloned) { |
| state = extend({}, state); |
| } |
| |
| var z2EmphasisLift = el.z2EmphasisLift; |
| state.z2 = el.z2 + (z2EmphasisLift != null ? z2EmphasisLift : Z2_EMPHASIS_LIFT); |
| } |
| } |
| |
| return state; |
| } |
| |
| function createSelectDefaultState(el, stateName, state) { |
| // const hasSelect = indexOf(el.currentStates, stateName) >= 0; |
| if (state) { |
| // TODO Share with textContent? |
| if (state.z2 == null) { |
| state = extend({}, state); |
| var z2SelectLift = el.z2SelectLift; |
| state.z2 = el.z2 + (z2SelectLift != null ? z2SelectLift : Z2_SELECT_LIFT); |
| } |
| } |
| |
| return state; |
| } |
| |
| function createBlurDefaultState(el, stateName, state) { |
| var hasBlur = indexOf(el.currentStates, stateName) >= 0; |
| var currentOpacity = el.style.opacity; |
| var fromState = !hasBlur ? getFromStateStyle(el, ['opacity'], stateName, { |
| opacity: 1 |
| }) : null; |
| state = state || {}; |
| var blurStyle = state.style || {}; |
| |
| if (blurStyle.opacity == null) { |
| // clone state |
| state = extend({}, state); |
| blurStyle = extend({ |
| // Already being applied 'emphasis'. DON'T mul opacity multiple times. |
| opacity: hasBlur ? currentOpacity : fromState.opacity * 0.1 |
| }, blurStyle); |
| state.style = blurStyle; |
| } |
| |
| return state; |
| } |
| |
| function elementStateProxy(stateName, targetStates) { |
| var state = this.states[stateName]; |
| |
| if (this.style) { |
| if (stateName === 'emphasis') { |
| return createEmphasisDefaultState(this, stateName, targetStates, state); |
| } else if (stateName === 'blur') { |
| return createBlurDefaultState(this, stateName, state); |
| } else if (stateName === 'select') { |
| return createSelectDefaultState(this, stateName, state); |
| } |
| } |
| |
| return state; |
| } |
| /** |
| * Set hover style (namely "emphasis style") of element. |
| * @param el Should not be `zrender/graphic/Group`. |
| * @param focus 'self' | 'selfInSeries' | 'series' |
| */ |
| |
| |
| function setDefaultStateProxy(el) { |
| el.stateProxy = elementStateProxy; |
| var textContent = el.getTextContent(); |
| var textGuide = el.getTextGuideLine(); |
| |
| if (textContent) { |
| textContent.stateProxy = elementStateProxy; |
| } |
| |
| if (textGuide) { |
| textGuide.stateProxy = elementStateProxy; |
| } |
| } |
| function enterEmphasisWhenMouseOver(el, e) { |
| !shouldSilent(el, e) // "emphasis" event highlight has higher priority than mouse highlight. |
| && !el.__highByOuter && traverseUpdateState(el, singleEnterEmphasis); |
| } |
| function leaveEmphasisWhenMouseOut(el, e) { |
| !shouldSilent(el, e) // "emphasis" event highlight has higher priority than mouse highlight. |
| && !el.__highByOuter && traverseUpdateState(el, singleLeaveEmphasis); |
| } |
| function enterEmphasis(el, highlightDigit) { |
| el.__highByOuter |= 1 << (highlightDigit || 0); |
| traverseUpdateState(el, singleEnterEmphasis); |
| } |
| function leaveEmphasis(el, highlightDigit) { |
| !(el.__highByOuter &= ~(1 << (highlightDigit || 0))) && traverseUpdateState(el, singleLeaveEmphasis); |
| } |
| function enterBlur(el) { |
| traverseUpdateState(el, singleEnterBlur); |
| } |
| function leaveBlur(el) { |
| traverseUpdateState(el, singleLeaveBlur); |
| } |
| function enterSelect(el) { |
| traverseUpdateState(el, singleEnterSelect); |
| } |
| function leaveSelect(el) { |
| traverseUpdateState(el, singleLeaveSelect); |
| } |
| |
| function shouldSilent(el, e) { |
| return el.__highDownSilentOnTouch && e.zrByTouch; |
| } |
| |
| function allLeaveBlur(api) { |
| var model = api.getModel(); |
| var leaveBlurredSeries = []; |
| var allComponentViews = []; |
| model.eachComponent(function (componentType, componentModel) { |
| var componentStates = getComponentStates(componentModel); |
| var isSeries = componentType === 'series'; |
| var view = isSeries ? api.getViewOfSeriesModel(componentModel) : api.getViewOfComponentModel(componentModel); |
| !isSeries && allComponentViews.push(view); |
| |
| if (componentStates.isBlured) { |
| // Leave blur anyway |
| view.group.traverse(function (child) { |
| singleLeaveBlur(child); |
| }); |
| isSeries && leaveBlurredSeries.push(componentModel); |
| } |
| |
| componentStates.isBlured = false; |
| }); |
| each(allComponentViews, function (view) { |
| if (view && view.toggleBlurSeries) { |
| view.toggleBlurSeries(leaveBlurredSeries, false, model); |
| } |
| }); |
| } |
| function blurSeries(targetSeriesIndex, focus, blurScope, api) { |
| var ecModel = api.getModel(); |
| blurScope = blurScope || 'coordinateSystem'; |
| |
| function leaveBlurOfIndices(data, dataIndices) { |
| for (var i = 0; i < dataIndices.length; i++) { |
| var itemEl = data.getItemGraphicEl(dataIndices[i]); |
| itemEl && leaveBlur(itemEl); |
| } |
| } |
| |
| if (targetSeriesIndex == null) { |
| return; |
| } |
| |
| if (!focus || focus === 'none') { |
| return; |
| } |
| |
| var targetSeriesModel = ecModel.getSeriesByIndex(targetSeriesIndex); |
| var targetCoordSys = targetSeriesModel.coordinateSystem; |
| |
| if (targetCoordSys && targetCoordSys.master) { |
| targetCoordSys = targetCoordSys.master; |
| } |
| |
| var blurredSeries = []; |
| ecModel.eachSeries(function (seriesModel) { |
| var sameSeries = targetSeriesModel === seriesModel; |
| var coordSys = seriesModel.coordinateSystem; |
| |
| if (coordSys && coordSys.master) { |
| coordSys = coordSys.master; |
| } |
| |
| var sameCoordSys = coordSys && targetCoordSys ? coordSys === targetCoordSys : sameSeries; // If there is no coordinate system. use sameSeries instead. |
| |
| if (!( // Not blur other series if blurScope series |
| blurScope === 'series' && !sameSeries // Not blur other coordinate system if blurScope is coordinateSystem |
| || blurScope === 'coordinateSystem' && !sameCoordSys // Not blur self series if focus is series. |
| || focus === 'series' && sameSeries // TODO blurScope: coordinate system |
| )) { |
| var view = api.getViewOfSeriesModel(seriesModel); |
| view.group.traverse(function (child) { |
| singleEnterBlur(child); |
| }); |
| |
| if (isArrayLike(focus)) { |
| leaveBlurOfIndices(seriesModel.getData(), focus); |
| } else if (isObject(focus)) { |
| var dataTypes = keys(focus); |
| |
| for (var d = 0; d < dataTypes.length; d++) { |
| leaveBlurOfIndices(seriesModel.getData(dataTypes[d]), focus[dataTypes[d]]); |
| } |
| } |
| |
| blurredSeries.push(seriesModel); |
| getComponentStates(seriesModel).isBlured = true; |
| } |
| }); |
| ecModel.eachComponent(function (componentType, componentModel) { |
| if (componentType === 'series') { |
| return; |
| } |
| |
| var view = api.getViewOfComponentModel(componentModel); |
| |
| if (view && view.toggleBlurSeries) { |
| view.toggleBlurSeries(blurredSeries, true, ecModel); |
| } |
| }); |
| } |
| function blurComponent(componentMainType, componentIndex, api) { |
| if (componentMainType == null || componentIndex == null) { |
| return; |
| } |
| |
| var componentModel = api.getModel().getComponent(componentMainType, componentIndex); |
| |
| if (!componentModel) { |
| return; |
| } |
| |
| getComponentStates(componentModel).isBlured = true; |
| var view = api.getViewOfComponentModel(componentModel); |
| |
| if (!view || !view.focusBlurEnabled) { |
| return; |
| } |
| |
| view.group.traverse(function (child) { |
| singleEnterBlur(child); |
| }); |
| } |
| function blurSeriesFromHighlightPayload(seriesModel, payload, api) { |
| var seriesIndex = seriesModel.seriesIndex; |
| var data = seriesModel.getData(payload.dataType); |
| |
| if (!data) { |
| if ("development" !== 'production') { |
| error("Unknown dataType " + payload.dataType); |
| } |
| |
| return; |
| } |
| |
| var dataIndex = queryDataIndex(data, payload); // Pick the first one if there is multiple/none exists. |
| |
| dataIndex = (isArray(dataIndex) ? dataIndex[0] : dataIndex) || 0; |
| var el = data.getItemGraphicEl(dataIndex); |
| |
| if (!el) { |
| var count = data.count(); |
| var current = 0; // If data on dataIndex is NaN. |
| |
| while (!el && current < count) { |
| el = data.getItemGraphicEl(current++); |
| } |
| } |
| |
| if (el) { |
| var ecData = getECData(el); |
| blurSeries(seriesIndex, ecData.focus, ecData.blurScope, api); |
| } else { |
| // If there is no element put on the data. Try getting it from raw option |
| // TODO Should put it on seriesModel? |
| var focus_1 = seriesModel.get(['emphasis', 'focus']); |
| var blurScope = seriesModel.get(['emphasis', 'blurScope']); |
| |
| if (focus_1 != null) { |
| blurSeries(seriesIndex, focus_1, blurScope, api); |
| } |
| } |
| } |
| function findComponentHighDownDispatchers(componentMainType, componentIndex, name, api) { |
| var ret = { |
| focusSelf: false, |
| dispatchers: null |
| }; |
| |
| if (componentMainType == null || componentMainType === 'series' || componentIndex == null || name == null) { |
| return ret; |
| } |
| |
| var componentModel = api.getModel().getComponent(componentMainType, componentIndex); |
| |
| if (!componentModel) { |
| return ret; |
| } |
| |
| var view = api.getViewOfComponentModel(componentModel); |
| |
| if (!view || !view.findHighDownDispatchers) { |
| return ret; |
| } |
| |
| var dispatchers = view.findHighDownDispatchers(name); // At presnet, the component (like Geo) only blur inside itself. |
| // So we do not use `blurScope` in component. |
| |
| var focusSelf; |
| |
| for (var i = 0; i < dispatchers.length; i++) { |
| if ("development" !== 'production' && !isHighDownDispatcher(dispatchers[i])) { |
| error('param should be highDownDispatcher'); |
| } |
| |
| if (getECData(dispatchers[i]).focus === 'self') { |
| focusSelf = true; |
| break; |
| } |
| } |
| |
| return { |
| focusSelf: focusSelf, |
| dispatchers: dispatchers |
| }; |
| } |
| function handleGlobalMouseOverForHighDown(dispatcher, e, api) { |
| if ("development" !== 'production' && !isHighDownDispatcher(dispatcher)) { |
| error('param should be highDownDispatcher'); |
| } |
| |
| var ecData = getECData(dispatcher); |
| |
| var _a = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api), |
| dispatchers = _a.dispatchers, |
| focusSelf = _a.focusSelf; // If `findHighDownDispatchers` is supported on the component, |
| // highlight/downplay elements with the same name. |
| |
| |
| if (dispatchers) { |
| if (focusSelf) { |
| blurComponent(ecData.componentMainType, ecData.componentIndex, api); |
| } |
| |
| each(dispatchers, function (dispatcher) { |
| return enterEmphasisWhenMouseOver(dispatcher, e); |
| }); |
| } else { |
| // Try blur all in the related series. Then emphasis the hoverred. |
| // TODO. progressive mode. |
| blurSeries(ecData.seriesIndex, ecData.focus, ecData.blurScope, api); |
| |
| if (ecData.focus === 'self') { |
| blurComponent(ecData.componentMainType, ecData.componentIndex, api); |
| } // Other than series, component that not support `findHighDownDispatcher` will |
| // also use it. But in this case, highlight/downplay are only supported in |
| // mouse hover but not in dispatchAction. |
| |
| |
| enterEmphasisWhenMouseOver(dispatcher, e); |
| } |
| } |
| function handleGlobalMouseOutForHighDown(dispatcher, e, api) { |
| if ("development" !== 'production' && !isHighDownDispatcher(dispatcher)) { |
| error('param should be highDownDispatcher'); |
| } |
| |
| allLeaveBlur(api); |
| var ecData = getECData(dispatcher); |
| var dispatchers = findComponentHighDownDispatchers(ecData.componentMainType, ecData.componentIndex, ecData.componentHighDownName, api).dispatchers; |
| |
| if (dispatchers) { |
| each(dispatchers, function (dispatcher) { |
| return leaveEmphasisWhenMouseOut(dispatcher, e); |
| }); |
| } else { |
| leaveEmphasisWhenMouseOut(dispatcher, e); |
| } |
| } |
| function toggleSelectionFromPayload(seriesModel, payload, api) { |
| if (!isSelectChangePayload(payload)) { |
| return; |
| } |
| |
| var dataType = payload.dataType; |
| var data = seriesModel.getData(dataType); |
| var dataIndex = queryDataIndex(data, payload); |
| |
| if (!isArray(dataIndex)) { |
| dataIndex = [dataIndex]; |
| } |
| |
| seriesModel[payload.type === TOGGLE_SELECT_ACTION_TYPE ? 'toggleSelect' : payload.type === SELECT_ACTION_TYPE ? 'select' : 'unselect'](dataIndex, dataType); |
| } |
| function updateSeriesElementSelection(seriesModel) { |
| var allData = seriesModel.getAllData(); |
| each(allData, function (_a) { |
| var data = _a.data, |
| type = _a.type; |
| data.eachItemGraphicEl(function (el, idx) { |
| seriesModel.isSelected(idx, type) ? enterSelect(el) : leaveSelect(el); |
| }); |
| }); |
| } |
| function getAllSelectedIndices(ecModel) { |
| var ret = []; |
| ecModel.eachSeries(function (seriesModel) { |
| var allData = seriesModel.getAllData(); |
| each(allData, function (_a) { |
| var data = _a.data, |
| type = _a.type; |
| var dataIndices = seriesModel.getSelectedDataIndices(); |
| |
| if (dataIndices.length > 0) { |
| var item = { |
| dataIndex: dataIndices, |
| seriesIndex: seriesModel.seriesIndex |
| }; |
| |
| if (type != null) { |
| item.dataType = type; |
| } |
| |
| ret.push(item); |
| } |
| }); |
| }); |
| return ret; |
| } |
| /** |
| * Enable the function that mouseover will trigger the emphasis state. |
| * |
| * NOTE: |
| * This function should be used on the element with dataIndex, seriesIndex. |
| * |
| */ |
| |
| function enableHoverEmphasis(el, focus, blurScope) { |
| setAsHighDownDispatcher(el, true); |
| traverseUpdateState(el, setDefaultStateProxy); |
| enableHoverFocus(el, focus, blurScope); |
| } |
| function disableHoverEmphasis(el) { |
| setAsHighDownDispatcher(el, false); |
| } |
| function toggleHoverEmphasis(el, focus, blurScope, isDisabled) { |
| isDisabled ? disableHoverEmphasis(el) : enableHoverEmphasis(el, focus, blurScope); |
| } |
| function enableHoverFocus(el, focus, blurScope) { |
| var ecData = getECData(el); |
| |
| if (focus != null) { |
| // TODO dataIndex may be set after this function. This check is not useful. |
| // if (ecData.dataIndex == null) { |
| // if (__DEV__) { |
| // console.warn('focus can only been set on element with dataIndex'); |
| // } |
| // } |
| // else { |
| ecData.focus = focus; |
| ecData.blurScope = blurScope; // } |
| } else if (ecData.focus) { |
| ecData.focus = null; |
| } |
| } |
| var OTHER_STATES = ['emphasis', 'blur', 'select']; |
| var defaultStyleGetterMap = { |
| itemStyle: 'getItemStyle', |
| lineStyle: 'getLineStyle', |
| areaStyle: 'getAreaStyle' |
| }; |
| /** |
| * Set emphasis/blur/selected states of element. |
| */ |
| |
| function setStatesStylesFromModel(el, itemModel, styleType, // default itemStyle |
| getter) { |
| styleType = styleType || 'itemStyle'; |
| |
| for (var i = 0; i < OTHER_STATES.length; i++) { |
| var stateName = OTHER_STATES[i]; |
| var model = itemModel.getModel([stateName, styleType]); |
| var state = el.ensureState(stateName); // Let it throw error if getterType is not found. |
| |
| state.style = getter ? getter(model) : model[defaultStyleGetterMap[styleType]](); |
| } |
| } |
| /** |
| * |
| * Set element as highlight / downplay dispatcher. |
| * It will be checked when element received mouseover event or from highlight action. |
| * It's in change of all highlight/downplay behavior of it's children. |
| * |
| * @param el |
| * @param el.highDownSilentOnTouch |
| * In touch device, mouseover event will be trigger on touchstart event |
| * (see module:zrender/dom/HandlerProxy). By this mechanism, we can |
| * conveniently use hoverStyle when tap on touch screen without additional |
| * code for compatibility. |
| * But if the chart/component has select feature, which usually also use |
| * hoverStyle, there might be conflict between 'select-highlight' and |
| * 'hover-highlight' especially when roam is enabled (see geo for example). |
| * In this case, `highDownSilentOnTouch` should be used to disable |
| * hover-highlight on touch device. |
| * @param asDispatcher If `false`, do not set as "highDownDispatcher". |
| */ |
| |
| function setAsHighDownDispatcher(el, asDispatcher) { |
| var disable = asDispatcher === false; |
| var extendedEl = el; // Make `highDownSilentOnTouch` and `onStateChange` only work after |
| // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly. |
| |
| if (el.highDownSilentOnTouch) { |
| extendedEl.__highDownSilentOnTouch = el.highDownSilentOnTouch; |
| } // Simple optimize, since this method might be |
| // called for each elements of a group in some cases. |
| |
| |
| if (!disable || extendedEl.__highDownDispatcher) { |
| // Emphasis, normal can be triggered manually by API or other components like hover link. |
| // el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent); |
| // Also keep previous record. |
| extendedEl.__highByOuter = extendedEl.__highByOuter || 0; |
| extendedEl.__highDownDispatcher = !disable; |
| } |
| } |
| function isHighDownDispatcher(el) { |
| return !!(el && el.__highDownDispatcher); |
| } |
| /** |
| * Support highlight/downplay record on each elements. |
| * For the case: hover highlight/downplay (legend, visualMap, ...) and |
| * user triggered highlight/downplay should not conflict. |
| * Only all of the highlightDigit cleared, return to normal. |
| * @param {string} highlightKey |
| * @return {number} highlightDigit |
| */ |
| |
| function getHighlightDigit(highlightKey) { |
| var highlightDigit = _highlightKeyMap[highlightKey]; |
| |
| if (highlightDigit == null && _highlightNextDigit <= 32) { |
| highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++; |
| } |
| |
| return highlightDigit; |
| } |
| function isSelectChangePayload(payload) { |
| var payloadType = payload.type; |
| return payloadType === SELECT_ACTION_TYPE || payloadType === UNSELECT_ACTION_TYPE || payloadType === TOGGLE_SELECT_ACTION_TYPE; |
| } |
| function isHighDownPayload(payload) { |
| var payloadType = payload.type; |
| return payloadType === HIGHLIGHT_ACTION_TYPE || payloadType === DOWNPLAY_ACTION_TYPE; |
| } |
| function savePathStates(el) { |
| var store = getSavedStates(el); |
| store.normalFill = el.style.fill; |
| store.normalStroke = el.style.stroke; |
| var selectState = el.states.select || {}; |
| store.selectFill = selectState.style && selectState.style.fill || null; |
| store.selectStroke = selectState.style && selectState.style.stroke || null; |
| } |
| |
| var CMD$2 = PathProxy.CMD; |
| var points = [[], [], []]; |
| var mathSqrt$1 = Math.sqrt; |
| var mathAtan2 = Math.atan2; |
| function transformPath(path, m) { |
| if (!m) { |
| return; |
| } |
| var data = path.data; |
| var len = path.len(); |
| 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; i < len;) { |
| cmd = data[i++]; |
| j = i; |
| nPoint = 0; |
| switch (cmd) { |
| case M: |
| nPoint = 1; |
| break; |
| case L: |
| nPoint = 1; |
| break; |
| case C: |
| nPoint = 3; |
| break; |
| case Q: |
| nPoint = 2; |
| break; |
| case A: |
| var x = m[4]; |
| var y = m[5]; |
| var sx = mathSqrt$1(m[0] * m[0] + m[1] * m[1]); |
| var sy = mathSqrt$1(m[2] * m[2] + m[3] * m[3]); |
| var angle = mathAtan2(-m[1] / sy, m[0] / sx); |
| data[i] *= sx; |
| data[i++] += x; |
| data[i] *= sy; |
| data[i++] += y; |
| data[i++] *= sx; |
| data[i++] *= sy; |
| data[i++] += angle; |
| data[i++] += angle; |
| i += 2; |
| j = i; |
| break; |
| case R: |
| p[0] = data[i++]; |
| p[1] = data[i++]; |
| applyTransform(p, p, m); |
| data[j++] = p[0]; |
| data[j++] = p[1]; |
| p[0] += data[i++]; |
| p[1] += data[i++]; |
| applyTransform(p, p, m); |
| data[j++] = p[0]; |
| data[j++] = p[1]; |
| } |
| for (k = 0; k < nPoint; k++) { |
| var p_1 = points[k]; |
| p_1[0] = data[i++]; |
| p_1[1] = data[i++]; |
| applyTransform(p_1, p_1, m); |
| data[j++] = p_1[0]; |
| data[j++] = p_1[1]; |
| } |
| } |
| path.increaseVersion(); |
| } |
| |
| var mathSqrt$2 = Math.sqrt; |
| var mathSin$2 = Math.sin; |
| var mathCos$2 = Math.cos; |
| var PI$1 = Math.PI; |
| function vMag(v) { |
| return Math.sqrt(v[0] * v[0] + v[1] * v[1]); |
| } |
| function vRatio(u, v) { |
| return (u[0] * v[0] + u[1] * v[1]) / (vMag(u) * vMag(v)); |
| } |
| function vAngle(u, v) { |
| return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) |
| * Math.acos(vRatio(u, v)); |
| } |
| function processArc(x1, y1, x2, y2, fa, fs, rx, ry, psiDeg, cmd, path) { |
| var psi = psiDeg * (PI$1 / 180.0); |
| var xp = mathCos$2(psi) * (x1 - x2) / 2.0 |
| + mathSin$2(psi) * (y1 - y2) / 2.0; |
| var yp = -1 * mathSin$2(psi) * (x1 - x2) / 2.0 |
| + mathCos$2(psi) * (y1 - y2) / 2.0; |
| var lambda = (xp * xp) / (rx * rx) + (yp * yp) / (ry * ry); |
| if (lambda > 1) { |
| rx *= mathSqrt$2(lambda); |
| ry *= mathSqrt$2(lambda); |
| } |
| var f = (fa === fs ? -1 : 1) |
| * mathSqrt$2((((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$2(psi) * cxp |
| - mathSin$2(psi) * cyp; |
| var cy = (y1 + y2) / 2.0 |
| + mathSin$2(psi) * cxp |
| + mathCos$2(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$1; |
| } |
| if (vRatio(u, v) >= 1) { |
| dTheta = 0; |
| } |
| if (dTheta < 0) { |
| var n = Math.round(dTheta / PI$1 * 1e6) / 1e6; |
| dTheta = PI$1 * 2 + (n % 2) * PI$1; |
| } |
| path.addData(cmd, cx, cy, rx, ry, theta, dTheta, psi, fs); |
| } |
| var commandReg = /([mlvhzcqtsa])([^mlvhzcqtsa]*)/ig; |
| var numberReg = /-?([0-9]*\.)?[0-9]+([eE]-?[0-9]+)?/g; |
| function createPathProxyFromString(data) { |
| var path = new PathProxy(); |
| if (!data) { |
| return path; |
| } |
| var cpx = 0; |
| var cpy = 0; |
| var subpathX = cpx; |
| var subpathY = cpy; |
| var prevCmd; |
| var CMD = PathProxy.CMD; |
| var cmdList = data.match(commandReg); |
| if (!cmdList) { |
| return path; |
| } |
| for (var l = 0; l < cmdList.length; l++) { |
| var cmdText = cmdList[l]; |
| var cmdStr = cmdText.charAt(0); |
| var cmd = void 0; |
| var p = cmdText.match(numberReg) || []; |
| var pLen = p.length; |
| for (var i = 0; i < pLen; i++) { |
| p[i] = parseFloat(p[i]); |
| } |
| var off = 0; |
| while (off < pLen) { |
| var ctlPtx = void 0; |
| var ctlPty = void 0; |
| var rx = void 0; |
| var ry = void 0; |
| var psi = void 0; |
| var fa = void 0; |
| var fs = void 0; |
| var x1 = cpx; |
| var y1 = cpy; |
| var len = void 0; |
| var pathData = void 0; |
| switch (cmdStr) { |
| case 'l': |
| cpx += p[off++]; |
| cpy += p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'L': |
| cpx = p[off++]; |
| cpy = p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'm': |
| cpx += p[off++]; |
| cpy += p[off++]; |
| cmd = CMD.M; |
| path.addData(cmd, cpx, cpy); |
| subpathX = cpx; |
| subpathY = cpy; |
| cmdStr = 'l'; |
| break; |
| case 'M': |
| cpx = p[off++]; |
| cpy = p[off++]; |
| cmd = CMD.M; |
| path.addData(cmd, cpx, cpy); |
| subpathX = cpx; |
| subpathY = cpy; |
| cmdStr = 'L'; |
| break; |
| case 'h': |
| cpx += p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'H': |
| cpx = p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'v': |
| cpy += p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'V': |
| cpy = p[off++]; |
| cmd = CMD.L; |
| path.addData(cmd, cpx, cpy); |
| break; |
| case 'C': |
| cmd = CMD.C; |
| path.addData(cmd, p[off++], p[off++], p[off++], p[off++], p[off++], p[off++]); |
| cpx = p[off - 2]; |
| cpy = p[off - 1]; |
| break; |
| case 'c': |
| cmd = CMD.C; |
| path.addData(cmd, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy, p[off++] + cpx, p[off++] + cpy); |
| cpx += p[off - 2]; |
| cpy += p[off - 1]; |
| break; |
| case 'S': |
| ctlPtx = cpx; |
| ctlPty = cpy; |
| len = path.len(); |
| pathData = path.data; |
| if (prevCmd === CMD.C) { |
| ctlPtx += cpx - pathData[len - 4]; |
| ctlPty += cpy - pathData[len - 3]; |
| } |
| cmd = CMD.C; |
| x1 = p[off++]; |
| y1 = p[off++]; |
| cpx = p[off++]; |
| cpy = p[off++]; |
| path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); |
| break; |
| case 's': |
| ctlPtx = cpx; |
| ctlPty = cpy; |
| len = path.len(); |
| pathData = path.data; |
| if (prevCmd === CMD.C) { |
| ctlPtx += cpx - pathData[len - 4]; |
| ctlPty += cpy - pathData[len - 3]; |
| } |
| cmd = CMD.C; |
| x1 = cpx + p[off++]; |
| y1 = cpy + p[off++]; |
| cpx += p[off++]; |
| cpy += p[off++]; |
| path.addData(cmd, ctlPtx, ctlPty, x1, y1, cpx, cpy); |
| break; |
| case 'Q': |
| x1 = p[off++]; |
| y1 = p[off++]; |
| cpx = p[off++]; |
| cpy = p[off++]; |
| cmd = CMD.Q; |
| path.addData(cmd, x1, y1, cpx, cpy); |
| break; |
| case 'q': |
| x1 = p[off++] + cpx; |
| y1 = p[off++] + cpy; |
| cpx += p[off++]; |
| cpy += p[off++]; |
| cmd = CMD.Q; |
| path.addData(cmd, x1, y1, cpx, cpy); |
| break; |
| case 'T': |
| ctlPtx = cpx; |
| ctlPty = cpy; |
| len = path.len(); |
| pathData = path.data; |
| if (prevCmd === CMD.Q) { |
| ctlPtx += cpx - pathData[len - 4]; |
| ctlPty += cpy - pathData[len - 3]; |
| } |
| cpx = p[off++]; |
| cpy = p[off++]; |
| cmd = CMD.Q; |
| path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); |
| break; |
| case 't': |
| ctlPtx = cpx; |
| ctlPty = cpy; |
| len = path.len(); |
| pathData = path.data; |
| if (prevCmd === CMD.Q) { |
| ctlPtx += cpx - pathData[len - 4]; |
| ctlPty += cpy - pathData[len - 3]; |
| } |
| cpx += p[off++]; |
| cpy += p[off++]; |
| cmd = CMD.Q; |
| path.addData(cmd, ctlPtx, ctlPty, cpx, cpy); |
| break; |
| case 'A': |
| rx = p[off++]; |
| ry = p[off++]; |
| psi = p[off++]; |
| fa = p[off++]; |
| fs = p[off++]; |
| x1 = cpx, y1 = cpy; |
| cpx = p[off++]; |
| cpy = p[off++]; |
| cmd = CMD.A; |
| processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path); |
| break; |
| case 'a': |
| rx = p[off++]; |
| ry = p[off++]; |
| psi = p[off++]; |
| fa = p[off++]; |
| fs = p[off++]; |
| x1 = cpx, y1 = cpy; |
| cpx += p[off++]; |
| cpy += p[off++]; |
| cmd = CMD.A; |
| processArc(x1, y1, cpx, cpy, fa, fs, rx, ry, psi, cmd, path); |
| break; |
| } |
| } |
| if (cmdStr === 'z' || cmdStr === 'Z') { |
| cmd = CMD.Z; |
| path.addData(cmd); |
| cpx = subpathX; |
| cpy = subpathY; |
| } |
| prevCmd = cmd; |
| } |
| path.toStatic(); |
| return path; |
| } |
| var SVGPath = (function (_super) { |
| __extends(SVGPath, _super); |
| function SVGPath() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| SVGPath.prototype.applyTransform = function (m) { }; |
| return SVGPath; |
| }(Path)); |
| function isPathProxy(path) { |
| return path.setData != null; |
| } |
| function createPathOptions(str, opts) { |
| var pathProxy = createPathProxyFromString(str); |
| var innerOpts = extend({}, opts); |
| innerOpts.buildPath = function (path) { |
| if (isPathProxy(path)) { |
| path.setData(pathProxy.data); |
| var ctx = path.getContext(); |
| if (ctx) { |
| path.rebuildPath(ctx, 1); |
| } |
| } |
| else { |
| var ctx = path; |
| pathProxy.rebuildPath(ctx, 1); |
| } |
| }; |
| innerOpts.applyTransform = function (m) { |
| transformPath(pathProxy, m); |
| this.dirtyShape(); |
| }; |
| return innerOpts; |
| } |
| function createFromString(str, opts) { |
| return new SVGPath(createPathOptions(str, opts)); |
| } |
| function extendFromString(str, defaultOpts) { |
| var innerOpts = createPathOptions(str, defaultOpts); |
| var Sub = (function (_super) { |
| __extends(Sub, _super); |
| function Sub(opts) { |
| var _this = _super.call(this, opts) || this; |
| _this.applyTransform = innerOpts.applyTransform; |
| _this.buildPath = innerOpts.buildPath; |
| return _this; |
| } |
| return Sub; |
| }(SVGPath)); |
| return Sub; |
| } |
| function mergePath(pathEls, opts) { |
| var pathList = []; |
| var len = pathEls.length; |
| for (var i = 0; i < len; i++) { |
| var pathEl = pathEls[i]; |
| pathList.push(pathEl.getUpdatedPathProxy(true)); |
| } |
| var pathBundle = new Path(opts); |
| pathBundle.createPathProxy(); |
| pathBundle.buildPath = function (path) { |
| if (isPathProxy(path)) { |
| path.appendPath(pathList); |
| var ctx = path.getContext(); |
| if (ctx) { |
| path.rebuildPath(ctx, 1); |
| } |
| } |
| }; |
| return pathBundle; |
| } |
| |
| var CircleShape = (function () { |
| function CircleShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.r = 0; |
| } |
| return CircleShape; |
| }()); |
| var Circle = (function (_super) { |
| __extends(Circle, _super); |
| function Circle(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Circle.prototype.getDefaultShape = function () { |
| return new CircleShape(); |
| }; |
| Circle.prototype.buildPath = function (ctx, shape) { |
| ctx.moveTo(shape.cx + shape.r, shape.cy); |
| ctx.arc(shape.cx, shape.cy, shape.r, 0, Math.PI * 2); |
| }; |
| return Circle; |
| }(Path)); |
| Circle.prototype.type = 'circle'; |
| |
| var EllipseShape = (function () { |
| function EllipseShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.rx = 0; |
| this.ry = 0; |
| } |
| return EllipseShape; |
| }()); |
| var Ellipse = (function (_super) { |
| __extends(Ellipse, _super); |
| function Ellipse(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Ellipse.prototype.getDefaultShape = function () { |
| return new EllipseShape(); |
| }; |
| Ellipse.prototype.buildPath = function (ctx, shape) { |
| var k = 0.5522848; |
| var x = shape.cx; |
| var y = shape.cy; |
| var a = shape.rx; |
| var b = shape.ry; |
| var ox = a * k; |
| var oy = b * k; |
| ctx.moveTo(x - a, y); |
| ctx.bezierCurveTo(x - a, y - oy, x - ox, y - b, x, y - b); |
| ctx.bezierCurveTo(x + ox, y - b, x + a, y - oy, x + a, y); |
| ctx.bezierCurveTo(x + a, y + oy, x + ox, y + b, x, y + b); |
| ctx.bezierCurveTo(x - ox, y + b, x - a, y + oy, x - a, y); |
| ctx.closePath(); |
| }; |
| return Ellipse; |
| }(Path)); |
| Ellipse.prototype.type = 'ellipse'; |
| |
| var PI$2 = Math.PI; |
| var PI2$5 = PI$2 * 2; |
| var mathSin$3 = Math.sin; |
| var mathCos$3 = Math.cos; |
| var mathACos = Math.acos; |
| var mathATan2 = Math.atan2; |
| var mathAbs$1 = Math.abs; |
| var mathSqrt$3 = Math.sqrt; |
| var mathMax$3 = Math.max; |
| var mathMin$3 = Math.min; |
| var e = 1e-4; |
| function intersect(x0, y0, x1, y1, x2, y2, x3, y3) { |
| var dx10 = x1 - x0; |
| var dy10 = y1 - y0; |
| var dx32 = x3 - x2; |
| var dy32 = y3 - y2; |
| var t = dy32 * dx10 - dx32 * dy10; |
| if (t * t < e) { |
| return; |
| } |
| t = (dx32 * (y0 - y2) - dy32 * (x0 - x2)) / t; |
| return [x0 + t * dx10, y0 + t * dy10]; |
| } |
| function computeCornerTangents(x0, y0, x1, y1, radius, cr, clockwise) { |
| var x01 = x0 - x1; |
| var y01 = y0 - y1; |
| var lo = (clockwise ? cr : -cr) / mathSqrt$3(x01 * x01 + y01 * y01); |
| var ox = lo * y01; |
| var oy = -lo * x01; |
| var x11 = x0 + ox; |
| var y11 = y0 + oy; |
| var x10 = x1 + ox; |
| var y10 = y1 + oy; |
| var x00 = (x11 + x10) / 2; |
| var y00 = (y11 + y10) / 2; |
| var dx = x10 - x11; |
| var dy = y10 - y11; |
| var d2 = dx * dx + dy * dy; |
| var r = radius - cr; |
| var s = x11 * y10 - x10 * y11; |
| var d = (dy < 0 ? -1 : 1) * mathSqrt$3(mathMax$3(0, r * r * d2 - s * s)); |
| var cx0 = (s * dy - dx * d) / d2; |
| var cy0 = (-s * dx - dy * d) / d2; |
| var cx1 = (s * dy + dx * d) / d2; |
| var cy1 = (-s * dx + dy * d) / d2; |
| var dx0 = cx0 - x00; |
| var dy0 = cy0 - y00; |
| var dx1 = cx1 - x00; |
| var dy1 = cy1 - y00; |
| if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) { |
| cx0 = cx1; |
| cy0 = cy1; |
| } |
| return { |
| cx: cx0, |
| cy: cy0, |
| x0: -ox, |
| y0: -oy, |
| x1: cx0 * (radius / r - 1), |
| y1: cy0 * (radius / r - 1) |
| }; |
| } |
| function normalizeCornerRadius(cr) { |
| var arr; |
| if (isArray(cr)) { |
| var len = cr.length; |
| if (!len) { |
| return cr; |
| } |
| if (len === 1) { |
| arr = [cr[0], cr[0], 0, 0]; |
| } |
| else if (len === 2) { |
| arr = [cr[0], cr[0], cr[1], cr[1]]; |
| } |
| else if (len === 3) { |
| arr = cr.concat(cr[2]); |
| } |
| else { |
| arr = cr; |
| } |
| } |
| else { |
| arr = [cr, cr, cr, cr]; |
| } |
| return arr; |
| } |
| function buildPath$1(ctx, shape) { |
| var _a; |
| var radius = mathMax$3(shape.r, 0); |
| var innerRadius = mathMax$3(shape.r0 || 0, 0); |
| var hasRadius = radius > 0; |
| var hasInnerRadius = innerRadius > 0; |
| if (!hasRadius && !hasInnerRadius) { |
| return; |
| } |
| if (!hasRadius) { |
| radius = innerRadius; |
| innerRadius = 0; |
| } |
| if (innerRadius > radius) { |
| var tmp = radius; |
| radius = innerRadius; |
| innerRadius = tmp; |
| } |
| var startAngle = shape.startAngle, endAngle = shape.endAngle; |
| if (isNaN(startAngle) || isNaN(endAngle)) { |
| return; |
| } |
| var cx = shape.cx, cy = shape.cy; |
| var clockwise = !!shape.clockwise; |
| var arc = mathAbs$1(endAngle - startAngle); |
| var mod = arc > PI2$5 && arc % PI2$5; |
| mod > e && (arc = mod); |
| if (!(radius > e)) { |
| ctx.moveTo(cx, cy); |
| } |
| else if (arc > PI2$5 - e) { |
| ctx.moveTo(cx + radius * mathCos$3(startAngle), cy + radius * mathSin$3(startAngle)); |
| ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); |
| if (innerRadius > e) { |
| ctx.moveTo(cx + innerRadius * mathCos$3(endAngle), cy + innerRadius * mathSin$3(endAngle)); |
| ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); |
| } |
| } |
| else { |
| var icrStart = void 0; |
| var icrEnd = void 0; |
| var ocrStart = void 0; |
| var ocrEnd = void 0; |
| var ocrs = void 0; |
| var ocre = void 0; |
| var icrs = void 0; |
| var icre = void 0; |
| var ocrMax = void 0; |
| var icrMax = void 0; |
| var limitedOcrMax = void 0; |
| var limitedIcrMax = void 0; |
| var xre = void 0; |
| var yre = void 0; |
| var xirs = void 0; |
| var yirs = void 0; |
| var xrs = radius * mathCos$3(startAngle); |
| var yrs = radius * mathSin$3(startAngle); |
| var xire = innerRadius * mathCos$3(endAngle); |
| var yire = innerRadius * mathSin$3(endAngle); |
| var hasArc = arc > e; |
| if (hasArc) { |
| var cornerRadius = shape.cornerRadius; |
| if (cornerRadius) { |
| _a = normalizeCornerRadius(cornerRadius), icrStart = _a[0], icrEnd = _a[1], ocrStart = _a[2], ocrEnd = _a[3]; |
| } |
| var halfRd = mathAbs$1(radius - innerRadius) / 2; |
| ocrs = mathMin$3(halfRd, ocrStart); |
| ocre = mathMin$3(halfRd, ocrEnd); |
| icrs = mathMin$3(halfRd, icrStart); |
| icre = mathMin$3(halfRd, icrEnd); |
| limitedOcrMax = ocrMax = mathMax$3(ocrs, ocre); |
| limitedIcrMax = icrMax = mathMax$3(icrs, icre); |
| if (ocrMax > e || icrMax > e) { |
| xre = radius * mathCos$3(endAngle); |
| yre = radius * mathSin$3(endAngle); |
| xirs = innerRadius * mathCos$3(startAngle); |
| yirs = innerRadius * mathSin$3(startAngle); |
| if (arc < PI$2) { |
| var it_1 = intersect(xrs, yrs, xirs, yirs, xre, yre, xire, yire); |
| if (it_1) { |
| var x0 = xrs - it_1[0]; |
| var y0 = yrs - it_1[1]; |
| var x1 = xre - it_1[0]; |
| var y1 = yre - it_1[1]; |
| var a = 1 / mathSin$3(mathACos((x0 * x1 + y0 * y1) / (mathSqrt$3(x0 * x0 + y0 * y0) * mathSqrt$3(x1 * x1 + y1 * y1))) / 2); |
| var b = mathSqrt$3(it_1[0] * it_1[0] + it_1[1] * it_1[1]); |
| limitedOcrMax = mathMin$3(ocrMax, (radius - b) / (a + 1)); |
| limitedIcrMax = mathMin$3(icrMax, (innerRadius - b) / (a - 1)); |
| } |
| } |
| } |
| } |
| if (!hasArc) { |
| ctx.moveTo(cx + xrs, cy + yrs); |
| } |
| else if (limitedOcrMax > e) { |
| var crStart = mathMin$3(ocrStart, limitedOcrMax); |
| var crEnd = mathMin$3(ocrEnd, limitedOcrMax); |
| var ct0 = computeCornerTangents(xirs, yirs, xrs, yrs, radius, crStart, clockwise); |
| var ct1 = computeCornerTangents(xre, yre, xire, yire, radius, crEnd, clockwise); |
| ctx.moveTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); |
| if (limitedOcrMax < ocrMax && crStart === crEnd) { |
| ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedOcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); |
| } |
| else { |
| crStart > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crStart, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); |
| ctx.arc(cx, cy, radius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), !clockwise); |
| crEnd > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crEnd, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); |
| } |
| } |
| else { |
| ctx.moveTo(cx + xrs, cy + yrs); |
| ctx.arc(cx, cy, radius, startAngle, endAngle, !clockwise); |
| } |
| if (!(innerRadius > e) || !hasArc) { |
| ctx.lineTo(cx + xire, cy + yire); |
| } |
| else if (limitedIcrMax > e) { |
| var crStart = mathMin$3(icrStart, limitedIcrMax); |
| var crEnd = mathMin$3(icrEnd, limitedIcrMax); |
| var ct0 = computeCornerTangents(xire, yire, xre, yre, innerRadius, -crEnd, clockwise); |
| var ct1 = computeCornerTangents(xrs, yrs, xirs, yirs, innerRadius, -crStart, clockwise); |
| ctx.lineTo(cx + ct0.cx + ct0.x0, cy + ct0.cy + ct0.y0); |
| if (limitedIcrMax < icrMax && crStart === crEnd) { |
| ctx.arc(cx + ct0.cx, cy + ct0.cy, limitedIcrMax, mathATan2(ct0.y0, ct0.x0), mathATan2(ct1.y0, ct1.x0), !clockwise); |
| } |
| else { |
| crEnd > 0 && ctx.arc(cx + ct0.cx, cy + ct0.cy, crEnd, mathATan2(ct0.y0, ct0.x0), mathATan2(ct0.y1, ct0.x1), !clockwise); |
| ctx.arc(cx, cy, innerRadius, mathATan2(ct0.cy + ct0.y1, ct0.cx + ct0.x1), mathATan2(ct1.cy + ct1.y1, ct1.cx + ct1.x1), clockwise); |
| crStart > 0 && ctx.arc(cx + ct1.cx, cy + ct1.cy, crStart, mathATan2(ct1.y1, ct1.x1), mathATan2(ct1.y0, ct1.x0), !clockwise); |
| } |
| } |
| else { |
| ctx.lineTo(cx + xire, cy + yire); |
| ctx.arc(cx, cy, innerRadius, endAngle, startAngle, clockwise); |
| } |
| } |
| ctx.closePath(); |
| } |
| |
| var SectorShape = (function () { |
| function SectorShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.r0 = 0; |
| this.r = 0; |
| this.startAngle = 0; |
| this.endAngle = Math.PI * 2; |
| this.clockwise = true; |
| this.cornerRadius = 0; |
| } |
| return SectorShape; |
| }()); |
| var Sector = (function (_super) { |
| __extends(Sector, _super); |
| function Sector(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Sector.prototype.getDefaultShape = function () { |
| return new SectorShape(); |
| }; |
| Sector.prototype.buildPath = function (ctx, shape) { |
| buildPath$1(ctx, shape); |
| }; |
| Sector.prototype.isZeroArea = function () { |
| return this.shape.startAngle === this.shape.endAngle |
| || this.shape.r === this.shape.r0; |
| }; |
| return Sector; |
| }(Path)); |
| Sector.prototype.type = 'sector'; |
| |
| var RingShape = (function () { |
| function RingShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.r = 0; |
| this.r0 = 0; |
| } |
| return RingShape; |
| }()); |
| var Ring = (function (_super) { |
| __extends(Ring, _super); |
| function Ring(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Ring.prototype.getDefaultShape = function () { |
| return new RingShape(); |
| }; |
| Ring.prototype.buildPath = function (ctx, shape) { |
| var x = shape.cx; |
| var y = shape.cy; |
| var PI2 = Math.PI * 2; |
| ctx.moveTo(x + shape.r, y); |
| ctx.arc(x, y, shape.r, 0, PI2, false); |
| ctx.moveTo(x + shape.r0, y); |
| ctx.arc(x, y, shape.r0, 0, PI2, true); |
| }; |
| return Ring; |
| }(Path)); |
| Ring.prototype.type = 'ring'; |
| |
| 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 = points.length; i < len; i++) { |
| min(min$1, min$1, points[i]); |
| max(max$1, max$1, points[i]); |
| } |
| min(min$1, min$1, constraint[0]); |
| max(max$1, max$1, constraint[1]); |
| } |
| for (var i = 0, len = points.length; i < len; i++) { |
| var point = points[i]; |
| if (isLoop) { |
| prevPoint = points[i ? i - 1 : len - 1]; |
| nextPoint = points[(i + 1) % len]; |
| } |
| else { |
| if (i === 0 || i === len - 1) { |
| cps.push(clone$1(points[i])); |
| continue; |
| } |
| else { |
| prevPoint = points[i - 1]; |
| nextPoint = points[i + 1]; |
| } |
| } |
| sub(v, nextPoint, prevPoint); |
| scale(v, v, smooth); |
| var d0 = distance(point, prevPoint); |
| var d1 = distance(point, nextPoint); |
| var sum = d0 + d1; |
| if (sum !== 0) { |
| d0 /= sum; |
| d1 /= sum; |
| } |
| scale(v1, v, -d0); |
| scale(v2, v, d1); |
| var cp0 = add([], point, v1); |
| var cp1 = add([], point, v2); |
| if (constraint) { |
| max(cp0, cp0, min$1); |
| min(cp0, cp0, max$1); |
| max(cp1, cp1, min$1); |
| min(cp1, cp1, max$1); |
| } |
| cps.push(cp0); |
| cps.push(cp1); |
| } |
| if (isLoop) { |
| cps.push(cps.shift()); |
| } |
| return cps; |
| } |
| |
| function buildPath$2(ctx, shape, closePath) { |
| var smooth = shape.smooth; |
| var points = shape.points; |
| if (points && points.length >= 2) { |
| if (smooth) { |
| 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 { |
| ctx.moveTo(points[0][0], points[0][1]); |
| for (var i = 1, l = points.length; i < l; i++) { |
| ctx.lineTo(points[i][0], points[i][1]); |
| } |
| } |
| closePath && ctx.closePath(); |
| } |
| } |
| |
| var PolygonShape = (function () { |
| function PolygonShape() { |
| this.points = null; |
| this.smooth = 0; |
| this.smoothConstraint = null; |
| } |
| return PolygonShape; |
| }()); |
| var Polygon = (function (_super) { |
| __extends(Polygon, _super); |
| function Polygon(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Polygon.prototype.getDefaultShape = function () { |
| return new PolygonShape(); |
| }; |
| Polygon.prototype.buildPath = function (ctx, shape) { |
| buildPath$2(ctx, shape, true); |
| }; |
| return Polygon; |
| }(Path)); |
| Polygon.prototype.type = 'polygon'; |
| |
| var PolylineShape = (function () { |
| function PolylineShape() { |
| this.points = null; |
| this.percent = 1; |
| this.smooth = 0; |
| this.smoothConstraint = null; |
| } |
| return PolylineShape; |
| }()); |
| var Polyline = (function (_super) { |
| __extends(Polyline, _super); |
| function Polyline(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Polyline.prototype.getDefaultStyle = function () { |
| return { |
| stroke: '#000', |
| fill: null |
| }; |
| }; |
| Polyline.prototype.getDefaultShape = function () { |
| return new PolylineShape(); |
| }; |
| Polyline.prototype.buildPath = function (ctx, shape) { |
| buildPath$2(ctx, shape, false); |
| }; |
| return Polyline; |
| }(Path)); |
| Polyline.prototype.type = 'polyline'; |
| |
| var subPixelOptimizeOutputShape$1 = {}; |
| var LineShape = (function () { |
| function LineShape() { |
| this.x1 = 0; |
| this.y1 = 0; |
| this.x2 = 0; |
| this.y2 = 0; |
| this.percent = 1; |
| } |
| return LineShape; |
| }()); |
| var Line = (function (_super) { |
| __extends(Line, _super); |
| function Line(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Line.prototype.getDefaultStyle = function () { |
| return { |
| stroke: '#000', |
| fill: null |
| }; |
| }; |
| Line.prototype.getDefaultShape = function () { |
| return new LineShape(); |
| }; |
| Line.prototype.buildPath = function (ctx, shape) { |
| var x1; |
| var y1; |
| var x2; |
| var y2; |
| if (this.subPixelOptimize) { |
| var optimizedShape = subPixelOptimizeLine(subPixelOptimizeOutputShape$1, shape, this.style); |
| x1 = optimizedShape.x1; |
| y1 = optimizedShape.y1; |
| x2 = optimizedShape.x2; |
| y2 = optimizedShape.y2; |
| } |
| else { |
| x1 = shape.x1; |
| y1 = shape.y1; |
| x2 = shape.x2; |
| y2 = shape.y2; |
| } |
| var percent = shape.percent; |
| if (percent === 0) { |
| return; |
| } |
| ctx.moveTo(x1, y1); |
| if (percent < 1) { |
| x2 = x1 * (1 - percent) + x2 * percent; |
| y2 = y1 * (1 - percent) + y2 * percent; |
| } |
| ctx.lineTo(x2, y2); |
| }; |
| Line.prototype.pointAt = function (p) { |
| var shape = this.shape; |
| return [ |
| shape.x1 * (1 - p) + shape.x2 * p, |
| shape.y1 * (1 - p) + shape.y2 * p |
| ]; |
| }; |
| return Line; |
| }(Path)); |
| Line.prototype.type = 'line'; |
| |
| var out = []; |
| var BezierCurveShape = (function () { |
| function BezierCurveShape() { |
| this.x1 = 0; |
| this.y1 = 0; |
| this.x2 = 0; |
| this.y2 = 0; |
| this.cpx1 = 0; |
| this.cpy1 = 0; |
| this.percent = 1; |
| } |
| return BezierCurveShape; |
| }()); |
| 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 = (function (_super) { |
| __extends(BezierCurve, _super); |
| function BezierCurve(opts) { |
| return _super.call(this, opts) || this; |
| } |
| BezierCurve.prototype.getDefaultStyle = function () { |
| return { |
| stroke: '#000', |
| fill: null |
| }; |
| }; |
| BezierCurve.prototype.getDefaultShape = function () { |
| return new BezierCurveShape(); |
| }; |
| BezierCurve.prototype.buildPath = function (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); |
| } |
| }; |
| BezierCurve.prototype.pointAt = function (t) { |
| return someVectorAt(this.shape, t, false); |
| }; |
| BezierCurve.prototype.tangentAt = function (t) { |
| var p = someVectorAt(this.shape, t, true); |
| return normalize(p, p); |
| }; |
| return BezierCurve; |
| }(Path)); |
| BezierCurve.prototype.type = 'bezier-curve'; |
| |
| var ArcShape = (function () { |
| function ArcShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.r = 0; |
| this.startAngle = 0; |
| this.endAngle = Math.PI * 2; |
| this.clockwise = true; |
| } |
| return ArcShape; |
| }()); |
| var Arc = (function (_super) { |
| __extends(Arc, _super); |
| function Arc(opts) { |
| return _super.call(this, opts) || this; |
| } |
| Arc.prototype.getDefaultStyle = function () { |
| return { |
| stroke: '#000', |
| fill: null |
| }; |
| }; |
| Arc.prototype.getDefaultShape = function () { |
| return new ArcShape(); |
| }; |
| Arc.prototype.buildPath = function (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); |
| }; |
| return Arc; |
| }(Path)); |
| Arc.prototype.type = 'arc'; |
| |
| var CompoundPath = (function (_super) { |
| __extends(CompoundPath, _super); |
| function CompoundPath() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| _this.type = 'compound'; |
| return _this; |
| } |
| CompoundPath.prototype._updatePathDirty = function () { |
| var paths = this.shape.paths; |
| var dirtyPath = this.shapeChanged(); |
| for (var i = 0; i < paths.length; i++) { |
| dirtyPath = dirtyPath || paths[i].shapeChanged(); |
| } |
| if (dirtyPath) { |
| this.dirtyShape(); |
| } |
| }; |
| CompoundPath.prototype.beforeBrush = function () { |
| this._updatePathDirty(); |
| var paths = this.shape.paths || []; |
| var scale = this.getGlobalScale(); |
| for (var i = 0; i < paths.length; i++) { |
| if (!paths[i].path) { |
| paths[i].createPathProxy(); |
| } |
| paths[i].path.setScale(scale[0], scale[1], paths[i].segmentIgnoreThreshold); |
| } |
| }; |
| CompoundPath.prototype.buildPath = function (ctx, shape) { |
| var paths = shape.paths || []; |
| for (var i = 0; i < paths.length; i++) { |
| paths[i].buildPath(ctx, paths[i].shape, true); |
| } |
| }; |
| CompoundPath.prototype.afterBrush = function () { |
| var paths = this.shape.paths || []; |
| for (var i = 0; i < paths.length; i++) { |
| paths[i].pathUpdated(); |
| } |
| }; |
| CompoundPath.prototype.getBoundingRect = function () { |
| this._updatePathDirty.call(this); |
| return Path.prototype.getBoundingRect.call(this); |
| }; |
| return CompoundPath; |
| }(Path)); |
| |
| var Gradient = (function () { |
| function Gradient(colorStops) { |
| this.colorStops = colorStops || []; |
| } |
| Gradient.prototype.addColorStop = function (offset, color) { |
| this.colorStops.push({ |
| offset: offset, |
| color: color |
| }); |
| }; |
| return Gradient; |
| }()); |
| |
| var LinearGradient = (function (_super) { |
| __extends(LinearGradient, _super); |
| function LinearGradient(x, y, x2, y2, colorStops, globalCoord) { |
| var _this = _super.call(this, colorStops) || this; |
| _this.x = x == null ? 0 : x; |
| _this.y = y == null ? 0 : y; |
| _this.x2 = x2 == null ? 1 : x2; |
| _this.y2 = y2 == null ? 0 : y2; |
| _this.type = 'linear'; |
| _this.global = globalCoord || false; |
| return _this; |
| } |
| return LinearGradient; |
| }(Gradient)); |
| |
| var RadialGradient = (function (_super) { |
| __extends(RadialGradient, _super); |
| function RadialGradient(x, y, r, colorStops, globalCoord) { |
| var _this = _super.call(this, colorStops) || this; |
| _this.x = x == null ? 0.5 : x; |
| _this.y = y == null ? 0.5 : y; |
| _this.r = r == null ? 0.5 : r; |
| _this.type = 'radial'; |
| _this.global = globalCoord || false; |
| return _this; |
| } |
| return RadialGradient; |
| }(Gradient)); |
| |
| var extent = [0, 0]; |
| var extent2 = [0, 0]; |
| var minTv$1 = new Point(); |
| var maxTv$1 = new Point(); |
| var OrientedBoundingRect = (function () { |
| function OrientedBoundingRect(rect, transform) { |
| this._corners = []; |
| this._axes = []; |
| this._origin = [0, 0]; |
| for (var i = 0; i < 4; i++) { |
| this._corners[i] = new Point(); |
| } |
| for (var i = 0; i < 2; i++) { |
| this._axes[i] = new Point(); |
| } |
| if (rect) { |
| this.fromBoundingRect(rect, transform); |
| } |
| } |
| OrientedBoundingRect.prototype.fromBoundingRect = function (rect, transform) { |
| var corners = this._corners; |
| var axes = this._axes; |
| var x = rect.x; |
| var y = rect.y; |
| var x2 = x + rect.width; |
| var y2 = y + rect.height; |
| corners[0].set(x, y); |
| corners[1].set(x2, y); |
| corners[2].set(x2, y2); |
| corners[3].set(x, y2); |
| if (transform) { |
| for (var i = 0; i < 4; i++) { |
| corners[i].transform(transform); |
| } |
| } |
| Point.sub(axes[0], corners[1], corners[0]); |
| Point.sub(axes[1], corners[3], corners[0]); |
| axes[0].normalize(); |
| axes[1].normalize(); |
| for (var i = 0; i < 2; i++) { |
| this._origin[i] = axes[i].dot(corners[0]); |
| } |
| }; |
| OrientedBoundingRect.prototype.intersect = function (other, mtv) { |
| var overlapped = true; |
| var noMtv = !mtv; |
| minTv$1.set(Infinity, Infinity); |
| maxTv$1.set(0, 0); |
| if (!this._intersectCheckOneSide(this, other, minTv$1, maxTv$1, noMtv, 1)) { |
| overlapped = false; |
| if (noMtv) { |
| return overlapped; |
| } |
| } |
| if (!this._intersectCheckOneSide(other, this, minTv$1, maxTv$1, noMtv, -1)) { |
| overlapped = false; |
| if (noMtv) { |
| return overlapped; |
| } |
| } |
| if (!noMtv) { |
| Point.copy(mtv, overlapped ? minTv$1 : maxTv$1); |
| } |
| return overlapped; |
| }; |
| OrientedBoundingRect.prototype._intersectCheckOneSide = function (self, other, minTv, maxTv, noMtv, inverse) { |
| var overlapped = true; |
| for (var i = 0; i < 2; i++) { |
| var axis = this._axes[i]; |
| this._getProjMinMaxOnAxis(i, self._corners, extent); |
| this._getProjMinMaxOnAxis(i, other._corners, extent2); |
| if (extent[1] < extent2[0] || extent[0] > extent2[1]) { |
| overlapped = false; |
| if (noMtv) { |
| return overlapped; |
| } |
| var dist0 = Math.abs(extent2[0] - extent[1]); |
| var dist1 = Math.abs(extent[0] - extent2[1]); |
| if (Math.min(dist0, dist1) > maxTv.len()) { |
| if (dist0 < dist1) { |
| Point.scale(maxTv, axis, -dist0 * inverse); |
| } |
| else { |
| Point.scale(maxTv, axis, dist1 * inverse); |
| } |
| } |
| } |
| else if (minTv) { |
| var dist0 = Math.abs(extent2[0] - extent[1]); |
| var dist1 = Math.abs(extent[0] - extent2[1]); |
| if (Math.min(dist0, dist1) < minTv.len()) { |
| if (dist0 < dist1) { |
| Point.scale(minTv, axis, dist0 * inverse); |
| } |
| else { |
| Point.scale(minTv, axis, -dist1 * inverse); |
| } |
| } |
| } |
| } |
| return overlapped; |
| }; |
| OrientedBoundingRect.prototype._getProjMinMaxOnAxis = function (dim, corners, out) { |
| var axis = this._axes[dim]; |
| var origin = this._origin; |
| var proj = corners[0].dot(axis) + origin[dim]; |
| var min = proj; |
| var max = proj; |
| for (var i = 1; i < corners.length; i++) { |
| var proj_1 = corners[i].dot(axis) + origin[dim]; |
| min = Math.min(proj_1, min); |
| max = Math.max(proj_1, max); |
| } |
| out[0] = min; |
| out[1] = max; |
| }; |
| return OrientedBoundingRect; |
| }()); |
| |
| var m = []; |
| var IncrementalDisplayable = (function (_super) { |
| __extends(IncrementalDisplayable, _super); |
| function IncrementalDisplayable() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| _this.notClear = true; |
| _this.incremental = true; |
| _this._displayables = []; |
| _this._temporaryDisplayables = []; |
| _this._cursor = 0; |
| return _this; |
| } |
| IncrementalDisplayable.prototype.traverse = function (cb, context) { |
| cb.call(context, this); |
| }; |
| IncrementalDisplayable.prototype.useStyle = function () { |
| this.style = {}; |
| }; |
| IncrementalDisplayable.prototype.getCursor = function () { |
| return this._cursor; |
| }; |
| IncrementalDisplayable.prototype.innerAfterBrush = function () { |
| this._cursor = this._displayables.length; |
| }; |
| IncrementalDisplayable.prototype.clearDisplaybles = function () { |
| this._displayables = []; |
| this._temporaryDisplayables = []; |
| this._cursor = 0; |
| this.markRedraw(); |
| this.notClear = false; |
| }; |
| IncrementalDisplayable.prototype.clearTemporalDisplayables = function () { |
| this._temporaryDisplayables = []; |
| }; |
| IncrementalDisplayable.prototype.addDisplayable = function (displayable, notPersistent) { |
| if (notPersistent) { |
| this._temporaryDisplayables.push(displayable); |
| } |
| else { |
| this._displayables.push(displayable); |
| } |
| this.markRedraw(); |
| }; |
| IncrementalDisplayable.prototype.addDisplayables = function (displayables, notPersistent) { |
| notPersistent = notPersistent || false; |
| for (var i = 0; i < displayables.length; i++) { |
| this.addDisplayable(displayables[i], notPersistent); |
| } |
| }; |
| IncrementalDisplayable.prototype.getDisplayables = function () { |
| return this._displayables; |
| }; |
| IncrementalDisplayable.prototype.getTemporalDisplayables = function () { |
| return this._temporaryDisplayables; |
| }; |
| IncrementalDisplayable.prototype.eachPendingDisplayable = function (cb) { |
| for (var i = this._cursor; i < this._displayables.length; i++) { |
| cb && cb(this._displayables[i]); |
| } |
| for (var i = 0; i < this._temporaryDisplayables.length; i++) { |
| cb && cb(this._temporaryDisplayables[i]); |
| } |
| }; |
| IncrementalDisplayable.prototype.update = function () { |
| this.updateTransform(); |
| for (var i = this._cursor; i < this._displayables.length; i++) { |
| var displayable = this._displayables[i]; |
| displayable.parent = this; |
| displayable.update(); |
| displayable.parent = null; |
| } |
| for (var i = 0; i < this._temporaryDisplayables.length; i++) { |
| var displayable = this._temporaryDisplayables[i]; |
| displayable.parent = this; |
| displayable.update(); |
| displayable.parent = null; |
| } |
| }; |
| IncrementalDisplayable.prototype.getBoundingRect = function () { |
| if (!this._rect) { |
| var rect = new BoundingRect(Infinity, Infinity, -Infinity, -Infinity); |
| for (var i = 0; i < this._displayables.length; i++) { |
| var displayable = this._displayables[i]; |
| var childRect = displayable.getBoundingRect().clone(); |
| if (displayable.needLocalTransform()) { |
| childRect.applyTransform(displayable.getLocalTransform(m)); |
| } |
| rect.union(childRect); |
| } |
| this._rect = rect; |
| } |
| return this._rect; |
| }; |
| IncrementalDisplayable.prototype.contain = function (x, y) { |
| var localPos = this.transformCoordToLocal(x, y); |
| var rect = this.getBoundingRect(); |
| if (rect.contain(localPos[0], localPos[1])) { |
| for (var i = 0; i < this._displayables.length; i++) { |
| var displayable = this._displayables[i]; |
| if (displayable.contain(x, y)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| }; |
| return IncrementalDisplayable; |
| }(Displayable)); |
| |
| var transitionStore = makeInner(); |
| /** |
| * Return null if animation is disabled. |
| */ |
| |
| function getAnimationConfig(animationType, animatableModel, dataIndex, // Extra opts can override the option in animatable model. |
| extraOpts, // TODO It's only for pictorial bar now. |
| extraDelayParams) { |
| var animationPayload; // Check if there is global animation configuration from dataZoom/resize can override the config in option. |
| // If animation is enabled. Will use this animation config in payload. |
| // If animation is disabled. Just ignore it. |
| |
| if (animatableModel && animatableModel.ecModel) { |
| var updatePayload = animatableModel.ecModel.getUpdatePayload(); |
| animationPayload = updatePayload && updatePayload.animation; |
| } |
| |
| var animationEnabled = animatableModel && animatableModel.isAnimationEnabled(); |
| var isUpdate = animationType === 'update'; |
| |
| if (animationEnabled) { |
| var duration = void 0; |
| var easing = void 0; |
| var delay = void 0; |
| |
| if (extraOpts) { |
| duration = retrieve2(extraOpts.duration, 200); |
| easing = retrieve2(extraOpts.easing, 'cubicOut'); |
| delay = 0; |
| } else { |
| duration = animatableModel.getShallow(isUpdate ? 'animationDurationUpdate' : 'animationDuration'); |
| easing = animatableModel.getShallow(isUpdate ? 'animationEasingUpdate' : 'animationEasing'); |
| delay = animatableModel.getShallow(isUpdate ? 'animationDelayUpdate' : 'animationDelay'); |
| } // animation from payload has highest priority. |
| |
| |
| if (animationPayload) { |
| animationPayload.duration != null && (duration = animationPayload.duration); |
| animationPayload.easing != null && (easing = animationPayload.easing); |
| animationPayload.delay != null && (delay = animationPayload.delay); |
| } |
| |
| if (isFunction(delay)) { |
| delay = delay(dataIndex, extraDelayParams); |
| } |
| |
| if (isFunction(duration)) { |
| duration = duration(dataIndex); |
| } |
| |
| var config = { |
| duration: duration || 0, |
| delay: delay, |
| easing: easing |
| }; |
| return config; |
| } else { |
| return null; |
| } |
| } |
| |
| function animateOrSetProps(animationType, el, props, animatableModel, dataIndex, cb, during) { |
| var isFrom = false; |
| var removeOpt; |
| |
| if (isFunction(dataIndex)) { |
| during = cb; |
| cb = dataIndex; |
| dataIndex = null; |
| } else if (isObject(dataIndex)) { |
| cb = dataIndex.cb; |
| during = dataIndex.during; |
| isFrom = dataIndex.isFrom; |
| removeOpt = dataIndex.removeOpt; |
| dataIndex = dataIndex.dataIndex; |
| } |
| |
| var isRemove = animationType === 'leave'; |
| |
| if (!isRemove) { |
| // Must stop the remove animation. |
| el.stopAnimation('leave'); |
| } |
| |
| var animationConfig = getAnimationConfig(animationType, animatableModel, dataIndex, isRemove ? removeOpt || {} : null, animatableModel && animatableModel.getAnimationDelayParams ? animatableModel.getAnimationDelayParams(el, dataIndex) : null); |
| |
| if (animationConfig && animationConfig.duration > 0) { |
| var duration = animationConfig.duration; |
| var animationDelay = animationConfig.delay; |
| var animationEasing = animationConfig.easing; |
| var animateConfig = { |
| duration: duration, |
| delay: animationDelay || 0, |
| easing: animationEasing, |
| done: cb, |
| force: !!cb || !!during, |
| // Set to final state in update/init animation. |
| // So the post processing based on the path shape can be done correctly. |
| setToFinal: !isRemove, |
| scope: animationType, |
| during: during |
| }; |
| isFrom ? el.animateFrom(props, animateConfig) : el.animateTo(props, animateConfig); |
| } else { |
| el.stopAnimation(); // If `isFrom`, the props is the "from" props. |
| |
| !isFrom && el.attr(props); // Call during at least once. |
| |
| during && during(1); |
| cb && cb(); |
| } |
| } |
| /** |
| * Update graphic element properties with or without animation according to the |
| * configuration in series. |
| * |
| * Caution: this method will stop previous animation. |
| * So do not use this method to one element twice before |
| * animation starts, unless you know what you are doing. |
| * @example |
| * graphic.updateProps(el, { |
| * position: [100, 100] |
| * }, seriesModel, dataIndex, function () { console.log('Animation done!'); }); |
| * // Or |
| * graphic.updateProps(el, { |
| * position: [100, 100] |
| * }, seriesModel, function () { console.log('Animation done!'); }); |
| */ |
| |
| |
| function updateProps(el, props, // TODO: TYPE AnimatableModel |
| animatableModel, dataIndex, cb, during) { |
| animateOrSetProps('update', el, props, animatableModel, dataIndex, cb, during); |
| } |
| /** |
| * Init graphic element properties with or without animation according to the |
| * configuration in series. |
| * |
| * Caution: this method will stop previous animation. |
| * So do not use this method to one element twice before |
| * animation starts, unless you know what you are doing. |
| */ |
| |
| function initProps(el, props, animatableModel, dataIndex, cb, during) { |
| animateOrSetProps('enter', el, props, animatableModel, dataIndex, cb, during); |
| } |
| /** |
| * If element is removed. |
| * It can determine if element is having remove animation. |
| */ |
| |
| function isElementRemoved(el) { |
| if (!el.__zr) { |
| return true; |
| } |
| |
| for (var i = 0; i < el.animators.length; i++) { |
| var animator = el.animators[i]; |
| |
| if (animator.scope === 'leave') { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| /** |
| * Remove graphic element |
| */ |
| |
| function removeElement(el, props, animatableModel, dataIndex, cb, during) { |
| // Don't do remove animation twice. |
| if (isElementRemoved(el)) { |
| return; |
| } |
| |
| animateOrSetProps('leave', el, props, animatableModel, dataIndex, cb, during); |
| } |
| |
| function fadeOutDisplayable(el, animatableModel, dataIndex, done) { |
| el.removeTextContent(); |
| el.removeTextGuideLine(); |
| removeElement(el, { |
| style: { |
| opacity: 0 |
| } |
| }, animatableModel, dataIndex, done); |
| } |
| |
| function removeElementWithFadeOut(el, animatableModel, dataIndex) { |
| function doRemove() { |
| el.parent && el.parent.remove(el); |
| } // Hide label and labelLine first |
| // TODO Also use fade out animation? |
| |
| |
| if (!el.isGroup) { |
| fadeOutDisplayable(el, animatableModel, dataIndex, doRemove); |
| } else { |
| el.traverse(function (disp) { |
| if (!disp.isGroup) { |
| // Can invoke doRemove multiple times. |
| fadeOutDisplayable(disp, animatableModel, dataIndex, doRemove); |
| } |
| }); |
| } |
| } |
| /** |
| * Save old style for style transition in universalTransition module. |
| * It's used when element will be reused in each render. |
| * For chart like map, heatmap, which will always create new element. |
| * We don't need to save this because universalTransition can get old style from the old element |
| */ |
| |
| function saveOldStyle(el) { |
| transitionStore(el).oldStyle = el.style; |
| } |
| |
| var mathMax$4 = Math.max; |
| var mathMin$4 = Math.min; |
| var _customShapeMap = {}; |
| /** |
| * Extend shape with parameters |
| */ |
| |
| function extendShape(opts) { |
| return Path.extend(opts); |
| } |
| var extendPathFromString = extendFromString; |
| /** |
| * Extend path |
| */ |
| |
| function extendPath(pathData, opts) { |
| return extendPathFromString(pathData, opts); |
| } |
| /** |
| * Register a user defined shape. |
| * The shape class can be fetched by `getShapeClass` |
| * This method will overwrite the registered shapes, including |
| * the registered built-in shapes, if using the same `name`. |
| * The shape can be used in `custom series` and |
| * `graphic component` by declaring `{type: name}`. |
| * |
| * @param name |
| * @param ShapeClass Can be generated by `extendShape`. |
| */ |
| |
| function registerShape(name, ShapeClass) { |
| _customShapeMap[name] = ShapeClass; |
| } |
| /** |
| * Find shape class registered by `registerShape`. Usually used in |
| * fetching user defined shape. |
| * |
| * [Caution]: |
| * (1) This method **MUST NOT be used inside echarts !!!**, unless it is prepared |
| * to use user registered shapes. |
| * Because the built-in shape (see `getBuiltInShape`) will be registered by |
| * `registerShape` by default. That enables users to get both built-in |
| * shapes as well as the shapes belonging to themsleves. But users can overwrite |
| * the built-in shapes by using names like 'circle', 'rect' via calling |
| * `registerShape`. So the echarts inner featrues should not fetch shapes from here |
| * in case that it is overwritten by users, except that some features, like |
| * `custom series`, `graphic component`, do it deliberately. |
| * |
| * (2) In the features like `custom series`, `graphic component`, the user input |
| * `{tpye: 'xxx'}` does not only specify shapes but also specify other graphic |
| * elements like `'group'`, `'text'`, `'image'` or event `'path'`. Those names |
| * are reserved names, that is, if some user registers a shape named `'image'`, |
| * the shape will not be used. If we intending to add some more reserved names |
| * in feature, that might bring break changes (disable some existing user shape |
| * names). But that case probably rarely happens. So we don't make more mechanism |
| * to resolve this issue here. |
| * |
| * @param name |
| * @return The shape class. If not found, return nothing. |
| */ |
| |
| function getShapeClass(name) { |
| if (_customShapeMap.hasOwnProperty(name)) { |
| return _customShapeMap[name]; |
| } |
| } |
| /** |
| * Create a path element from path data string |
| * @param pathData |
| * @param opts |
| * @param rect |
| * @param layout 'center' or 'cover' default to be cover |
| */ |
| |
| function makePath(pathData, opts, rect, layout) { |
| var path = createFromString(pathData, opts); |
| |
| if (rect) { |
| if (layout === 'center') { |
| rect = centerGraphic(rect, path.getBoundingRect()); |
| } |
| |
| resizePath(path, rect); |
| } |
| |
| return path; |
| } |
| /** |
| * Create a image element from image url |
| * @param imageUrl image url |
| * @param opts options |
| * @param rect constrain rect |
| * @param layout 'center' or 'cover'. Default to be 'cover' |
| */ |
| |
| function makeImage(imageUrl, rect, layout) { |
| var zrImg = new ZRImage({ |
| style: { |
| image: imageUrl, |
| x: rect.x, |
| y: rect.y, |
| width: rect.width, |
| height: rect.height |
| }, |
| onload: function (img) { |
| if (layout === 'center') { |
| var boundingRect = { |
| width: img.width, |
| height: img.height |
| }; |
| zrImg.setStyle(centerGraphic(rect, boundingRect)); |
| } |
| } |
| }); |
| return zrImg; |
| } |
| /** |
| * Get position of centered element in bounding box. |
| * |
| * @param rect element local bounding box |
| * @param boundingRect constraint bounding box |
| * @return element position containing x, y, width, and height |
| */ |
| |
| function centerGraphic(rect, boundingRect) { |
| // Set rect to center, keep width / height ratio. |
| var aspect = boundingRect.width / boundingRect.height; |
| var width = rect.height * aspect; |
| var height; |
| |
| if (width <= rect.width) { |
| height = rect.height; |
| } else { |
| width = rect.width; |
| height = width / aspect; |
| } |
| |
| var cx = rect.x + rect.width / 2; |
| var cy = rect.y + rect.height / 2; |
| return { |
| x: cx - width / 2, |
| y: cy - height / 2, |
| width: width, |
| height: height |
| }; |
| } |
| |
| var mergePath$1 = mergePath; |
| /** |
| * Resize a path to fit the rect |
| * @param path |
| * @param rect |
| */ |
| |
| function resizePath(path, rect) { |
| if (!path.applyTransform) { |
| return; |
| } |
| |
| var pathRect = path.getBoundingRect(); |
| var m = pathRect.calculateTransform(rect); |
| path.applyTransform(m); |
| } |
| /** |
| * Sub pixel optimize line for canvas |
| */ |
| |
| function subPixelOptimizeLine$1(shape, lineWidth) { |
| subPixelOptimizeLine(shape, shape, { |
| lineWidth: lineWidth |
| }); |
| return shape; |
| } |
| /** |
| * Get transform matrix of target (param target), |
| * in coordinate of its ancestor (param ancestor) |
| * |
| * @param target |
| * @param [ancestor] |
| */ |
| |
| function getTransform(target, ancestor) { |
| var mat = identity([]); |
| |
| while (target && target !== ancestor) { |
| mul$1(mat, target.getLocalTransform(), mat); |
| target = target.parent; |
| } |
| |
| return mat; |
| } |
| |
| function isNotGroup(el) { |
| return !el.isGroup; |
| } |
| |
| function isPath(el) { |
| return el.shape != null; |
| } |
| /** |
| * Apply group transition animation from g1 to g2. |
| * If no animatableModel, no animation. |
| */ |
| |
| |
| function groupTransition(g1, g2, animatableModel) { |
| if (!g1 || !g2) { |
| return; |
| } |
| |
| function getElMap(g) { |
| var elMap = {}; |
| g.traverse(function (el) { |
| if (isNotGroup(el) && el.anid) { |
| elMap[el.anid] = el; |
| } |
| }); |
| return elMap; |
| } |
| |
| function getAnimatableProps(el) { |
| var obj = { |
| x: el.x, |
| y: el.y, |
| rotation: el.rotation |
| }; |
| |
| if (isPath(el)) { |
| obj.shape = extend({}, el.shape); |
| } |
| |
| return obj; |
| } |
| |
| var elMap1 = getElMap(g1); |
| g2.traverse(function (el) { |
| if (isNotGroup(el) && el.anid) { |
| var oldEl = elMap1[el.anid]; |
| |
| if (oldEl) { |
| var newProp = getAnimatableProps(el); |
| el.attr(getAnimatableProps(oldEl)); |
| updateProps(el, newProp, animatableModel, getECData(el).dataIndex); |
| } |
| } |
| }); |
| } |
| function clipPointsByRect(points, rect) { |
| // FIXME: This way might be incorrect when graphic clipped by a corner |
| // and when element has a border. |
| return map(points, function (point) { |
| var x = point[0]; |
| x = mathMax$4(x, rect.x); |
| x = mathMin$4(x, rect.x + rect.width); |
| var y = point[1]; |
| y = mathMax$4(y, rect.y); |
| y = mathMin$4(y, rect.y + rect.height); |
| return [x, y]; |
| }); |
| } |
| /** |
| * Return a new clipped rect. If rect size are negative, return undefined. |
| */ |
| |
| function clipRectByRect(targetRect, rect) { |
| var x = mathMax$4(targetRect.x, rect.x); |
| var x2 = mathMin$4(targetRect.x + targetRect.width, rect.x + rect.width); |
| var y = mathMax$4(targetRect.y, rect.y); |
| var y2 = mathMin$4(targetRect.y + targetRect.height, rect.y + rect.height); // If the total rect is cliped, nothing, including the border, |
| // should be painted. So return undefined. |
| |
| if (x2 >= x && y2 >= y) { |
| return { |
| x: x, |
| y: y, |
| width: x2 - x, |
| height: y2 - y |
| }; |
| } |
| } |
| function createIcon(iconStr, // Support 'image://' or 'path://' or direct svg path. |
| opt, rect) { |
| var innerOpts = extend({ |
| rectHover: true |
| }, opt); |
| var style = innerOpts.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 ZRImage(innerOpts)) : makePath(iconStr.replace('path://', ''), innerOpts, rect, 'center'); |
| } |
| } |
| |
| function setTooltipConfig(opt) { |
| var itemTooltipOption = opt.itemTooltipOption; |
| var componentModel = opt.componentModel; |
| var itemName = opt.itemName; |
| var itemTooltipOptionObj = isString(itemTooltipOption) ? { |
| formatter: itemTooltipOption |
| } : itemTooltipOption; |
| var mainType = componentModel.mainType; |
| var componentIndex = componentModel.componentIndex; |
| var formatterParams = { |
| componentType: mainType, |
| name: itemName, |
| $vars: ['name'] |
| }; |
| formatterParams[mainType + 'Index'] = componentIndex; |
| var formatterParamsExtra = opt.formatterParamsExtra; |
| |
| if (formatterParamsExtra) { |
| each(keys(formatterParamsExtra), function (key) { |
| if (!hasOwn(formatterParams, key)) { |
| formatterParams[key] = formatterParamsExtra[key]; |
| formatterParams.$vars.push(key); |
| } |
| }); |
| } |
| |
| var ecData = getECData(opt.el); |
| ecData.componentMainType = mainType; |
| ecData.componentIndex = componentIndex; |
| ecData.tooltipConfig = { |
| name: itemName, |
| option: defaults({ |
| content: itemName, |
| formatterParams: formatterParams |
| }, itemTooltipOptionObj) |
| }; |
| } |
| |
| function traverseElement(el, cb) { |
| var stopped; // TODO |
| // Polyfill for fixing zrender group traverse don't visit it's root issue. |
| |
| if (el.isGroup) { |
| stopped = cb(el); |
| } |
| |
| if (!stopped) { |
| el.traverse(cb); |
| } |
| } |
| |
| function traverseElements(els, cb) { |
| if (els) { |
| if (isArray(els)) { |
| for (var i = 0; i < els.length; i++) { |
| traverseElement(els[i], cb); |
| } |
| } else { |
| traverseElement(els, cb); |
| } |
| } |
| } // Register built-in shapes. These shapes might be overwritten |
| // by users, although we do not recommend that. |
| |
| registerShape('circle', Circle); |
| registerShape('ellipse', Ellipse); |
| 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 EMPTY_OBJ = {}; |
| function setLabelText(label, labelTexts) { |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| var stateName = SPECIAL_STATES[i]; |
| var text = labelTexts[stateName]; |
| var state = label.ensureState(stateName); |
| state.style = state.style || {}; |
| state.style.text = text; |
| } |
| |
| var oldStates = label.currentStates.slice(); |
| label.clearStates(true); |
| label.setStyle({ |
| text: labelTexts.normal |
| }); |
| label.useStates(oldStates, true); |
| } |
| |
| function getLabelText(opt, stateModels, interpolatedValue) { |
| var labelFetcher = opt.labelFetcher; |
| var labelDataIndex = opt.labelDataIndex; |
| var labelDimIndex = opt.labelDimIndex; |
| var normalModel = stateModels.normal; |
| var baseText; |
| |
| if (labelFetcher) { |
| baseText = labelFetcher.getFormattedLabel(labelDataIndex, 'normal', null, labelDimIndex, normalModel && normalModel.get('formatter'), interpolatedValue != null ? { |
| interpolatedValue: interpolatedValue |
| } : null); |
| } |
| |
| if (baseText == null) { |
| baseText = isFunction(opt.defaultText) ? opt.defaultText(labelDataIndex, opt, interpolatedValue) : opt.defaultText; |
| } |
| |
| var statesText = { |
| normal: baseText |
| }; |
| |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| var stateName = SPECIAL_STATES[i]; |
| var stateModel = stateModels[stateName]; |
| statesText[stateName] = retrieve2(labelFetcher ? labelFetcher.getFormattedLabel(labelDataIndex, stateName, null, labelDimIndex, stateModel && stateModel.get('formatter')) : null, baseText); |
| } |
| |
| return statesText; |
| } |
| |
| function setLabelStyle(targetEl, labelStatesModels, opt, stateSpecified // TODO specified position? |
| ) { |
| opt = opt || EMPTY_OBJ; |
| var isSetOnText = targetEl instanceof ZRText; |
| var needsCreateText = false; |
| |
| for (var i = 0; i < DISPLAY_STATES.length; i++) { |
| var stateModel = labelStatesModels[DISPLAY_STATES[i]]; |
| |
| if (stateModel && stateModel.getShallow('show')) { |
| needsCreateText = true; |
| break; |
| } |
| } |
| |
| var textContent = isSetOnText ? targetEl : targetEl.getTextContent(); |
| |
| if (needsCreateText) { |
| if (!isSetOnText) { |
| // Reuse the previous |
| if (!textContent) { |
| textContent = new ZRText(); |
| targetEl.setTextContent(textContent); |
| } // Use same state proxy |
| |
| |
| if (targetEl.stateProxy) { |
| textContent.stateProxy = targetEl.stateProxy; |
| } |
| } |
| |
| var labelStatesTexts = getLabelText(opt, labelStatesModels); |
| var normalModel = labelStatesModels.normal; |
| var showNormal = !!normalModel.getShallow('show'); |
| var normalStyle = createTextStyle(normalModel, stateSpecified && stateSpecified.normal, opt, false, !isSetOnText); |
| normalStyle.text = labelStatesTexts.normal; |
| |
| if (!isSetOnText) { |
| // Always create new |
| targetEl.setTextConfig(createTextConfig(normalModel, opt, false)); |
| } |
| |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| var stateName = SPECIAL_STATES[i]; |
| var stateModel = labelStatesModels[stateName]; |
| |
| if (stateModel) { |
| var stateObj = textContent.ensureState(stateName); |
| var stateShow = !!retrieve2(stateModel.getShallow('show'), showNormal); |
| |
| if (stateShow !== showNormal) { |
| stateObj.ignore = !stateShow; |
| } |
| |
| stateObj.style = createTextStyle(stateModel, stateSpecified && stateSpecified[stateName], opt, true, !isSetOnText); |
| stateObj.style.text = labelStatesTexts[stateName]; |
| |
| if (!isSetOnText) { |
| var targetElEmphasisState = targetEl.ensureState(stateName); |
| targetElEmphasisState.textConfig = createTextConfig(stateModel, opt, true); |
| } |
| } |
| } // PENDING: if there is many requirements that emphasis position |
| // need to be different from normal position, we might consider |
| // auto silent is those cases. |
| |
| |
| textContent.silent = !!normalModel.getShallow('silent'); // Keep x and y |
| |
| if (textContent.style.x != null) { |
| normalStyle.x = textContent.style.x; |
| } |
| |
| if (textContent.style.y != null) { |
| normalStyle.y = textContent.style.y; |
| } |
| |
| textContent.ignore = !showNormal; // Always create new style. |
| |
| textContent.useStyle(normalStyle); |
| textContent.dirty(); |
| |
| if (opt.enableTextSetter) { |
| labelInner(textContent).setLabelText = function (interpolatedValue) { |
| var labelStatesTexts = getLabelText(opt, labelStatesModels, interpolatedValue); |
| setLabelText(textContent, labelStatesTexts); |
| }; |
| } |
| } else if (textContent) { |
| // Not display rich text. |
| textContent.ignore = true; |
| } |
| |
| targetEl.dirty(); |
| } |
| function getLabelStatesModels(itemModel, labelName) { |
| labelName = labelName || 'label'; |
| var statesModels = { |
| normal: itemModel.getModel(labelName) |
| }; |
| |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| var stateName = SPECIAL_STATES[i]; |
| statesModels[stateName] = itemModel.getModel([stateName, labelName]); |
| } |
| |
| return statesModels; |
| } |
| /** |
| * Set basic textStyle properties. |
| */ |
| |
| function createTextStyle(textStyleModel, specifiedTextStyle, // Fixed style in the code. Can't be set by model. |
| opt, isNotNormal, isAttached // If text is attached on an element. If so, auto color will handling in zrender. |
| ) { |
| var textStyle = {}; |
| setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached); |
| specifiedTextStyle && extend(textStyle, specifiedTextStyle); // textStyle.host && textStyle.host.dirty && textStyle.host.dirty(false); |
| |
| return textStyle; |
| } |
| function createTextConfig(textStyleModel, opt, isNotNormal) { |
| opt = opt || {}; |
| var textConfig = {}; |
| var labelPosition; |
| var labelRotate = textStyleModel.getShallow('rotate'); |
| var labelDistance = retrieve2(textStyleModel.getShallow('distance'), isNotNormal ? null : 5); |
| var labelOffset = textStyleModel.getShallow('offset'); |
| labelPosition = textStyleModel.getShallow('position') || (isNotNormal ? null : 'inside'); // 'outside' is not a valid zr textPostion value, but used |
| // in bar series, and magric type should be considered. |
| |
| labelPosition === 'outside' && (labelPosition = opt.defaultOutsidePosition || 'top'); |
| |
| if (labelPosition != null) { |
| textConfig.position = labelPosition; |
| } |
| |
| if (labelOffset != null) { |
| textConfig.offset = labelOffset; |
| } |
| |
| if (labelRotate != null) { |
| labelRotate *= Math.PI / 180; |
| textConfig.rotation = labelRotate; |
| } |
| |
| if (labelDistance != null) { |
| textConfig.distance = labelDistance; |
| } // fill and auto is determined by the color of path fill if it's not specified by developers. |
| |
| |
| textConfig.outsideFill = textStyleModel.get('color') === 'inherit' ? opt.inheritColor || null : 'auto'; |
| return textConfig; |
| } |
| /** |
| * The uniform entry of set text style, that is, retrieve style definitions |
| * from `model` and set to `textStyle` object. |
| * |
| * Never in merge mode, but in overwrite mode, that is, all of the text style |
| * properties will be set. (Consider the states of normal and emphasis and |
| * default value can be adopted, merge would make the logic too complicated |
| * to manage.) |
| */ |
| |
| function setTextStyleCommon(textStyle, textStyleModel, opt, isNotNormal, isAttached) { |
| // Consider there will be abnormal when merge hover style to normal style if given default value. |
| opt = opt || EMPTY_OBJ; |
| var ecModel = textStyleModel.ecModel; |
| var globalTextStyle = ecModel && ecModel.option.textStyle; // Consider case: |
| // { |
| // data: [{ |
| // value: 12, |
| // label: { |
| // rich: { |
| // // no 'a' here but using parent 'a'. |
| // } |
| // } |
| // }], |
| // rich: { |
| // a: { ... } |
| // } |
| // } |
| |
| var richItemNames = getRichItemNames(textStyleModel); |
| var richResult; |
| |
| if (richItemNames) { |
| richResult = {}; |
| |
| for (var name_1 in richItemNames) { |
| if (richItemNames.hasOwnProperty(name_1)) { |
| // Cascade is supported in rich. |
| var richTextStyle = textStyleModel.getModel(['rich', name_1]); // In rich, never `disableBox`. |
| // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`, |
| // the default color `'blue'` will not be adopted if no color declared in `rich`. |
| // That might confuses users. So probably we should put `textStyleModel` as the |
| // root ancestor of the `richTextStyle`. But that would be a break change. |
| |
| setTokenTextStyle(richResult[name_1] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true); |
| } |
| } |
| } |
| |
| if (richResult) { |
| textStyle.rich = richResult; |
| } |
| |
| var overflow = textStyleModel.get('overflow'); |
| |
| if (overflow) { |
| textStyle.overflow = overflow; |
| } |
| |
| var margin = textStyleModel.get('minMargin'); |
| |
| if (margin != null) { |
| textStyle.margin = margin; |
| } |
| |
| setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false); |
| } // Consider case: |
| // { |
| // data: [{ |
| // value: 12, |
| // label: { |
| // rich: { |
| // // no 'a' here but using parent 'a'. |
| // } |
| // } |
| // }], |
| // rich: { |
| // a: { ... } |
| // } |
| // } |
| // TODO TextStyleModel |
| |
| |
| function getRichItemNames(textStyleModel) { |
| // Use object to remove duplicated names. |
| var richItemNameMap; |
| |
| while (textStyleModel && textStyleModel !== textStyleModel.ecModel) { |
| var rich = (textStyleModel.option || EMPTY_OBJ).rich; |
| |
| if (rich) { |
| richItemNameMap = richItemNameMap || {}; |
| var richKeys = keys(rich); |
| |
| for (var i = 0; i < richKeys.length; i++) { |
| var richKey = richKeys[i]; |
| richItemNameMap[richKey] = 1; |
| } |
| } |
| |
| textStyleModel = textStyleModel.parentModel; |
| } |
| |
| return richItemNameMap; |
| } |
| |
| var TEXT_PROPS_WITH_GLOBAL = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'textShadowColor', 'textShadowBlur', 'textShadowOffsetX', 'textShadowOffsetY']; |
| var TEXT_PROPS_SELF = ['align', 'lineHeight', 'width', 'height', 'tag', 'verticalAlign']; |
| var TEXT_PROPS_BOX = ['padding', 'borderWidth', 'borderRadius', 'borderDashOffset', 'backgroundColor', 'borderColor', 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; |
| |
| function setTokenTextStyle(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, isBlock, inRich) { |
| // In merge mode, default value should not be given. |
| globalTextStyle = !isNotNormal && globalTextStyle || EMPTY_OBJ; |
| var inheritColor = opt && opt.inheritColor; |
| var fillColor = textStyleModel.getShallow('color'); |
| var strokeColor = textStyleModel.getShallow('textBorderColor'); |
| var opacity = retrieve2(textStyleModel.getShallow('opacity'), globalTextStyle.opacity); |
| |
| if (fillColor === 'inherit' || fillColor === 'auto') { |
| if ("development" !== 'production') { |
| if (fillColor === 'auto') { |
| deprecateReplaceLog('color: \'auto\'', 'color: \'inherit\''); |
| } |
| } |
| |
| if (inheritColor) { |
| fillColor = inheritColor; |
| } else { |
| fillColor = null; |
| } |
| } |
| |
| if (strokeColor === 'inherit' || strokeColor === 'auto') { |
| if ("development" !== 'production') { |
| if (strokeColor === 'auto') { |
| deprecateReplaceLog('color: \'auto\'', 'color: \'inherit\''); |
| } |
| } |
| |
| if (inheritColor) { |
| strokeColor = inheritColor; |
| } else { |
| strokeColor = null; |
| } |
| } |
| |
| if (!isAttached) { |
| // Only use default global textStyle.color if text is individual. |
| // Otherwise it will use the strategy of attached text color because text may be on a path. |
| fillColor = fillColor || globalTextStyle.color; |
| strokeColor = strokeColor || globalTextStyle.textBorderColor; |
| } |
| |
| if (fillColor != null) { |
| textStyle.fill = fillColor; |
| } |
| |
| if (strokeColor != null) { |
| textStyle.stroke = strokeColor; |
| } |
| |
| var textBorderWidth = retrieve2(textStyleModel.getShallow('textBorderWidth'), globalTextStyle.textBorderWidth); |
| |
| if (textBorderWidth != null) { |
| textStyle.lineWidth = textBorderWidth; |
| } |
| |
| var textBorderType = retrieve2(textStyleModel.getShallow('textBorderType'), globalTextStyle.textBorderType); |
| |
| if (textBorderType != null) { |
| textStyle.lineDash = textBorderType; |
| } |
| |
| var textBorderDashOffset = retrieve2(textStyleModel.getShallow('textBorderDashOffset'), globalTextStyle.textBorderDashOffset); |
| |
| if (textBorderDashOffset != null) { |
| textStyle.lineDashOffset = textBorderDashOffset; |
| } |
| |
| if (!isNotNormal && opacity == null && !inRich) { |
| opacity = opt && opt.defaultOpacity; |
| } |
| |
| if (opacity != null) { |
| textStyle.opacity = opacity; |
| } // TODO |
| |
| |
| if (!isNotNormal && !isAttached) { |
| // Set default finally. |
| if (textStyle.fill == null && opt.inheritColor) { |
| textStyle.fill = opt.inheritColor; |
| } |
| } // Do not use `getFont` here, because merge should be supported, where |
| // part of these properties may be changed in emphasis style, and the |
| // others should remain their original value got from normal style. |
| |
| |
| for (var i = 0; i < TEXT_PROPS_WITH_GLOBAL.length; i++) { |
| var key = TEXT_PROPS_WITH_GLOBAL[i]; |
| var val = retrieve2(textStyleModel.getShallow(key), globalTextStyle[key]); |
| |
| if (val != null) { |
| textStyle[key] = val; |
| } |
| } |
| |
| for (var i = 0; i < TEXT_PROPS_SELF.length; i++) { |
| var key = TEXT_PROPS_SELF[i]; |
| var val = textStyleModel.getShallow(key); |
| |
| if (val != null) { |
| textStyle[key] = val; |
| } |
| } |
| |
| if (textStyle.verticalAlign == null) { |
| var baseline = textStyleModel.getShallow('baseline'); |
| |
| if (baseline != null) { |
| textStyle.verticalAlign = baseline; |
| } |
| } |
| |
| if (!isBlock || !opt.disableBox) { |
| for (var i = 0; i < TEXT_PROPS_BOX.length; i++) { |
| var key = TEXT_PROPS_BOX[i]; |
| var val = textStyleModel.getShallow(key); |
| |
| if (val != null) { |
| textStyle[key] = val; |
| } |
| } |
| |
| var borderType = textStyleModel.getShallow('borderType'); |
| |
| if (borderType != null) { |
| textStyle.borderDash = borderType; |
| } |
| |
| if ((textStyle.backgroundColor === 'auto' || textStyle.backgroundColor === 'inherit') && inheritColor) { |
| if ("development" !== 'production') { |
| if (textStyle.backgroundColor === 'auto') { |
| deprecateReplaceLog('backgroundColor: \'auto\'', 'backgroundColor: \'inherit\''); |
| } |
| } |
| |
| textStyle.backgroundColor = inheritColor; |
| } |
| |
| if ((textStyle.borderColor === 'auto' || textStyle.borderColor === 'inherit') && inheritColor) { |
| if ("development" !== 'production') { |
| if (textStyle.borderColor === 'auto') { |
| deprecateReplaceLog('borderColor: \'auto\'', 'borderColor: \'inherit\''); |
| } |
| } |
| |
| textStyle.borderColor = inheritColor; |
| } |
| } |
| } |
| |
| function getFont(opt, ecModel) { |
| var gTextStyleModel = ecModel && ecModel.getModel('textStyle'); |
| return trim([// FIXME in node-canvas fontWeight is before fontStyle |
| opt.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(' ')); |
| } |
| var labelInner = makeInner(); |
| function setLabelValueAnimation(label, labelStatesModels, value, getDefaultText) { |
| if (!label) { |
| return; |
| } |
| |
| var obj = labelInner(label); |
| obj.prevValue = obj.value; |
| obj.value = value; |
| var normalLabelModel = labelStatesModels.normal; |
| obj.valueAnimation = normalLabelModel.get('valueAnimation'); |
| |
| if (obj.valueAnimation) { |
| obj.precision = normalLabelModel.get('precision'); |
| obj.defaultInterpolatedText = getDefaultText; |
| obj.statesModels = labelStatesModels; |
| } |
| } |
| |
| var PATH_COLOR = ['textStyle', 'color']; |
| var textStyleParams = ['fontStyle', 'fontWeight', 'fontSize', 'fontFamily', 'padding', 'lineHeight', 'rich', 'width', 'height', 'overflow']; // TODO Performance improvement? |
| |
| var tmpText = new ZRText(); |
| |
| var TextStyleMixin = |
| /** @class */ |
| function () { |
| function TextStyleMixin() {} |
| /** |
| * Get color property or get color from option.textStyle.color |
| */ |
| // TODO Callback |
| |
| |
| TextStyleMixin.prototype.getTextColor = function (isEmphasis) { |
| var ecModel = this.ecModel; |
| return this.getShallow('color') || (!isEmphasis && ecModel ? ecModel.get(PATH_COLOR) : null); |
| }; |
| /** |
| * Create font string from fontStyle, fontWeight, fontSize, fontFamily |
| * @return {string} |
| */ |
| |
| |
| TextStyleMixin.prototype.getFont = function () { |
| return getFont({ |
| fontStyle: this.getShallow('fontStyle'), |
| fontWeight: this.getShallow('fontWeight'), |
| fontSize: this.getShallow('fontSize'), |
| fontFamily: this.getShallow('fontFamily') |
| }, this.ecModel); |
| }; |
| |
| TextStyleMixin.prototype.getTextRect = function (text) { |
| var style = { |
| text: text, |
| verticalAlign: this.getShallow('verticalAlign') || this.getShallow('baseline') |
| }; |
| |
| for (var i = 0; i < textStyleParams.length; i++) { |
| style[textStyleParams[i]] = this.getShallow(textStyleParams[i]); |
| } |
| |
| tmpText.useStyle(style); |
| tmpText.update(); |
| return tmpText.getBoundingRect(); |
| }; |
| |
| return TextStyleMixin; |
| }(); |
| |
| var LINE_STYLE_KEY_MAP = [['lineWidth', 'width'], ['stroke', 'color'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'type'], ['lineDashOffset', 'dashOffset'], ['lineCap', 'cap'], ['lineJoin', 'join'], ['miterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`. |
| // So do not transfer decal directly. |
| ]; |
| var getLineStyle = makeStyleMapper(LINE_STYLE_KEY_MAP); |
| |
| var LineStyleMixin = |
| /** @class */ |
| function () { |
| function LineStyleMixin() {} |
| |
| LineStyleMixin.prototype.getLineStyle = function (excludes) { |
| return getLineStyle(this, excludes); |
| }; |
| |
| return LineStyleMixin; |
| }(); |
| |
| var ITEM_STYLE_KEY_MAP = [['fill', 'color'], ['stroke', 'borderColor'], ['lineWidth', 'borderWidth'], ['opacity'], ['shadowBlur'], ['shadowOffsetX'], ['shadowOffsetY'], ['shadowColor'], ['lineDash', 'borderType'], ['lineDashOffset', 'borderDashOffset'], ['lineCap', 'borderCap'], ['lineJoin', 'borderJoin'], ['miterLimit', 'borderMiterLimit'] // Option decal is in `DecalObject` but style.decal is in `PatternObject`. |
| // So do not transfer decal directly. |
| ]; |
| var getItemStyle = makeStyleMapper(ITEM_STYLE_KEY_MAP); |
| |
| var ItemStyleMixin = |
| /** @class */ |
| function () { |
| function ItemStyleMixin() {} |
| |
| ItemStyleMixin.prototype.getItemStyle = function (excludes, includes) { |
| return getItemStyle(this, excludes, includes); |
| }; |
| |
| return ItemStyleMixin; |
| }(); |
| |
| var Model = |
| /** @class */ |
| function () { |
| function Model(option, parentModel, ecModel) { |
| this.parentModel = parentModel; |
| this.ecModel = ecModel; |
| this.option = option; // Simple optimization |
| // if (this.init) { |
| // if (arguments.length <= 4) { |
| // this.init(option, parentModel, ecModel, extraOpt); |
| // } |
| // else { |
| // this.init.apply(this, arguments); |
| // } |
| // } |
| } |
| |
| Model.prototype.init = function (option, parentModel, ecModel) { |
| var rest = []; |
| |
| for (var _i = 3; _i < arguments.length; _i++) { |
| rest[_i - 3] = arguments[_i]; |
| } |
| }; |
| /** |
| * Merge the input option to me. |
| */ |
| |
| |
| Model.prototype.mergeOption = function (option, ecModel) { |
| merge(this.option, option, true); |
| }; // `path` can be 'a.b.c', so the return value type have to be `ModelOption` |
| // TODO: TYPE strict key check? |
| // get(path: string | string[], ignoreParent?: boolean): ModelOption; |
| |
| |
| Model.prototype.get = function (path, ignoreParent) { |
| if (path == null) { |
| return this.option; |
| } |
| |
| return this._doGet(this.parsePath(path), !ignoreParent && this.parentModel); |
| }; |
| |
| Model.prototype.getShallow = function (key, ignoreParent) { |
| var option = this.option; |
| var val = option == null ? option : option[key]; |
| |
| if (val == null && !ignoreParent) { |
| var parentModel = this.parentModel; |
| |
| if (parentModel) { |
| // FIXME:TS do not know how to make it works |
| val = parentModel.getShallow(key); |
| } |
| } |
| |
| return val; |
| }; // `path` can be 'a.b.c', so the return value type have to be `Model<ModelOption>` |
| // getModel(path: string | string[], parentModel?: Model): Model; |
| // TODO 'a.b.c' is deprecated |
| |
| |
| Model.prototype.getModel = function (path, parentModel) { |
| var hasPath = path != null; |
| var pathFinal = hasPath ? this.parsePath(path) : null; |
| var obj = hasPath ? this._doGet(pathFinal) : this.option; |
| parentModel = parentModel || this.parentModel && this.parentModel.getModel(this.resolveParentPath(pathFinal)); |
| return new Model(obj, parentModel, this.ecModel); |
| }; |
| /** |
| * If model has option |
| */ |
| |
| |
| Model.prototype.isEmpty = function () { |
| return this.option == null; |
| }; |
| |
| Model.prototype.restoreData = function () {}; // Pending |
| |
| |
| Model.prototype.clone = function () { |
| var Ctor = this.constructor; |
| return new Ctor(clone(this.option)); |
| }; // setReadOnly(properties): void { |
| // clazzUtil.setReadOnly(this, properties); |
| // } |
| // If path is null/undefined, return null/undefined. |
| |
| |
| Model.prototype.parsePath = function (path) { |
| if (typeof path === 'string') { |
| return path.split('.'); |
| } |
| |
| return path; |
| }; // Resolve path for parent. Perhaps useful when parent use a different property. |
| // Default to be a identity resolver. |
| // Can be modified to a different resolver. |
| |
| |
| Model.prototype.resolveParentPath = function (path) { |
| return path; |
| }; // FIXME:TS check whether put this method here |
| |
| |
| Model.prototype.isAnimationEnabled = function () { |
| if (!env.node && this.option) { |
| if (this.option.animation != null) { |
| return !!this.option.animation; |
| } else if (this.parentModel) { |
| return this.parentModel.isAnimationEnabled(); |
| } |
| } |
| }; |
| |
| Model.prototype._doGet = function (pathArr, parentModel) { |
| var obj = this.option; |
| |
| if (!pathArr) { |
| return obj; |
| } |
| |
| for (var i = 0; i < pathArr.length; i++) { |
| // Ignore empty |
| if (!pathArr[i]) { |
| continue; |
| } // obj could be number/string/... (like 0) |
| |
| |
| obj = obj && typeof obj === 'object' ? obj[pathArr[i]] : null; |
| |
| if (obj == null) { |
| break; |
| } |
| } |
| |
| if (obj == null && parentModel) { |
| obj = parentModel._doGet(this.resolveParentPath(pathArr), parentModel.parentModel); |
| } |
| |
| return obj; |
| }; |
| |
| return Model; |
| }(); |
| |
| enableClassExtend(Model); |
| enableClassCheck(Model); |
| mixin(Model, LineStyleMixin); |
| mixin(Model, ItemStyleMixin); |
| mixin(Model, AreaStyleMixin); |
| mixin(Model, TextStyleMixin); |
| |
| var base = Math.round(Math.random() * 10); |
| /** |
| * @public |
| * @param {string} type |
| * @return {string} |
| */ |
| |
| function getUID(type) { |
| // Considering the case of crossing js context, |
| // use Math.random to make id as unique as possible. |
| return [type || '', base++].join('_'); |
| } |
| /** |
| * Implements `SubTypeDefaulterManager` for `target`. |
| */ |
| |
| function enableSubTypeDefaulter(target) { |
| var subTypeDefaulters = {}; |
| |
| target.registerSubTypeDefaulter = function (componentType, defaulter) { |
| var componentTypeInfo = parseClassType(componentType); |
| subTypeDefaulters[componentTypeInfo.main] = defaulter; |
| }; |
| |
| target.determineSubType = function (componentType, option) { |
| var type = option.type; |
| |
| if (!type) { |
| var componentTypeMain = parseClassType(componentType).main; |
| |
| if (target.hasSubTypes(componentType) && subTypeDefaulters[componentTypeMain]) { |
| type = subTypeDefaulters[componentTypeMain](option); |
| } |
| } |
| |
| return type; |
| }; |
| } |
| /** |
| * Implements `TopologicalTravelable<any>` for `entity`. |
| * |
| * Topological travel on Activity Network (Activity On Vertices). |
| * Dependencies is defined in Model.prototype.dependencies, like ['xAxis', 'yAxis']. |
| * If 'xAxis' or 'yAxis' is absent in componentTypeList, just ignore it in topology. |
| * If there is circular dependencey, Error will be thrown. |
| */ |
| |
| function enableTopologicalTravel(entity, dependencyGetter) { |
| /** |
| * @param targetNameList Target Component type list. |
| * Can be ['aa', 'bb', 'aa.xx'] |
| * @param fullNameList By which we can build dependency graph. |
| * @param callback Params: componentType, dependencies. |
| * @param context Scope of callback. |
| */ |
| entity.topologicalTravel = function (targetNameList, fullNameList, callback, context) { |
| if (!targetNameList.length) { |
| return; |
| } |
| |
| var result = makeDepndencyGraph(fullNameList); |
| var graph = result.graph; |
| var noEntryList = result.noEntryList; |
| var targetNameSet = {}; |
| each(targetNameList, function (name) { |
| targetNameSet[name] = true; |
| }); |
| |
| while (noEntryList.length) { |
| var currComponentType = noEntryList.pop(); |
| var currVertex = graph[currComponentType]; |
| var isInTargetNameSet = !!targetNameSet[currComponentType]; |
| |
| if (isInTargetNameSet) { |
| callback.call(context, currComponentType, currVertex.originalDeps.slice()); |
| delete targetNameSet[currComponentType]; |
| } |
| |
| each(currVertex.successor, isInTargetNameSet ? removeEdgeAndAdd : removeEdge); |
| } |
| |
| each(targetNameSet, function () { |
| var errMsg = ''; |
| |
| if ("development" !== 'production') { |
| errMsg = makePrintable('Circular dependency may exists: ', targetNameSet, targetNameList, fullNameList); |
| } |
| |
| throw new Error(errMsg); |
| }); |
| |
| function removeEdge(succComponentType) { |
| graph[succComponentType].entryCount--; |
| |
| if (graph[succComponentType].entryCount === 0) { |
| noEntryList.push(succComponentType); |
| } |
| } // Consider this case: legend depends on series, and we call |
| // chart.setOption({series: [...]}), where only series is in option. |
| // If we do not have 'removeEdgeAndAdd', legendModel.mergeOption will |
| // not be called, but only sereis.mergeOption is called. Thus legend |
| // have no chance to update its local record about series (like which |
| // name of series is available in legend). |
| |
| |
| function removeEdgeAndAdd(succComponentType) { |
| targetNameSet[succComponentType] = true; |
| removeEdge(succComponentType); |
| } |
| }; |
| |
| function makeDepndencyGraph(fullNameList) { |
| var graph = {}; |
| var noEntryList = []; |
| each(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(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(originalDeps, function (dep) { |
| indexOf(fullNameList, dep) >= 0 && availableDeps.push(dep); |
| }); |
| return availableDeps; |
| } |
| } |
| function inheritDefaultOption(superOption, subOption) { |
| // See also `model/Component.ts#getDefaultOption` |
| return merge(merge({}, superOption, true), subOption, true); |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| |
| /** |
| * Language: English. |
| */ |
| var langEN = { |
| time: { |
| month: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], |
| monthAbbr: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], |
| dayOfWeek: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], |
| dayOfWeekAbbr: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] |
| }, |
| legend: { |
| selector: { |
| all: 'All', |
| inverse: 'Inv' |
| } |
| }, |
| toolbox: { |
| brush: { |
| title: { |
| rect: 'Box Select', |
| polygon: 'Lasso Select', |
| lineX: 'Horizontally Select', |
| lineY: 'Vertically Select', |
| keep: 'Keep Selections', |
| clear: 'Clear Selections' |
| } |
| }, |
| dataView: { |
| title: 'Data View', |
| lang: ['Data View', 'Close', 'Refresh'] |
| }, |
| dataZoom: { |
| title: { |
| zoom: 'Zoom', |
| back: 'Zoom Reset' |
| } |
| }, |
| magicType: { |
| title: { |
| line: 'Switch to Line Chart', |
| bar: 'Switch to Bar Chart', |
| stack: 'Stack', |
| tiled: 'Tile' |
| } |
| }, |
| restore: { |
| title: 'Restore' |
| }, |
| saveAsImage: { |
| title: 'Save as Image', |
| lang: ['Right Click to Save Image'] |
| } |
| }, |
| series: { |
| typeNames: { |
| pie: 'Pie chart', |
| bar: 'Bar chart', |
| line: 'Line chart', |
| scatter: 'Scatter plot', |
| effectScatter: 'Ripple scatter plot', |
| radar: 'Radar chart', |
| tree: 'Tree', |
| treemap: 'Treemap', |
| boxplot: 'Boxplot', |
| candlestick: 'Candlestick', |
| k: 'K line chart', |
| heatmap: 'Heat map', |
| map: 'Map', |
| parallel: 'Parallel coordinate map', |
| lines: 'Line graph', |
| graph: 'Relationship graph', |
| sankey: 'Sankey diagram', |
| funnel: 'Funnel chart', |
| gauge: 'Gauge', |
| pictorialBar: 'Pictorial bar', |
| themeRiver: 'Theme River Map', |
| sunburst: 'Sunburst' |
| } |
| }, |
| aria: { |
| general: { |
| withTitle: 'This is a chart about "{title}"', |
| withoutTitle: 'This is a chart' |
| }, |
| series: { |
| single: { |
| prefix: '', |
| withName: ' with type {seriesType} named {seriesName}.', |
| withoutName: ' with type {seriesType}.' |
| }, |
| multiple: { |
| prefix: '. It consists of {seriesCount} series count.', |
| withName: ' The {seriesId} series is a {seriesType} representing {seriesName}.', |
| withoutName: ' The {seriesId} series is a {seriesType}.', |
| separator: { |
| middle: '', |
| end: '' |
| } |
| } |
| }, |
| data: { |
| allData: 'The data is as follows: ', |
| partialData: 'The first {displayCnt} items are: ', |
| withName: 'the data for {name} is {value}', |
| withoutName: '{value}', |
| separator: { |
| middle: ', ', |
| end: '. ' |
| } |
| } |
| } |
| }; |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var langZH = { |
| time: { |
| month: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'], |
| monthAbbr: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], |
| dayOfWeek: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'], |
| dayOfWeekAbbr: ['日', '一', '二', '三', '四', '五', '六'] |
| }, |
| legend: { |
| selector: { |
| all: '全选', |
| inverse: '反选' |
| } |
| }, |
| toolbox: { |
| brush: { |
| title: { |
| rect: '矩形选择', |
| polygon: '圈选', |
| lineX: '横向选择', |
| lineY: '纵向选择', |
| keep: '保持选择', |
| clear: '清除选择' |
| } |
| }, |
| dataView: { |
| title: '数据视图', |
| lang: ['数据视图', '关闭', '刷新'] |
| }, |
| dataZoom: { |
| title: { |
| zoom: '区域缩放', |
| back: '区域缩放还原' |
| } |
| }, |
| magicType: { |
| title: { |
| line: '切换为折线图', |
| bar: '切换为柱状图', |
| stack: '切换为堆叠', |
| tiled: '切换为平铺' |
| } |
| }, |
| restore: { |
| title: '还原' |
| }, |
| saveAsImage: { |
| title: '保存为图片', |
| lang: ['右键另存为图片'] |
| } |
| }, |
| series: { |
| typeNames: { |
| pie: '饼图', |
| bar: '柱状图', |
| line: '折线图', |
| scatter: '散点图', |
| effectScatter: '涟漪散点图', |
| radar: '雷达图', |
| tree: '树图', |
| treemap: '矩形树图', |
| boxplot: '箱型图', |
| candlestick: 'K线图', |
| k: 'K线图', |
| heatmap: '热力图', |
| map: '地图', |
| parallel: '平行坐标图', |
| lines: '线图', |
| graph: '关系图', |
| sankey: '桑基图', |
| funnel: '漏斗图', |
| gauge: '仪表盘图', |
| pictorialBar: '象形柱图', |
| themeRiver: '主题河流图', |
| sunburst: '旭日图' |
| } |
| }, |
| aria: { |
| general: { |
| withTitle: '这是一个关于“{title}”的图表。', |
| withoutTitle: '这是一个图表,' |
| }, |
| series: { |
| single: { |
| prefix: '', |
| withName: '图表类型是{seriesType},表示{seriesName}。', |
| withoutName: '图表类型是{seriesType}。' |
| }, |
| multiple: { |
| prefix: '它由{seriesCount}个图表系列组成。', |
| withName: '第{seriesId}个系列是一个表示{seriesName}的{seriesType},', |
| withoutName: '第{seriesId}个系列是一个{seriesType},', |
| separator: { |
| middle: ';', |
| end: '。' |
| } |
| } |
| }, |
| data: { |
| allData: '其数据是——', |
| partialData: '其中,前{displayCnt}项是——', |
| withName: '{name}的数据是{value}', |
| withoutName: '{value}', |
| separator: { |
| middle: ',', |
| end: '' |
| } |
| } |
| } |
| }; |
| |
| var LOCALE_ZH = 'ZH'; |
| var LOCALE_EN = 'EN'; |
| var DEFAULT_LOCALE = LOCALE_EN; |
| var localeStorage = {}; |
| var localeModels = {}; |
| var SYSTEM_LANG = !env.domSupported ? DEFAULT_LOCALE : function () { |
| var langStr = ( |
| /* eslint-disable-next-line */ |
| document.documentElement.lang || navigator.language || navigator.browserLanguage).toUpperCase(); |
| return langStr.indexOf(LOCALE_ZH) > -1 ? LOCALE_ZH : DEFAULT_LOCALE; |
| }(); |
| function registerLocale(locale, localeObj) { |
| locale = locale.toUpperCase(); |
| localeModels[locale] = new Model(localeObj); |
| localeStorage[locale] = localeObj; |
| } // export function getLocale(locale: string) { |
| // return localeStorage[locale]; |
| // } |
| |
| function createLocaleObject(locale) { |
| if (isString(locale)) { |
| var localeObj = localeStorage[locale.toUpperCase()] || {}; |
| |
| if (locale === LOCALE_ZH || locale === LOCALE_EN) { |
| return clone(localeObj); |
| } else { |
| return merge(clone(localeObj), clone(localeStorage[DEFAULT_LOCALE]), false); |
| } |
| } else { |
| return merge(clone(locale), clone(localeStorage[DEFAULT_LOCALE]), false); |
| } |
| } |
| function getLocaleModel(lang) { |
| return localeModels[lang]; |
| } |
| function getDefaultLocaleModel() { |
| return localeModels[DEFAULT_LOCALE]; |
| } // Default locale |
| |
| registerLocale(LOCALE_EN, langEN); |
| registerLocale(LOCALE_ZH, langZH); |
| |
| var ONE_SECOND = 1000; |
| var ONE_MINUTE = ONE_SECOND * 60; |
| var ONE_HOUR = ONE_MINUTE * 60; |
| var ONE_DAY = ONE_HOUR * 24; |
| var ONE_YEAR = ONE_DAY * 365; |
| var defaultLeveledFormatter = { |
| year: '{yyyy}', |
| month: '{MMM}', |
| day: '{d}', |
| hour: '{HH}:{mm}', |
| minute: '{HH}:{mm}', |
| second: '{HH}:{mm}:{ss}', |
| millisecond: '{HH}:{mm}:{ss} {SSS}', |
| none: '{yyyy}-{MM}-{dd} {HH}:{mm}:{ss} {SSS}' |
| }; |
| var fullDayFormatter = '{yyyy}-{MM}-{dd}'; |
| var fullLeveledFormatter = { |
| year: '{yyyy}', |
| month: '{yyyy}-{MM}', |
| day: fullDayFormatter, |
| hour: fullDayFormatter + ' ' + defaultLeveledFormatter.hour, |
| minute: fullDayFormatter + ' ' + defaultLeveledFormatter.minute, |
| second: fullDayFormatter + ' ' + defaultLeveledFormatter.second, |
| millisecond: defaultLeveledFormatter.none |
| }; |
| var primaryTimeUnits = ['year', 'month', 'day', 'hour', 'minute', 'second', 'millisecond']; |
| var timeUnits = ['year', 'half-year', 'quarter', 'month', 'week', 'half-week', 'day', 'half-day', 'quarter-day', 'hour', 'minute', 'second', 'millisecond']; |
| function pad(str, len) { |
| str += ''; |
| return '0000'.substr(0, len - str.length) + str; |
| } |
| function getPrimaryTimeUnit(timeUnit) { |
| switch (timeUnit) { |
| case 'half-year': |
| case 'quarter': |
| return 'month'; |
| |
| case 'week': |
| case 'half-week': |
| return 'day'; |
| |
| case 'half-day': |
| case 'quarter-day': |
| return 'hour'; |
| |
| default: |
| // year, minutes, second, milliseconds |
| return timeUnit; |
| } |
| } |
| function isPrimaryTimeUnit(timeUnit) { |
| return timeUnit === getPrimaryTimeUnit(timeUnit); |
| } |
| function getDefaultFormatPrecisionOfInterval(timeUnit) { |
| switch (timeUnit) { |
| case 'year': |
| case 'month': |
| return 'day'; |
| |
| case 'millisecond': |
| return 'millisecond'; |
| |
| default: |
| // Also for day, hour, minute, second |
| return 'second'; |
| } |
| } |
| function format( // Note: The result based on `isUTC` are totally different, which can not be just simply |
| // substituted by the result without `isUTC`. So we make the param `isUTC` mandatory. |
| time, template, isUTC, lang) { |
| var date = parseDate(time); |
| var y = date[fullYearGetterName(isUTC)](); |
| var M = date[monthGetterName(isUTC)]() + 1; |
| var q = Math.floor((M - 1) / 3) + 1; |
| var d = date[dateGetterName(isUTC)](); |
| var e = date['get' + (isUTC ? 'UTC' : '') + 'Day'](); |
| var H = date[hoursGetterName(isUTC)](); |
| var h = (H - 1) % 12 + 1; |
| var m = date[minutesGetterName(isUTC)](); |
| var s = date[secondsGetterName(isUTC)](); |
| var S = date[millisecondsGetterName(isUTC)](); |
| var localeModel = lang instanceof Model ? lang : getLocaleModel(lang || SYSTEM_LANG) || getDefaultLocaleModel(); |
| var timeModel = localeModel.getModel('time'); |
| var month = timeModel.get('month'); |
| var monthAbbr = timeModel.get('monthAbbr'); |
| var dayOfWeek = timeModel.get('dayOfWeek'); |
| var dayOfWeekAbbr = timeModel.get('dayOfWeekAbbr'); |
| return (template || '').replace(/{yyyy}/g, y + '').replace(/{yy}/g, y % 100 + '').replace(/{Q}/g, q + '').replace(/{MMMM}/g, month[M - 1]).replace(/{MMM}/g, monthAbbr[M - 1]).replace(/{MM}/g, pad(M, 2)).replace(/{M}/g, M + '').replace(/{dd}/g, pad(d, 2)).replace(/{d}/g, d + '').replace(/{eeee}/g, dayOfWeek[e]).replace(/{ee}/g, dayOfWeekAbbr[e]).replace(/{e}/g, e + '').replace(/{HH}/g, pad(H, 2)).replace(/{H}/g, H + '').replace(/{hh}/g, pad(h + '', 2)).replace(/{h}/g, h + '').replace(/{mm}/g, pad(m, 2)).replace(/{m}/g, m + '').replace(/{ss}/g, pad(s, 2)).replace(/{s}/g, s + '').replace(/{SSS}/g, pad(S, 3)).replace(/{S}/g, S + ''); |
| } |
| function leveledFormat(tick, idx, formatter, lang, isUTC) { |
| var template = null; |
| |
| if (isString(formatter)) { |
| // Single formatter for all units at all levels |
| template = formatter; |
| } else if (isFunction(formatter)) { |
| // Callback formatter |
| template = formatter(tick.value, idx, { |
| level: tick.level |
| }); |
| } else { |
| var defaults$1 = extend({}, defaultLeveledFormatter); |
| |
| if (tick.level > 0) { |
| for (var i = 0; i < primaryTimeUnits.length; ++i) { |
| defaults$1[primaryTimeUnits[i]] = "{primary|" + defaults$1[primaryTimeUnits[i]] + "}"; |
| } |
| } |
| |
| var mergedFormatter = formatter ? formatter.inherit === false ? formatter // Use formatter with bigger units |
| : defaults(formatter, defaults$1) : defaults$1; |
| var unit = getUnitFromValue(tick.value, isUTC); |
| |
| if (mergedFormatter[unit]) { |
| template = mergedFormatter[unit]; |
| } else if (mergedFormatter.inherit) { |
| // Unit formatter is not defined and should inherit from bigger units |
| var targetId = timeUnits.indexOf(unit); |
| |
| for (var i = targetId - 1; i >= 0; --i) { |
| if (mergedFormatter[unit]) { |
| template = mergedFormatter[unit]; |
| break; |
| } |
| } |
| |
| template = template || defaults$1.none; |
| } |
| |
| if (isArray(template)) { |
| var levelId = tick.level == null ? 0 : tick.level >= 0 ? tick.level : template.length + tick.level; |
| levelId = Math.min(levelId, template.length - 1); |
| template = template[levelId]; |
| } |
| } |
| |
| return format(new Date(tick.value), template, isUTC, lang); |
| } |
| function getUnitFromValue(value, isUTC) { |
| var date = parseDate(value); |
| var M = date[monthGetterName(isUTC)]() + 1; |
| var d = date[dateGetterName(isUTC)](); |
| var h = date[hoursGetterName(isUTC)](); |
| var m = date[minutesGetterName(isUTC)](); |
| var s = date[secondsGetterName(isUTC)](); |
| var S = date[millisecondsGetterName(isUTC)](); |
| var isSecond = S === 0; |
| var isMinute = isSecond && s === 0; |
| var isHour = isMinute && m === 0; |
| var isDay = isHour && h === 0; |
| var isMonth = isDay && d === 1; |
| var isYear = isMonth && M === 1; |
| |
| if (isYear) { |
| return 'year'; |
| } else if (isMonth) { |
| return 'month'; |
| } else if (isDay) { |
| return 'day'; |
| } else if (isHour) { |
| return 'hour'; |
| } else if (isMinute) { |
| return 'minute'; |
| } else if (isSecond) { |
| return 'second'; |
| } else { |
| return 'millisecond'; |
| } |
| } |
| function getUnitValue(value, unit, isUTC) { |
| var date = isNumber(value) ? parseDate(value) : value; |
| unit = unit || getUnitFromValue(value, isUTC); |
| |
| switch (unit) { |
| case 'year': |
| return date[fullYearGetterName(isUTC)](); |
| |
| case 'half-year': |
| return date[monthGetterName(isUTC)]() >= 6 ? 1 : 0; |
| |
| case 'quarter': |
| return Math.floor((date[monthGetterName(isUTC)]() + 1) / 4); |
| |
| case 'month': |
| return date[monthGetterName(isUTC)](); |
| |
| case 'day': |
| return date[dateGetterName(isUTC)](); |
| |
| case 'half-day': |
| return date[hoursGetterName(isUTC)]() / 24; |
| |
| case 'hour': |
| return date[hoursGetterName(isUTC)](); |
| |
| case 'minute': |
| return date[minutesGetterName(isUTC)](); |
| |
| case 'second': |
| return date[secondsGetterName(isUTC)](); |
| |
| case 'millisecond': |
| return date[millisecondsGetterName(isUTC)](); |
| } |
| } |
| function fullYearGetterName(isUTC) { |
| return isUTC ? 'getUTCFullYear' : 'getFullYear'; |
| } |
| function monthGetterName(isUTC) { |
| return isUTC ? 'getUTCMonth' : 'getMonth'; |
| } |
| function dateGetterName(isUTC) { |
| return isUTC ? 'getUTCDate' : 'getDate'; |
| } |
| function hoursGetterName(isUTC) { |
| return isUTC ? 'getUTCHours' : 'getHours'; |
| } |
| function minutesGetterName(isUTC) { |
| return isUTC ? 'getUTCMinutes' : 'getMinutes'; |
| } |
| function secondsGetterName(isUTC) { |
| return isUTC ? 'getUTCSeconds' : 'getSeconds'; |
| } |
| function millisecondsGetterName(isUTC) { |
| return isUTC ? 'getUTCMilliseconds' : 'getMilliseconds'; |
| } |
| function fullYearSetterName(isUTC) { |
| return isUTC ? 'setUTCFullYear' : 'setFullYear'; |
| } |
| function monthSetterName(isUTC) { |
| return isUTC ? 'setUTCMonth' : 'setMonth'; |
| } |
| function dateSetterName(isUTC) { |
| return isUTC ? 'setUTCDate' : 'setDate'; |
| } |
| function hoursSetterName(isUTC) { |
| return isUTC ? 'setUTCHours' : 'setHours'; |
| } |
| function minutesSetterName(isUTC) { |
| return isUTC ? 'setUTCMinutes' : 'setMinutes'; |
| } |
| function secondsSetterName(isUTC) { |
| return isUTC ? 'setUTCSeconds' : 'setSeconds'; |
| } |
| function millisecondsSetterName(isUTC) { |
| return isUTC ? 'setUTCMilliseconds' : 'setMilliseconds'; |
| } |
| |
| function getTextRect(text, font, align, verticalAlign, padding, rich, truncate, lineHeight) { |
| var textEl = new ZRText({ |
| style: { |
| text: text, |
| font: font, |
| align: align, |
| verticalAlign: verticalAlign, |
| padding: padding, |
| rich: rich, |
| overflow: truncate ? 'truncate' : null, |
| lineHeight: lineHeight |
| } |
| }); |
| return textEl.getBoundingRect(); |
| } |
| |
| /** |
| * Add a comma each three digit. |
| */ |
| |
| function addCommas(x) { |
| if (!isNumeric(x)) { |
| return isString(x) ? x : '-'; |
| } |
| |
| var parts = (x + '').split('.'); |
| return parts[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, '$1,') + (parts.length > 1 ? '.' + parts[1] : ''); |
| } |
| 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 TPL_VAR_ALIAS = ['a', 'b', 'c', 'd', 'e', 'f', 'g']; |
| |
| var wrapVar = function (varName, seriesIdx) { |
| return '{' + varName + (seriesIdx == null ? '' : seriesIdx) + '}'; |
| }; |
| /** |
| * Template formatter |
| * @param {Array.<Object>|Object} paramsList |
| */ |
| |
| |
| 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 < seriesLen; seriesIdx++) { |
| for (var k = 0; k < $vars.length; k++) { |
| var val = paramsList[seriesIdx][$vars[k]]; |
| tpl = tpl.replace(wrapVar(TPL_VAR_ALIAS[k], seriesIdx), encode ? encodeHTML(val) : val); |
| } |
| } |
| |
| return tpl; |
| } |
| function getTooltipMarker(inOpt, extraCssText) { |
| var opt = isString(inOpt) ? { |
| color: inOpt, |
| extraCssText: extraCssText |
| } : inOpt || {}; |
| var color = opt.color; |
| var type = opt.type; |
| extraCssText = opt.extraCssText; |
| var renderMode = opt.renderMode || 'html'; |
| |
| if (!color) { |
| return ''; |
| } |
| |
| if (renderMode === 'html') { |
| return type === 'subItem' ? '<span style="display:inline-block;vertical-align:middle;margin-right:8px;margin-left:3px;' + 'border-radius:4px;width:4px;height:4px;background-color:' // Only support string |
| + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>' : '<span style="display:inline-block;margin-right:4px;' + 'border-radius:10px;width:10px;height:10px;background-color:' + encodeHTML(color) + ';' + (extraCssText || '') + '"></span>'; |
| } else { |
| // Should better not to auto generate style name by auto-increment number here. |
| // Because this util is usually called in tooltip formatter, which is probably |
| // called repeatedly when mouse move and the auto-increment number increases fast. |
| // Users can make their own style name by theirselves, make it unique and readable. |
| var markerId = opt.markerId || 'markerX'; |
| return { |
| renderMode: renderMode, |
| content: '{' + markerId + '|} ', |
| style: type === 'subItem' ? { |
| width: 4, |
| height: 4, |
| borderRadius: 2, |
| backgroundColor: color |
| } : { |
| width: 10, |
| height: 10, |
| borderRadius: 5, |
| backgroundColor: color |
| } |
| }; |
| } |
| } |
| /** |
| * @deprecated Use `time/format` instead. |
| * ISO Date format |
| * @param {string} tpl |
| * @param {number} value |
| * @param {boolean} [isUTC=false] Default in local time. |
| * see `module:echarts/scale/Time` |
| * and `module:echarts/util/number#parseDate`. |
| * @inner |
| */ |
| |
| function formatTime(tpl, value, isUTC) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('echarts.format.formatTime', 'echarts.time.format'); |
| } |
| |
| if (tpl === 'week' || tpl === 'month' || tpl === 'quarter' || tpl === 'half-year' || tpl === 'year') { |
| tpl = 'MM-dd\nyyyy'; |
| } |
| |
| var date = parseDate(value); |
| var getUTC = isUTC ? 'getUTC' : 'get'; |
| var y = date[getUTC + 'FullYear'](); |
| var M = date[getUTC + 'Month']() + 1; |
| var d = date[getUTC + 'Date'](); |
| var h = date[getUTC + 'Hours'](); |
| var m = date[getUTC + 'Minutes'](); |
| var s = date[getUTC + 'Seconds'](); |
| var S = date[getUTC + 'Milliseconds'](); |
| tpl = tpl.replace('MM', pad(M, 2)).replace('M', M).replace('yyyy', y).replace('yy', pad(y % 100 + '', 2)).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; |
| } |
| /** |
| * Capital first |
| * @param {string} str |
| * @return {string} |
| */ |
| |
| function capitalFirst(str) { |
| return str ? str.charAt(0).toUpperCase() + str.substr(1) : str; |
| } |
| /** |
| * @return Never be null/undefined. |
| */ |
| |
| function convertToColorString(color, defaultColor) { |
| defaultColor = defaultColor || 'transparent'; |
| return isString(color) ? color : isObject(color) ? color.colorStops && (color.colorStops[0] || {}).color || defaultColor : defaultColor; |
| } |
| |
| var each$1 = each; |
| /** |
| * @public |
| */ |
| |
| var LOCATION_PARAMS = ['left', 'right', 'top', 'bottom', 'width', 'height']; |
| /** |
| * @public |
| */ |
| |
| 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 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 |
| // FIXME compare before adding gap? |
| |
| if (nextX > maxWidth || child.newline) { |
| x = 0; |
| nextX = moveX; |
| y += currentLineMaxSize + gap; |
| currentLineMaxSize = rect.height; |
| } else { |
| // FIXME: consider rect.y is not `0`? |
| currentLineMaxSize = 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 |
| |
| if (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; |
| } |
| |
| child.x = x; |
| child.y = y; |
| child.markRedraw(); |
| orient === 'horizontal' ? x = nextX + gap : y = nextY + gap; |
| }); |
| } |
| /** |
| * VBox layouting |
| * @param {module:zrender/graphic/Group} group |
| * @param {number} gap |
| * @param {number} [width=Infinity] |
| * @param {number} [height=Infinity] |
| */ |
| |
| var vbox = curry(boxLayout, 'vertical'); |
| /** |
| * HBox layouting |
| * @param {module:zrender/graphic/Group} group |
| * @param {number} gap |
| * @param {number} [width=Infinity] |
| * @param {number} [height=Infinity] |
| */ |
| |
| var hbox = curry(boxLayout, 'horizontal'); |
| /** |
| * Parse position info. |
| */ |
| |
| 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 |
| |
| if (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 |
| // 1. Graph should not exceeds the container |
| // 2. Aspect must be keeped |
| // 3. Graph should take the space as more as possible |
| // FIXME |
| // Margin is not considered, because there is no case that both |
| // using margin and aspect so far. |
| if (isNaN(width) && isNaN(height)) { |
| if (aspect > containerWidth / containerHeight) { |
| width = containerWidth * 0.8; |
| } else { |
| height = containerHeight * 0.8; |
| } |
| } // Calculate width or height with given aspect |
| |
| |
| if (isNaN(width)) { |
| width = aspect * height; |
| } |
| |
| if (isNaN(height)) { |
| height = width / aspect; |
| } |
| } // If left is not specified, calculate left from right and width |
| |
| |
| if (isNaN(left)) { |
| left = containerWidth - right - width - horizontalMargin; |
| } |
| |
| if (isNaN(top)) { |
| top = containerHeight - bottom - height - verticalMargin; |
| } // Align left and top |
| |
| |
| switch (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 |
| |
| |
| left = left || 0; |
| top = top || 0; |
| |
| if (isNaN(width)) { |
| // Width may be NaN if only one value is given except width |
| width = containerWidth - horizontalMargin - left - (right || 0); |
| } |
| |
| if (isNaN(height)) { |
| // Height may be NaN if only one value is given except height |
| height = containerHeight - verticalMargin - top - (bottom || 0); |
| } |
| |
| var rect = new BoundingRect(left + margin[3], top + margin[0], width, height); |
| rect.margin = margin; |
| return rect; |
| } |
| function fetchLayoutMode(ins) { |
| var layoutMode = ins.layoutMode || ins.constructor.layoutMode; |
| return isObject(layoutMode) ? layoutMode : layoutMode ? { |
| type: layoutMode |
| } : null; |
| } |
| /** |
| * Consider Case: |
| * When default option has {left: 0, width: 100}, and we set {right: 0} |
| * through setOption or media query, using normal zrUtil.merge will cause |
| * {right: 0} does not take effect. |
| * |
| * @example |
| * ComponentModel.extend({ |
| * init: function () { |
| * ... |
| * let inputPositionParams = layout.getLayoutParams(option); |
| * this.mergeOption(inputPositionParams); |
| * }, |
| * mergeOption: function (newOption) { |
| * newOption && zrUtil.merge(thisOption, newOption, true); |
| * layout.mergeLayoutParam(thisOption, newOption); |
| * } |
| * }); |
| * |
| * @param targetOption |
| * @param newOption |
| * @param opt |
| */ |
| |
| function mergeLayoutParam(targetOption, newOption, opt) { |
| var ignoreSize = opt && opt.ignoreSize; |
| !isArray(ignoreSize) && (ignoreSize = [ignoreSize, ignoreSize]); |
| var hResult = merge(HV_NAMES[0], 0); |
| var vResult = merge(HV_NAMES[1], 1); |
| copy(HV_NAMES[0], targetOption, hResult); |
| copy(HV_NAMES[1], targetOption, vResult); |
| |
| function merge(names, hvIdx) { |
| var newParams = {}; |
| var newValueCount = 0; |
| var merged = {}; |
| var mergedValueCount = 0; |
| var enoughParamNumber = 2; |
| each$1(names, function (name) { |
| merged[name] = targetOption[name]; |
| }); |
| each$1(names, function (name) { |
| // Consider case: newOption.width is null, which is |
| // set by user for removing width setting. |
| hasProp(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. |
| if (hasValue(newOption, names[1])) { |
| merged[names[2]] = null; |
| } else if (hasValue(newOption, names[2])) { |
| merged[names[1]] = null; |
| } |
| |
| return merged; |
| } // Case: newOption: {width: ..., right: ...}, |
| // or targetOption: {right: ...} and newOption: {width: ...}, |
| // There is no conflict when merged only has params count |
| // little than enoughParamNumber. |
| |
| |
| if (mergedValueCount === enoughParamNumber || !newValueCount) { |
| return merged; |
| } // Case: newOption: {width: ..., right: ...}, |
| // Than we can make sure user only want those two, and ignore |
| // all origin params in targetOption. |
| else if (newValueCount >= enoughParamNumber) { |
| return newParams; |
| } else { |
| // Chose another param from targetOption by priority. |
| for (var i = 0; i < names.length; i++) { |
| var name_1 = names[i]; |
| |
| if (!hasProp(newParams, name_1) && hasProp(targetOption, name_1)) { |
| newParams[name_1] = targetOption[name_1]; |
| break; |
| } |
| } |
| |
| return newParams; |
| } |
| } |
| |
| function hasProp(obj, name) { |
| return obj.hasOwnProperty(name); |
| } |
| |
| function hasValue(obj, name) { |
| return obj[name] != null && obj[name] !== 'auto'; |
| } |
| |
| function copy(names, target, source) { |
| each$1(names, function (name) { |
| target[name] = source[name]; |
| }); |
| } |
| } |
| /** |
| * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. |
| */ |
| |
| function getLayoutParams(source) { |
| return copyLayoutParams({}, source); |
| } |
| /** |
| * Retrieve 'left', 'right', 'top', 'bottom', 'width', 'height' from object. |
| * @param {Object} source |
| * @return {Object} Result contains those props. |
| */ |
| |
| function copyLayoutParams(target, source) { |
| source && target && each$1(LOCATION_PARAMS, function (name) { |
| source.hasOwnProperty(name) && (target[name] = source[name]); |
| }); |
| return target; |
| } |
| |
| var inner = makeInner(); |
| |
| var ComponentModel = |
| /** @class */ |
| function (_super) { |
| __extends(ComponentModel, _super); |
| |
| function ComponentModel(option, parentModel, ecModel) { |
| var _this = _super.call(this, option, parentModel, ecModel) || this; |
| |
| _this.uid = getUID('ec_cpt_model'); |
| return _this; |
| } |
| |
| ComponentModel.prototype.init = function (option, parentModel, ecModel) { |
| this.mergeDefaultAndTheme(option, ecModel); |
| }; |
| |
| ComponentModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { |
| var layoutMode = fetchLayoutMode(this); |
| 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); |
| } |
| }; |
| |
| ComponentModel.prototype.mergeOption = function (option, ecModel) { |
| merge(this.option, option, true); |
| var layoutMode = fetchLayoutMode(this); |
| |
| if (layoutMode) { |
| mergeLayoutParam(this.option, option, layoutMode); |
| } |
| }; |
| /** |
| * Called immediately after `init` or `mergeOption` of this instance called. |
| */ |
| |
| |
| ComponentModel.prototype.optionUpdated = function (newCptOption, isInit) {}; |
| /** |
| * [How to declare defaultOption]: |
| * |
| * (A) If using class declaration in typescript (since echarts 5): |
| * ```ts |
| * import {ComponentOption} from '../model/option.js'; |
| * export interface XxxOption extends ComponentOption { |
| * aaa: number |
| * } |
| * export class XxxModel extends Component { |
| * static type = 'xxx'; |
| * static defaultOption: XxxOption = { |
| * aaa: 123 |
| * } |
| * } |
| * Component.registerClass(XxxModel); |
| * ``` |
| * ```ts |
| * import {inheritDefaultOption} from '../util/component.js'; |
| * import {XxxModel, XxxOption} from './XxxModel.js'; |
| * export interface XxxSubOption extends XxxOption { |
| * bbb: number |
| * } |
| * class XxxSubModel extends XxxModel { |
| * static defaultOption: XxxSubOption = inheritDefaultOption(XxxModel.defaultOption, { |
| * bbb: 456 |
| * }) |
| * fn() { |
| * let opt = this.getDefaultOption(); |
| * // opt is {aaa: 123, bbb: 456} |
| * } |
| * } |
| * ``` |
| * |
| * (B) If using class extend (previous approach in echarts 3 & 4): |
| * ```js |
| * let XxxComponent = Component.extend({ |
| * defaultOption: { |
| * xx: 123 |
| * } |
| * }) |
| * ``` |
| * ```js |
| * let XxxSubComponent = XxxComponent.extend({ |
| * defaultOption: { |
| * yy: 456 |
| * }, |
| * fn: function () { |
| * let opt = this.getDefaultOption(); |
| * // opt is {xx: 123, yy: 456} |
| * } |
| * }) |
| * ``` |
| */ |
| |
| |
| ComponentModel.prototype.getDefaultOption = function () { |
| var ctor = this.constructor; // If using class declaration, it is different to travel super class |
| // in legacy env and auto merge defaultOption. So if using class |
| // declaration, defaultOption should be merged manually. |
| |
| if (!isExtendedClass(ctor)) { |
| // When using ts class, defaultOption must be declared as static. |
| return ctor.defaultOption; |
| } // FIXME: remove this approach? |
| |
| |
| var fields = inner(this); |
| |
| if (!fields.defaultOption) { |
| var optList = []; |
| var clz = ctor; |
| |
| while (clz) { |
| var opt = clz.prototype.defaultOption; |
| opt && optList.push(opt); |
| clz = clz.superClass; |
| } |
| |
| var defaultOption = {}; |
| |
| for (var i = optList.length - 1; i >= 0; i--) { |
| defaultOption = merge(defaultOption, optList[i], true); |
| } |
| |
| fields.defaultOption = defaultOption; |
| } |
| |
| return fields.defaultOption; |
| }; |
| /** |
| * Notice: always force to input param `useDefault` in case that forget to consider it. |
| * The same behavior as `modelUtil.parseFinder`. |
| * |
| * @param useDefault In many cases like series refer axis and axis refer grid, |
| * If axis index / axis id not specified, use the first target as default. |
| * In other cases like dataZoom refer axis, if not specified, measn no refer. |
| */ |
| |
| |
| ComponentModel.prototype.getReferringComponents = function (mainType, opt) { |
| var indexKey = mainType + 'Index'; |
| var idKey = mainType + 'Id'; |
| return queryReferringComponents(this.ecModel, mainType, { |
| index: this.get(indexKey, true), |
| id: this.get(idKey, true) |
| }, opt); |
| }; |
| |
| ComponentModel.prototype.getBoxLayoutParams = function () { |
| // Consider itself having box layout configs. |
| var boxLayoutModel = this; |
| return { |
| left: boxLayoutModel.get('left'), |
| top: boxLayoutModel.get('top'), |
| right: boxLayoutModel.get('right'), |
| bottom: boxLayoutModel.get('bottom'), |
| width: boxLayoutModel.get('width'), |
| height: boxLayoutModel.get('height') |
| }; |
| }; |
| /** |
| * Get key for zlevel. |
| * If developers don't configure zlevel. We will assign zlevel to series based on the key. |
| * For example, lines with trail effect and progressive series will in an individual zlevel. |
| */ |
| |
| |
| ComponentModel.prototype.getZLevelKey = function () { |
| return ''; |
| }; |
| |
| ComponentModel.prototype.setZLevel = function (zlevel) { |
| this.option.zlevel = zlevel; |
| }; |
| |
| ComponentModel.protoInitialize = function () { |
| var proto = ComponentModel.prototype; |
| proto.type = 'component'; |
| proto.id = ''; |
| proto.name = ''; |
| proto.mainType = ''; |
| proto.subType = ''; |
| proto.componentIndex = 0; |
| }(); |
| |
| return ComponentModel; |
| }(Model); |
| |
| mountExtend(ComponentModel, Model); |
| enableClassManagement(ComponentModel); |
| enableSubTypeDefaulter(ComponentModel); |
| enableTopologicalTravel(ComponentModel, getDependencies); |
| |
| function getDependencies(componentType) { |
| var deps = []; |
| each(ComponentModel.getClassesByMainType(componentType), function (clz) { |
| deps = deps.concat(clz.dependencies || clz.prototype.dependencies || []); |
| }); // Ensure main type. |
| |
| deps = map(deps, function (type) { |
| return parseClassType(type).main; |
| }); // Hack dataset for convenience. |
| |
| if (componentType !== 'dataset' && indexOf(deps, 'dataset') <= 0) { |
| deps.unshift('dataset'); |
| } |
| |
| return deps; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var platform = ''; // Navigator not exists in node |
| |
| if (typeof navigator !== 'undefined') { |
| /* global navigator */ |
| platform = navigator.platform || ''; |
| } |
| |
| var decalColor = 'rgba(0, 0, 0, 0.2)'; |
| var globalDefault = { |
| darkMode: 'auto', |
| // backgroundColor: 'rgba(0,0,0,0)', |
| colorBy: 'series', |
| color: ['#5470c6', '#91cc75', '#fac858', '#ee6666', '#73c0de', '#3ba272', '#fc8452', '#9a60b4', '#ea7ccc'], |
| gradientColor: ['#f6efa6', '#d88273', '#bf444c'], |
| aria: { |
| decal: { |
| decals: [{ |
| color: decalColor, |
| dashArrayX: [1, 0], |
| dashArrayY: [2, 5], |
| symbolSize: 1, |
| rotation: Math.PI / 6 |
| }, { |
| color: decalColor, |
| symbol: 'circle', |
| dashArrayX: [[8, 8], [0, 8, 8, 0]], |
| dashArrayY: [6, 0], |
| symbolSize: 0.8 |
| }, { |
| color: decalColor, |
| dashArrayX: [1, 0], |
| dashArrayY: [4, 3], |
| rotation: -Math.PI / 4 |
| }, { |
| color: decalColor, |
| dashArrayX: [[6, 6], [0, 6, 6, 0]], |
| dashArrayY: [6, 0] |
| }, { |
| color: decalColor, |
| dashArrayX: [[1, 0], [1, 6]], |
| dashArrayY: [1, 0, 6, 0], |
| rotation: Math.PI / 4 |
| }, { |
| color: decalColor, |
| symbol: 'triangle', |
| dashArrayX: [[9, 9], [0, 9, 9, 0]], |
| dashArrayY: [7, 2], |
| symbolSize: 0.75 |
| }] |
| } |
| }, |
| // If xAxis and yAxis declared, grid is created by default. |
| // grid: {}, |
| textStyle: { |
| // color: '#000', |
| // decoration: 'none', |
| // PENDING |
| fontFamily: platform.match(/^Win/) ? 'Microsoft YaHei' : 'sans-serif', |
| // fontFamily: 'Arial, Verdana, sans-serif', |
| fontSize: 12, |
| fontStyle: 'normal', |
| fontWeight: 'normal' |
| }, |
| // http://blogs.adobe.com/webplatform/2014/02/24/using-blend-modes-in-html-canvas/ |
| // https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation |
| // Default is source-over |
| blendMode: null, |
| stateAnimation: { |
| duration: 300, |
| easing: 'cubicOut' |
| }, |
| animation: 'auto', |
| animationDuration: 1000, |
| animationDurationUpdate: 500, |
| animationEasing: 'cubicInOut', |
| animationEasingUpdate: 'cubicInOut', |
| animationThreshold: 2000, |
| // Configuration for progressive/incremental rendering |
| progressiveThreshold: 3000, |
| progressive: 400, |
| // Threshold of if use single hover layer to optimize. |
| // It is recommended that `hoverLayerThreshold` is equivalent to or less than |
| // `progressiveThreshold`, otherwise hover will cause restart of progressive, |
| // which is unexpected. |
| // see example <echarts/test/heatmap-large.html>. |
| hoverLayerThreshold: 3000, |
| // See: module:echarts/scale/Time |
| useUTC: false |
| }; |
| |
| var VISUAL_DIMENSIONS = createHashMap(['tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName']); |
| var 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_TYPED_ARRAY = 'typedArray'; |
| var SOURCE_FORMAT_UNKNOWN = 'unknown'; |
| var SERIES_LAYOUT_BY_COLUMN = 'column'; |
| var SERIES_LAYOUT_BY_ROW = 'row'; |
| |
| var BE_ORDINAL = { |
| Must: 1, |
| Might: 2, |
| Not: 3 // Other cases |
| |
| }; |
| var innerGlobalModel = makeInner(); |
| /** |
| * MUST be called before mergeOption of all series. |
| */ |
| |
| function resetSourceDefaulter(ecModel) { |
| // `datasetMap` is used to make default encode. |
| innerGlobalModel(ecModel).datasetMap = createHashMap(); |
| } |
| /** |
| * [The strategy of the arrengment of data dimensions for dataset]: |
| * "value way": all axes are non-category axes. So series one by one take |
| * several (the number is coordSysDims.length) dimensions from dataset. |
| * The result of data arrengment of data dimensions like: |
| * | ser0_x | ser0_y | ser1_x | ser1_y | ser2_x | ser2_y | |
| * "category way": at least one axis is category axis. So the the first data |
| * dimension is always mapped to the first category axis and shared by |
| * all of the series. The other data dimensions are taken by series like |
| * "value way" does. |
| * The result of data arrengment of data dimensions like: |
| * | ser_shared_x | ser0_y | ser1_y | ser2_y | |
| * |
| * @return encode Never be `null/undefined`. |
| */ |
| |
| function makeSeriesEncodeForAxisCoordSys(coordDimensions, seriesModel, source) { |
| var encode = {}; |
| var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur. |
| |
| if (!datasetModel || !coordDimensions) { |
| return encode; |
| } |
| |
| var encodeItemName = []; |
| var encodeSeriesName = []; |
| var ecModel = seriesModel.ecModel; |
| var datasetMap = innerGlobalModel(ecModel).datasetMap; |
| var key = datasetModel.uid + '_' + source.seriesLayoutBy; |
| var baseCategoryDimIndex; |
| var categoryWayValueDimStart; |
| coordDimensions = coordDimensions.slice(); |
| each(coordDimensions, function (coordDimInfoLoose, coordDimIdx) { |
| var coordDimInfo = isObject(coordDimInfoLoose) ? coordDimInfoLoose : coordDimensions[coordDimIdx] = { |
| name: coordDimInfoLoose |
| }; |
| |
| if (coordDimInfo.type === 'ordinal' && baseCategoryDimIndex == null) { |
| baseCategoryDimIndex = coordDimIdx; |
| categoryWayValueDimStart = getDataDimCountOnCoordDim(coordDimInfo); |
| } |
| |
| encode[coordDimInfo.name] = []; |
| }); |
| var datasetRecord = datasetMap.get(key) || datasetMap.set(key, { |
| categoryWayDim: categoryWayValueDimStart, |
| valueWayDim: 0 |
| }); // TODO |
| // Auto detect first time axis and do arrangement. |
| |
| each(coordDimensions, function (coordDimInfo, coordDimIdx) { |
| var coordDimName = coordDimInfo.name; |
| var count = getDataDimCountOnCoordDim(coordDimInfo); // In value way. |
| |
| if (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? |
| // especially when encode x y specified. |
| // consider: when multiple series share one dimension |
| // category axis, series name should better use |
| // the other dimension name. On the other hand, use |
| // both dimensions name. |
| } // In category way, the first category axis. |
| else if (baseCategoryDimIndex === coordDimIdx) { |
| pushDim(encode[coordDimName], 0, count); |
| pushDim(encodeItemName, 0, count); |
| } // In category way, the other axis. |
| else { |
| 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 < idxCount; i++) { |
| dimIdxArr.push(idxFrom + i); |
| } |
| } |
| |
| function getDataDimCountOnCoordDim(coordDimInfo) { |
| var dimsDef = coordDimInfo.dimsDef; |
| return dimsDef ? dimsDef.length : 1; |
| } |
| |
| encodeItemName.length && (encode.itemName = encodeItemName); |
| encodeSeriesName.length && (encode.seriesName = encodeSeriesName); |
| return encode; |
| } |
| /** |
| * Work for data like [{name: ..., value: ...}, ...]. |
| * |
| * @return encode Never be `null/undefined`. |
| */ |
| |
| function makeSeriesEncodeForNameBased(seriesModel, source, dimCount) { |
| var encode = {}; |
| var datasetModel = querySeriesUpstreamDatasetModel(seriesModel); // Currently only make default when using dataset, util more reqirements occur. |
| |
| if (!datasetModel) { |
| return encode; |
| } |
| |
| var sourceFormat = source.sourceFormat; |
| var dimensionsDefine = source.dimensionsDefine; |
| var potentialNameDimIndex; |
| |
| if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { |
| each(dimensionsDefine, function (dim, idx) { |
| if ((isObject(dim) ? dim.name : dim) === 'name') { |
| potentialNameDimIndex = idx; |
| } |
| }); |
| } |
| |
| var idxResult = function () { |
| var idxRes0 = {}; |
| var idxRes1 = {}; |
| var guessRecords = []; // 5 is an experience value. |
| |
| for (var i = 0, len = Math.min(5, dimCount); i < len; i++) { |
| var guessResult = doGuessOrdinal(source.data, sourceFormat, source.seriesLayoutBy, dimensionsDefine, source.startIndex, i); |
| guessRecords.push(guessResult); |
| var isPureNumber = guessResult === BE_ORDINAL.Not; // [Strategy of idxRes0]: find the first BE_ORDINAL.Not as the value dim, |
| // and then find a name dim with the priority: |
| // "BE_ORDINAL.Might|BE_ORDINAL.Must" > "other dim" > "the value dim itself". |
| |
| if (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), |
| // find the first BE_ORDINAL.Might as the value dim, |
| // and then find a name dim with the priority: |
| // "other dim" > "the value dim itself". |
| // That is for backward compat: number-like (e.g., `'3'`, `'55'`) can be |
| // treated as number. |
| |
| |
| if (!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. |
| |
| var nameDimIndex = potentialNameDimIndex != null ? potentialNameDimIndex : idxResult.n; // By default, label uses itemName in charts. |
| // So we don't set encodeLabel here. |
| |
| encode.itemName = [nameDimIndex]; |
| encode.seriesName = [nameDimIndex]; |
| } |
| |
| return encode; |
| } |
| /** |
| * @return If return null/undefined, indicate that should not use datasetModel. |
| */ |
| |
| function querySeriesUpstreamDatasetModel(seriesModel) { |
| // Caution: consider the scenario: |
| // A dataset is declared and a series is not expected to use the dataset, |
| // and at the beginning `setOption({series: { noData })` (just prepare other |
| // option but no data), then `setOption({series: {data: [...]}); In this case, |
| // the user should set an empty array to avoid that dataset is used by default. |
| var thisData = seriesModel.get('data', true); |
| |
| if (!thisData) { |
| return queryReferringComponents(seriesModel.ecModel, 'dataset', { |
| index: seriesModel.get('datasetIndex', true), |
| id: seriesModel.get('datasetId', true) |
| }, SINGLE_REFERRING).models[0]; |
| } |
| } |
| /** |
| * @return Always return an array event empty. |
| */ |
| |
| function queryDatasetUpstreamDatasetModels(datasetModel) { |
| // Only these attributes declared, we by defualt reference to `datasetIndex: 0`. |
| // Otherwise, no reference. |
| if (!datasetModel.get('transform', true) && !datasetModel.get('fromTransformResult', true)) { |
| return []; |
| } |
| |
| return queryReferringComponents(datasetModel.ecModel, 'dataset', { |
| index: datasetModel.get('fromDatasetIndex', true), |
| id: datasetModel.get('fromDatasetId', true) |
| }, SINGLE_REFERRING).models; |
| } |
| /** |
| * The rule should not be complex, otherwise user might not |
| * be able to known where the data is wrong. |
| * The code is ugly, but how to make it neat? |
| */ |
| |
| function guessOrdinal(source, dimIndex) { |
| return doGuessOrdinal(source.data, source.sourceFormat, source.seriesLayoutBy, source.dimensionsDefine, source.startIndex, dimIndex); |
| } // dimIndex may be overflow source data. |
| // return {BE_ORDINAL} |
| |
| function doGuessOrdinal(data, sourceFormat, seriesLayoutBy, dimensionsDefine, startIndex, dimIndex) { |
| var result; // Experience value. |
| |
| var maxLoop = 5; |
| |
| if (isTypedArray(data)) { |
| return BE_ORDINAL.Not; |
| } // When sourceType is 'objectRows' or 'keyedColumns', dimensionsDefine |
| // always exists in source. |
| |
| |
| var dimName; |
| var dimType; |
| |
| if (dimensionsDefine) { |
| var dimDefItem = dimensionsDefine[dimIndex]; |
| |
| if (isObject(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) { |
| var dataArrayRows = data; |
| |
| if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { |
| var sample = dataArrayRows[dimIndex]; |
| |
| for (var i = 0; i < (sample || []).length && i < maxLoop; i++) { |
| if ((result = detectValue(sample[startIndex + i])) != null) { |
| return result; |
| } |
| } |
| } else { |
| for (var i = 0; i < dataArrayRows.length && i < maxLoop; i++) { |
| var row = dataArrayRows[startIndex + i]; |
| |
| if (row && (result = detectValue(row[dimIndex])) != null) { |
| return result; |
| } |
| } |
| } |
| } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { |
| var dataObjectRows = data; |
| |
| if (!dimName) { |
| return BE_ORDINAL.Not; |
| } |
| |
| for (var i = 0; i < dataObjectRows.length && i < maxLoop; i++) { |
| var item = dataObjectRows[i]; |
| |
| if (item && (result = detectValue(item[dimName])) != null) { |
| return result; |
| } |
| } |
| } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { |
| var dataKeyedColumns = data; |
| |
| if (!dimName) { |
| return BE_ORDINAL.Not; |
| } |
| |
| var sample = dataKeyedColumns[dimName]; |
| |
| if (!sample || isTypedArray(sample)) { |
| return BE_ORDINAL.Not; |
| } |
| |
| for (var i = 0; i < sample.length && i < maxLoop; i++) { |
| if ((result = detectValue(sample[i])) != null) { |
| return result; |
| } |
| } |
| } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { |
| var dataOriginal = data; |
| |
| for (var i = 0; i < dataOriginal.length && i < maxLoop; i++) { |
| var item = dataOriginal[i]; |
| var val = getDataItemValue(item); |
| |
| if (!isArray(val)) { |
| return BE_ORDINAL.Not; |
| } |
| |
| if ((result = detectValue(val[dimIndex])) != null) { |
| return result; |
| } |
| } |
| } |
| |
| function detectValue(val) { |
| var beStr = isString(val); // Consider usage convenience, '1', '2' will be treated as "number". |
| // `isFinit('')` get `true`. |
| |
| if (val != null && isFinite(val) && val !== '') { |
| return beStr ? BE_ORDINAL.Might : BE_ORDINAL.Not; |
| } else if (beStr && val !== '-') { |
| return BE_ORDINAL.Must; |
| } |
| } |
| |
| return BE_ORDINAL.Not; |
| } |
| |
| var internalOptionCreatorMap = createHashMap(); |
| function concatInternalOptions(ecModel, mainType, newCmptOptionList) { |
| var internalOptionCreator = internalOptionCreatorMap.get(mainType); |
| |
| if (!internalOptionCreator) { |
| return newCmptOptionList; |
| } |
| |
| var internalOptions = internalOptionCreator(ecModel); |
| |
| if (!internalOptions) { |
| return newCmptOptionList; |
| } |
| |
| if ("development" !== 'production') { |
| for (var i = 0; i < internalOptions.length; i++) { |
| assert(isComponentIdInternal(internalOptions[i])); |
| } |
| } |
| |
| return newCmptOptionList.concat(internalOptions); |
| } |
| |
| var innerColor = makeInner(); |
| var innerDecal = makeInner(); |
| |
| var PaletteMixin = |
| /** @class */ |
| function () { |
| function PaletteMixin() {} |
| |
| PaletteMixin.prototype.getColorFromPalette = function (name, scope, requestNum) { |
| var defaultPalette = normalizeToArray(this.get('color', true)); |
| var layeredPalette = this.get('colorLayer', true); |
| return getFromPalette(this, innerColor, defaultPalette, layeredPalette, name, scope, requestNum); |
| }; |
| |
| PaletteMixin.prototype.clearColorPalette = function () { |
| clearPalette(this, innerColor); |
| }; |
| |
| return PaletteMixin; |
| }(); |
| |
| function getDecalFromPalette(ecModel, name, scope, requestNum) { |
| var defaultDecals = normalizeToArray(ecModel.get(['aria', 'decal', 'decals'])); |
| return getFromPalette(ecModel, innerDecal, defaultDecals, null, name, scope, requestNum); |
| } |
| |
| function getNearestPalette(palettes, requestColorNum) { |
| var paletteNum = palettes.length; // TODO palettes must be in order |
| |
| for (var i = 0; i < paletteNum; i++) { |
| if (palettes[i].length > requestColorNum) { |
| return palettes[i]; |
| } |
| } |
| |
| return palettes[paletteNum - 1]; |
| } |
| /** |
| * @param name MUST NOT be null/undefined. Otherwise call this function |
| * twise with the same parameters will get different result. |
| * @param scope default this. |
| * @return Can be null/undefined |
| */ |
| |
| |
| function getFromPalette(that, inner, defaultPalette, layeredPalette, name, scope, requestNum) { |
| scope = scope || that; |
| var scopeFields = inner(scope); |
| var paletteIdx = scopeFields.paletteIdx || 0; |
| var paletteNameMap = scopeFields.paletteNameMap = scopeFields.paletteNameMap || {}; // Use `hasOwnProperty` to avoid conflict with Object.prototype. |
| |
| if (paletteNameMap.hasOwnProperty(name)) { |
| return paletteNameMap[name]; |
| } |
| |
| var palette = requestNum == null || !layeredPalette ? defaultPalette : getNearestPalette(layeredPalette, requestNum); // In case can't find in layered color palette. |
| |
| palette = palette || defaultPalette; |
| |
| if (!palette || !palette.length) { |
| return; |
| } |
| |
| var pickedPaletteItem = palette[paletteIdx]; |
| |
| if (name) { |
| paletteNameMap[name] = pickedPaletteItem; |
| } |
| |
| scopeFields.paletteIdx = (paletteIdx + 1) % palette.length; |
| return pickedPaletteItem; |
| } |
| |
| function clearPalette(that, inner) { |
| inner(that).paletteIdx = 0; |
| inner(that).paletteNameMap = {}; |
| } |
| |
| // Internal method names: |
| // ----------------------- |
| |
| var reCreateSeriesIndices; |
| var assertSeriesInitialized; |
| var initBase; |
| var OPTION_INNER_KEY = '\0_ec_inner'; |
| var OPTION_INNER_VALUE = 1; |
| var BUITIN_COMPONENTS_MAP = { |
| grid: 'GridComponent', |
| polar: 'PolarComponent', |
| geo: 'GeoComponent', |
| singleAxis: 'SingleAxisComponent', |
| parallel: 'ParallelComponent', |
| calendar: 'CalendarComponent', |
| graphic: 'GraphicComponent', |
| toolbox: 'ToolboxComponent', |
| tooltip: 'TooltipComponent', |
| axisPointer: 'AxisPointerComponent', |
| brush: 'BrushComponent', |
| title: 'TitleComponent', |
| timeline: 'TimelineComponent', |
| markPoint: 'MarkPointComponent', |
| markLine: 'MarkLineComponent', |
| markArea: 'MarkAreaComponent', |
| legend: 'LegendComponent', |
| dataZoom: 'DataZoomComponent', |
| visualMap: 'VisualMapComponent', |
| // aria: 'AriaComponent', |
| // dataset: 'DatasetComponent', |
| // Dependencies |
| xAxis: 'GridComponent', |
| yAxis: 'GridComponent', |
| angleAxis: 'PolarComponent', |
| radiusAxis: 'PolarComponent' |
| }; |
| var BUILTIN_CHARTS_MAP = { |
| line: 'LineChart', |
| bar: 'BarChart', |
| pie: 'PieChart', |
| scatter: 'ScatterChart', |
| radar: 'RadarChart', |
| map: 'MapChart', |
| tree: 'TreeChart', |
| treemap: 'TreemapChart', |
| graph: 'GraphChart', |
| gauge: 'GaugeChart', |
| funnel: 'FunnelChart', |
| parallel: 'ParallelChart', |
| sankey: 'SankeyChart', |
| boxplot: 'BoxplotChart', |
| candlestick: 'CandlestickChart', |
| effectScatter: 'EffectScatterChart', |
| lines: 'LinesChart', |
| heatmap: 'HeatmapChart', |
| pictorialBar: 'PictorialBarChart', |
| themeRiver: 'ThemeRiverChart', |
| sunburst: 'SunburstChart', |
| custom: 'CustomChart' |
| }; |
| var componetsMissingLogPrinted = {}; |
| |
| function checkMissingComponents(option) { |
| each(option, function (componentOption, mainType) { |
| if (!ComponentModel.hasClass(mainType)) { |
| var componentImportName = BUITIN_COMPONENTS_MAP[mainType]; |
| |
| if (componentImportName && !componetsMissingLogPrinted[componentImportName]) { |
| error("Component " + mainType + " is used but not imported.\nimport { " + componentImportName + " } from 'echarts/components';\necharts.use([" + componentImportName + "]);"); |
| componetsMissingLogPrinted[componentImportName] = true; |
| } |
| } |
| }); |
| } |
| |
| var GlobalModel = |
| /** @class */ |
| function (_super) { |
| __extends(GlobalModel, _super); |
| |
| function GlobalModel() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| GlobalModel.prototype.init = function (option, parentModel, ecModel, theme, locale, optionManager) { |
| theme = theme || {}; |
| this.option = null; // Mark as not initialized. |
| |
| this._theme = new Model(theme); |
| this._locale = new Model(locale); |
| this._optionManager = optionManager; |
| }; |
| |
| GlobalModel.prototype.setOption = function (option, opts, optionPreprocessorFuncs) { |
| if ("development" !== 'production') { |
| assert(option != null, 'option is null/undefined'); |
| assert(option[OPTION_INNER_KEY] !== OPTION_INNER_VALUE, 'please use chart.getOption()'); |
| } |
| |
| var innerOpt = normalizeSetOptionInput(opts); |
| |
| this._optionManager.setOption(option, optionPreprocessorFuncs, innerOpt); |
| |
| this._resetOption(null, innerOpt); |
| }; |
| /** |
| * @param type null/undefined: reset all. |
| * 'recreate': force recreate all. |
| * 'timeline': only reset timeline option |
| * 'media': only reset media query option |
| * @return Whether option changed. |
| */ |
| |
| |
| GlobalModel.prototype.resetOption = function (type, opt) { |
| return this._resetOption(type, normalizeSetOptionInput(opt)); |
| }; |
| |
| GlobalModel.prototype._resetOption = function (type, opt) { |
| var optionChanged = false; |
| var optionManager = this._optionManager; |
| |
| if (!type || type === 'recreate') { |
| var baseOption = optionManager.mountOption(type === 'recreate'); |
| |
| if ("development" !== 'production') { |
| checkMissingComponents(baseOption); |
| } |
| |
| if (!this.option || type === 'recreate') { |
| initBase(this, baseOption); |
| } else { |
| this.restoreData(); |
| |
| this._mergeOption(baseOption, opt); |
| } |
| |
| optionChanged = true; |
| } |
| |
| if (type === 'timeline' || type === 'media') { |
| this.restoreData(); |
| } // By design, if `setOption(option2)` at the second time, and `option2` is a `ECUnitOption`, |
| // it should better not have the same props with `MediaUnit['option']`. |
| // Because either `option2` or `MediaUnit['option']` will be always merged to "current option" |
| // rather than original "baseOption". If they both override a prop, the result might be |
| // unexpected when media state changed after `setOption` called. |
| // If we really need to modify a props in each `MediaUnit['option']`, use the full version |
| // (`{baseOption, media}`) in `setOption`. |
| // For `timeline`, the case is the same. |
| |
| |
| if (!type || type === 'recreate' || type === 'timeline') { |
| var timelineOption = optionManager.getTimelineOption(this); |
| |
| if (timelineOption) { |
| optionChanged = true; |
| |
| this._mergeOption(timelineOption, opt); |
| } |
| } |
| |
| if (!type || type === 'recreate' || type === 'media') { |
| var mediaOptions = optionManager.getMediaOption(this); |
| |
| if (mediaOptions.length) { |
| each(mediaOptions, function (mediaOption) { |
| optionChanged = true; |
| |
| this._mergeOption(mediaOption, opt); |
| }, this); |
| } |
| } |
| |
| return optionChanged; |
| }; |
| |
| GlobalModel.prototype.mergeOption = function (option) { |
| this._mergeOption(option, null); |
| }; |
| |
| GlobalModel.prototype._mergeOption = function (newOption, opt) { |
| var option = this.option; |
| var componentsMap = this._componentsMap; |
| var componentsCount = this._componentsCount; |
| var newCmptTypes = []; |
| var newCmptTypeMap = createHashMap(); |
| var replaceMergeMainTypeMap = opt && opt.replaceMergeMainTypeMap; |
| resetSourceDefaulter(this); // If no component class, merge directly. |
| // For example: color, animaiton options, etc. |
| |
| each(newOption, function (componentOption, mainType) { |
| if (componentOption == null) { |
| return; |
| } |
| |
| if (!ComponentModel.hasClass(mainType)) { |
| // globalSettingTask.dirty(); |
| option[mainType] = option[mainType] == null ? clone(componentOption) : merge(option[mainType], componentOption, true); |
| } else if (mainType) { |
| newCmptTypes.push(mainType); |
| newCmptTypeMap.set(mainType, true); |
| } |
| }); |
| |
| if (replaceMergeMainTypeMap) { |
| // If there is a mainType `xxx` in `replaceMerge` but not declared in option, |
| // we trade it as it is declared in option as `{xxx: []}`. Because: |
| // (1) for normal merge, `{xxx: null/undefined}` are the same meaning as `{xxx: []}`. |
| // (2) some preprocessor may convert some of `{xxx: null/undefined}` to `{xxx: []}`. |
| replaceMergeMainTypeMap.each(function (val, mainTypeInReplaceMerge) { |
| if (ComponentModel.hasClass(mainTypeInReplaceMerge) && !newCmptTypeMap.get(mainTypeInReplaceMerge)) { |
| newCmptTypes.push(mainTypeInReplaceMerge); |
| newCmptTypeMap.set(mainTypeInReplaceMerge, true); |
| } |
| }); |
| } |
| |
| ComponentModel.topologicalTravel(newCmptTypes, ComponentModel.getAllClassMainTypes(), visitComponent, this); |
| |
| function visitComponent(mainType) { |
| var newCmptOptionList = concatInternalOptions(this, mainType, normalizeToArray(newOption[mainType])); |
| var oldCmptList = componentsMap.get(mainType); |
| var mergeMode = // `!oldCmptList` means init. See the comment in `mappingToExists` |
| !oldCmptList ? 'replaceAll' : replaceMergeMainTypeMap && replaceMergeMainTypeMap.get(mainType) ? 'replaceMerge' : 'normalMerge'; |
| var mappingResult = mappingToExists(oldCmptList, newCmptOptionList, mergeMode); // Set mainType and complete subType. |
| |
| setComponentTypeToKeyInfo(mappingResult, mainType, ComponentModel); // Empty it before the travel, in order to prevent `this._componentsMap` |
| // from being used in the `init`/`mergeOption`/`optionUpdated` of some |
| // components, which is probably incorrect logic. |
| |
| option[mainType] = null; |
| componentsMap.set(mainType, null); |
| componentsCount.set(mainType, 0); |
| var optionsByMainType = []; |
| var cmptsByMainType = []; |
| var cmptsCountByMainType = 0; |
| var tooltipExists; |
| var tooltipWarningLogged; |
| each(mappingResult, function (resultItem, index) { |
| var componentModel = resultItem.existing; |
| var newCmptOption = resultItem.newOption; |
| |
| if (!newCmptOption) { |
| if (componentModel) { |
| // Consider where is no new option and should be merged using {}, |
| // see removeEdgeAndAdd in topologicalTravel and |
| // ComponentModel.getAllClassMainTypes. |
| componentModel.mergeOption({}, this); |
| componentModel.optionUpdated({}, false); |
| } // If no both `resultItem.exist` and `resultItem.option`, |
| // either it is in `replaceMerge` and not matched by any id, |
| // or it has been removed in previous `replaceMerge` and left a "hole" in this component index. |
| |
| } else { |
| var isSeriesType = mainType === 'series'; |
| var ComponentModelClass = ComponentModel.getClass(mainType, resultItem.keyInfo.subType, !isSeriesType // Give a more detailed warn later if series don't exists |
| ); |
| |
| if (!ComponentModelClass) { |
| if ("development" !== 'production') { |
| var subType = resultItem.keyInfo.subType; |
| var seriesImportName = BUILTIN_CHARTS_MAP[subType]; |
| |
| if (!componetsMissingLogPrinted[subType]) { |
| componetsMissingLogPrinted[subType] = true; |
| |
| if (seriesImportName) { |
| error("Series " + subType + " is used but not imported.\nimport { " + seriesImportName + " } from 'echarts/charts';\necharts.use([" + seriesImportName + "]);"); |
| } else { |
| error("Unknown series " + subType); |
| } |
| } |
| } |
| |
| return; |
| } // TODO Before multiple tooltips get supported, we do this check to avoid unexpected exception. |
| |
| |
| if (mainType === 'tooltip') { |
| if (tooltipExists) { |
| if ("development" !== 'production') { |
| if (!tooltipWarningLogged) { |
| warn('Currently only one tooltip component is allowed.'); |
| tooltipWarningLogged = true; |
| } |
| } |
| |
| return; |
| } |
| |
| tooltipExists = true; |
| } |
| |
| if (componentModel && componentModel.constructor === ComponentModelClass) { |
| componentModel.name = resultItem.keyInfo.name; // componentModel.settingTask && componentModel.settingTask.dirty(); |
| |
| componentModel.mergeOption(newCmptOption, this); |
| componentModel.optionUpdated(newCmptOption, false); |
| } else { |
| // PENDING Global as parent ? |
| var extraOpt = extend({ |
| componentIndex: index |
| }, resultItem.keyInfo); |
| componentModel = new ComponentModelClass(newCmptOption, this, this, extraOpt); // Assign `keyInfo` |
| |
| extend(componentModel, extraOpt); |
| |
| if (resultItem.brandNew) { |
| componentModel.__requireNewView = true; |
| } |
| |
| componentModel.init(newCmptOption, this, this); // Call optionUpdated after init. |
| // newCmptOption has been used as componentModel.option |
| // and may be merged with theme and default, so pass null |
| // to avoid confusion. |
| |
| componentModel.optionUpdated(null, true); |
| } |
| } |
| |
| if (componentModel) { |
| optionsByMainType.push(componentModel.option); |
| cmptsByMainType.push(componentModel); |
| cmptsCountByMainType++; |
| } else { |
| // Always do assign to avoid elided item in array. |
| optionsByMainType.push(void 0); |
| cmptsByMainType.push(void 0); |
| } |
| }, this); |
| option[mainType] = optionsByMainType; |
| componentsMap.set(mainType, cmptsByMainType); |
| componentsCount.set(mainType, cmptsCountByMainType); // Backup series for filtering. |
| |
| if (mainType === 'series') { |
| reCreateSeriesIndices(this); |
| } |
| } // If no series declared, ensure `_seriesIndices` initialized. |
| |
| |
| if (!this._seriesIndices) { |
| reCreateSeriesIndices(this); |
| } |
| }; |
| /** |
| * Get option for output (cloned option and inner info removed) |
| */ |
| |
| |
| GlobalModel.prototype.getOption = function () { |
| var option = clone(this.option); |
| each(option, function (optInMainType, mainType) { |
| if (ComponentModel.hasClass(mainType)) { |
| var opts = normalizeToArray(optInMainType); // Inner cmpts need to be removed. |
| // Inner cmpts might not be at last since ec5.0, but still |
| // compatible for users: if inner cmpt at last, splice the returned array. |
| |
| var realLen = opts.length; |
| var metNonInner = false; |
| |
| for (var i = realLen - 1; i >= 0; i--) { |
| // Remove options with inner id. |
| if (opts[i] && !isComponentIdInternal(opts[i])) { |
| metNonInner = true; |
| } else { |
| opts[i] = null; |
| !metNonInner && realLen--; |
| } |
| } |
| |
| opts.length = realLen; |
| option[mainType] = opts; |
| } |
| }); |
| delete option[OPTION_INNER_KEY]; |
| return option; |
| }; |
| |
| GlobalModel.prototype.getTheme = function () { |
| return this._theme; |
| }; |
| |
| GlobalModel.prototype.getLocaleModel = function () { |
| return this._locale; |
| }; |
| |
| GlobalModel.prototype.setUpdatePayload = function (payload) { |
| this._payload = payload; |
| }; |
| |
| GlobalModel.prototype.getUpdatePayload = function () { |
| return this._payload; |
| }; |
| /** |
| * @param idx If not specified, return the first one. |
| */ |
| |
| |
| GlobalModel.prototype.getComponent = function (mainType, idx) { |
| var list = this._componentsMap.get(mainType); |
| |
| if (list) { |
| var cmpt = list[idx || 0]; |
| |
| if (cmpt) { |
| return cmpt; |
| } else if (idx == null) { |
| for (var i = 0; i < list.length; i++) { |
| if (list[i]) { |
| return list[i]; |
| } |
| } |
| } |
| } |
| }; |
| /** |
| * @return Never be null/undefined. |
| */ |
| |
| |
| GlobalModel.prototype.queryComponents = function (condition) { |
| var mainType = condition.mainType; |
| |
| if (!mainType) { |
| return []; |
| } |
| |
| var index = condition.index; |
| var id = condition.id; |
| var name = condition.name; |
| |
| var cmpts = this._componentsMap.get(mainType); |
| |
| if (!cmpts || !cmpts.length) { |
| return []; |
| } |
| |
| var result; |
| |
| if (index != null) { |
| result = []; |
| each(normalizeToArray(index), function (idx) { |
| cmpts[idx] && result.push(cmpts[idx]); |
| }); |
| } else if (id != null) { |
| result = queryByIdOrName('id', id, cmpts); |
| } else if (name != null) { |
| result = queryByIdOrName('name', name, cmpts); |
| } else { |
| // Return all non-empty components in that mainType |
| result = filter(cmpts, function (cmpt) { |
| return !!cmpt; |
| }); |
| } |
| |
| return filterBySubType(result, condition); |
| }; |
| /** |
| * The interface is different from queryComponents, |
| * which is convenient for inner usage. |
| * |
| * @usage |
| * let result = findComponents( |
| * {mainType: 'dataZoom', query: {dataZoomId: 'abc'}} |
| * ); |
| * let result = findComponents( |
| * {mainType: 'series', subType: 'pie', query: {seriesName: 'uio'}} |
| * ); |
| * let result = findComponents( |
| * {mainType: 'series', |
| * filter: function (model, index) {...}} |
| * ); |
| * // result like [component0, componnet1, ...] |
| */ |
| |
| |
| GlobalModel.prototype.findComponents = function (condition) { |
| var query = condition.query; |
| var mainType = condition.mainType; |
| var queryCond = getQueryCond(query); |
| var result = queryCond ? this.queryComponents(queryCond) // Retrieve all non-empty components. |
| : filter(this._componentsMap.get(mainType), function (cmpt) { |
| return !!cmpt; |
| }); |
| 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. |
| index: q[indexAttr], |
| id: q[idAttr], |
| name: q[nameAttr] |
| } : null; |
| } |
| |
| function doFilter(res) { |
| return condition.filter ? filter(res, condition.filter) : res; |
| } |
| }; |
| |
| GlobalModel.prototype.eachComponent = function (mainType, cb, context) { |
| var componentsMap = this._componentsMap; |
| |
| if (isFunction(mainType)) { |
| var ctxForAll_1 = cb; |
| var cbForAll_1 = mainType; |
| componentsMap.each(function (cmpts, componentType) { |
| for (var i = 0; cmpts && i < cmpts.length; i++) { |
| var cmpt = cmpts[i]; |
| cmpt && cbForAll_1.call(ctxForAll_1, componentType, cmpt, cmpt.componentIndex); |
| } |
| }); |
| } else { |
| var cmpts = isString(mainType) ? componentsMap.get(mainType) : isObject(mainType) ? this.findComponents(mainType) : null; |
| |
| for (var i = 0; cmpts && i < cmpts.length; i++) { |
| var cmpt = cmpts[i]; |
| cmpt && cb.call(context, cmpt, cmpt.componentIndex); |
| } |
| } |
| }; |
| /** |
| * Get series list before filtered by name. |
| */ |
| |
| |
| GlobalModel.prototype.getSeriesByName = function (name) { |
| var nameStr = convertOptionIdName(name, null); |
| return filter(this._componentsMap.get('series'), function (oneSeries) { |
| return !!oneSeries && nameStr != null && oneSeries.name === nameStr; |
| }); |
| }; |
| /** |
| * Get series list before filtered by index. |
| */ |
| |
| |
| GlobalModel.prototype.getSeriesByIndex = function (seriesIndex) { |
| return this._componentsMap.get('series')[seriesIndex]; |
| }; |
| /** |
| * Get series list before filtered by type. |
| * FIXME: rename to getRawSeriesByType? |
| */ |
| |
| |
| GlobalModel.prototype.getSeriesByType = function (subType) { |
| return filter(this._componentsMap.get('series'), function (oneSeries) { |
| return !!oneSeries && oneSeries.subType === subType; |
| }); |
| }; |
| /** |
| * Get all series before filtered. |
| */ |
| |
| |
| GlobalModel.prototype.getSeries = function () { |
| return filter(this._componentsMap.get('series'), function (oneSeries) { |
| return !!oneSeries; |
| }); |
| }; |
| /** |
| * Count series before filtered. |
| */ |
| |
| |
| GlobalModel.prototype.getSeriesCount = function () { |
| return this._componentsCount.get('series'); |
| }; |
| /** |
| * After filtering, series may be different |
| * from raw series. |
| */ |
| |
| |
| GlobalModel.prototype.eachSeries = function (cb, context) { |
| assertSeriesInitialized(this); |
| each(this._seriesIndices, function (rawSeriesIndex) { |
| var series = this._componentsMap.get('series')[rawSeriesIndex]; |
| |
| cb.call(context, series, rawSeriesIndex); |
| }, this); |
| }; |
| /** |
| * Iterate raw series before filtered. |
| * |
| * @param {Function} cb |
| * @param {*} context |
| */ |
| |
| |
| GlobalModel.prototype.eachRawSeries = function (cb, context) { |
| each(this._componentsMap.get('series'), function (series) { |
| series && cb.call(context, series, series.componentIndex); |
| }); |
| }; |
| /** |
| * After filtering, series may be different. |
| * from raw series. |
| */ |
| |
| |
| GlobalModel.prototype.eachSeriesByType = function (subType, cb, context) { |
| assertSeriesInitialized(this); |
| each(this._seriesIndices, function (rawSeriesIndex) { |
| var series = this._componentsMap.get('series')[rawSeriesIndex]; |
| |
| if (series.subType === subType) { |
| cb.call(context, series, rawSeriesIndex); |
| } |
| }, this); |
| }; |
| /** |
| * Iterate raw series before filtered of given type. |
| */ |
| |
| |
| GlobalModel.prototype.eachRawSeriesByType = function (subType, cb, context) { |
| return each(this.getSeriesByType(subType), cb, context); |
| }; |
| |
| GlobalModel.prototype.isSeriesFiltered = function (seriesModel) { |
| assertSeriesInitialized(this); |
| return this._seriesIndicesMap.get(seriesModel.componentIndex) == null; |
| }; |
| |
| GlobalModel.prototype.getCurrentSeriesIndices = function () { |
| return (this._seriesIndices || []).slice(); |
| }; |
| |
| GlobalModel.prototype.filterSeries = function (cb, context) { |
| assertSeriesInitialized(this); |
| var newSeriesIndices = []; |
| each(this._seriesIndices, function (seriesRawIdx) { |
| var series = this._componentsMap.get('series')[seriesRawIdx]; |
| |
| cb.call(context, series, seriesRawIdx) && newSeriesIndices.push(seriesRawIdx); |
| }, this); |
| this._seriesIndices = newSeriesIndices; |
| this._seriesIndicesMap = createHashMap(newSeriesIndices); |
| }; |
| |
| GlobalModel.prototype.restoreData = function (payload) { |
| reCreateSeriesIndices(this); |
| var componentsMap = this._componentsMap; |
| var componentTypes = []; |
| componentsMap.each(function (components, componentType) { |
| if (ComponentModel.hasClass(componentType)) { |
| componentTypes.push(componentType); |
| } |
| }); |
| ComponentModel.topologicalTravel(componentTypes, ComponentModel.getAllClassMainTypes(), function (componentType) { |
| each(componentsMap.get(componentType), function (component) { |
| if (component && (componentType !== 'series' || !isNotTargetSeries(component, payload))) { |
| component.restoreData(); |
| } |
| }); |
| }); |
| }; |
| |
| GlobalModel.internalField = function () { |
| reCreateSeriesIndices = function (ecModel) { |
| var seriesIndices = ecModel._seriesIndices = []; |
| each(ecModel._componentsMap.get('series'), function (series) { |
| // series may have been removed by `replaceMerge`. |
| series && seriesIndices.push(series.componentIndex); |
| }); |
| ecModel._seriesIndicesMap = createHashMap(seriesIndices); |
| }; |
| |
| assertSeriesInitialized = function (ecModel) { |
| // Components that use _seriesIndices should depends on series component, |
| // which make sure that their initialization is after series. |
| if ("development" !== 'production') { |
| if (!ecModel._seriesIndices) { |
| throw new Error('Option should contains series.'); |
| } |
| } |
| }; |
| |
| initBase = function (ecModel, baseOption) { |
| // Using OPTION_INNER_KEY to mark that this option cannot be used outside, |
| // i.e. `chart.setOption(chart.getModel().option);` is forbidden. |
| ecModel.option = {}; |
| ecModel.option[OPTION_INNER_KEY] = OPTION_INNER_VALUE; // Init with series: [], in case of calling findSeries method |
| // before series initialized. |
| |
| ecModel._componentsMap = createHashMap({ |
| series: [] |
| }); |
| ecModel._componentsCount = createHashMap(); // If user spefied `option.aria`, aria will be enable. This detection should be |
| // performed before theme and globalDefault merge. |
| |
| var airaOption = baseOption.aria; |
| |
| if (isObject(airaOption) && airaOption.enabled == null) { |
| airaOption.enabled = true; |
| } |
| |
| mergeTheme(baseOption, ecModel._theme.option); // TODO Needs clone when merging to the unexisted property |
| |
| merge(baseOption, globalDefault, false); |
| |
| ecModel._mergeOption(baseOption, null); |
| }; |
| }(); |
| |
| return GlobalModel; |
| }(Model); |
| |
| function isNotTargetSeries(seriesModel, payload) { |
| if (payload) { |
| var index = payload.seriesIndex; |
| var id = payload.seriesId; |
| var name_1 = payload.seriesName; |
| return index != null && seriesModel.componentIndex !== index || id != null && seriesModel.id !== id || name_1 != null && seriesModel.name !== name_1; |
| } |
| } |
| |
| function mergeTheme(option, theme) { |
| // PENDING |
| // NOT use `colorLayer` in theme if option has `color` |
| var notMergeColorLayer = option.color && !option.colorLayer; |
| each(theme, function (themeItem, name) { |
| if (name === 'colorLayer' && notMergeColorLayer) { |
| return; |
| } // If it is component model mainType, the model handles that merge later. |
| // otherwise, merge them here. |
| |
| |
| if (!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 queryByIdOrName(attr, idOrName, cmpts) { |
| // Here is a break from echarts4: string and number are |
| // treated as equal. |
| if (isArray(idOrName)) { |
| var keyMap_1 = createHashMap(); |
| each(idOrName, function (idOrNameItem) { |
| if (idOrNameItem != null) { |
| var idName = convertOptionIdName(idOrNameItem, null); |
| idName != null && keyMap_1.set(idOrNameItem, true); |
| } |
| }); |
| return filter(cmpts, function (cmpt) { |
| return cmpt && keyMap_1.get(cmpt[attr]); |
| }); |
| } else { |
| var idName_1 = convertOptionIdName(idOrName, null); |
| return filter(cmpts, function (cmpt) { |
| return cmpt && idName_1 != null && cmpt[attr] === idName_1; |
| }); |
| } |
| } |
| |
| function filterBySubType(components, condition) { |
| // Using hasOwnProperty for restrict. Consider |
| // subType is undefined in user payload. |
| return condition.hasOwnProperty('subType') ? filter(components, function (cmpt) { |
| return cmpt && cmpt.subType === condition.subType; |
| }) : components; |
| } |
| |
| function normalizeSetOptionInput(opts) { |
| var replaceMergeMainTypeMap = createHashMap(); |
| opts && each(normalizeToArray(opts.replaceMerge), function (mainType) { |
| if ("development" !== 'production') { |
| assert(ComponentModel.hasClass(mainType), '"' + mainType + '" is not valid component main type in "replaceMerge"'); |
| } |
| |
| replaceMergeMainTypeMap.set(mainType, true); |
| }); |
| return { |
| replaceMergeMainTypeMap: replaceMergeMainTypeMap |
| }; |
| } |
| |
| mixin(GlobalModel, PaletteMixin); |
| |
| var availableMethods = ['getDom', 'getZr', 'getWidth', 'getHeight', 'getDevicePixelRatio', 'dispatchAction', 'isSSR', 'isDisposed', 'on', 'off', 'getDataURL', 'getConnectedDataURL', // 'getModel', |
| 'getOption', // 'getViewOfComponentModel', |
| // 'getViewOfSeriesModel', |
| 'getId', 'updateLabelLayout']; |
| |
| var ExtensionAPI = |
| /** @class */ |
| function () { |
| function ExtensionAPI(ecInstance) { |
| each(availableMethods, function (methodName) { |
| this[methodName] = bind(ecInstance[methodName], ecInstance); |
| }, this); |
| } |
| |
| return ExtensionAPI; |
| }(); |
| |
| var coordinateSystemCreators = {}; |
| |
| var CoordinateSystemManager = |
| /** @class */ |
| function () { |
| function CoordinateSystemManager() { |
| this._coordinateSystems = []; |
| } |
| |
| CoordinateSystemManager.prototype.create = function (ecModel, api) { |
| var coordinateSystems = []; |
| each(coordinateSystemCreators, function (creator, type) { |
| var list = creator.create(ecModel, api); |
| coordinateSystems = coordinateSystems.concat(list || []); |
| }); |
| this._coordinateSystems = coordinateSystems; |
| }; |
| |
| CoordinateSystemManager.prototype.update = function (ecModel, api) { |
| each(this._coordinateSystems, function (coordSys) { |
| coordSys.update && coordSys.update(ecModel, api); |
| }); |
| }; |
| |
| CoordinateSystemManager.prototype.getCoordinateSystems = function () { |
| return this._coordinateSystems.slice(); |
| }; |
| |
| CoordinateSystemManager.register = function (type, creator) { |
| coordinateSystemCreators[type] = creator; |
| }; |
| |
| CoordinateSystemManager.get = function (type) { |
| return coordinateSystemCreators[type]; |
| }; |
| |
| return CoordinateSystemManager; |
| }(); |
| |
| var QUERY_REG = /^(min|max)?(.+)$/; // Key: mainType |
| // type FakeComponentsMap = HashMap<(MappingExistingItem & { subType: string })[]>; |
| |
| /** |
| * TERM EXPLANATIONS: |
| * See `ECOption` and `ECUnitOption` in `src/util/types.ts`. |
| */ |
| |
| var OptionManager = |
| /** @class */ |
| function () { |
| // timeline.notMerge is not supported in ec3. Firstly there is rearly |
| // case that notMerge is needed. Secondly supporting 'notMerge' requires |
| // rawOption cloned and backuped when timeline changed, which does no |
| // good to performance. What's more, that both timeline and setOption |
| // method supply 'notMerge' brings complex and some problems. |
| // Consider this case: |
| // (step1) chart.setOption({timeline: {notMerge: false}, ...}, false); |
| // (step2) chart.setOption({timeline: {notMerge: true}, ...}, false); |
| function OptionManager(api) { |
| this._timelineOptions = []; |
| this._mediaList = []; |
| /** |
| * -1, means default. |
| * empty means no media. |
| */ |
| |
| this._currentMediaIndices = []; |
| this._api = api; |
| } |
| |
| OptionManager.prototype.setOption = function (rawOption, optionPreprocessorFuncs, opt) { |
| if (rawOption) { |
| // That set dat primitive is dangerous if user reuse the data when setOption again. |
| each(normalizeToArray(rawOption.series), function (series) { |
| series && series.data && isTypedArray(series.data) && setAsPrimitive(series.data); |
| }); |
| each(normalizeToArray(rawOption.dataset), function (dataset) { |
| dataset && dataset.source && isTypedArray(dataset.source) && setAsPrimitive(dataset.source); |
| }); |
| } // Caution: some series modify option data, if do not clone, |
| // it should ensure that the repeat modify correctly |
| // (create a new object when modify itself). |
| |
| |
| rawOption = clone(rawOption); // FIXME |
| // If some property is set in timeline options or media option but |
| // not set in baseOption, a warning should be given. |
| |
| var optionBackup = this._optionBackup; |
| var newParsedOption = parseRawOption(rawOption, optionPreprocessorFuncs, !optionBackup); |
| this._newBaseOption = newParsedOption.baseOption; // For setOption at second time (using merge mode); |
| |
| if (optionBackup) { |
| // FIXME |
| // the restore merge solution is essentially incorrect. |
| // the mapping can not be 100% consistent with ecModel, which probably brings |
| // potential bug! |
| // The first merge is delayed, because in most cases, users do not call `setOption` twice. |
| // let fakeCmptsMap = this._fakeCmptsMap; |
| // if (!fakeCmptsMap) { |
| // fakeCmptsMap = this._fakeCmptsMap = createHashMap(); |
| // mergeToBackupOption(fakeCmptsMap, null, optionBackup.baseOption, null); |
| // } |
| // mergeToBackupOption( |
| // fakeCmptsMap, optionBackup.baseOption, newParsedOption.baseOption, opt |
| // ); |
| // For simplicity, timeline options and media options do not support merge, |
| // that is, if you `setOption` twice and both has timeline options, the latter |
| // timeline options will not be merged to the former, but just substitute them. |
| if (newParsedOption.timelineOptions.length) { |
| optionBackup.timelineOptions = newParsedOption.timelineOptions; |
| } |
| |
| if (newParsedOption.mediaList.length) { |
| optionBackup.mediaList = newParsedOption.mediaList; |
| } |
| |
| if (newParsedOption.mediaDefault) { |
| optionBackup.mediaDefault = newParsedOption.mediaDefault; |
| } |
| } else { |
| this._optionBackup = newParsedOption; |
| } |
| }; |
| |
| OptionManager.prototype.mountOption = function (isRecreate) { |
| var optionBackup = this._optionBackup; |
| this._timelineOptions = optionBackup.timelineOptions; |
| this._mediaList = optionBackup.mediaList; |
| this._mediaDefault = optionBackup.mediaDefault; |
| this._currentMediaIndices = []; |
| return clone(isRecreate // this._optionBackup.baseOption, which is created at the first `setOption` |
| // called, and is merged into every new option by inner method `mergeToBackupOption` |
| // each time `setOption` called, can be only used in `isRecreate`, because |
| // its reliability is under suspicion. In other cases option merge is |
| // performed by `model.mergeOption`. |
| ? optionBackup.baseOption : this._newBaseOption); |
| }; |
| |
| OptionManager.prototype.getTimelineOption = function (ecModel) { |
| var option; |
| var timelineOptions = this._timelineOptions; |
| |
| if (timelineOptions.length) { |
| // getTimelineOption can only be called after ecModel inited, |
| // so we can get currentIndex from timelineModel. |
| var timelineModel = ecModel.getComponent('timeline'); |
| |
| if (timelineModel) { |
| option = clone( // FIXME:TS as TimelineModel or quivlant interface |
| timelineOptions[timelineModel.getCurrentIndex()]); |
| } |
| } |
| |
| return option; |
| }; |
| |
| OptionManager.prototype.getMediaOption = function (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. |
| |
| if (!mediaList.length && !mediaDefault) { |
| return result; |
| } // Multi media may be applied, the latter defined media has higher priority. |
| |
| |
| for (var i = 0, len = mediaList.length; i < len; i++) { |
| if (applyMediaQuery(mediaList[i].query, ecWidth, ecHeight)) { |
| indices.push(i); |
| } |
| } // FIXME |
| // Whether mediaDefault should force users to provide? Otherwise |
| // the change by media query can not be recorvered. |
| |
| |
| if (!indices.length && mediaDefault) { |
| indices = [-1]; |
| } |
| |
| if (indices.length && !indicesEquals(indices, this._currentMediaIndices)) { |
| result = map(indices, function (index) { |
| return clone(index === -1 ? mediaDefault.option : mediaList[index].option); |
| }); |
| } // Otherwise return nothing. |
| |
| |
| this._currentMediaIndices = indices; |
| return result; |
| }; |
| |
| return OptionManager; |
| }(); |
| /** |
| * [RAW_OPTION_PATTERNS] |
| * (Note: "series: []" represents all other props in `ECUnitOption`) |
| * |
| * (1) No prop "baseOption" declared: |
| * Root option is used as "baseOption" (except prop "options" and "media"). |
| * ```js |
| * option = { |
| * series: [], |
| * timeline: {}, |
| * options: [], |
| * }; |
| * option = { |
| * series: [], |
| * media: {}, |
| * }; |
| * option = { |
| * series: [], |
| * timeline: {}, |
| * options: [], |
| * media: {}, |
| * } |
| * ``` |
| * |
| * (2) Prop "baseOption" declared: |
| * If "baseOption" declared, `ECUnitOption` props can only be declared |
| * inside "baseOption" except prop "timeline" (compat ec2). |
| * ```js |
| * option = { |
| * baseOption: { |
| * timeline: {}, |
| * series: [], |
| * }, |
| * options: [] |
| * }; |
| * option = { |
| * baseOption: { |
| * series: [], |
| * }, |
| * media: [] |
| * }; |
| * option = { |
| * baseOption: { |
| * timeline: {}, |
| * series: [], |
| * }, |
| * options: [] |
| * media: [] |
| * }; |
| * option = { |
| * // ec3 compat ec2: allow (only) `timeline` declared |
| * // outside baseOption. Keep this setting for compat. |
| * timeline: {}, |
| * baseOption: { |
| * series: [], |
| * }, |
| * options: [], |
| * media: [] |
| * }; |
| * ``` |
| */ |
| |
| |
| function parseRawOption( // `rawOption` May be modified |
| rawOption, optionPreprocessorFuncs, isNew) { |
| var mediaList = []; |
| var mediaDefault; |
| var baseOption; |
| var declaredBaseOption = rawOption.baseOption; // Compatible with ec2, [RAW_OPTION_PATTERNS] above. |
| |
| var timelineOnRoot = rawOption.timeline; |
| var timelineOptionsOnRoot = rawOption.options; |
| var mediaOnRoot = rawOption.media; |
| var hasMedia = !!rawOption.media; |
| var hasTimeline = !!(timelineOptionsOnRoot || timelineOnRoot || declaredBaseOption && declaredBaseOption.timeline); |
| |
| if (declaredBaseOption) { |
| baseOption = declaredBaseOption; // For merge option. |
| |
| if (!baseOption.timeline) { |
| baseOption.timeline = timelineOnRoot; |
| } |
| } // For convenience, enable to use the root option as the `baseOption`: |
| // `{ ...normalOptionProps, media: [{ ... }, { ... }] }` |
| else { |
| if (hasTimeline || hasMedia) { |
| rawOption.options = rawOption.media = null; |
| } |
| |
| baseOption = rawOption; |
| } |
| |
| if (hasMedia) { |
| if (isArray(mediaOnRoot)) { |
| each(mediaOnRoot, function (singleMedia) { |
| if ("development" !== 'production') { |
| // Real case of wrong config. |
| if (singleMedia && !singleMedia.option && isObject(singleMedia.query) && isObject(singleMedia.query.option)) { |
| error('Illegal media option. Must be like { media: [ { query: {}, option: {} } ] }'); |
| } |
| } |
| |
| if (singleMedia && singleMedia.option) { |
| if (singleMedia.query) { |
| mediaList.push(singleMedia); |
| } else if (!mediaDefault) { |
| // Use the first media default. |
| mediaDefault = singleMedia; |
| } |
| } |
| }); |
| } else { |
| if ("development" !== 'production') { |
| // Real case of wrong config. |
| error('Illegal media option. Must be an array. Like { media: [ {...}, {...} ] }'); |
| } |
| } |
| } |
| |
| doPreprocess(baseOption); |
| each(timelineOptionsOnRoot, function (option) { |
| return doPreprocess(option); |
| }); |
| each(mediaList, function (media) { |
| return doPreprocess(media.option); |
| }); |
| |
| function doPreprocess(option) { |
| each(optionPreprocessorFuncs, function (preProcess) { |
| preProcess(option, isNew); |
| }); |
| } |
| |
| return { |
| baseOption: baseOption, |
| timelineOptions: timelineOptionsOnRoot || [], |
| mediaDefault: mediaDefault, |
| mediaList: mediaList |
| }; |
| } |
| /** |
| * @see <http://www.w3.org/TR/css3-mediaqueries/#media1> |
| * Support: width, height, aspectRatio |
| * Can use max or min as prefix. |
| */ |
| |
| |
| function applyMediaQuery(query, ecWidth, ecHeight) { |
| var realMap = { |
| width: ecWidth, |
| height: ecHeight, |
| aspectratio: ecWidth / ecHeight // lower case for convenience. |
| |
| }; |
| var applicable = true; |
| each(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)) { |
| applicable = false; |
| } |
| }); |
| return applicable; |
| } |
| |
| function compare(real, expect, operator) { |
| if (operator === 'min') { |
| return real >= expect; |
| } else if (operator === 'max') { |
| return real <= expect; |
| } else { |
| // Equals |
| return real === expect; |
| } |
| } |
| |
| function indicesEquals(indices1, indices2) { |
| // indices is always order by asc and has only finite number. |
| return indices1.join(',') === indices2.join(','); |
| } |
| |
| var each$2 = each; |
| var isObject$1 = isObject; |
| 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 < len; i++) { |
| var styleName = POSSIBLE_STYLES[i]; |
| var normalItemStyleOpt = itemStyleOpt.normal; |
| var emphasisItemStyleOpt = itemStyleOpt.emphasis; |
| |
| if (normalItemStyleOpt && normalItemStyleOpt[styleName]) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog("itemStyle.normal." + styleName, styleName); |
| } |
| |
| opt[styleName] = opt[styleName] || {}; |
| |
| if (!opt[styleName].normal) { |
| opt[styleName].normal = normalItemStyleOpt[styleName]; |
| } else { |
| merge(opt[styleName].normal, normalItemStyleOpt[styleName]); |
| } |
| |
| normalItemStyleOpt[styleName] = null; |
| } |
| |
| if (emphasisItemStyleOpt && emphasisItemStyleOpt[styleName]) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog("itemStyle.emphasis." + styleName, "emphasis." + styleName); |
| } |
| |
| opt[styleName] = opt[styleName] || {}; |
| |
| if (!opt[styleName].emphasis) { |
| opt[styleName].emphasis = emphasisItemStyleOpt[styleName]; |
| } else { |
| merge(opt[styleName].emphasis, emphasisItemStyleOpt[styleName]); |
| } |
| |
| emphasisItemStyleOpt[styleName] = null; |
| } |
| } |
| } |
| |
| function convertNormalEmphasis(opt, optType, useExtend) { |
| if (opt && opt[optType] && (opt[optType].normal || opt[optType].emphasis)) { |
| var normalOpt = opt[optType].normal; |
| var emphasisOpt = opt[optType].emphasis; |
| |
| if (normalOpt) { |
| if ("development" !== 'production') { |
| // eslint-disable-next-line max-len |
| deprecateLog("'normal' hierarchy in " + optType + " has been removed since 4.0. All style properties are configured in " + optType + " directly now."); |
| } // Timeline controlStyle has other properties besides normal and emphasis |
| |
| |
| if (useExtend) { |
| opt[optType].normal = opt[optType].emphasis = null; |
| defaults(opt[optType], normalOpt); |
| } else { |
| opt[optType] = normalOpt; |
| } |
| } |
| |
| if (emphasisOpt) { |
| if ("development" !== 'production') { |
| deprecateLog(optType + ".emphasis has been changed to emphasis." + optType + " since 4.0"); |
| } |
| |
| opt.emphasis = opt.emphasis || {}; |
| opt.emphasis[optType] = emphasisOpt; // Also compat the case user mix the style and focus together in ec3 style |
| // for example: { itemStyle: { normal: {}, emphasis: {focus, shadowBlur} } } |
| |
| if (emphasisOpt.focus) { |
| opt.emphasis.focus = emphasisOpt.focus; |
| } |
| |
| if (emphasisOpt.blurScope) { |
| opt.emphasis.blurScope = emphasisOpt.blurScope; |
| } |
| } |
| } |
| } |
| |
| function removeEC3NormalStatus(opt) { |
| convertNormalEmphasis(opt, 'itemStyle'); |
| convertNormalEmphasis(opt, 'lineStyle'); |
| convertNormalEmphasis(opt, 'areaStyle'); |
| convertNormalEmphasis(opt, 'label'); |
| convertNormalEmphasis(opt, 'labelLine'); // treemap |
| |
| convertNormalEmphasis(opt, 'upperLabel'); // graph |
| |
| convertNormalEmphasis(opt, 'edgeLabel'); |
| } |
| |
| function compatTextStyle(opt, propName) { |
| // Check whether is not object (string\null\undefined ...) |
| var labelOptSingle = isObject$1(opt) && opt[propName]; |
| var textStyle = isObject$1(labelOptSingle) && labelOptSingle.textStyle; |
| |
| if (textStyle) { |
| if ("development" !== 'production') { |
| // eslint-disable-next-line max-len |
| deprecateLog("textStyle hierarchy in " + propName + " has been removed since 4.0. All textStyle properties are configured in " + propName + " directly now."); |
| } |
| |
| for (var i = 0, len = TEXT_STYLE_OPTIONS.length; i < len; i++) { |
| var textPropName = TEXT_STYLE_OPTIONS[i]; |
| |
| if (textStyle.hasOwnProperty(textPropName)) { |
| labelOptSingle[textPropName] = textStyle[textPropName]; |
| } |
| } |
| } |
| } |
| |
| function compatEC3CommonStyles(opt) { |
| if (opt) { |
| removeEC3NormalStatus(opt); |
| compatTextStyle(opt, 'label'); |
| opt.emphasis && compatTextStyle(opt.emphasis, 'label'); |
| } |
| } |
| |
| function processSeries(seriesOpt) { |
| if (!isObject$1(seriesOpt)) { |
| return; |
| } |
| |
| compatEC2ItemStyle(seriesOpt); |
| removeEC3NormalStatus(seriesOpt); |
| compatTextStyle(seriesOpt, 'label'); // treemap |
| |
| compatTextStyle(seriesOpt, 'upperLabel'); // graph |
| |
| compatTextStyle(seriesOpt, 'edgeLabel'); |
| |
| if (seriesOpt.emphasis) { |
| compatTextStyle(seriesOpt.emphasis, 'label'); // treemap |
| |
| compatTextStyle(seriesOpt.emphasis, 'upperLabel'); // graph |
| |
| compatTextStyle(seriesOpt.emphasis, 'edgeLabel'); |
| } |
| |
| var markPoint = seriesOpt.markPoint; |
| |
| if (markPoint) { |
| compatEC2ItemStyle(markPoint); |
| compatEC3CommonStyles(markPoint); |
| } |
| |
| var markLine = seriesOpt.markLine; |
| |
| if (markLine) { |
| compatEC2ItemStyle(markLine); |
| compatEC3CommonStyles(markLine); |
| } |
| |
| var markArea = seriesOpt.markArea; |
| |
| if (markArea) { |
| compatEC3CommonStyles(markArea); |
| } |
| |
| var data = seriesOpt.data; // Break with ec3: if `setOption` again, there may be no `type` in option, |
| // then the backward compat based on option type will not be performed. |
| |
| if (seriesOpt.type === 'graph') { |
| data = data || seriesOpt.nodes; |
| var edgeData = seriesOpt.links || seriesOpt.edges; |
| |
| if (edgeData && !isTypedArray(edgeData)) { |
| for (var i = 0; i < edgeData.length; i++) { |
| compatEC3CommonStyles(edgeData[i]); |
| } |
| } |
| |
| each(seriesOpt.categories, function (opt) { |
| removeEC3NormalStatus(opt); |
| }); |
| } |
| |
| if (data && !isTypedArray(data)) { |
| for (var i = 0; i < data.length; i++) { |
| compatEC3CommonStyles(data[i]); |
| } |
| } // mark point data |
| |
| |
| markPoint = seriesOpt.markPoint; |
| |
| if (markPoint && markPoint.data) { |
| var mpData = markPoint.data; |
| |
| for (var i = 0; i < mpData.length; i++) { |
| compatEC3CommonStyles(mpData[i]); |
| } |
| } // mark line data |
| |
| |
| markLine = seriesOpt.markLine; |
| |
| if (markLine && markLine.data) { |
| var mlData = markLine.data; |
| |
| for (var i = 0; i < mlData.length; i++) { |
| if (isArray(mlData[i])) { |
| compatEC3CommonStyles(mlData[i][0]); |
| compatEC3CommonStyles(mlData[i][1]); |
| } else { |
| compatEC3CommonStyles(mlData[i]); |
| } |
| } |
| } // Series |
| |
| |
| if (seriesOpt.type === 'gauge') { |
| compatTextStyle(seriesOpt, 'axisLabel'); |
| compatTextStyle(seriesOpt, 'title'); |
| compatTextStyle(seriesOpt, 'detail'); |
| } else if (seriesOpt.type === 'treemap') { |
| convertNormalEmphasis(seriesOpt.breadcrumb, 'itemStyle'); |
| each(seriesOpt.levels, function (opt) { |
| removeEC3NormalStatus(opt); |
| }); |
| } else if (seriesOpt.type === 'tree') { |
| removeEC3NormalStatus(seriesOpt.leaves); |
| } // sunburst starts from ec4, so it does not need to compat levels. |
| |
| } |
| |
| function toArr(o) { |
| return isArray(o) ? o : o ? [o] : []; |
| } |
| |
| function toObj(o) { |
| return (isArray(o) ? o[0] : o) || {}; |
| } |
| |
| function globalCompatStyle(option, isTheme) { |
| each$2(toArr(option.series), function (seriesOpt) { |
| isObject$1(seriesOpt) && processSeries(seriesOpt); |
| }); |
| var axes = ['xAxis', 'yAxis', 'radiusAxis', 'angleAxis', 'singleAxis', 'parallelAxis', 'radar']; |
| isTheme && axes.push('valueAxis', 'categoryAxis', 'logAxis', 'timeAxis'); |
| each$2(axes, function (axisName) { |
| each$2(toArr(option[axisName]), function (axisOpt) { |
| if (axisOpt) { |
| compatTextStyle(axisOpt, 'axisLabel'); |
| compatTextStyle(axisOpt.axisPointer, 'label'); |
| } |
| }); |
| }); |
| each$2(toArr(option.parallel), function (parallelOpt) { |
| var parallelAxisDefault = parallelOpt && parallelOpt.parallelAxisDefault; |
| compatTextStyle(parallelAxisDefault, 'axisLabel'); |
| compatTextStyle(parallelAxisDefault && parallelAxisDefault.axisPointer, 'label'); |
| }); |
| each$2(toArr(option.calendar), function (calendarOpt) { |
| convertNormalEmphasis(calendarOpt, 'itemStyle'); |
| compatTextStyle(calendarOpt, 'dayLabel'); |
| compatTextStyle(calendarOpt, 'monthLabel'); |
| compatTextStyle(calendarOpt, 'yearLabel'); |
| }); // radar.name.textStyle |
| |
| each$2(toArr(option.radar), function (radarOpt) { |
| compatTextStyle(radarOpt, 'name'); // Use axisName instead of name because component has name property |
| |
| if (radarOpt.name && radarOpt.axisName == null) { |
| radarOpt.axisName = radarOpt.name; |
| delete radarOpt.name; |
| |
| if ("development" !== 'production') { |
| deprecateLog('name property in radar component has been changed to axisName'); |
| } |
| } |
| |
| if (radarOpt.nameGap != null && radarOpt.axisNameGap == null) { |
| radarOpt.axisNameGap = radarOpt.nameGap; |
| delete radarOpt.nameGap; |
| |
| if ("development" !== 'production') { |
| deprecateLog('nameGap property in radar component has been changed to axisNameGap'); |
| } |
| } |
| |
| if ("development" !== 'production') { |
| each$2(radarOpt.indicator, function (indicatorOpt) { |
| if (indicatorOpt.text) { |
| deprecateReplaceLog('text', 'name', 'radar.indicator'); |
| } |
| }); |
| } |
| }); |
| each$2(toArr(option.geo), function (geoOpt) { |
| if (isObject$1(geoOpt)) { |
| compatEC3CommonStyles(geoOpt); |
| each$2(toArr(geoOpt.regions), function (regionObj) { |
| compatEC3CommonStyles(regionObj); |
| }); |
| } |
| }); |
| each$2(toArr(option.timeline), function (timelineOpt) { |
| compatEC3CommonStyles(timelineOpt); |
| convertNormalEmphasis(timelineOpt, 'label'); |
| convertNormalEmphasis(timelineOpt, 'itemStyle'); |
| convertNormalEmphasis(timelineOpt, 'controlStyle', true); |
| var data = timelineOpt.data; |
| isArray(data) && each(data, function (item) { |
| if (isObject(item)) { |
| convertNormalEmphasis(item, 'label'); |
| convertNormalEmphasis(item, 'itemStyle'); |
| } |
| }); |
| }); |
| each$2(toArr(option.toolbox), function (toolboxOpt) { |
| convertNormalEmphasis(toolboxOpt, 'iconStyle'); |
| each$2(toolboxOpt.feature, function (featureOpt) { |
| convertNormalEmphasis(featureOpt, 'iconStyle'); |
| }); |
| }); |
| compatTextStyle(toObj(option.axisPointer), 'label'); |
| compatTextStyle(toObj(option.tooltip).axisPointer, 'label'); // Clean logs |
| // storedLogs = {}; |
| } |
| |
| function get(opt, path) { |
| var pathArr = path.split(','); |
| var obj = opt; |
| |
| for (var i = 0; i < pathArr.length; i++) { |
| obj = obj && obj[pathArr[i]]; |
| |
| if (obj == null) { |
| break; |
| } |
| } |
| |
| return obj; |
| } |
| |
| function set$1(opt, path, val, overwrite) { |
| var pathArr = path.split(','); |
| var obj = opt; |
| var key; |
| var i = 0; |
| |
| for (; i < pathArr.length - 1; i++) { |
| key = pathArr[i]; |
| |
| if (obj[key] == null) { |
| obj[key] = {}; |
| } |
| |
| obj = obj[key]; |
| } |
| |
| if (overwrite || obj[pathArr[i]] == null) { |
| obj[pathArr[i]] = val; |
| } |
| } |
| |
| function compatLayoutProperties(option) { |
| option && each(LAYOUT_PROPERTIES, function (prop) { |
| if (prop[0] in option && !(prop[1] in option)) { |
| option[prop[1]] = option[prop[0]]; |
| } |
| }); |
| } |
| |
| var LAYOUT_PROPERTIES = [['x', 'left'], ['y', 'top'], ['x2', 'right'], ['y2', 'bottom']]; |
| var COMPATITABLE_COMPONENTS = ['grid', 'geo', 'parallel', 'legend', 'toolbox', 'title', 'visualMap', 'dataZoom', 'timeline']; |
| var BAR_ITEM_STYLE_MAP = [['borderRadius', 'barBorderRadius'], ['borderColor', 'barBorderColor'], ['borderWidth', 'barBorderWidth']]; |
| |
| function compatBarItemStyle(option) { |
| var itemStyle = option && option.itemStyle; |
| |
| if (itemStyle) { |
| for (var i = 0; i < BAR_ITEM_STYLE_MAP.length; i++) { |
| var oldName = BAR_ITEM_STYLE_MAP[i][1]; |
| var newName = BAR_ITEM_STYLE_MAP[i][0]; |
| |
| if (itemStyle[oldName] != null) { |
| itemStyle[newName] = itemStyle[oldName]; |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog(oldName, newName); |
| } |
| } |
| } |
| } |
| } |
| |
| function compatPieLabel(option) { |
| if (!option) { |
| return; |
| } |
| |
| if (option.alignTo === 'edge' && option.margin != null && option.edgeDistance == null) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('label.margin', 'label.edgeDistance', 'pie'); |
| } |
| |
| option.edgeDistance = option.margin; |
| } |
| } |
| |
| function compatSunburstState(option) { |
| if (!option) { |
| return; |
| } |
| |
| if (option.downplay && !option.blur) { |
| option.blur = option.downplay; |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog('downplay', 'blur', 'sunburst'); |
| } |
| } |
| } |
| |
| function compatGraphFocus(option) { |
| if (!option) { |
| return; |
| } |
| |
| if (option.focusNodeAdjacency != null) { |
| option.emphasis = option.emphasis || {}; |
| |
| if (option.emphasis.focus == null) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('focusNodeAdjacency', 'emphasis: { focus: \'adjacency\'}', 'graph/sankey'); |
| } |
| |
| option.emphasis.focus = 'adjacency'; |
| } |
| } |
| } |
| |
| function traverseTree(data, cb) { |
| if (data) { |
| for (var i = 0; i < data.length; i++) { |
| cb(data[i]); |
| data[i] && traverseTree(data[i].children, cb); |
| } |
| } |
| } |
| |
| function globalBackwardCompat(option, isTheme) { |
| globalCompatStyle(option, isTheme); // Make sure series array for model initialization. |
| |
| option.series = normalizeToArray(option.series); |
| each(option.series, function (seriesOpt) { |
| if (!isObject(seriesOpt)) { |
| return; |
| } |
| |
| var seriesType = seriesOpt.type; |
| |
| if (seriesType === 'line') { |
| if (seriesOpt.clipOverflow != null) { |
| seriesOpt.clip = seriesOpt.clipOverflow; |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog('clipOverflow', 'clip', 'line'); |
| } |
| } |
| } else if (seriesType === 'pie' || seriesType === 'gauge') { |
| if (seriesOpt.clockWise != null) { |
| seriesOpt.clockwise = seriesOpt.clockWise; |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog('clockWise', 'clockwise'); |
| } |
| } |
| |
| compatPieLabel(seriesOpt.label); |
| var data = seriesOpt.data; |
| |
| if (data && !isTypedArray(data)) { |
| for (var i = 0; i < data.length; i++) { |
| compatPieLabel(data[i]); |
| } |
| } |
| |
| if (seriesOpt.hoverOffset != null) { |
| seriesOpt.emphasis = seriesOpt.emphasis || {}; |
| |
| if (seriesOpt.emphasis.scaleSize = null) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('hoverOffset', 'emphasis.scaleSize'); |
| } |
| |
| seriesOpt.emphasis.scaleSize = seriesOpt.hoverOffset; |
| } |
| } |
| } else if (seriesType === 'gauge') { |
| var pointerColor = get(seriesOpt, 'pointer.color'); |
| pointerColor != null && set$1(seriesOpt, 'itemStyle.color', pointerColor); |
| } else if (seriesType === 'bar') { |
| compatBarItemStyle(seriesOpt); |
| compatBarItemStyle(seriesOpt.backgroundStyle); |
| compatBarItemStyle(seriesOpt.emphasis); |
| var data = seriesOpt.data; |
| |
| if (data && !isTypedArray(data)) { |
| for (var i = 0; i < data.length; i++) { |
| if (typeof data[i] === 'object') { |
| compatBarItemStyle(data[i]); |
| compatBarItemStyle(data[i] && data[i].emphasis); |
| } |
| } |
| } |
| } else if (seriesType === 'sunburst') { |
| var highlightPolicy = seriesOpt.highlightPolicy; |
| |
| if (highlightPolicy) { |
| seriesOpt.emphasis = seriesOpt.emphasis || {}; |
| |
| if (!seriesOpt.emphasis.focus) { |
| seriesOpt.emphasis.focus = highlightPolicy; |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog('highlightPolicy', 'emphasis.focus', 'sunburst'); |
| } |
| } |
| } |
| |
| compatSunburstState(seriesOpt); |
| traverseTree(seriesOpt.data, compatSunburstState); |
| } else if (seriesType === 'graph' || seriesType === 'sankey') { |
| compatGraphFocus(seriesOpt); // TODO nodes, edges? |
| } else if (seriesType === 'map') { |
| if (seriesOpt.mapType && !seriesOpt.map) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('mapType', 'map', 'map'); |
| } |
| |
| seriesOpt.map = seriesOpt.mapType; |
| } |
| |
| if (seriesOpt.mapLocation) { |
| if ("development" !== 'production') { |
| deprecateLog('`mapLocation` is not used anymore.'); |
| } |
| |
| defaults(seriesOpt, seriesOpt.mapLocation); |
| } |
| } |
| |
| if (seriesOpt.hoverAnimation != null) { |
| seriesOpt.emphasis = seriesOpt.emphasis || {}; |
| |
| if (seriesOpt.emphasis && seriesOpt.emphasis.scale == null) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('hoverAnimation', 'emphasis.scale'); |
| } |
| |
| seriesOpt.emphasis.scale = seriesOpt.hoverAnimation; |
| } |
| } |
| |
| compatLayoutProperties(seriesOpt); |
| }); // dataRange has changed to visualMap |
| |
| if (option.dataRange) { |
| option.visualMap = option.dataRange; |
| } |
| |
| each(COMPATITABLE_COMPONENTS, function (componentName) { |
| var options = option[componentName]; |
| |
| if (options) { |
| if (!isArray(options)) { |
| options = [options]; |
| } |
| |
| each(options, function (option) { |
| compatLayoutProperties(option); |
| }); |
| } |
| }); |
| } |
| |
| // data processing stage is blocked in stream. |
| // See <module:echarts/stream/Scheduler#performDataProcessorTasks> |
| // (2) Only register once when import repeatedly. |
| // Should be executed after series is filtered and before stack calculation. |
| |
| function dataStack(ecModel) { |
| var stackInfoMap = createHashMap(); |
| ecModel.eachSeries(function (seriesModel) { |
| var stack = seriesModel.get('stack'); // Compatible: when `stack` is set as '', do not stack. |
| |
| if (stack) { |
| var stackInfoList = stackInfoMap.get(stack) || stackInfoMap.set(stack, []); |
| var data = seriesModel.getData(); |
| var stackInfo = { |
| // Used for calculate axis extent automatically. |
| // TODO: Type getCalculationInfo return more specific type? |
| stackResultDimension: 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. |
| |
| if (!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(stackInfoList, function (targetStackInfo, idxInStack) { |
| var resultVal = []; |
| var resultNaN = [NaN, NaN]; |
| var dims = [targetStackInfo.stackResultDimension, targetStackInfo.stackedOverDimension]; |
| var targetData = targetStackInfo.data; |
| var isStackedByIndex = targetStackInfo.isStackedByIndex; |
| var stackStrategy = targetStackInfo.seriesModel.get('stackStrategy') || 'samesign'; // Should not write on raw data, because stack series model list changes |
| // depending on legend selection. |
| |
| targetData.modify(dims, function (v0, v1, dataIndex) { |
| var sum = targetData.get(targetStackInfo.stackedDimension, dataIndex); // Consider `connectNulls` of line area, if value is NaN, stackedOver |
| // should also be NaN, to draw a appropriate belt area. |
| |
| if (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. |
| |
| |
| var stackedOver = NaN; |
| |
| for (var j = idxInStack - 1; j >= 0; j--) { |
| var stackInfo = stackInfoList[j]; // Has been optimized by inverted indices on `stackedByDimension`. |
| |
| if (!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 |
| |
| if (stackStrategy === 'all' // single stack group |
| || stackStrategy === 'positive' && val > 0 || stackStrategy === 'negative' && val < 0 || stackStrategy === 'samesign' && sum > 0 && val > 0 // All positive stack |
| || stackStrategy === 'samesign' && sum < 0 && val < 0 // All negative stack |
| ) { |
| // The sum has to be very small to be affected by the |
| // floating arithmetic problem. An incorrect result will probably |
| // cause axis min/max to be filtered incorrectly. |
| sum = addSafe(sum, val); |
| stackedOver = val; |
| break; |
| } |
| } |
| } |
| |
| resultVal[0] = sum; |
| resultVal[1] = stackedOver; |
| return resultVal; |
| }); |
| }); |
| } |
| |
| var SourceImpl = |
| /** @class */ |
| function () { |
| function SourceImpl(fields) { |
| this.data = fields.data || (fields.sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS ? {} : []); |
| this.sourceFormat = fields.sourceFormat || SOURCE_FORMAT_UNKNOWN; // Visit config |
| |
| this.seriesLayoutBy = fields.seriesLayoutBy || SERIES_LAYOUT_BY_COLUMN; |
| this.startIndex = fields.startIndex || 0; |
| this.dimensionsDetectedCount = fields.dimensionsDetectedCount; |
| this.metaRawOption = fields.metaRawOption; |
| var dimensionsDefine = this.dimensionsDefine = fields.dimensionsDefine; |
| |
| if (dimensionsDefine) { |
| for (var i = 0; i < dimensionsDefine.length; i++) { |
| var dim = dimensionsDefine[i]; |
| |
| if (dim.type == null) { |
| if (guessOrdinal(this, i) === BE_ORDINAL.Must) { |
| dim.type = 'ordinal'; |
| } |
| } |
| } |
| } |
| } |
| |
| return SourceImpl; |
| }(); |
| |
| function isSourceInstance(val) { |
| return val instanceof SourceImpl; |
| } |
| /** |
| * Create a source from option. |
| * NOTE: Created source is immutable. Don't change any properties in it. |
| */ |
| |
| function createSource(sourceData, thisMetaRawOption, // can be null. If not provided, auto detect it from `sourceData`. |
| sourceFormat) { |
| sourceFormat = sourceFormat || detectSourceFormat(sourceData); |
| var seriesLayoutBy = thisMetaRawOption.seriesLayoutBy; |
| var determined = determineSourceDimensions(sourceData, sourceFormat, seriesLayoutBy, thisMetaRawOption.sourceHeader, thisMetaRawOption.dimensions); |
| var source = new SourceImpl({ |
| data: sourceData, |
| sourceFormat: sourceFormat, |
| seriesLayoutBy: seriesLayoutBy, |
| dimensionsDefine: determined.dimensionsDefine, |
| startIndex: determined.startIndex, |
| dimensionsDetectedCount: determined.dimensionsDetectedCount, |
| metaRawOption: clone(thisMetaRawOption) |
| }); |
| return source; |
| } |
| /** |
| * Wrap original series data for some compatibility cases. |
| */ |
| |
| function createSourceFromSeriesDataOption(data) { |
| return new SourceImpl({ |
| data: data, |
| sourceFormat: isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL |
| }); |
| } |
| /** |
| * Clone source but excludes source data. |
| */ |
| |
| function cloneSourceShallow(source) { |
| return new SourceImpl({ |
| data: source.data, |
| sourceFormat: source.sourceFormat, |
| seriesLayoutBy: source.seriesLayoutBy, |
| dimensionsDefine: clone(source.dimensionsDefine), |
| startIndex: source.startIndex, |
| dimensionsDetectedCount: source.dimensionsDetectedCount |
| }); |
| } |
| /** |
| * Note: An empty array will be detected as `SOURCE_FORMAT_ARRAY_ROWS`. |
| */ |
| |
| function detectSourceFormat(data) { |
| 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? |
| if (data.length === 0) { |
| sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; |
| } |
| |
| for (var i = 0, len = data.length; i < len; i++) { |
| var item = data[i]; |
| |
| if (item == null) { |
| continue; |
| } else if (isArray(item)) { |
| sourceFormat = SOURCE_FORMAT_ARRAY_ROWS; |
| break; |
| } else if (isObject(item)) { |
| sourceFormat = SOURCE_FORMAT_OBJECT_ROWS; |
| break; |
| } |
| } |
| } else if (isObject(data)) { |
| for (var key in data) { |
| if (hasOwn(data, key) && isArrayLike(data[key])) { |
| sourceFormat = SOURCE_FORMAT_KEYED_COLUMNS; |
| break; |
| } |
| } |
| } |
| |
| return sourceFormat; |
| } |
| /** |
| * Determine the source definitions from data standalone dimensions definitions |
| * are not specified. |
| */ |
| |
| function determineSourceDimensions(data, sourceFormat, seriesLayoutBy, sourceHeader, // standalone raw dimensions definition, like: |
| // { |
| // dimensions: ['aa', 'bb', { name: 'cc', type: 'time' }] |
| // } |
| // in `dataset` or `series` |
| dimensionsDefine) { |
| var dimensionsDetectedCount; |
| var startIndex; // PENDING: Could data be null/undefined here? |
| // currently, if `dataset.source` not specified, error thrown. |
| // if `series.data` not specified, nothing rendered without error thrown. |
| // Should test these cases. |
| |
| if (!data) { |
| return { |
| dimensionsDefine: normalizeDimensionsOption(dimensionsDefine), |
| startIndex: startIndex, |
| dimensionsDetectedCount: dimensionsDetectedCount |
| }; |
| } |
| |
| if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { |
| var dataArrayRows = data; // Rule: Most of the first line are string: it is header. |
| // Caution: consider a line with 5 string and 1 number, |
| // it still can not be sure it is a head, because the |
| // 5 string may be 5 values of category columns. |
| |
| if (sourceHeader === 'auto' || sourceHeader == null) { |
| arrayRowsTravelFirst(function (val) { |
| // '-' is regarded as null/undefined. |
| if (val != null && val !== '-') { |
| if (isString(val)) { |
| startIndex == null && (startIndex = 1); |
| } else { |
| startIndex = 0; |
| } |
| } // 10 is an experience number, avoid long loop. |
| |
| }, seriesLayoutBy, dataArrayRows, 10); |
| } else { |
| startIndex = isNumber(sourceHeader) ? sourceHeader : sourceHeader ? 1 : 0; |
| } |
| |
| if (!dimensionsDefine && startIndex === 1) { |
| dimensionsDefine = []; |
| arrayRowsTravelFirst(function (val, index) { |
| dimensionsDefine[index] = val != null ? val + '' : ''; |
| }, seriesLayoutBy, dataArrayRows, Infinity); |
| } |
| |
| dimensionsDetectedCount = dimensionsDefine ? dimensionsDefine.length : seriesLayoutBy === SERIES_LAYOUT_BY_ROW ? dataArrayRows.length : dataArrayRows[0] ? dataArrayRows[0].length : null; |
| } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { |
| if (!dimensionsDefine) { |
| dimensionsDefine = objectRowsCollectDimensions(data); |
| } |
| } else if (sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS) { |
| if (!dimensionsDefine) { |
| dimensionsDefine = []; |
| each(data, function (colArr, key) { |
| dimensionsDefine.push(key); |
| }); |
| } |
| } else if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { |
| var value0 = getDataItemValue(data[0]); |
| dimensionsDetectedCount = isArray(value0) && value0.length || 1; |
| } else if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { |
| if ("development" !== 'production') { |
| assert(!!dimensionsDefine, 'dimensions must be given if data is TypedArray.'); |
| } |
| } |
| |
| return { |
| startIndex: startIndex, |
| dimensionsDefine: normalizeDimensionsOption(dimensionsDefine), |
| dimensionsDetectedCount: dimensionsDetectedCount |
| }; |
| } |
| |
| function objectRowsCollectDimensions(data) { |
| var firstIndex = 0; |
| var obj; |
| |
| while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line |
| |
| |
| if (obj) { |
| var dimensions_1 = []; |
| each(obj, function (value, key) { |
| dimensions_1.push(key); |
| }); |
| return dimensions_1; |
| } |
| } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'], |
| // which is reasonable. But dimension name is duplicated. |
| // Returns undefined or an array contains only object without null/undefined or string. |
| |
| |
| function normalizeDimensionsOption(dimensionsDefine) { |
| if (!dimensionsDefine) { |
| // The meaning of null/undefined is different from empty array. |
| return; |
| } |
| |
| var nameMap = createHashMap(); |
| return map(dimensionsDefine, function (rawItem, index) { |
| rawItem = isObject(rawItem) ? rawItem : { |
| name: rawItem |
| }; // Other fields will be discarded. |
| |
| var item = { |
| name: rawItem.name, |
| displayName: rawItem.displayName, |
| type: rawItem.type |
| }; // User can set null in dimensions. |
| // We don't auto specify name, otherwise a given name may |
| // cause it to be referred unexpectedly. |
| |
| if (item.name == null) { |
| return item; |
| } // Also consider number form like 2012. |
| |
| |
| item.name += ''; // User may also specify displayName. |
| // displayName will always exists except user not |
| // specified or dim name is not specified or detected. |
| // (A auto generated dim name will not be used as |
| // displayName). |
| |
| if (item.displayName == null) { |
| item.displayName = item.name; |
| } |
| |
| var exist = nameMap.get(item.name); |
| |
| if (!exist) { |
| nameMap.set(item.name, { |
| count: 1 |
| }); |
| } else { |
| item.name += '-' + exist.count++; |
| } |
| |
| return item; |
| }); |
| } |
| |
| function arrayRowsTravelFirst(cb, seriesLayoutBy, data, maxLoop) { |
| if (seriesLayoutBy === SERIES_LAYOUT_BY_ROW) { |
| for (var i = 0; i < data.length && i < maxLoop; i++) { |
| cb(data[i] ? data[i][0] : null, i); |
| } |
| } else { |
| var value0 = data[0] || []; |
| |
| for (var i = 0; i < value0.length && i < maxLoop; i++) { |
| cb(value0[i], i); |
| } |
| } |
| } |
| |
| function shouldRetrieveDataByName(source) { |
| var sourceFormat = source.sourceFormat; |
| return sourceFormat === SOURCE_FORMAT_OBJECT_ROWS || sourceFormat === SOURCE_FORMAT_KEYED_COLUMNS; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var _a, _b, _c; // TODO |
| var providerMethods; |
| var mountMethods; |
| /** |
| * If normal array used, mutable chunk size is supported. |
| * If typed array used, chunk size must be fixed. |
| */ |
| |
| var DefaultDataProvider = |
| /** @class */ |
| function () { |
| function DefaultDataProvider(sourceParam, dimSize) { |
| // let source: Source; |
| var source = !isSourceInstance(sourceParam) ? createSourceFromSeriesDataOption(sourceParam) : sourceParam; // declare source is Source; |
| |
| this._source = source; |
| var data = this._data = source.data; // Typed array. TODO IE10+? |
| |
| if (source.sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { |
| if ("development" !== 'production') { |
| if (dimSize == null) { |
| throw new Error('Typed array data must specify dimension size'); |
| } |
| } |
| |
| this._offset = 0; |
| this._dimSize = dimSize; |
| this._data = data; |
| } |
| |
| mountMethods(this, data, source); |
| } |
| |
| DefaultDataProvider.prototype.getSource = function () { |
| return this._source; |
| }; |
| |
| DefaultDataProvider.prototype.count = function () { |
| return 0; |
| }; |
| |
| DefaultDataProvider.prototype.getItem = function (idx, out) { |
| return; |
| }; |
| |
| DefaultDataProvider.prototype.appendData = function (newData) {}; |
| |
| DefaultDataProvider.prototype.clean = function () {}; |
| |
| DefaultDataProvider.protoInitialize = function () { |
| // PENDING: To avoid potential incompat (e.g., prototype |
| // is visited somewhere), still init them on prototype. |
| var proto = DefaultDataProvider.prototype; |
| proto.pure = false; |
| proto.persistent = true; |
| }(); |
| |
| DefaultDataProvider.internalField = function () { |
| var _a; |
| |
| mountMethods = function (provider, data, source) { |
| var sourceFormat = source.sourceFormat; |
| var seriesLayoutBy = source.seriesLayoutBy; |
| var startIndex = source.startIndex; |
| var dimsDef = source.dimensionsDefine; |
| var methods = providerMethods[getMethodMapKey(sourceFormat, seriesLayoutBy)]; |
| |
| if ("development" !== 'production') { |
| assert(methods, 'Invalide sourceFormat: ' + sourceFormat); |
| } |
| |
| extend(provider, methods); |
| |
| if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) { |
| provider.getItem = getItemForTypedArray; |
| provider.count = countForTypedArray; |
| provider.fillStorage = fillStorageForTypedArray; |
| } else { |
| var rawItemGetter = getRawSourceItemGetter(sourceFormat, seriesLayoutBy); |
| provider.getItem = bind(rawItemGetter, null, data, startIndex, dimsDef); |
| var rawCounter = getRawSourceDataCounter(sourceFormat, seriesLayoutBy); |
| provider.count = bind(rawCounter, null, data, startIndex, dimsDef); |
| } |
| }; |
| |
| var getItemForTypedArray = function (idx, out) { |
| idx = idx - this._offset; |
| out = out || []; |
| var data = this._data; |
| var dimSize = this._dimSize; |
| var offset = dimSize * idx; |
| |
| for (var i = 0; i < dimSize; i++) { |
| out[i] = data[offset + i]; |
| } |
| |
| return out; |
| }; |
| |
| var fillStorageForTypedArray = function (start, end, storage, extent) { |
| var data = this._data; |
| var dimSize = this._dimSize; |
| |
| for (var dim = 0; dim < dimSize; dim++) { |
| var dimExtent = extent[dim]; |
| var min = dimExtent[0] == null ? Infinity : dimExtent[0]; |
| var max = dimExtent[1] == null ? -Infinity : dimExtent[1]; |
| var count = end - start; |
| var arr = storage[dim]; |
| |
| for (var i = 0; i < count; i++) { |
| // appendData with TypedArray will always do replace in provider. |
| var val = data[i * dimSize + dim]; |
| arr[start + i] = val; |
| val < min && (min = val); |
| val > max && (max = val); |
| } |
| |
| dimExtent[0] = min; |
| dimExtent[1] = max; |
| } |
| }; |
| |
| var countForTypedArray = function () { |
| return this._data ? this._data.length / this._dimSize : 0; |
| }; |
| |
| providerMethods = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = { |
| pure: true, |
| appendData: appendDataSimply |
| }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = { |
| pure: true, |
| appendData: function () { |
| throw new Error('Do not support appendData when set seriesLayoutBy: "row".'); |
| } |
| }, _a[SOURCE_FORMAT_OBJECT_ROWS] = { |
| pure: true, |
| appendData: appendDataSimply |
| }, _a[SOURCE_FORMAT_KEYED_COLUMNS] = { |
| pure: true, |
| appendData: function (newData) { |
| var data = this._data; |
| each(newData, function (newCol, key) { |
| var oldCol = data[key] || (data[key] = []); |
| |
| for (var i = 0; i < (newCol || []).length; i++) { |
| oldCol.push(newCol[i]); |
| } |
| }); |
| } |
| }, _a[SOURCE_FORMAT_ORIGINAL] = { |
| appendData: appendDataSimply |
| }, _a[SOURCE_FORMAT_TYPED_ARRAY] = { |
| persistent: false, |
| pure: true, |
| appendData: function (newData) { |
| if ("development" !== 'production') { |
| assert(isTypedArray(newData), 'Added data must be TypedArray if data in initialization is TypedArray'); |
| } |
| |
| this._data = newData; |
| }, |
| // Clean self if data is already used. |
| clean: function () { |
| // PENDING |
| this._offset += this.count(); |
| this._data = null; |
| } |
| }, _a); |
| |
| function appendDataSimply(newData) { |
| for (var i = 0; i < newData.length; i++) { |
| this._data.push(newData[i]); |
| } |
| } |
| }(); |
| |
| return DefaultDataProvider; |
| }(); |
| |
| var getItemSimply = function (rawData, startIndex, dimsDef, idx) { |
| return rawData[idx]; |
| }; |
| |
| var rawSourceItemGetterMap = (_a = {}, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef, idx) { |
| return rawData[idx + startIndex]; |
| }, _a[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef, idx, out) { |
| idx += startIndex; |
| var item = out || []; |
| var data = rawData; |
| |
| for (var i = 0; i < data.length; i++) { |
| var row = data[i]; |
| item[i] = row ? row[idx] : null; |
| } |
| |
| return item; |
| }, _a[SOURCE_FORMAT_OBJECT_ROWS] = getItemSimply, _a[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef, idx, out) { |
| var item = out || []; |
| |
| for (var i = 0; i < dimsDef.length; i++) { |
| var dimName = dimsDef[i].name; |
| |
| if ("development" !== 'production') { |
| if (dimName == null) { |
| throw new Error(); |
| } |
| } |
| |
| var col = rawData[dimName]; |
| item[i] = col ? col[idx] : null; |
| } |
| |
| return item; |
| }, _a[SOURCE_FORMAT_ORIGINAL] = getItemSimply, _a); |
| function getRawSourceItemGetter(sourceFormat, seriesLayoutBy) { |
| var method = rawSourceItemGetterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; |
| |
| if ("development" !== 'production') { |
| assert(method, 'Do not support get item on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); |
| } |
| |
| return method; |
| } |
| |
| var countSimply = function (rawData, startIndex, dimsDef) { |
| return rawData.length; |
| }; |
| |
| var rawSourceDataCounterMap = (_b = {}, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_COLUMN] = function (rawData, startIndex, dimsDef) { |
| return Math.max(0, rawData.length - startIndex); |
| }, _b[SOURCE_FORMAT_ARRAY_ROWS + '_' + SERIES_LAYOUT_BY_ROW] = function (rawData, startIndex, dimsDef) { |
| var row = rawData[0]; |
| return row ? Math.max(0, row.length - startIndex) : 0; |
| }, _b[SOURCE_FORMAT_OBJECT_ROWS] = countSimply, _b[SOURCE_FORMAT_KEYED_COLUMNS] = function (rawData, startIndex, dimsDef) { |
| var dimName = dimsDef[0].name; |
| |
| if ("development" !== 'production') { |
| if (dimName == null) { |
| throw new Error(); |
| } |
| } |
| |
| var col = rawData[dimName]; |
| return col ? col.length : 0; |
| }, _b[SOURCE_FORMAT_ORIGINAL] = countSimply, _b); |
| function getRawSourceDataCounter(sourceFormat, seriesLayoutBy) { |
| var method = rawSourceDataCounterMap[getMethodMapKey(sourceFormat, seriesLayoutBy)]; |
| |
| if ("development" !== 'production') { |
| assert(method, 'Do not support count on "' + sourceFormat + '", "' + seriesLayoutBy + '".'); |
| } |
| |
| return method; |
| } |
| |
| var getRawValueSimply = function (dataItem, dimIndex, property) { |
| return dataItem[dimIndex]; |
| }; |
| |
| var rawSourceValueGetterMap = (_c = {}, _c[SOURCE_FORMAT_ARRAY_ROWS] = getRawValueSimply, _c[SOURCE_FORMAT_OBJECT_ROWS] = function (dataItem, dimIndex, property) { |
| return dataItem[property]; |
| }, _c[SOURCE_FORMAT_KEYED_COLUMNS] = getRawValueSimply, _c[SOURCE_FORMAT_ORIGINAL] = function (dataItem, dimIndex, property) { |
| // FIXME: In some case (markpoint in geo (geo-map.html)), |
| // dataItem is {coord: [...]} |
| var value = getDataItemValue(dataItem); |
| return !(value instanceof Array) ? value : value[dimIndex]; |
| }, _c[SOURCE_FORMAT_TYPED_ARRAY] = getRawValueSimply, _c); |
| function getRawSourceValueGetter(sourceFormat) { |
| var method = rawSourceValueGetterMap[sourceFormat]; |
| |
| if ("development" !== 'production') { |
| assert(method, 'Do not support get value on "' + sourceFormat + '".'); |
| } |
| |
| return method; |
| } |
| |
| function getMethodMapKey(sourceFormat, seriesLayoutBy) { |
| return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + seriesLayoutBy : sourceFormat; |
| } // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem, |
| // Consider persistent. |
| // Caution: why use raw value to display on label or tooltip? |
| // A reason is to avoid format. For example time value we do not know |
| // how to format is expected. More over, if stack is used, calculated |
| // value may be 0.91000000001, which have brings trouble to display. |
| // TODO: consider how to treat null/undefined/NaN when display? |
| |
| |
| function retrieveRawValue(data, dataIndex, // If dimIndex is null/undefined, return OptionDataItem. |
| // Otherwise, return OptionDataValue. |
| dim) { |
| if (!data) { |
| return; |
| } // Consider data may be not persistent. |
| |
| |
| var dataItem = data.getRawDataItem(dataIndex); |
| |
| if (dataItem == null) { |
| return; |
| } |
| |
| var store = data.getStore(); |
| var sourceFormat = store.getSource().sourceFormat; |
| |
| if (dim != null) { |
| var dimIndex = data.getDimensionIndex(dim); |
| var property = store.getDimensionProperty(dimIndex); |
| return getRawSourceValueGetter(sourceFormat)(dataItem, dimIndex, property); |
| } else { |
| var result = dataItem; |
| |
| if (sourceFormat === SOURCE_FORMAT_ORIGINAL) { |
| result = getDataItemValue(dataItem); |
| } |
| |
| return result; |
| } |
| } |
| |
| var DIMENSION_LABEL_REG = /\{@(.+?)\}/g; |
| |
| var DataFormatMixin = |
| /** @class */ |
| function () { |
| function DataFormatMixin() {} |
| /** |
| * Get params for formatter |
| */ |
| |
| |
| DataFormatMixin.prototype.getDataParams = function (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 style = data.getItemVisual(dataIndex, 'style'); |
| var color = style && style[data.getItemVisual(dataIndex, 'drawType') || 'fill']; |
| var borderColor = style && style.stroke; |
| var mainType = this.mainType; |
| var isSeries = mainType === 'series'; |
| var userOutput = data.userOutput && data.userOutput.get(); |
| 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.fullDimensions : null, |
| encode: userOutput ? userOutput.encode : null, |
| // Param name list for mapping `a`, `b`, `c`, `d`, `e` |
| $vars: ['seriesName', 'name', 'value'] |
| }; |
| }; |
| /** |
| * Format label |
| * @param dataIndex |
| * @param status 'normal' by default |
| * @param dataType |
| * @param labelDimIndex Only used in some chart that |
| * use formatter in different dimensions, like radar. |
| * @param formatter Formatter given outside. |
| * @return return null/undefined if no formatter |
| */ |
| |
| |
| DataFormatMixin.prototype.getFormattedLabel = function (dataIndex, status, dataType, labelDimIndex, formatter, extendParams) { |
| status = status || 'normal'; |
| var data = this.getData(dataType); |
| var params = this.getDataParams(dataIndex, dataType); |
| |
| if (extendParams) { |
| params.value = extendParams.interpolatedValue; |
| } |
| |
| if (labelDimIndex != null && isArray(params.value)) { |
| params.value = params.value[labelDimIndex]; |
| } |
| |
| if (!formatter) { |
| var itemModel = data.getItemModel(dataIndex); // @ts-ignore |
| |
| formatter = itemModel.get(status === 'normal' ? ['label', 'formatter'] : [status, 'label', 'formatter']); |
| } |
| |
| if (isFunction(formatter)) { |
| params.status = status; |
| params.dimensionIndex = labelDimIndex; |
| return formatter(params); |
| } else if (isString(formatter)) { |
| var str = formatTpl(formatter, params); // Support 'aaa{@[3]}bbb{@product}ccc'. |
| // Do not support '}' in dim name util have to. |
| |
| return str.replace(DIMENSION_LABEL_REG, function (origin, dimStr) { |
| var len = dimStr.length; |
| var dimLoose = dimStr; |
| |
| if (dimLoose.charAt(0) === '[' && dimLoose.charAt(len - 1) === ']') { |
| dimLoose = +dimLoose.slice(1, len - 1); // Also support: '[]' => 0 |
| |
| if ("development" !== 'production') { |
| if (isNaN(dimLoose)) { |
| error("Invalide label formatter: @" + dimStr + ", only support @[0], @[1], @[2], ..."); |
| } |
| } |
| } |
| |
| var val = retrieveRawValue(data, dataIndex, dimLoose); |
| |
| if (extendParams && isArray(extendParams.interpolatedValue)) { |
| var dimIndex = data.getDimensionIndex(dimLoose); |
| |
| if (dimIndex >= 0) { |
| val = extendParams.interpolatedValue[dimIndex]; |
| } |
| } |
| |
| return val != null ? val + '' : ''; |
| }); |
| } |
| }; |
| /** |
| * Get raw value in option |
| */ |
| |
| |
| DataFormatMixin.prototype.getRawValue = function (idx, dataType) { |
| return retrieveRawValue(this.getData(dataType), idx); |
| }; |
| /** |
| * Should be implemented. |
| * @param {number} dataIndex |
| * @param {boolean} [multipleSeries=false] |
| * @param {string} [dataType] |
| */ |
| |
| |
| DataFormatMixin.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { |
| // Empty function |
| return; |
| }; |
| |
| return DataFormatMixin; |
| }(); |
| |
| /** |
| * @param {Object} define |
| * @return See the return of `createTask`. |
| */ |
| |
| function createTask(define) { |
| return new Task(define); |
| } |
| |
| var Task = |
| /** @class */ |
| function () { |
| function Task(define) { |
| define = define || {}; |
| this._reset = define.reset; |
| this._plan = define.plan; |
| this._count = define.count; |
| this._onDirty = define.onDirty; |
| this._dirty = true; |
| } |
| /** |
| * @param step Specified step. |
| * @param skip Skip customer perform call. |
| * @param modBy Sampling window size. |
| * @param modDataCount Sampling count. |
| * @return whether unfinished. |
| */ |
| |
| |
| Task.prototype.perform = function (performArgs) { |
| var upTask = this._upstream; |
| var skip = performArgs && performArgs.skip; // TODO some refactor. |
| // Pull data. Must pull data each time, because context.data |
| // may be updated by Series.setData. |
| |
| if (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 |
| // elements uniformed distributed when progress, especially when moving or zooming. |
| |
| |
| var 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 |
| |
| return val; |
| } |
| |
| var forceFirstProgress; |
| |
| if (this._dirty || planResult === 'reset') { |
| this._dirty = false; |
| forceFirstProgress = this._doReset(skip); |
| } |
| |
| this._modBy = modBy; |
| this._modDataCount = modDataCount; |
| var step = performArgs && performArgs.step; |
| |
| if (upTask) { |
| if ("development" !== 'production') { |
| assert(upTask._outputDueEnd != null); |
| } |
| |
| this._dueEnd = upTask._outputDueEnd; |
| } // DataTask or overallTask |
| else { |
| if ("development" !== 'production') { |
| assert(!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. |
| // If no progress, pass index from upstream to downstream each time plan called. |
| |
| |
| if (this._progress) { |
| var start = this._dueIndex; |
| var end = Math.min(step != null ? this._dueIndex + step : Infinity, this._dueEnd); |
| |
| if (!skip && (forceFirstProgress || start < end)) { |
| var progress = this._progress; |
| |
| if (isArray(progress)) { |
| for (var i = 0; i < progress.length; i++) { |
| this._doProgress(progress[i], start, end, modBy, modDataCount); |
| } |
| } else { |
| this._doProgress(progress, start, end, modBy, modDataCount); |
| } |
| } |
| |
| this._dueIndex = end; // If no `outputDueEnd`, assume that output data and |
| // input data is the same, so use `dueIndex` as `outputDueEnd`. |
| |
| var outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : end; |
| |
| if ("development" !== 'production') { |
| // ??? Can not rollback. |
| assert(outputDueEnd >= this._outputDueEnd); |
| } |
| |
| this._outputDueEnd = outputDueEnd; |
| } else { |
| // (1) Some overall task has no progress. |
| // (2) Stubs, that its host overall task do not let it has progress, has no progress. |
| // This should always be performed so it can be passed to downstream. |
| this._dueIndex = this._outputDueEnd = this._settedOutputEnd != null ? this._settedOutputEnd : this._dueEnd; |
| } |
| |
| return this.unfinished(); |
| }; |
| |
| Task.prototype.dirty = function () { |
| this._dirty = true; |
| this._onDirty && this._onDirty(this.context); |
| }; |
| |
| Task.prototype._doProgress = function (progress, start, end, modBy, modDataCount) { |
| iterator.reset(start, end, modBy, modDataCount); |
| this._callingProgress = progress; |
| |
| this._callingProgress({ |
| start: start, |
| end: end, |
| count: end - start, |
| next: iterator.next |
| }, this.context); |
| }; |
| |
| Task.prototype._doReset = function (skip) { |
| this._dueIndex = this._outputDueEnd = this._dueEnd = 0; |
| this._settedOutputEnd = null; |
| var progress; |
| var forceFirstProgress; |
| |
| if (!skip && this._reset) { |
| progress = this._reset(this.context); |
| |
| if (progress && progress.progress) { |
| forceFirstProgress = progress.forceFirstProgress; |
| progress = progress.progress; |
| } // To simplify no progress checking, array must has item. |
| |
| |
| if (isArray(progress) && !progress.length) { |
| progress = null; |
| } |
| } |
| |
| this._progress = progress; |
| this._modBy = this._modDataCount = null; |
| var downstream = this._downstream; |
| downstream && downstream.dirty(); |
| return forceFirstProgress; |
| }; |
| |
| Task.prototype.unfinished = function () { |
| return this._progress && this._dueIndex < this._dueEnd; |
| }; |
| /** |
| * @param downTask The downstream task. |
| * @return The downstream task. |
| */ |
| |
| |
| Task.prototype.pipe = function (downTask) { |
| if ("development" !== 'production') { |
| assert(downTask && !downTask._disposed && downTask !== this); |
| } // If already downstream, do not dirty downTask. |
| |
| |
| if (this._downstream !== downTask || this._dirty) { |
| this._downstream = downTask; |
| downTask._upstream = this; |
| downTask.dirty(); |
| } |
| }; |
| |
| Task.prototype.dispose = function () { |
| if (this._disposed) { |
| return; |
| } |
| |
| this._upstream && (this._upstream._downstream = null); |
| this._downstream && (this._downstream._upstream = null); |
| this._dirty = false; |
| this._disposed = true; |
| }; |
| |
| Task.prototype.getUpstream = function () { |
| return this._upstream; |
| }; |
| |
| Task.prototype.getDownstream = function () { |
| return this._downstream; |
| }; |
| |
| Task.prototype.setOutputEnd = function (end) { |
| // This only happens in dataTask, dataZoom, map, currently. |
| // where dataZoom do not set end each time, but only set |
| // when reset. So we should record the set end, in case |
| // that the stub of dataZoom perform again and earse the |
| // set end by upstream. |
| this._outputDueEnd = this._settedOutputEnd = end; |
| }; |
| |
| return Task; |
| }(); |
| |
| var iterator = function () { |
| var end; |
| var current; |
| var modBy; |
| var modDataCount; |
| var winCount; |
| var it = { |
| reset: function (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 ? current++ : null; |
| } |
| |
| function modNext() { |
| var dataIndex = current % winCount * modBy + Math.ceil(current / winCount); |
| var result = current >= end ? null : dataIndex < modDataCount ? dataIndex // If modDataCount is smaller than data.count() (consider `appendData` case), |
| // Use normal linear rendering mode. |
| : current; |
| current++; |
| return result; |
| } |
| }(); // ----------------------------------------------------------------------------- |
| // For stream debug (Should be commented out after used!) |
| // @usage: printTask(this, 'begin'); |
| // @usage: printTask(this, null, {someExtraProp}); |
| // @usage: Use `__idxInPipeline` as conditional breakpiont. |
| // |
| // window.printTask = function (task: any, prefix: string, extra: { [key: string]: unknown }): void { |
| // window.ecTaskUID == null && (window.ecTaskUID = 0); |
| // task.uidDebug == null && (task.uidDebug = `task_${window.ecTaskUID++}`); |
| // task.agent && task.agent.uidDebug == null && (task.agent.uidDebug = `task_${window.ecTaskUID++}`); |
| // let props = []; |
| // if (task.__pipeline) { |
| // let val = `${task.__idxInPipeline}/${task.__pipeline.tail.__idxInPipeline} ${task.agent ? '(stub)' : ''}`; |
| // props.push({text: '__idxInPipeline/total', value: val}); |
| // } else { |
| // let stubCount = 0; |
| // task.agentStubMap.each(() => stubCount++); |
| // props.push({text: 'idx', value: `overall (stubs: ${stubCount})`}); |
| // } |
| // props.push({text: 'uid', value: task.uidDebug}); |
| // if (task.__pipeline) { |
| // props.push({text: 'pipelineId', value: task.__pipeline.id}); |
| // task.agent && props.push( |
| // {text: 'stubFor', value: task.agent.uidDebug} |
| // ); |
| // } |
| // props.push( |
| // {text: 'dirty', value: task._dirty}, |
| // {text: 'dueIndex', value: task._dueIndex}, |
| // {text: 'dueEnd', value: task._dueEnd}, |
| // {text: 'outputDueEnd', value: task._outputDueEnd} |
| // ); |
| // if (extra) { |
| // Object.keys(extra).forEach(key => { |
| // props.push({text: key, value: extra[key]}); |
| // }); |
| // } |
| // let args = ['color: blue']; |
| // let msg = `%c[${prefix || 'T'}] %c` + props.map(item => ( |
| // args.push('color: green', 'color: red'), |
| // `${item.text}: %c${item.value}` |
| // )).join('%c, '); |
| // console.log.apply(console, [msg].concat(args)); |
| // // console.log(this); |
| // }; |
| // window.printPipeline = function (task: any, prefix: string) { |
| // const pipeline = task.__pipeline; |
| // let currTask = pipeline.head; |
| // while (currTask) { |
| // window.printTask(currTask, prefix); |
| // currTask = currTask._downstream; |
| // } |
| // }; |
| // window.showChain = function (chainHeadTask) { |
| // var chain = []; |
| // var task = chainHeadTask; |
| // while (task) { |
| // chain.push({ |
| // task: task, |
| // up: task._upstream, |
| // down: task._downstream, |
| // idxInPipeline: task.__idxInPipeline |
| // }); |
| // task = task._downstream; |
| // } |
| // return chain; |
| // }; |
| // window.findTaskInChain = function (task, chainHeadTask) { |
| // let chain = window.showChain(chainHeadTask); |
| // let result = []; |
| // for (let i = 0; i < chain.length; i++) { |
| // let chainItem = chain[i]; |
| // if (chainItem.task === task) { |
| // result.push(i); |
| // } |
| // } |
| // return result; |
| // }; |
| // window.printChainAEachInChainB = function (chainHeadTaskA, chainHeadTaskB) { |
| // let chainA = window.showChain(chainHeadTaskA); |
| // for (let i = 0; i < chainA.length; i++) { |
| // console.log('chainAIdx:', i, 'inChainB:', window.findTaskInChain(chainA[i].task, chainHeadTaskB)); |
| // } |
| // }; |
| |
| /** |
| * Convert raw the value in to inner value in List. |
| * |
| * [Performance sensitive] |
| * |
| * [Caution]: this is the key logic of user value parser. |
| * For backward compatibility, do not modify it until you have to! |
| */ |
| |
| function parseDataValue(value, // For high performance, do not omit the second param. |
| opt) { |
| // Performance sensitive. |
| var dimType = opt && opt.type; |
| |
| if (dimType === 'ordinal') { |
| // If given value is a category string |
| return value; |
| } |
| |
| if (dimType === 'time' // spead up when using timestamp |
| && !isNumber(value) && value != null && value !== '-') { |
| value = +parseDate(value); |
| } // dimType defaults 'number'. |
| // If dimType is not ordinal and value is null or undefined or NaN or '-', |
| // parse to NaN. |
| // number-like string (like ' 123 ') can be converted to a number. |
| // where null/undefined or other string will be converted to NaN. |
| |
| |
| return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN |
| // If object, also parse to NaN |
| : +value; |
| } |
| var valueParserMap = createHashMap({ |
| 'number': function (val) { |
| // Do not use `numericToNumber` here. We have `numericToNumber` by default. |
| // Here the number parser can have loose rule: |
| // enable to cut suffix: "120px" => 120, "14%" => 14. |
| return parseFloat(val); |
| }, |
| 'time': function (val) { |
| // return timestamp. |
| return +parseDate(val); |
| }, |
| 'trim': function (val) { |
| return isString(val) ? trim(val) : val; |
| } |
| }); |
| |
| /** |
| * TODO: disable writable. |
| * This structure will be exposed to users. |
| */ |
| |
| var ExternalSource = |
| /** @class */ |
| function () { |
| function ExternalSource() {} |
| |
| ExternalSource.prototype.getRawData = function () { |
| // Only built-in transform available. |
| throw new Error('not supported'); |
| }; |
| |
| ExternalSource.prototype.getRawDataItem = function (dataIndex) { |
| // Only built-in transform available. |
| throw new Error('not supported'); |
| }; |
| |
| ExternalSource.prototype.cloneRawData = function () { |
| return; |
| }; |
| /** |
| * @return If dimension not found, return null/undefined. |
| */ |
| |
| |
| ExternalSource.prototype.getDimensionInfo = function (dim) { |
| return; |
| }; |
| /** |
| * dimensions defined if and only if either: |
| * (a) dataset.dimensions are declared. |
| * (b) dataset data include dimensions definitions in data (detected or via specified `sourceHeader`). |
| * If dimensions are defined, `dimensionInfoAll` is corresponding to |
| * the defined dimensions. |
| * Otherwise, `dimensionInfoAll` is determined by data columns. |
| * @return Always return an array (even empty array). |
| */ |
| |
| |
| ExternalSource.prototype.cloneAllDimensionInfo = function () { |
| return; |
| }; |
| |
| ExternalSource.prototype.count = function () { |
| return; |
| }; |
| /** |
| * Only support by dimension index. |
| * No need to support by dimension name in transform function, |
| * because transform function is not case-specific, no need to use name literally. |
| */ |
| |
| |
| ExternalSource.prototype.retrieveValue = function (dataIndex, dimIndex) { |
| return; |
| }; |
| |
| ExternalSource.prototype.retrieveValueFromItem = function (dataItem, dimIndex) { |
| return; |
| }; |
| |
| ExternalSource.prototype.convertValue = function (rawVal, dimInfo) { |
| return parseDataValue(rawVal, dimInfo); |
| }; |
| |
| return ExternalSource; |
| }(); |
| |
| function createExternalSource(internalSource, externalTransform) { |
| var extSource = new ExternalSource(); |
| var data = internalSource.data; |
| var sourceFormat = extSource.sourceFormat = internalSource.sourceFormat; |
| var sourceHeaderCount = internalSource.startIndex; |
| var errMsg = ''; |
| |
| if (internalSource.seriesLayoutBy !== SERIES_LAYOUT_BY_COLUMN) { |
| // For the logic simplicity in transformer, only 'culumn' is |
| // supported in data transform. Otherwise, the `dimensionsDefine` |
| // might be detected by 'row', which probably confuses users. |
| if ("development" !== 'production') { |
| errMsg = '`seriesLayoutBy` of upstream dataset can only be "column" in data transform.'; |
| } |
| |
| throwError(errMsg); |
| } // [MEMO] |
| // Create a new dimensions structure for exposing. |
| // Do not expose all dimension info to users directly. |
| // Because the dimension is probably auto detected from data and not might reliable. |
| // Should not lead the transformers to think that is reliable and return it. |
| // See [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. |
| |
| |
| var dimensions = []; |
| var dimsByName = {}; |
| var dimsDef = internalSource.dimensionsDefine; |
| |
| if (dimsDef) { |
| each(dimsDef, function (dimDef, idx) { |
| var name = dimDef.name; |
| var dimDefExt = { |
| index: idx, |
| name: name, |
| displayName: dimDef.displayName |
| }; |
| dimensions.push(dimDefExt); // Users probably do not specify dimension name. For simplicity, data transform |
| // does not generate dimension name. |
| |
| if (name != null) { |
| // Dimension name should not be duplicated. |
| // For simplicity, data transform forbids name duplication, do not generate |
| // new name like module `completeDimensions.ts` did, but just tell users. |
| var errMsg_1 = ''; |
| |
| if (hasOwn(dimsByName, name)) { |
| if ("development" !== 'production') { |
| errMsg_1 = 'dimension name "' + name + '" duplicated.'; |
| } |
| |
| throwError(errMsg_1); |
| } |
| |
| dimsByName[name] = dimDefExt; |
| } |
| }); |
| } // If dimension definitions are not defined and can not be detected. |
| // e.g., pure data `[[11, 22], ...]`. |
| else { |
| for (var i = 0; i < internalSource.dimensionsDetectedCount || 0; i++) { |
| // Do not generete name or anything others. The consequence process in |
| // `transform` or `series` probably have there own name generation strategry. |
| dimensions.push({ |
| index: i |
| }); |
| } |
| } // Implement public methods: |
| |
| |
| var rawItemGetter = getRawSourceItemGetter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); |
| |
| if (externalTransform.__isBuiltIn) { |
| extSource.getRawDataItem = function (dataIndex) { |
| return rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); |
| }; |
| |
| extSource.getRawData = bind(getRawData, null, internalSource); |
| } |
| |
| extSource.cloneRawData = bind(cloneRawData, null, internalSource); |
| var rawCounter = getRawSourceDataCounter(sourceFormat, SERIES_LAYOUT_BY_COLUMN); |
| extSource.count = bind(rawCounter, null, data, sourceHeaderCount, dimensions); |
| var rawValueGetter = getRawSourceValueGetter(sourceFormat); |
| |
| extSource.retrieveValue = function (dataIndex, dimIndex) { |
| var rawItem = rawItemGetter(data, sourceHeaderCount, dimensions, dataIndex); |
| return retrieveValueFromItem(rawItem, dimIndex); |
| }; |
| |
| var retrieveValueFromItem = extSource.retrieveValueFromItem = function (dataItem, dimIndex) { |
| if (dataItem == null) { |
| return; |
| } |
| |
| var dimDef = dimensions[dimIndex]; // When `dimIndex` is `null`, `rawValueGetter` return the whole item. |
| |
| if (dimDef) { |
| return rawValueGetter(dataItem, dimIndex, dimDef.name); |
| } |
| }; |
| |
| extSource.getDimensionInfo = bind(getDimensionInfo, null, dimensions, dimsByName); |
| extSource.cloneAllDimensionInfo = bind(cloneAllDimensionInfo, null, dimensions); |
| return extSource; |
| } |
| |
| function getRawData(upstream) { |
| var sourceFormat = upstream.sourceFormat; |
| |
| if (!isSupportedSourceFormat(sourceFormat)) { |
| var errMsg = ''; |
| |
| if ("development" !== 'production') { |
| errMsg = '`getRawData` is not supported in source format ' + sourceFormat; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| return upstream.data; |
| } |
| |
| function cloneRawData(upstream) { |
| var sourceFormat = upstream.sourceFormat; |
| var data = upstream.data; |
| |
| if (!isSupportedSourceFormat(sourceFormat)) { |
| var errMsg = ''; |
| |
| if ("development" !== 'production') { |
| errMsg = '`cloneRawData` is not supported in source format ' + sourceFormat; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| if (sourceFormat === SOURCE_FORMAT_ARRAY_ROWS) { |
| var result = []; |
| |
| for (var i = 0, len = data.length; i < len; i++) { |
| // Not strictly clone for performance |
| result.push(data[i].slice()); |
| } |
| |
| return result; |
| } else if (sourceFormat === SOURCE_FORMAT_OBJECT_ROWS) { |
| var result = []; |
| |
| for (var i = 0, len = data.length; i < len; i++) { |
| // Not strictly clone for performance |
| result.push(extend({}, data[i])); |
| } |
| |
| return result; |
| } |
| } |
| |
| function getDimensionInfo(dimensions, dimsByName, dim) { |
| if (dim == null) { |
| return; |
| } // Keep the same logic as `List::getDimension` did. |
| |
| |
| if (isNumber(dim) // If being a number-like string but not being defined a dimension name. |
| || !isNaN(dim) && !hasOwn(dimsByName, dim)) { |
| return dimensions[dim]; |
| } else if (hasOwn(dimsByName, dim)) { |
| return dimsByName[dim]; |
| } |
| } |
| |
| function cloneAllDimensionInfo(dimensions) { |
| return clone(dimensions); |
| } |
| |
| var externalTransformMap = createHashMap(); |
| function registerExternalTransform(externalTransform) { |
| externalTransform = clone(externalTransform); |
| var type = externalTransform.type; |
| var errMsg = ''; |
| |
| if (!type) { |
| if ("development" !== 'production') { |
| errMsg = 'Must have a `type` when `registerTransform`.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| var typeParsed = type.split(':'); |
| |
| if (typeParsed.length !== 2) { |
| if ("development" !== 'production') { |
| errMsg = 'Name must include namespace like "ns:regression".'; |
| } |
| |
| throwError(errMsg); |
| } // Namespace 'echarts:xxx' is official namespace, where the transforms should |
| // be called directly via 'xxx' rather than 'echarts:xxx'. |
| |
| |
| var isBuiltIn = false; |
| |
| if (typeParsed[0] === 'echarts') { |
| type = typeParsed[1]; |
| isBuiltIn = true; |
| } |
| |
| externalTransform.__isBuiltIn = isBuiltIn; |
| externalTransformMap.set(type, externalTransform); |
| } |
| function applyDataTransform(rawTransOption, sourceList, infoForPrint) { |
| var pipedTransOption = normalizeToArray(rawTransOption); |
| var pipeLen = pipedTransOption.length; |
| var errMsg = ''; |
| |
| if (!pipeLen) { |
| if ("development" !== 'production') { |
| errMsg = 'If `transform` declared, it should at least contain one transform.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| for (var i = 0, len = pipeLen; i < len; i++) { |
| var transOption = pipedTransOption[i]; |
| sourceList = applySingleDataTransform(transOption, sourceList, infoForPrint, pipeLen === 1 ? null : i); // piped transform only support single input, except the fist one. |
| // piped transform only support single output, except the last one. |
| |
| if (i !== len - 1) { |
| sourceList.length = Math.max(sourceList.length, 1); |
| } |
| } |
| |
| return sourceList; |
| } |
| |
| function applySingleDataTransform(transOption, upSourceList, infoForPrint, // If `pipeIndex` is null/undefined, no piped transform. |
| pipeIndex) { |
| var errMsg = ''; |
| |
| if (!upSourceList.length) { |
| if ("development" !== 'production') { |
| errMsg = 'Must have at least one upstream dataset.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| if (!isObject(transOption)) { |
| if ("development" !== 'production') { |
| errMsg = 'transform declaration must be an object rather than ' + typeof transOption + '.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| var transType = transOption.type; |
| var externalTransform = externalTransformMap.get(transType); |
| |
| if (!externalTransform) { |
| if ("development" !== 'production') { |
| errMsg = 'Can not find transform on type "' + transType + '".'; |
| } |
| |
| throwError(errMsg); |
| } // Prepare source |
| |
| |
| var extUpSourceList = map(upSourceList, function (upSource) { |
| return createExternalSource(upSource, externalTransform); |
| }); |
| var resultList = normalizeToArray(externalTransform.transform({ |
| upstream: extUpSourceList[0], |
| upstreamList: extUpSourceList, |
| config: clone(transOption.config) |
| })); |
| |
| if ("development" !== 'production') { |
| if (transOption.print) { |
| var printStrArr = map(resultList, function (extSource) { |
| var pipeIndexStr = pipeIndex != null ? ' === pipe index: ' + pipeIndex : ''; |
| return ['=== dataset index: ' + infoForPrint.datasetIndex + pipeIndexStr + ' ===', '- transform result data:', makePrintable(extSource.data), '- transform result dimensions:', makePrintable(extSource.dimensions)].join('\n'); |
| }).join('\n'); |
| log(printStrArr); |
| } |
| } |
| |
| return map(resultList, function (result, resultIndex) { |
| var errMsg = ''; |
| |
| if (!isObject(result)) { |
| if ("development" !== 'production') { |
| errMsg = 'A transform should not return some empty results.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| if (!result.data) { |
| if ("development" !== 'production') { |
| errMsg = 'Transform result data should be not be null or undefined'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| var sourceFormat = detectSourceFormat(result.data); |
| |
| if (!isSupportedSourceFormat(sourceFormat)) { |
| if ("development" !== 'production') { |
| errMsg = 'Transform result data should be array rows or object rows.'; |
| } |
| |
| throwError(errMsg); |
| } |
| |
| var resultMetaRawOption; |
| var firstUpSource = upSourceList[0]; |
| /** |
| * Intuitively, the end users known the content of the original `dataset.source`, |
| * calucating the transform result in mind. |
| * Suppose the original `dataset.source` is: |
| * ```js |
| * [ |
| * ['product', '2012', '2013', '2014', '2015'], |
| * ['AAA', 41.1, 30.4, 65.1, 53.3], |
| * ['BBB', 86.5, 92.1, 85.7, 83.1], |
| * ['CCC', 24.1, 67.2, 79.5, 86.4] |
| * ] |
| * ``` |
| * The dimension info have to be detected from the source data. |
| * Some of the transformers (like filter, sort) will follow the dimension info |
| * of upstream, while others use new dimensions (like aggregate). |
| * Transformer can output a field `dimensions` to define the its own output dimensions. |
| * We also allow transformers to ignore the output `dimensions` field, and |
| * inherit the upstream dimensions definition. It can reduce the burden of handling |
| * dimensions in transformers. |
| * |
| * See also [DIMENSION_INHERIT_RULE] in `sourceManager.ts`. |
| */ |
| |
| if (firstUpSource && resultIndex === 0 // If transformer returns `dimensions`, it means that the transformer has different |
| // dimensions definitions. We do not inherit anything from upstream. |
| && !result.dimensions) { |
| var startIndex = firstUpSource.startIndex; // We copy the header of upstream to the result, because: |
| // (1) The returned data always does not contain header line and can not be used |
| // as dimension-detection. In this case we can not use "detected dimensions" of |
| // upstream directly, because it might be detected based on different `seriesLayoutBy`. |
| // (2) We should support that the series read the upstream source in `seriesLayoutBy: 'row'`. |
| // So the original detected header should be add to the result, otherwise they can not be read. |
| |
| if (startIndex) { |
| result.data = firstUpSource.data.slice(0, startIndex).concat(result.data); |
| } |
| |
| resultMetaRawOption = { |
| seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, |
| sourceHeader: startIndex, |
| dimensions: firstUpSource.metaRawOption.dimensions |
| }; |
| } else { |
| resultMetaRawOption = { |
| seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN, |
| sourceHeader: 0, |
| dimensions: result.dimensions |
| }; |
| } |
| |
| return createSource(result.data, resultMetaRawOption, null); |
| }); |
| } |
| |
| function isSupportedSourceFormat(sourceFormat) { |
| return sourceFormat === SOURCE_FORMAT_ARRAY_ROWS || sourceFormat === SOURCE_FORMAT_OBJECT_ROWS; |
| } |
| |
| var UNDEFINED = 'undefined'; |
| /* global Float64Array, Int32Array, Uint32Array, Uint16Array */ |
| // Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is |
| // different from the Ctor of typed array. |
| |
| var CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array; |
| var CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array; |
| var CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array; |
| var CtorFloat64Array = typeof Float64Array === UNDEFINED ? Array : Float64Array; |
| /** |
| * Multi dimensional data store |
| */ |
| |
| var dataCtors = { |
| 'float': CtorFloat64Array, |
| 'int': CtorInt32Array, |
| // Ordinal data type can be string or int |
| 'ordinal': Array, |
| 'number': Array, |
| 'time': CtorFloat64Array |
| }; |
| var defaultDimValueGetters; |
| |
| function getIndicesCtor(rawCount) { |
| // The possible max value in this._indicies is always this._rawCount despite of filtering. |
| return rawCount > 65535 ? CtorUint32Array : CtorUint16Array; |
| } |
| |
| function getInitialExtent() { |
| return [Infinity, -Infinity]; |
| } |
| |
| function cloneChunk(originalChunk) { |
| var Ctor = originalChunk.constructor; // Only shallow clone is enough when Array. |
| |
| return Ctor === Array ? originalChunk.slice() : new Ctor(originalChunk); |
| } |
| |
| function prepareStore(store, dimIdx, dimType, end, append) { |
| var DataCtor = dataCtors[dimType || 'float']; |
| |
| if (append) { |
| var oldStore = store[dimIdx]; |
| var oldLen = oldStore && oldStore.length; |
| |
| if (!(oldLen === end)) { |
| var newStore = new DataCtor(end); // The cost of the copy is probably inconsiderable |
| // within the initial chunkSize. |
| |
| for (var j = 0; j < oldLen; j++) { |
| newStore[j] = oldStore[j]; |
| } |
| |
| store[dimIdx] = newStore; |
| } |
| } else { |
| store[dimIdx] = new DataCtor(end); |
| } |
| } |
| /** |
| * Basically, DataStore API keep immutable. |
| */ |
| |
| var DataStore = |
| /** @class */ |
| function () { |
| function DataStore() { |
| this._chunks = []; // It will not be calculated util needed. |
| |
| this._rawExtent = []; |
| this._extent = []; |
| this._count = 0; |
| this._rawCount = 0; |
| this._calcDimNameToIdx = createHashMap(); |
| } |
| /** |
| * Initialize from data |
| */ |
| |
| |
| DataStore.prototype.initData = function (provider, inputDimensions, dimValueGetter) { |
| if ("development" !== 'production') { |
| assert(isFunction(provider.getItem) && isFunction(provider.count), 'Invalid data provider.'); |
| } |
| |
| this._provider = provider; // Clear |
| |
| this._chunks = []; |
| this._indices = null; |
| this.getRawIndex = this._getRawIdxIdentity; |
| var source = provider.getSource(); |
| var defaultGetter = this.defaultDimValueGetter = defaultDimValueGetters[source.sourceFormat]; // Default dim value getter |
| |
| this._dimValueGetter = dimValueGetter || defaultGetter; // Reset raw extent. |
| |
| this._rawExtent = []; |
| var willRetrieveDataByName = shouldRetrieveDataByName(source); |
| this._dimensions = map(inputDimensions, function (dim) { |
| if ("development" !== 'production') { |
| if (willRetrieveDataByName) { |
| assert(dim.property != null); |
| } |
| } |
| |
| return { |
| // Only pick these two props. Not leak other properties like orderMeta. |
| type: dim.type, |
| property: dim.property |
| }; |
| }); |
| |
| this._initDataFromProvider(0, provider.count()); |
| }; |
| |
| DataStore.prototype.getProvider = function () { |
| return this._provider; |
| }; |
| /** |
| * Caution: even when a `source` instance owned by a series, the created data store |
| * may still be shared by different sereis (the source hash does not use all `source` |
| * props, see `sourceManager`). In this case, the `source` props that are not used in |
| * hash (like `source.dimensionDefine`) probably only belongs to a certain series and |
| * thus should not be fetch here. |
| */ |
| |
| |
| DataStore.prototype.getSource = function () { |
| return this._provider.getSource(); |
| }; |
| /** |
| * @caution Only used in dataStack. |
| */ |
| |
| |
| DataStore.prototype.ensureCalculationDimension = function (dimName, type) { |
| var calcDimNameToIdx = this._calcDimNameToIdx; |
| var dimensions = this._dimensions; |
| var calcDimIdx = calcDimNameToIdx.get(dimName); |
| |
| if (calcDimIdx != null) { |
| if (dimensions[calcDimIdx].type === type) { |
| return calcDimIdx; |
| } |
| } else { |
| calcDimIdx = dimensions.length; |
| } |
| |
| dimensions[calcDimIdx] = { |
| type: type |
| }; |
| calcDimNameToIdx.set(dimName, calcDimIdx); |
| this._chunks[calcDimIdx] = new dataCtors[type || 'float'](this._rawCount); |
| this._rawExtent[calcDimIdx] = getInitialExtent(); |
| return calcDimIdx; |
| }; |
| |
| DataStore.prototype.collectOrdinalMeta = function (dimIdx, ordinalMeta) { |
| var chunk = this._chunks[dimIdx]; |
| var dim = this._dimensions[dimIdx]; |
| var rawExtents = this._rawExtent; |
| var offset = dim.ordinalOffset || 0; |
| var len = chunk.length; |
| |
| if (offset === 0) { |
| // We need to reset the rawExtent if collect is from start. |
| // Because this dimension may be guessed as number and calcuating a wrong extent. |
| rawExtents[dimIdx] = getInitialExtent(); |
| } |
| |
| var dimRawExtent = rawExtents[dimIdx]; // Parse from previous data offset. len may be changed after appendData |
| |
| for (var i = offset; i < len; i++) { |
| var val = chunk[i] = ordinalMeta.parseAndCollect(chunk[i]); |
| |
| if (!isNaN(val)) { |
| dimRawExtent[0] = Math.min(val, dimRawExtent[0]); |
| dimRawExtent[1] = Math.max(val, dimRawExtent[1]); |
| } |
| } |
| |
| dim.ordinalMeta = ordinalMeta; |
| dim.ordinalOffset = len; |
| dim.type = 'ordinal'; // Force to be ordinal |
| }; |
| |
| DataStore.prototype.getOrdinalMeta = function (dimIdx) { |
| var dimInfo = this._dimensions[dimIdx]; |
| var ordinalMeta = dimInfo.ordinalMeta; |
| return ordinalMeta; |
| }; |
| |
| DataStore.prototype.getDimensionProperty = function (dimIndex) { |
| var item = this._dimensions[dimIndex]; |
| return item && item.property; |
| }; |
| /** |
| * Caution: Can be only called on raw data (before `this._indices` created). |
| */ |
| |
| |
| DataStore.prototype.appendData = function (data) { |
| if ("development" !== 'production') { |
| assert(!this._indices, 'appendData can only be called on raw data.'); |
| } |
| |
| var provider = this._provider; |
| var start = this.count(); |
| provider.appendData(data); |
| var end = provider.count(); |
| |
| if (!provider.persistent) { |
| end += start; |
| } |
| |
| if (start < end) { |
| this._initDataFromProvider(start, end, true); |
| } |
| |
| return [start, end]; |
| }; |
| |
| DataStore.prototype.appendValues = function (values, minFillLen) { |
| var chunks = this._chunks; |
| var dimensions = this._dimensions; |
| var dimLen = dimensions.length; |
| var rawExtent = this._rawExtent; |
| var start = this.count(); |
| var end = start + Math.max(values.length, minFillLen || 0); |
| |
| for (var i = 0; i < dimLen; i++) { |
| var dim = dimensions[i]; |
| prepareStore(chunks, i, dim.type, end, true); |
| } |
| |
| var emptyDataItem = []; |
| |
| for (var idx = start; idx < end; idx++) { |
| var sourceIdx = idx - start; // Store the data by dimensions |
| |
| for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) { |
| var dim = dimensions[dimIdx]; |
| var val = defaultDimValueGetters.arrayRows.call(this, values[sourceIdx] || emptyDataItem, dim.property, sourceIdx, dimIdx); |
| chunks[dimIdx][idx] = val; |
| var dimRawExtent = rawExtent[dimIdx]; |
| val < dimRawExtent[0] && (dimRawExtent[0] = val); |
| val > dimRawExtent[1] && (dimRawExtent[1] = val); |
| } |
| } |
| |
| this._rawCount = this._count = end; |
| return { |
| start: start, |
| end: end |
| }; |
| }; |
| |
| DataStore.prototype._initDataFromProvider = function (start, end, append) { |
| var provider = this._provider; |
| var chunks = this._chunks; |
| var dimensions = this._dimensions; |
| var dimLen = dimensions.length; |
| var rawExtent = this._rawExtent; |
| var dimNames = map(dimensions, function (dim) { |
| return dim.property; |
| }); |
| |
| for (var i = 0; i < dimLen; i++) { |
| var dim = dimensions[i]; |
| |
| if (!rawExtent[i]) { |
| rawExtent[i] = getInitialExtent(); |
| } |
| |
| prepareStore(chunks, i, dim.type, end, append); |
| } |
| |
| if (provider.fillStorage) { |
| provider.fillStorage(start, end, chunks, rawExtent); |
| } else { |
| var dataItem = []; |
| |
| for (var idx = start; idx < end; idx++) { |
| // NOTICE: Try not to write things into dataItem |
| dataItem = provider.getItem(idx, dataItem); // Each data item is value |
| // [1, 2] |
| // 2 |
| // Bar chart, line chart which uses category axis |
| // only gives the 'y' value. 'x' value is the indices of category |
| // Use a tempValue to normalize the value to be a (x, y) value |
| // Store the data by dimensions |
| |
| for (var dimIdx = 0; dimIdx < dimLen; dimIdx++) { |
| var dimStorage = chunks[dimIdx]; // PENDING NULL is empty or zero |
| |
| var val = this._dimValueGetter(dataItem, dimNames[dimIdx], idx, dimIdx); |
| |
| dimStorage[idx] = val; |
| var dimRawExtent = rawExtent[dimIdx]; |
| val < dimRawExtent[0] && (dimRawExtent[0] = val); |
| val > dimRawExtent[1] && (dimRawExtent[1] = val); |
| } |
| } |
| } |
| |
| if (!provider.persistent && provider.clean) { |
| // Clean unused data if data source is typed array. |
| provider.clean(); |
| } |
| |
| this._rawCount = this._count = end; // Reset data extent |
| |
| this._extent = []; |
| }; |
| |
| DataStore.prototype.count = function () { |
| return this._count; |
| }; |
| /** |
| * Get value. Return NaN if idx is out of range. |
| */ |
| |
| |
| DataStore.prototype.get = function (dim, idx) { |
| if (!(idx >= 0 && idx < this._count)) { |
| return NaN; |
| } |
| |
| var dimStore = this._chunks[dim]; |
| return dimStore ? dimStore[this.getRawIndex(idx)] : NaN; |
| }; |
| |
| DataStore.prototype.getValues = function (dimensions, idx) { |
| var values = []; |
| var dimArr = []; |
| |
| if (idx == null) { |
| idx = dimensions; // TODO get all from store? |
| |
| dimensions = []; // All dimensions |
| |
| for (var i = 0; i < this._dimensions.length; i++) { |
| dimArr.push(i); |
| } |
| } else { |
| dimArr = dimensions; |
| } |
| |
| for (var i = 0, len = dimArr.length; i < len; i++) { |
| values.push(this.get(dimArr[i], idx)); |
| } |
| |
| return values; |
| }; |
| /** |
| * @param dim concrete dim |
| */ |
| |
| |
| DataStore.prototype.getByRawIndex = function (dim, rawIdx) { |
| if (!(rawIdx >= 0 && rawIdx < this._rawCount)) { |
| return NaN; |
| } |
| |
| var dimStore = this._chunks[dim]; |
| return dimStore ? dimStore[rawIdx] : NaN; |
| }; |
| /** |
| * Get sum of data in one dimension |
| */ |
| |
| |
| DataStore.prototype.getSum = function (dim) { |
| var dimData = this._chunks[dim]; |
| var sum = 0; |
| |
| if (dimData) { |
| for (var i = 0, len = this.count(); i < len; i++) { |
| var value = this.get(dim, i); |
| |
| if (!isNaN(value)) { |
| sum += value; |
| } |
| } |
| } |
| |
| return sum; |
| }; |
| /** |
| * Get median of data in one dimension |
| */ |
| |
| |
| DataStore.prototype.getMedian = function (dim) { |
| var dimDataArray = []; // map all data of one dimension |
| |
| this.each([dim], function (val) { |
| if (!isNaN(val)) { |
| dimDataArray.push(val); |
| } |
| }); // TODO |
| // Use quick select? |
| |
| var sortedDimDataArray = dimDataArray.sort(function (a, b) { |
| return a - b; |
| }); |
| var len = this.count(); // calculate median |
| |
| return len === 0 ? 0 : len % 2 === 1 ? sortedDimDataArray[(len - 1) / 2] : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2; |
| }; |
| /** |
| * Retrieve the index with given raw data index. |
| */ |
| |
| |
| DataStore.prototype.indexOfRawIndex = function (rawIndex) { |
| if (rawIndex >= this._rawCount || rawIndex < 0) { |
| return -1; |
| } |
| |
| if (!this._indices) { |
| return rawIndex; |
| } // Indices are ascending |
| |
| |
| var indices = this._indices; // If rawIndex === dataIndex |
| |
| var rawDataIndex = indices[rawIndex]; |
| |
| if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) { |
| return rawIndex; |
| } |
| |
| var left = 0; |
| var right = this._count - 1; |
| |
| while (left <= right) { |
| var mid = (left + right) / 2 | 0; |
| |
| if (indices[mid] < rawIndex) { |
| left = mid + 1; |
| } else if (indices[mid] > rawIndex) { |
| right = mid - 1; |
| } else { |
| return mid; |
| } |
| } |
| |
| return -1; |
| }; |
| /** |
| * Retrieve the index of nearest value. |
| * @param dim |
| * @param value |
| * @param [maxDistance=Infinity] |
| * @return If and only if multiple indices have |
| * the same value, they are put to the result. |
| */ |
| |
| |
| DataStore.prototype.indicesOfNearest = function (dim, value, maxDistance) { |
| var chunks = this._chunks; |
| var dimData = chunks[dim]; |
| var nearestIndices = []; |
| |
| if (!dimData) { |
| return nearestIndices; |
| } |
| |
| if (maxDistance == null) { |
| maxDistance = Infinity; |
| } |
| |
| var minDist = Infinity; |
| var minDiff = -1; |
| var nearestIndicesLen = 0; // Check the test case of `test/ut/spec/data/SeriesData.js`. |
| |
| for (var i = 0, len = this.count(); i < len; i++) { |
| var dataIndex = this.getRawIndex(i); |
| var diff = value - dimData[dataIndex]; |
| var dist = Math.abs(diff); |
| |
| if (dist <= maxDistance) { |
| // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`, |
| // we'd better not push both of them to `nearestIndices`, otherwise it is easy to |
| // get more than one item in `nearestIndices` (more specifically, in `tooltip`). |
| // So we chose the one that `diff >= 0` in this csae. |
| // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them |
| // should be push to `nearestIndices`. |
| if (dist < minDist || dist === minDist && diff >= 0 && minDiff < 0) { |
| minDist = dist; |
| minDiff = diff; |
| nearestIndicesLen = 0; |
| } |
| |
| if (diff === minDiff) { |
| nearestIndices[nearestIndicesLen++] = i; |
| } |
| } |
| } |
| |
| nearestIndices.length = nearestIndicesLen; |
| return nearestIndices; |
| }; |
| |
| DataStore.prototype.getIndices = function () { |
| var newIndices; |
| var indices = this._indices; |
| |
| if (indices) { |
| var Ctor = indices.constructor; |
| var thisCount = this._count; // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`. |
| |
| if (Ctor === Array) { |
| newIndices = new Ctor(thisCount); |
| |
| for (var i = 0; i < thisCount; i++) { |
| newIndices[i] = indices[i]; |
| } |
| } else { |
| newIndices = new Ctor(indices.buffer, 0, thisCount); |
| } |
| } else { |
| var Ctor = getIndicesCtor(this._rawCount); |
| newIndices = new Ctor(this.count()); |
| |
| for (var i = 0; i < newIndices.length; i++) { |
| newIndices[i] = i; |
| } |
| } |
| |
| return newIndices; |
| }; |
| /** |
| * Data filter. |
| */ |
| |
| |
| DataStore.prototype.filter = function (dims, cb) { |
| if (!this._count) { |
| return this; |
| } |
| |
| var newStore = this.clone(); |
| var count = newStore.count(); |
| var Ctor = getIndicesCtor(newStore._rawCount); |
| var newIndices = new Ctor(count); |
| var value = []; |
| var dimSize = dims.length; |
| var offset = 0; |
| var dim0 = dims[0]; |
| var chunks = newStore._chunks; |
| |
| for (var i = 0; i < count; i++) { |
| var keep = void 0; |
| var rawIdx = newStore.getRawIndex(i); // Simple optimization |
| |
| if (dimSize === 0) { |
| keep = cb(i); |
| } else if (dimSize === 1) { |
| var val = chunks[dim0][rawIdx]; |
| keep = cb(val, i); |
| } else { |
| var k = 0; |
| |
| for (; k < dimSize; k++) { |
| value[k] = chunks[dims[k]][rawIdx]; |
| } |
| |
| value[k] = i; |
| keep = cb.apply(null, value); |
| } |
| |
| if (keep) { |
| newIndices[offset++] = rawIdx; |
| } |
| } // Set indices after filtered. |
| |
| |
| if (offset < count) { |
| newStore._indices = newIndices; |
| } |
| |
| newStore._count = offset; // Reset data extent |
| |
| newStore._extent = []; |
| |
| newStore._updateGetRawIdx(); |
| |
| return newStore; |
| }; |
| /** |
| * Select data in range. (For optimization of filter) |
| * (Manually inline code, support 5 million data filtering in data zoom.) |
| */ |
| |
| |
| DataStore.prototype.selectRange = function (range) { |
| var newStore = this.clone(); |
| var len = newStore._count; |
| |
| if (!len) { |
| return this; |
| } |
| |
| var dims = keys(range); |
| var dimSize = dims.length; |
| |
| if (!dimSize) { |
| return this; |
| } |
| |
| var originalCount = newStore.count(); |
| var Ctor = getIndicesCtor(newStore._rawCount); |
| var newIndices = new Ctor(originalCount); |
| var offset = 0; |
| var dim0 = dims[0]; |
| var min = range[dim0][0]; |
| var max = range[dim0][1]; |
| var storeArr = newStore._chunks; |
| var quickFinished = false; |
| |
| if (!newStore._indices) { |
| // Extreme optimization for common case. About 2x faster in chrome. |
| var idx = 0; |
| |
| if (dimSize === 1) { |
| var dimStorage = storeArr[dims[0]]; |
| |
| for (var i = 0; i < len; i++) { |
| var val = dimStorage[i]; // NaN will not be filtered. Consider the case, in line chart, empty |
| // value indicates the line should be broken. But for the case like |
| // scatter plot, a data item with empty value will not be rendered, |
| // but the axis extent may be effected if some other dim of the data |
| // item has value. Fortunately it is not a significant negative effect. |
| |
| if (val >= min && val <= max || isNaN(val)) { |
| newIndices[offset++] = idx; |
| } |
| |
| idx++; |
| } |
| |
| quickFinished = true; |
| } else if (dimSize === 2) { |
| var dimStorage = storeArr[dims[0]]; |
| var dimStorage2 = storeArr[dims[1]]; |
| var min2 = range[dims[1]][0]; |
| var max2 = range[dims[1]][1]; |
| |
| for (var i = 0; i < len; i++) { |
| var val = dimStorage[i]; |
| var val2 = dimStorage2[i]; // Do not filter NaN, see comment above. |
| |
| if ((val >= min && val <= max || isNaN(val)) && (val2 >= min2 && val2 <= max2 || isNaN(val2))) { |
| newIndices[offset++] = idx; |
| } |
| |
| idx++; |
| } |
| |
| quickFinished = true; |
| } |
| } |
| |
| if (!quickFinished) { |
| if (dimSize === 1) { |
| for (var i = 0; i < originalCount; i++) { |
| var rawIndex = newStore.getRawIndex(i); |
| var val = storeArr[dims[0]][rawIndex]; // Do not filter NaN, see comment above. |
| |
| if (val >= min && val <= max || isNaN(val)) { |
| newIndices[offset++] = rawIndex; |
| } |
| } |
| } else { |
| for (var i = 0; i < originalCount; i++) { |
| var keep = true; |
| var rawIndex = newStore.getRawIndex(i); |
| |
| for (var k = 0; k < dimSize; k++) { |
| var dimk = dims[k]; |
| var val = storeArr[dimk][rawIndex]; // Do not filter NaN, see comment above. |
| |
| if (val < range[dimk][0] || val > range[dimk][1]) { |
| keep = false; |
| } |
| } |
| |
| if (keep) { |
| newIndices[offset++] = newStore.getRawIndex(i); |
| } |
| } |
| } |
| } // Set indices after filtered. |
| |
| |
| if (offset < originalCount) { |
| newStore._indices = newIndices; |
| } |
| |
| newStore._count = offset; // Reset data extent |
| |
| newStore._extent = []; |
| |
| newStore._updateGetRawIdx(); |
| |
| return newStore; |
| }; // /** |
| // * Data mapping to a plain array |
| // */ |
| // mapArray(dims: DimensionIndex[], cb: MapArrayCb): any[] { |
| // const result: any[] = []; |
| // this.each(dims, function () { |
| // result.push(cb && (cb as MapArrayCb).apply(null, arguments)); |
| // }); |
| // return result; |
| // } |
| |
| /** |
| * Data mapping to a new List with given dimensions |
| */ |
| |
| |
| DataStore.prototype.map = function (dims, cb) { |
| // TODO only clone picked chunks. |
| var target = this.clone(dims); |
| |
| this._updateDims(target, dims, cb); |
| |
| return target; |
| }; |
| /** |
| * @caution Danger!! Only used in dataStack. |
| */ |
| |
| |
| DataStore.prototype.modify = function (dims, cb) { |
| this._updateDims(this, dims, cb); |
| }; |
| |
| DataStore.prototype._updateDims = function (target, dims, cb) { |
| var targetChunks = target._chunks; |
| var tmpRetValue = []; |
| var dimSize = dims.length; |
| var dataCount = target.count(); |
| var values = []; |
| var rawExtent = target._rawExtent; |
| |
| for (var i = 0; i < dims.length; i++) { |
| rawExtent[dims[i]] = getInitialExtent(); |
| } |
| |
| for (var dataIndex = 0; dataIndex < dataCount; dataIndex++) { |
| var rawIndex = target.getRawIndex(dataIndex); |
| |
| for (var k = 0; k < dimSize; k++) { |
| values[k] = targetChunks[dims[k]][rawIndex]; |
| } |
| |
| values[dimSize] = dataIndex; |
| var retValue = cb && cb.apply(null, values); |
| |
| if (retValue != null) { |
| // a number or string (in oridinal dimension)? |
| if (typeof retValue !== 'object') { |
| tmpRetValue[0] = retValue; |
| retValue = tmpRetValue; |
| } |
| |
| for (var i = 0; i < retValue.length; i++) { |
| var dim = dims[i]; |
| var val = retValue[i]; |
| var rawExtentOnDim = rawExtent[dim]; |
| var dimStore = targetChunks[dim]; |
| |
| if (dimStore) { |
| dimStore[rawIndex] = val; |
| } |
| |
| if (val < rawExtentOnDim[0]) { |
| rawExtentOnDim[0] = val; |
| } |
| |
| if (val > rawExtentOnDim[1]) { |
| rawExtentOnDim[1] = val; |
| } |
| } |
| } |
| } |
| }; |
| /** |
| * Large data down sampling using largest-triangle-three-buckets |
| * @param {string} valueDimension |
| * @param {number} targetCount |
| */ |
| |
| |
| DataStore.prototype.lttbDownSample = function (valueDimension, rate) { |
| var target = this.clone([valueDimension], true); |
| var targetStorage = target._chunks; |
| var dimStore = targetStorage[valueDimension]; |
| var len = this.count(); |
| var sampledIndex = 0; |
| var frameSize = Math.floor(1 / rate); |
| var currentRawIndex = this.getRawIndex(0); |
| var maxArea; |
| var area; |
| var nextRawIndex; |
| var newIndices = new (getIndicesCtor(this._rawCount))(Math.min((Math.ceil(len / frameSize) + 2) * 2, len)); // First frame use the first data. |
| |
| newIndices[sampledIndex++] = currentRawIndex; |
| |
| for (var i = 1; i < len - 1; i += frameSize) { |
| var nextFrameStart = Math.min(i + frameSize, len - 1); |
| var nextFrameEnd = Math.min(i + frameSize * 2, len); |
| var avgX = (nextFrameEnd + nextFrameStart) / 2; |
| var avgY = 0; |
| |
| for (var idx = nextFrameStart; idx < nextFrameEnd; idx++) { |
| var rawIndex = this.getRawIndex(idx); |
| var y = dimStore[rawIndex]; |
| |
| if (isNaN(y)) { |
| continue; |
| } |
| |
| avgY += y; |
| } |
| |
| avgY /= nextFrameEnd - nextFrameStart; |
| var frameStart = i; |
| var frameEnd = Math.min(i + frameSize, len); |
| var pointAX = i - 1; |
| var pointAY = dimStore[currentRawIndex]; |
| maxArea = -1; |
| nextRawIndex = frameStart; |
| var firstNaNIndex = -1; |
| var countNaN = 0; // Find a point from current frame that construct a triangel with largest area with previous selected point |
| // And the average of next frame. |
| |
| for (var idx = frameStart; idx < frameEnd; idx++) { |
| var rawIndex = this.getRawIndex(idx); |
| var y = dimStore[rawIndex]; |
| |
| if (isNaN(y)) { |
| countNaN++; |
| |
| if (firstNaNIndex < 0) { |
| firstNaNIndex = rawIndex; |
| } |
| |
| continue; |
| } // Calculate triangle area over three buckets |
| |
| |
| area = Math.abs((pointAX - avgX) * (y - pointAY) - (pointAX - idx) * (avgY - pointAY)); |
| |
| if (area > maxArea) { |
| maxArea = area; |
| nextRawIndex = rawIndex; // Next a is this b |
| } |
| } |
| |
| if (countNaN > 0 && countNaN < frameEnd - frameStart) { |
| // Append first NaN point in every bucket. |
| // It is necessary to ensure the correct order of indices. |
| newIndices[sampledIndex++] = Math.min(firstNaNIndex, nextRawIndex); |
| nextRawIndex = Math.max(firstNaNIndex, nextRawIndex); |
| } |
| |
| newIndices[sampledIndex++] = nextRawIndex; |
| currentRawIndex = nextRawIndex; // This a is the next a (chosen b) |
| } // First frame use the last data. |
| |
| |
| newIndices[sampledIndex++] = this.getRawIndex(len - 1); |
| target._count = sampledIndex; |
| target._indices = newIndices; |
| target.getRawIndex = this._getRawIdx; |
| return target; |
| }; |
| /** |
| * Large data down sampling on given dimension |
| * @param sampleIndex Sample index for name and id |
| */ |
| |
| |
| DataStore.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) { |
| var target = this.clone([dimension], true); |
| var targetStorage = target._chunks; |
| var frameValues = []; |
| var frameSize = Math.floor(1 / rate); |
| var dimStore = targetStorage[dimension]; |
| var len = this.count(); |
| var rawExtentOnDim = target._rawExtent[dimension] = getInitialExtent(); |
| var newIndices = new (getIndicesCtor(this._rawCount))(Math.ceil(len / frameSize)); |
| var offset = 0; |
| |
| for (var i = 0; i < len; i += frameSize) { |
| // Last frame |
| if (frameSize > len - i) { |
| frameSize = len - i; |
| frameValues.length = frameSize; |
| } |
| |
| for (var k = 0; k < frameSize; k++) { |
| var dataIdx = this.getRawIndex(i + k); |
| frameValues[k] = dimStore[dataIdx]; |
| } |
| |
| var value = sampleValue(frameValues); |
| var sampleFrameIdx = this.getRawIndex(Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)); // Only write value on the filtered data |
| |
| dimStore[sampleFrameIdx] = value; |
| |
| if (value < rawExtentOnDim[0]) { |
| rawExtentOnDim[0] = value; |
| } |
| |
| if (value > rawExtentOnDim[1]) { |
| rawExtentOnDim[1] = value; |
| } |
| |
| newIndices[offset++] = sampleFrameIdx; |
| } |
| |
| target._count = offset; |
| target._indices = newIndices; |
| |
| target._updateGetRawIdx(); |
| |
| return target; |
| }; |
| /** |
| * Data iteration |
| * @param ctx default this |
| * @example |
| * list.each('x', function (x, idx) {}); |
| * list.each(['x', 'y'], function (x, y, idx) {}); |
| * list.each(function (idx) {}) |
| */ |
| |
| |
| DataStore.prototype.each = function (dims, cb) { |
| if (!this._count) { |
| return; |
| } |
| |
| var dimSize = dims.length; |
| var chunks = this._chunks; |
| |
| for (var i = 0, len = this.count(); i < len; i++) { |
| var rawIdx = this.getRawIndex(i); // Simple optimization |
| |
| switch (dimSize) { |
| case 0: |
| cb(i); |
| break; |
| |
| case 1: |
| cb(chunks[dims[0]][rawIdx], i); |
| break; |
| |
| case 2: |
| cb(chunks[dims[0]][rawIdx], chunks[dims[1]][rawIdx], i); |
| break; |
| |
| default: |
| var k = 0; |
| var value = []; |
| |
| for (; k < dimSize; k++) { |
| value[k] = chunks[dims[k]][rawIdx]; |
| } // Index |
| |
| |
| value[k] = i; |
| cb.apply(null, value); |
| } |
| } |
| }; |
| /** |
| * Get extent of data in one dimension |
| */ |
| |
| |
| DataStore.prototype.getDataExtent = function (dim) { |
| // Make sure use concrete dim as cache name. |
| var dimData = this._chunks[dim]; |
| var initialExtent = getInitialExtent(); |
| |
| if (!dimData) { |
| return initialExtent; |
| } // Make more strict checkings to ensure hitting cache. |
| |
| |
| var currEnd = this.count(); // Consider the most cases when using data zoom, `getDataExtent` |
| // happened before filtering. We cache raw extent, which is not |
| // necessary to be cleared and recalculated when restore data. |
| |
| var useRaw = !this._indices; |
| var dimExtent; |
| |
| if (useRaw) { |
| return this._rawExtent[dim].slice(); |
| } |
| |
| dimExtent = this._extent[dim]; |
| |
| if (dimExtent) { |
| return dimExtent.slice(); |
| } |
| |
| dimExtent = initialExtent; |
| var min = dimExtent[0]; |
| var max = dimExtent[1]; |
| |
| for (var i = 0; i < currEnd; i++) { |
| var rawIdx = this.getRawIndex(i); |
| var value = dimData[rawIdx]; |
| value < min && (min = value); |
| value > max && (max = value); |
| } |
| |
| dimExtent = [min, max]; |
| this._extent[dim] = dimExtent; |
| return dimExtent; |
| }; |
| /** |
| * Get raw data item |
| */ |
| |
| |
| DataStore.prototype.getRawDataItem = function (idx) { |
| var rawIdx = this.getRawIndex(idx); |
| |
| if (!this._provider.persistent) { |
| var val = []; |
| var chunks = this._chunks; |
| |
| for (var i = 0; i < chunks.length; i++) { |
| val.push(chunks[i][rawIdx]); |
| } |
| |
| return val; |
| } else { |
| return this._provider.getItem(rawIdx); |
| } |
| }; |
| /** |
| * Clone shallow. |
| * |
| * @param clonedDims Determine which dims to clone. Will share the data if not specified. |
| */ |
| |
| |
| DataStore.prototype.clone = function (clonedDims, ignoreIndices) { |
| var target = new DataStore(); |
| var chunks = this._chunks; |
| var clonedDimsMap = clonedDims && reduce(clonedDims, function (obj, dimIdx) { |
| obj[dimIdx] = true; |
| return obj; |
| }, {}); |
| |
| if (clonedDimsMap) { |
| for (var i = 0; i < chunks.length; i++) { |
| // Not clone if dim is not picked. |
| target._chunks[i] = !clonedDimsMap[i] ? chunks[i] : cloneChunk(chunks[i]); |
| } |
| } else { |
| target._chunks = chunks; |
| } |
| |
| this._copyCommonProps(target); |
| |
| if (!ignoreIndices) { |
| target._indices = this._cloneIndices(); |
| } |
| |
| target._updateGetRawIdx(); |
| |
| return target; |
| }; |
| |
| DataStore.prototype._copyCommonProps = function (target) { |
| target._count = this._count; |
| target._rawCount = this._rawCount; |
| target._provider = this._provider; |
| target._dimensions = this._dimensions; |
| target._extent = clone(this._extent); |
| target._rawExtent = clone(this._rawExtent); |
| }; |
| |
| DataStore.prototype._cloneIndices = function () { |
| if (this._indices) { |
| var Ctor = this._indices.constructor; |
| var indices = void 0; |
| |
| if (Ctor === Array) { |
| var thisCount = this._indices.length; |
| indices = new Ctor(thisCount); |
| |
| for (var i = 0; i < thisCount; i++) { |
| indices[i] = this._indices[i]; |
| } |
| } else { |
| indices = new Ctor(this._indices); |
| } |
| |
| return indices; |
| } |
| |
| return null; |
| }; |
| |
| DataStore.prototype._getRawIdxIdentity = function (idx) { |
| return idx; |
| }; |
| |
| DataStore.prototype._getRawIdx = function (idx) { |
| if (idx < this._count && idx >= 0) { |
| return this._indices[idx]; |
| } |
| |
| return -1; |
| }; |
| |
| DataStore.prototype._updateGetRawIdx = function () { |
| this.getRawIndex = this._indices ? this._getRawIdx : this._getRawIdxIdentity; |
| }; |
| |
| DataStore.internalField = function () { |
| function getDimValueSimply(dataItem, property, dataIndex, dimIndex) { |
| return parseDataValue(dataItem[dimIndex], this._dimensions[dimIndex]); |
| } |
| |
| defaultDimValueGetters = { |
| arrayRows: getDimValueSimply, |
| objectRows: function (dataItem, property, dataIndex, dimIndex) { |
| return parseDataValue(dataItem[property], this._dimensions[dimIndex]); |
| }, |
| keyedColumns: getDimValueSimply, |
| original: function (dataItem, property, dataIndex, dimIndex) { |
| // Performance sensitive, do not use modelUtil.getDataItemValue. |
| // If dataItem is an plain object with no value field, the let `value` |
| // will be assigned with the object, but it will be tread correctly |
| // in the `convertValue`. |
| var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); |
| return parseDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array. |
| : value, this._dimensions[dimIndex]); |
| }, |
| typedArray: function (dataItem, property, dataIndex, dimIndex) { |
| return dataItem[dimIndex]; |
| } |
| }; |
| }(); |
| |
| return DataStore; |
| }(); |
| |
| /** |
| * [REQUIREMENT_MEMO]: |
| * (0) `metaRawOption` means `dimensions`/`sourceHeader`/`seriesLayoutBy` in raw option. |
| * (1) Keep support the feature: `metaRawOption` can be specified both on `series` and |
| * `root-dataset`. Them on `series` has higher priority. |
| * (2) Do not support to set `metaRawOption` on a `non-root-dataset`, because it might |
| * confuse users: whether those props indicate how to visit the upstream source or visit |
| * the transform result source, and some transforms has nothing to do with these props, |
| * and some transforms might have multiple upstream. |
| * (3) Transforms should specify `metaRawOption` in each output, just like they can be |
| * declared in `root-dataset`. |
| * (4) At present only support visit source in `SERIES_LAYOUT_BY_COLUMN` in transforms. |
| * That is for reducing complexity in transforms. |
| * PENDING: Whether to provide transposition transform? |
| * |
| * [IMPLEMENTAION_MEMO]: |
| * "sourceVisitConfig" are calculated from `metaRawOption` and `data`. |
| * They will not be calculated until `source` is about to be visited (to prevent from |
| * duplicate calcuation). `source` is visited only in series and input to transforms. |
| * |
| * [DIMENSION_INHERIT_RULE]: |
| * By default the dimensions are inherited from ancestors, unless a transform return |
| * a new dimensions definition. |
| * Consider the case: |
| * ```js |
| * dataset: [{ |
| * source: [ ['Product', 'Sales', 'Prise'], ['Cookies', 321, 44.21], ...] |
| * }, { |
| * transform: { type: 'filter', ... } |
| * }] |
| * dataset: [{ |
| * dimension: ['Product', 'Sales', 'Prise'], |
| * source: [ ['Cookies', 321, 44.21], ...] |
| * }, { |
| * transform: { type: 'filter', ... } |
| * }] |
| * ``` |
| * The two types of option should have the same behavior after transform. |
| * |
| * |
| * [SCENARIO]: |
| * (1) Provide source data directly: |
| * ```js |
| * series: { |
| * encode: {...}, |
| * dimensions: [...] |
| * seriesLayoutBy: 'row', |
| * data: [[...]] |
| * } |
| * ``` |
| * (2) Series refer to dataset. |
| * ```js |
| * series: [{ |
| * encode: {...} |
| * // Ignore datasetIndex means `datasetIndex: 0` |
| * // and the dimensions defination in dataset is used |
| * }, { |
| * encode: {...}, |
| * seriesLayoutBy: 'column', |
| * datasetIndex: 1 |
| * }] |
| * ``` |
| * (3) dataset transform |
| * ```js |
| * dataset: [{ |
| * source: [...] |
| * }, { |
| * source: [...] |
| * }, { |
| * // By default from 0. |
| * transform: { type: 'filter', config: {...} } |
| * }, { |
| * // Piped. |
| * transform: [ |
| * { type: 'filter', config: {...} }, |
| * { type: 'sort', config: {...} } |
| * ] |
| * }, { |
| * id: 'regressionData', |
| * fromDatasetIndex: 1, |
| * // Third-party transform |
| * transform: { type: 'ecStat:regression', config: {...} } |
| * }, { |
| * // retrieve the extra result. |
| * id: 'regressionFormula', |
| * fromDatasetId: 'regressionData', |
| * fromTransformResult: 1 |
| * }] |
| * ``` |
| */ |
| |
| var SourceManager = |
| /** @class */ |
| function () { |
| function SourceManager(sourceHost) { |
| // Cached source. Do not repeat calculating if not dirty. |
| this._sourceList = []; |
| this._storeList = []; // version sign of each upstream source manager. |
| |
| this._upstreamSignList = []; |
| this._versionSignBase = 0; |
| this._dirty = true; |
| this._sourceHost = sourceHost; |
| } |
| /** |
| * Mark dirty. |
| */ |
| |
| |
| SourceManager.prototype.dirty = function () { |
| this._setLocalSource([], []); |
| |
| this._storeList = []; |
| this._dirty = true; |
| }; |
| |
| SourceManager.prototype._setLocalSource = function (sourceList, upstreamSignList) { |
| this._sourceList = sourceList; |
| this._upstreamSignList = upstreamSignList; |
| this._versionSignBase++; |
| |
| if (this._versionSignBase > 9e10) { |
| this._versionSignBase = 0; |
| } |
| }; |
| /** |
| * For detecting whether the upstream source is dirty, so that |
| * the local cached source (in `_sourceList`) should be discarded. |
| */ |
| |
| |
| SourceManager.prototype._getVersionSign = function () { |
| return this._sourceHost.uid + '_' + this._versionSignBase; |
| }; |
| /** |
| * Always return a source instance. Otherwise throw error. |
| */ |
| |
| |
| SourceManager.prototype.prepareSource = function () { |
| // For the case that call `setOption` multiple time but no data changed, |
| // cache the result source to prevent from repeating transform. |
| if (this._isDirty()) { |
| this._createSource(); |
| |
| this._dirty = false; |
| } |
| }; |
| |
| SourceManager.prototype._createSource = function () { |
| this._setLocalSource([], []); |
| |
| var sourceHost = this._sourceHost; |
| |
| var upSourceMgrList = this._getUpstreamSourceManagers(); |
| |
| var hasUpstream = !!upSourceMgrList.length; |
| var resultSourceList; |
| var upstreamSignList; |
| |
| if (isSeries(sourceHost)) { |
| var seriesModel = sourceHost; |
| var data = void 0; |
| var sourceFormat = void 0; |
| var upSource = void 0; // Has upstream dataset |
| |
| if (hasUpstream) { |
| var upSourceMgr = upSourceMgrList[0]; |
| upSourceMgr.prepareSource(); |
| upSource = upSourceMgr.getSource(); |
| data = upSource.data; |
| sourceFormat = upSource.sourceFormat; |
| upstreamSignList = [upSourceMgr._getVersionSign()]; |
| } // Series data is from own. |
| else { |
| data = seriesModel.get('data', true); |
| sourceFormat = isTypedArray(data) ? SOURCE_FORMAT_TYPED_ARRAY : SOURCE_FORMAT_ORIGINAL; |
| upstreamSignList = []; |
| } // See [REQUIREMENT_MEMO], merge settings on series and parent dataset if it is root. |
| |
| |
| var newMetaRawOption = this._getSourceMetaRawOption() || {}; |
| var upMetaRawOption = upSource && upSource.metaRawOption || {}; |
| var seriesLayoutBy = retrieve2(newMetaRawOption.seriesLayoutBy, upMetaRawOption.seriesLayoutBy) || null; |
| var sourceHeader = retrieve2(newMetaRawOption.sourceHeader, upMetaRawOption.sourceHeader); // Note here we should not use `upSource.dimensionsDefine`. Consider the case: |
| // `upSource.dimensionsDefine` is detected by `seriesLayoutBy: 'column'`, |
| // but series need `seriesLayoutBy: 'row'`. |
| |
| var dimensions = retrieve2(newMetaRawOption.dimensions, upMetaRawOption.dimensions); // We share source with dataset as much as possible |
| // to avoid extra memory cost of high dimensional data. |
| |
| var needsCreateSource = seriesLayoutBy !== upMetaRawOption.seriesLayoutBy || !!sourceHeader !== !!upMetaRawOption.sourceHeader || dimensions; |
| resultSourceList = needsCreateSource ? [createSource(data, { |
| seriesLayoutBy: seriesLayoutBy, |
| sourceHeader: sourceHeader, |
| dimensions: dimensions |
| }, sourceFormat)] : []; |
| } else { |
| var datasetModel = sourceHost; // Has upstream dataset. |
| |
| if (hasUpstream) { |
| var result = this._applyTransform(upSourceMgrList); |
| |
| resultSourceList = result.sourceList; |
| upstreamSignList = result.upstreamSignList; |
| } // Is root dataset. |
| else { |
| var sourceData = datasetModel.get('source', true); |
| resultSourceList = [createSource(sourceData, this._getSourceMetaRawOption(), null)]; |
| upstreamSignList = []; |
| } |
| } |
| |
| if ("development" !== 'production') { |
| assert(resultSourceList && upstreamSignList); |
| } |
| |
| this._setLocalSource(resultSourceList, upstreamSignList); |
| }; |
| |
| SourceManager.prototype._applyTransform = function (upMgrList) { |
| var datasetModel = this._sourceHost; |
| var transformOption = datasetModel.get('transform', true); |
| var fromTransformResult = datasetModel.get('fromTransformResult', true); |
| |
| if ("development" !== 'production') { |
| assert(fromTransformResult != null || transformOption != null); |
| } |
| |
| if (fromTransformResult != null) { |
| var errMsg = ''; |
| |
| if (upMgrList.length !== 1) { |
| if ("development" !== 'production') { |
| errMsg = 'When using `fromTransformResult`, there should be only one upstream dataset'; |
| } |
| |
| doThrow(errMsg); |
| } |
| } |
| |
| var sourceList; |
| var upSourceList = []; |
| var upstreamSignList = []; |
| each(upMgrList, function (upMgr) { |
| upMgr.prepareSource(); |
| var upSource = upMgr.getSource(fromTransformResult || 0); |
| var errMsg = ''; |
| |
| if (fromTransformResult != null && !upSource) { |
| if ("development" !== 'production') { |
| errMsg = 'Can not retrieve result by `fromTransformResult`: ' + fromTransformResult; |
| } |
| |
| doThrow(errMsg); |
| } |
| |
| upSourceList.push(upSource); |
| upstreamSignList.push(upMgr._getVersionSign()); |
| }); |
| |
| if (transformOption) { |
| sourceList = applyDataTransform(transformOption, upSourceList, { |
| datasetIndex: datasetModel.componentIndex |
| }); |
| } else if (fromTransformResult != null) { |
| sourceList = [cloneSourceShallow(upSourceList[0])]; |
| } |
| |
| return { |
| sourceList: sourceList, |
| upstreamSignList: upstreamSignList |
| }; |
| }; |
| |
| SourceManager.prototype._isDirty = function () { |
| if (this._dirty) { |
| return true; |
| } // All sourceList is from the some upstream. |
| |
| |
| var upSourceMgrList = this._getUpstreamSourceManagers(); |
| |
| for (var i = 0; i < upSourceMgrList.length; i++) { |
| var upSrcMgr = upSourceMgrList[i]; |
| |
| if ( // Consider the case that there is ancestor diry, call it recursively. |
| // The performance is probably not an issue because usually the chain is not long. |
| upSrcMgr._isDirty() || this._upstreamSignList[i] !== upSrcMgr._getVersionSign()) { |
| return true; |
| } |
| } |
| }; |
| /** |
| * @param sourceIndex By default 0, means "main source". |
| * In most cases there is only one source. |
| */ |
| |
| |
| SourceManager.prototype.getSource = function (sourceIndex) { |
| sourceIndex = sourceIndex || 0; |
| var source = this._sourceList[sourceIndex]; |
| |
| if (!source) { |
| // Series may share source instance with dataset. |
| var upSourceMgrList = this._getUpstreamSourceManagers(); |
| |
| return upSourceMgrList[0] && upSourceMgrList[0].getSource(sourceIndex); |
| } |
| |
| return source; |
| }; |
| /** |
| * |
| * Get a data store which can be shared across series. |
| * Only available for series. |
| * |
| * @param seriesDimRequest Dimensions that are generated in series. |
| * Should have been sorted by `storeDimIndex` asc. |
| */ |
| |
| |
| SourceManager.prototype.getSharedDataStore = function (seriesDimRequest) { |
| if ("development" !== 'production') { |
| assert(isSeries(this._sourceHost), 'Can only call getDataStore on series source manager.'); |
| } |
| |
| var schema = seriesDimRequest.makeStoreSchema(); |
| return this._innerGetDataStore(schema.dimensions, seriesDimRequest.source, schema.hash); |
| }; |
| |
| SourceManager.prototype._innerGetDataStore = function (storeDims, seriesSource, sourceReadKey) { |
| // TODO Can use other sourceIndex? |
| var sourceIndex = 0; |
| var storeList = this._storeList; |
| var cachedStoreMap = storeList[sourceIndex]; |
| |
| if (!cachedStoreMap) { |
| cachedStoreMap = storeList[sourceIndex] = {}; |
| } |
| |
| var cachedStore = cachedStoreMap[sourceReadKey]; |
| |
| if (!cachedStore) { |
| var upSourceMgr = this._getUpstreamSourceManagers()[0]; |
| |
| if (isSeries(this._sourceHost) && upSourceMgr) { |
| cachedStore = upSourceMgr._innerGetDataStore(storeDims, seriesSource, sourceReadKey); |
| } else { |
| cachedStore = new DataStore(); // Always create store from source of series. |
| |
| cachedStore.initData(new DefaultDataProvider(seriesSource, storeDims.length), storeDims); |
| } |
| |
| cachedStoreMap[sourceReadKey] = cachedStore; |
| } |
| |
| return cachedStore; |
| }; |
| /** |
| * PENDING: Is it fast enough? |
| * If no upstream, return empty array. |
| */ |
| |
| |
| SourceManager.prototype._getUpstreamSourceManagers = function () { |
| // Always get the relationship from the raw option. |
| // Do not cache the link of the dependency graph, so that |
| // there is no need to update them when change happens. |
| var sourceHost = this._sourceHost; |
| |
| if (isSeries(sourceHost)) { |
| var datasetModel = querySeriesUpstreamDatasetModel(sourceHost); |
| return !datasetModel ? [] : [datasetModel.getSourceManager()]; |
| } else { |
| return map(queryDatasetUpstreamDatasetModels(sourceHost), function (datasetModel) { |
| return datasetModel.getSourceManager(); |
| }); |
| } |
| }; |
| |
| SourceManager.prototype._getSourceMetaRawOption = function () { |
| var sourceHost = this._sourceHost; |
| var seriesLayoutBy; |
| var sourceHeader; |
| var dimensions; |
| |
| if (isSeries(sourceHost)) { |
| seriesLayoutBy = sourceHost.get('seriesLayoutBy', true); |
| sourceHeader = sourceHost.get('sourceHeader', true); |
| dimensions = sourceHost.get('dimensions', true); |
| } // See [REQUIREMENT_MEMO], `non-root-dataset` do not support them. |
| else if (!this._getUpstreamSourceManagers().length) { |
| var model = sourceHost; |
| seriesLayoutBy = model.get('seriesLayoutBy', true); |
| sourceHeader = model.get('sourceHeader', true); |
| dimensions = model.get('dimensions', true); |
| } |
| |
| return { |
| seriesLayoutBy: seriesLayoutBy, |
| sourceHeader: sourceHeader, |
| dimensions: dimensions |
| }; |
| }; |
| |
| return SourceManager; |
| }(); |
| // disable the transform merge, but do not disable transform clone from rawOption. |
| |
| function disableTransformOptionMerge(datasetModel) { |
| var transformOption = datasetModel.option.transform; |
| transformOption && setAsPrimitive(datasetModel.option.transform); |
| } |
| |
| function isSeries(sourceHost) { |
| // Avoid circular dependency with Series.ts |
| return sourceHost.mainType === 'series'; |
| } |
| |
| function doThrow(errMsg) { |
| throw new Error(errMsg); |
| } |
| |
| function createTooltipMarkup(type, option) { |
| option.type = type; |
| return option; |
| } |
| |
| function retrieveVisualColorForTooltipMarker(series, dataIndex) { |
| var style = series.getData().getItemVisual(dataIndex, 'style'); |
| var color = style[series.visualDrawType]; |
| return convertToColorString(color); |
| } |
| |
| function defaultSeriesFormatTooltip(opt) { |
| var series = opt.series; |
| var dataIndex = opt.dataIndex; |
| var multipleSeries = opt.multipleSeries; |
| var data = series.getData(); |
| var tooltipDims = data.mapDimensionsAll('defaultedTooltip'); |
| var tooltipDimLen = tooltipDims.length; |
| var value = series.getRawValue(dataIndex); |
| var isValueArr = isArray(value); |
| var markerColor = retrieveVisualColorForTooltipMarker(series, dataIndex); // Complicated rule for pretty tooltip. |
| |
| var inlineValue; |
| var inlineValueType; |
| var subBlocks; |
| var sortParam; |
| |
| if (tooltipDimLen > 1 || isValueArr && !tooltipDimLen) { |
| var formatArrResult = formatTooltipArrayValue(value, series, dataIndex, tooltipDims, markerColor); |
| inlineValue = formatArrResult.inlineValues; |
| inlineValueType = formatArrResult.inlineValueTypes; |
| subBlocks = formatArrResult.blocks; // Only support tooltip sort by the first inline value. It's enough in most cases. |
| |
| sortParam = formatArrResult.inlineValues[0]; |
| } else if (tooltipDimLen) { |
| var dimInfo = data.getDimensionInfo(tooltipDims[0]); |
| sortParam = inlineValue = retrieveRawValue(data, dataIndex, tooltipDims[0]); |
| inlineValueType = dimInfo.type; |
| } else { |
| sortParam = inlineValue = isValueArr ? value[0] : value; |
| } // Do not show generated series name. It might not be readable. |
| |
| |
| var seriesNameSpecified = isNameSpecified(series); |
| var seriesName = seriesNameSpecified && series.name || ''; |
| var itemName = data.getName(dataIndex); |
| var inlineName = multipleSeries ? seriesName : itemName; |
| return createTooltipMarkup('section', { |
| header: seriesName, |
| // When series name not specified, do not show a header line with only '-'. |
| // This case alway happen in tooltip.trigger: 'item'. |
| noHeader: multipleSeries || !seriesNameSpecified, |
| sortParam: sortParam, |
| blocks: [createTooltipMarkup('nameValue', { |
| markerType: 'item', |
| markerColor: markerColor, |
| // Do not mix display seriesName and itemName in one tooltip, |
| // which might confuses users. |
| name: inlineName, |
| // name dimension might be auto assigned, where the name might |
| // be not readable. So we check trim here. |
| noName: !trim(inlineName), |
| value: inlineValue, |
| valueType: inlineValueType |
| })].concat(subBlocks || []) |
| }); |
| } |
| |
| function formatTooltipArrayValue(value, series, dataIndex, tooltipDims, colorStr) { |
| // check: category-no-encode-has-axis-data in dataset.html |
| var data = series.getData(); |
| var isValueMultipleLine = reduce(value, function (isValueMultipleLine, val, idx) { |
| var dimItem = data.getDimensionInfo(idx); |
| return isValueMultipleLine = isValueMultipleLine || dimItem && dimItem.tooltip !== false && dimItem.displayName != null; |
| }, false); |
| var inlineValues = []; |
| var inlineValueTypes = []; |
| var blocks = []; |
| tooltipDims.length ? each(tooltipDims, function (dim) { |
| setEachItem(retrieveRawValue(data, dataIndex, dim), dim); |
| }) // By default, all dims is used on tooltip. |
| : each(value, setEachItem); |
| |
| function setEachItem(val, dim) { |
| var dimInfo = data.getDimensionInfo(dim); // If `dimInfo.tooltip` is not set, show tooltip. |
| |
| if (!dimInfo || dimInfo.otherDims.tooltip === false) { |
| return; |
| } |
| |
| if (isValueMultipleLine) { |
| blocks.push(createTooltipMarkup('nameValue', { |
| markerType: 'subItem', |
| markerColor: colorStr, |
| name: dimInfo.displayName, |
| value: val, |
| valueType: dimInfo.type |
| })); |
| } else { |
| inlineValues.push(val); |
| inlineValueTypes.push(dimInfo.type); |
| } |
| } |
| |
| return { |
| inlineValues: inlineValues, |
| inlineValueTypes: inlineValueTypes, |
| blocks: blocks |
| }; |
| } |
| |
| var inner$1 = makeInner(); |
| |
| function getSelectionKey(data, dataIndex) { |
| return data.getName(dataIndex) || data.getId(dataIndex); |
| } |
| |
| var SERIES_UNIVERSAL_TRANSITION_PROP = '__universalTransitionEnabled'; |
| |
| var SeriesModel = |
| /** @class */ |
| function (_super) { |
| __extends(SeriesModel, _super); |
| |
| function SeriesModel() { |
| // [Caution]: Because this class or desecendants can be used as `XXX.extend(subProto)`, |
| // the class members must not be initialized in constructor or declaration place. |
| // Otherwise there is bad case: |
| // class A {xxx = 1;} |
| // enableClassExtend(A); |
| // class B extends A {} |
| // var C = B.extend({xxx: 5}); |
| // var c = new C(); |
| // console.log(c.xxx); // expect 5 but always 1. |
| var _this = _super !== null && _super.apply(this, arguments) || this; // --------------------------------------- |
| // Props about data selection |
| // --------------------------------------- |
| |
| |
| _this._selectedDataIndicesMap = {}; |
| return _this; |
| } |
| |
| SeriesModel.prototype.init = function (option, parentModel, ecModel) { |
| this.seriesIndex = this.componentIndex; |
| this.dataTask = createTask({ |
| count: dataTaskCount, |
| reset: dataTaskReset |
| }); |
| this.dataTask.context = { |
| model: this |
| }; |
| this.mergeDefaultAndTheme(option, ecModel); |
| var sourceManager = inner$1(this).sourceManager = new SourceManager(this); |
| sourceManager.prepareSource(); |
| var data = this.getInitialData(option, ecModel); |
| wrapData(data, this); |
| this.dataTask.context.data = data; |
| |
| if ("development" !== 'production') { |
| assert(data, 'getInitialData returned invalid data.'); |
| } |
| |
| inner$1(this).dataBeforeProcessed = data; // If we reverse the order (make data firstly, and then make |
| // dataBeforeProcessed by cloneShallow), cloneShallow will |
| // cause data.graph.data !== data when using |
| // module:echarts/data/Graph or module:echarts/data/Tree. |
| // See module:echarts/data/helper/linkSeriesData |
| // Theoretically, it is unreasonable to call `seriesModel.getData()` in the model |
| // init or merge stage, because the data can be restored. So we do not `restoreData` |
| // and `setData` here, which forbids calling `seriesModel.getData()` in this stage. |
| // Call `seriesModel.getRawData()` instead. |
| // this.restoreData(); |
| |
| autoSeriesName(this); |
| |
| this._initSelectedMapFromData(data); |
| }; |
| /** |
| * Util for merge default and theme to option |
| */ |
| |
| |
| SeriesModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { |
| var layoutMode = fetchLayoutMode(this); |
| var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; // Backward compat: using subType on theme. |
| // But if name duplicate between series subType |
| // (for example: parallel) add component mainType, |
| // add suffix 'Series'. |
| |
| var themeSubType = this.subType; |
| |
| if (ComponentModel.hasClass(themeSubType)) { |
| themeSubType += 'Series'; |
| } |
| |
| merge(option, ecModel.getTheme().get(this.subType)); |
| merge(option, this.getDefaultOption()); // Default label emphasis `show` |
| |
| defaultEmphasis(option, 'label', ['show']); |
| this.fillDataTextStyle(option.data); |
| |
| if (layoutMode) { |
| mergeLayoutParam(option, inputPositionParams, layoutMode); |
| } |
| }; |
| |
| SeriesModel.prototype.mergeOption = function (newSeriesOption, ecModel) { |
| // this.settingTask.dirty(); |
| newSeriesOption = merge(this.option, newSeriesOption, true); |
| this.fillDataTextStyle(newSeriesOption.data); |
| var layoutMode = fetchLayoutMode(this); |
| |
| if (layoutMode) { |
| mergeLayoutParam(this.option, newSeriesOption, layoutMode); |
| } |
| |
| var sourceManager = inner$1(this).sourceManager; |
| sourceManager.dirty(); |
| sourceManager.prepareSource(); |
| var data = this.getInitialData(newSeriesOption, ecModel); |
| wrapData(data, this); |
| this.dataTask.dirty(); |
| this.dataTask.context.data = data; |
| inner$1(this).dataBeforeProcessed = data; |
| autoSeriesName(this); |
| |
| this._initSelectedMapFromData(data); |
| }; |
| |
| SeriesModel.prototype.fillDataTextStyle = function (data) { |
| // Default data label emphasis `show` |
| // FIXME Tree structure data ? |
| // FIXME Performance ? |
| if (data && !isTypedArray(data)) { |
| var props = ['show']; |
| |
| for (var i = 0; i < data.length; i++) { |
| if (data[i] && data[i].label) { |
| defaultEmphasis(data[i], 'label', props); |
| } |
| } |
| } |
| }; |
| /** |
| * Init a data structure from data related option in series |
| * Must be overridden. |
| */ |
| |
| |
| SeriesModel.prototype.getInitialData = function (option, ecModel) { |
| return; |
| }; |
| /** |
| * Append data to list |
| */ |
| |
| |
| SeriesModel.prototype.appendData = function (params) { |
| // FIXME ??? |
| // (1) If data from dataset, forbidden append. |
| // (2) support append data of dataset. |
| var data = this.getRawData(); |
| data.appendData(params.data); |
| }; |
| /** |
| * Consider some method like `filter`, `map` need make new data, |
| * We should make sure that `seriesModel.getData()` get correct |
| * data in the stream procedure. So we fetch data from upstream |
| * each time `task.perform` called. |
| */ |
| |
| |
| SeriesModel.prototype.getData = function (dataType) { |
| var task = getCurrentTask(this); |
| |
| if (task) { |
| var data = task.context.data; |
| return dataType == null ? data : data.getLinkedData(dataType); |
| } else { |
| // When series is not alive (that may happen when click toolbox |
| // restore or setOption with not merge mode), series data may |
| // be still need to judge animation or something when graphic |
| // elements want to know whether fade out. |
| return inner$1(this).data; |
| } |
| }; |
| |
| SeriesModel.prototype.getAllData = function () { |
| var mainData = this.getData(); |
| return mainData && mainData.getLinkedDataAll ? mainData.getLinkedDataAll() : [{ |
| data: mainData |
| }]; |
| }; |
| |
| SeriesModel.prototype.setData = function (data) { |
| var task = getCurrentTask(this); |
| |
| if (task) { |
| var context = task.context; // Consider case: filter, data sample. |
| // FIXME:TS never used, so comment it |
| // if (context.data !== data && task.modifyOutputEnd) { |
| // task.setOutputEnd(data.count()); |
| // } |
| |
| context.outputData = data; // Caution: setData should update context.data, |
| // Because getData may be called multiply in a |
| // single stage and expect to get the data just |
| // set. (For example, AxisProxy, x y both call |
| // getData and setDate sequentially). |
| // So the context.data should be fetched from |
| // upstream each time when a stage starts to be |
| // performed. |
| |
| if (task !== this.dataTask) { |
| context.data = data; |
| } |
| } |
| |
| inner$1(this).data = data; |
| }; |
| |
| SeriesModel.prototype.getEncode = function () { |
| var encode = this.get('encode', true); |
| |
| if (encode) { |
| return createHashMap(encode); |
| } |
| }; |
| |
| SeriesModel.prototype.getSourceManager = function () { |
| return inner$1(this).sourceManager; |
| }; |
| |
| SeriesModel.prototype.getSource = function () { |
| return this.getSourceManager().getSource(); |
| }; |
| /** |
| * Get data before processed |
| */ |
| |
| |
| SeriesModel.prototype.getRawData = function () { |
| return inner$1(this).dataBeforeProcessed; |
| }; |
| |
| SeriesModel.prototype.getColorBy = function () { |
| var colorBy = this.get('colorBy'); |
| return colorBy || 'series'; |
| }; |
| |
| SeriesModel.prototype.isColorBySeries = function () { |
| return this.getColorBy() === 'series'; |
| }; |
| /** |
| * Get base axis if has coordinate system and has axis. |
| * By default use coordSys.getBaseAxis(); |
| * Can be overridden for some chart. |
| * @return {type} description |
| */ |
| |
| |
| SeriesModel.prototype.getBaseAxis = function () { |
| var coordSys = this.coordinateSystem; // @ts-ignore |
| |
| return coordSys && coordSys.getBaseAxis && coordSys.getBaseAxis(); |
| }; |
| /** |
| * Default tooltip formatter |
| * |
| * @param dataIndex |
| * @param multipleSeries |
| * @param dataType |
| * @param renderMode valid values: 'html'(by default) and 'richText'. |
| * 'html' is used for rendering tooltip in extra DOM form, and the result |
| * string is used as DOM HTML content. |
| * 'richText' is used for rendering tooltip in rich text form, for those where |
| * DOM operation is not supported. |
| * @return formatted tooltip with `html` and `markers` |
| * Notice: The override method can also return string |
| */ |
| |
| |
| SeriesModel.prototype.formatTooltip = function (dataIndex, multipleSeries, dataType) { |
| return defaultSeriesFormatTooltip({ |
| series: this, |
| dataIndex: dataIndex, |
| multipleSeries: multipleSeries |
| }); |
| }; |
| |
| SeriesModel.prototype.isAnimationEnabled = function () { |
| var ecModel = this.ecModel; // Disable animation if using echarts in node but not give ssr flag. |
| // In ssr mode, renderToString will generate svg with css animation. |
| |
| if (env.node && !(ecModel && ecModel.ssr)) { |
| return false; |
| } |
| |
| var animationEnabled = this.getShallow('animation'); |
| |
| if (animationEnabled) { |
| if (this.getData().count() > this.getShallow('animationThreshold')) { |
| animationEnabled = false; |
| } |
| } |
| |
| return !!animationEnabled; |
| }; |
| |
| SeriesModel.prototype.restoreData = function () { |
| this.dataTask.dirty(); |
| }; |
| |
| SeriesModel.prototype.getColorFromPalette = function (name, scope, requestColorNum) { |
| var ecModel = this.ecModel; // PENDING |
| |
| var color = PaletteMixin.prototype.getColorFromPalette.call(this, name, scope, requestColorNum); |
| |
| if (!color) { |
| color = ecModel.getColorFromPalette(name, scope, requestColorNum); |
| } |
| |
| return color; |
| }; |
| /** |
| * Use `data.mapDimensionsAll(coordDim)` instead. |
| * @deprecated |
| */ |
| |
| |
| SeriesModel.prototype.coordDimToDataDim = function (coordDim) { |
| return this.getRawData().mapDimensionsAll(coordDim); |
| }; |
| /** |
| * Get progressive rendering count each step |
| */ |
| |
| |
| SeriesModel.prototype.getProgressive = function () { |
| return this.get('progressive'); |
| }; |
| /** |
| * Get progressive rendering count each step |
| */ |
| |
| |
| SeriesModel.prototype.getProgressiveThreshold = function () { |
| return this.get('progressiveThreshold'); |
| }; // PENGING If selectedMode is null ? |
| |
| |
| SeriesModel.prototype.select = function (innerDataIndices, dataType) { |
| this._innerSelect(this.getData(dataType), innerDataIndices); |
| }; |
| |
| SeriesModel.prototype.unselect = function (innerDataIndices, dataType) { |
| var selectedMap = this.option.selectedMap; |
| |
| if (!selectedMap) { |
| return; |
| } |
| |
| var selectedMode = this.option.selectedMode; |
| var data = this.getData(dataType); |
| |
| if (selectedMode === 'series' || selectedMap === 'all') { |
| this.option.selectedMap = {}; |
| this._selectedDataIndicesMap = {}; |
| return; |
| } |
| |
| for (var i = 0; i < innerDataIndices.length; i++) { |
| var dataIndex = innerDataIndices[i]; |
| var nameOrId = getSelectionKey(data, dataIndex); |
| selectedMap[nameOrId] = false; |
| this._selectedDataIndicesMap[nameOrId] = -1; |
| } |
| }; |
| |
| SeriesModel.prototype.toggleSelect = function (innerDataIndices, dataType) { |
| var tmpArr = []; |
| |
| for (var i = 0; i < innerDataIndices.length; i++) { |
| tmpArr[0] = innerDataIndices[i]; |
| this.isSelected(innerDataIndices[i], dataType) ? this.unselect(tmpArr, dataType) : this.select(tmpArr, dataType); |
| } |
| }; |
| |
| SeriesModel.prototype.getSelectedDataIndices = function () { |
| if (this.option.selectedMap === 'all') { |
| return [].slice.call(this.getData().getIndices()); |
| } |
| |
| var selectedDataIndicesMap = this._selectedDataIndicesMap; |
| var nameOrIds = keys(selectedDataIndicesMap); |
| var dataIndices = []; |
| |
| for (var i = 0; i < nameOrIds.length; i++) { |
| var dataIndex = selectedDataIndicesMap[nameOrIds[i]]; |
| |
| if (dataIndex >= 0) { |
| dataIndices.push(dataIndex); |
| } |
| } |
| |
| return dataIndices; |
| }; |
| |
| SeriesModel.prototype.isSelected = function (dataIndex, dataType) { |
| var selectedMap = this.option.selectedMap; |
| |
| if (!selectedMap) { |
| return false; |
| } |
| |
| var data = this.getData(dataType); |
| return (selectedMap === 'all' || selectedMap[getSelectionKey(data, dataIndex)]) && !data.getItemModel(dataIndex).get(['select', 'disabled']); |
| }; |
| |
| SeriesModel.prototype.isUniversalTransitionEnabled = function () { |
| if (this[SERIES_UNIVERSAL_TRANSITION_PROP]) { |
| return true; |
| } |
| |
| var universalTransitionOpt = this.option.universalTransition; // Quick reject |
| |
| if (!universalTransitionOpt) { |
| return false; |
| } |
| |
| if (universalTransitionOpt === true) { |
| return true; |
| } // Can be simply 'universalTransition: true' |
| |
| |
| return universalTransitionOpt && universalTransitionOpt.enabled; |
| }; |
| |
| SeriesModel.prototype._innerSelect = function (data, innerDataIndices) { |
| var _a, _b; |
| |
| var option = this.option; |
| var selectedMode = option.selectedMode; |
| var len = innerDataIndices.length; |
| |
| if (!selectedMode || !len) { |
| return; |
| } |
| |
| if (selectedMode === 'series') { |
| option.selectedMap = 'all'; |
| } else if (selectedMode === 'multiple') { |
| if (!isObject(option.selectedMap)) { |
| option.selectedMap = {}; |
| } |
| |
| var selectedMap = option.selectedMap; |
| |
| for (var i = 0; i < len; i++) { |
| var dataIndex = innerDataIndices[i]; // TODO different types of data share same object. |
| |
| var nameOrId = getSelectionKey(data, dataIndex); |
| selectedMap[nameOrId] = true; |
| this._selectedDataIndicesMap[nameOrId] = data.getRawIndex(dataIndex); |
| } |
| } else if (selectedMode === 'single' || selectedMode === true) { |
| var lastDataIndex = innerDataIndices[len - 1]; |
| var nameOrId = getSelectionKey(data, lastDataIndex); |
| option.selectedMap = (_a = {}, _a[nameOrId] = true, _a); |
| this._selectedDataIndicesMap = (_b = {}, _b[nameOrId] = data.getRawIndex(lastDataIndex), _b); |
| } |
| }; |
| |
| SeriesModel.prototype._initSelectedMapFromData = function (data) { |
| // Ignore select info in data if selectedMap exists. |
| // NOTE It's only for legacy usage. edge data is not supported. |
| if (this.option.selectedMap) { |
| return; |
| } |
| |
| var dataIndices = []; |
| |
| if (data.hasItemOption) { |
| data.each(function (idx) { |
| var rawItem = data.getRawDataItem(idx); |
| |
| if (rawItem && rawItem.selected) { |
| dataIndices.push(idx); |
| } |
| }); |
| } |
| |
| if (dataIndices.length > 0) { |
| this._innerSelect(data, dataIndices); |
| } |
| }; // /** |
| // * @see {module:echarts/stream/Scheduler} |
| // */ |
| // abstract pipeTask: null |
| |
| |
| SeriesModel.registerClass = function (clz) { |
| return ComponentModel.registerClass(clz); |
| }; |
| |
| SeriesModel.protoInitialize = function () { |
| var proto = SeriesModel.prototype; |
| proto.type = 'series.__base__'; |
| proto.seriesIndex = 0; |
| proto.ignoreStyleOnData = false; |
| proto.hasSymbolVisual = false; |
| proto.defaultSymbol = 'circle'; // Make sure the values can be accessed! |
| |
| proto.visualStyleAccessPath = 'itemStyle'; |
| proto.visualDrawType = 'fill'; |
| }(); |
| |
| return SeriesModel; |
| }(ComponentModel); |
| |
| mixin(SeriesModel, DataFormatMixin); |
| mixin(SeriesModel, PaletteMixin); |
| mountExtend(SeriesModel, ComponentModel); |
| /** |
| * MUST be called after `prepareSource` called |
| * Here we need to make auto series, especially for auto legend. But we |
| * do not modify series.name in option to avoid side effects. |
| */ |
| |
| function autoSeriesName(seriesModel) { |
| // User specified name has higher priority, otherwise it may cause |
| // series can not be queried unexpectedly. |
| var name = seriesModel.name; |
| |
| if (!isNameSpecified(seriesModel)) { |
| seriesModel.name = getSeriesAutoName(seriesModel) || name; |
| } |
| } |
| |
| function getSeriesAutoName(seriesModel) { |
| var data = seriesModel.getRawData(); |
| var dataDims = data.mapDimensionsAll('seriesName'); |
| var nameArr = []; |
| each(dataDims, function (dataDim) { |
| var dimInfo = data.getDimensionInfo(dataDim); |
| dimInfo.displayName && nameArr.push(dimInfo.displayName); |
| }); |
| return nameArr.join(' '); |
| } |
| |
| function dataTaskCount(context) { |
| return context.model.getRawData().count(); |
| } |
| |
| function dataTaskReset(context) { |
| var seriesModel = context.model; |
| seriesModel.setData(seriesModel.getRawData().cloneShallow()); |
| return dataTaskProgress; |
| } |
| |
| function dataTaskProgress(param, context) { |
| // Avoid repead cloneShallow when data just created in reset. |
| if (context.outputData && param.end > context.outputData.count()) { |
| context.model.getRawData().cloneShallow(context.outputData); |
| } |
| } // TODO refactor |
| |
| |
| function wrapData(data, seriesModel) { |
| each(concatArray(data.CHANGABLE_METHODS, data.DOWNSAMPLE_METHODS), function (methodName) { |
| data.wrapMethod(methodName, curry(onDataChange, seriesModel)); |
| }); |
| } |
| |
| function onDataChange(seriesModel, newList) { |
| var task = getCurrentTask(seriesModel); |
| |
| if (task) { |
| // Consider case: filter, selectRange |
| task.setOutputEnd((newList || this).count()); |
| } |
| |
| return newList; |
| } |
| |
| function getCurrentTask(seriesModel) { |
| var scheduler = (seriesModel.ecModel || {}).scheduler; |
| var pipeline = scheduler && scheduler.getPipeline(seriesModel.uid); |
| |
| if (pipeline) { |
| // When pipline finished, the currrentTask keep the last |
| // task (renderTask). |
| var task = pipeline.currentTask; |
| |
| if (task) { |
| var agentStubMap = task.agentStubMap; |
| |
| if (agentStubMap) { |
| task = agentStubMap.get(seriesModel.uid); |
| } |
| } |
| |
| return task; |
| } |
| } |
| |
| var ComponentView = |
| /** @class */ |
| function () { |
| function ComponentView() { |
| this.group = new Group(); |
| this.uid = getUID('viewComponent'); |
| } |
| |
| ComponentView.prototype.init = function (ecModel, api) {}; |
| |
| ComponentView.prototype.render = function (model, ecModel, api, payload) {}; |
| |
| ComponentView.prototype.dispose = function (ecModel, api) {}; |
| |
| ComponentView.prototype.updateView = function (model, ecModel, api, payload) {// Do nothing; |
| }; |
| |
| ComponentView.prototype.updateLayout = function (model, ecModel, api, payload) {// Do nothing; |
| }; |
| |
| ComponentView.prototype.updateVisual = function (model, ecModel, api, payload) {// Do nothing; |
| }; |
| /** |
| * Hook for toggle blur target series. |
| * Can be used in marker for blur or leave blur the markers |
| */ |
| |
| |
| ComponentView.prototype.toggleBlurSeries = function (seriesModels, isBlur, ecModel) {// Do nothing; |
| }; |
| /** |
| * Traverse the new rendered elements. |
| * |
| * It will traverse the new added element in progressive rendering. |
| * And traverse all in normal rendering. |
| */ |
| |
| |
| ComponentView.prototype.eachRendered = function (cb) { |
| var group = this.group; |
| |
| if (group) { |
| group.traverse(cb); |
| } |
| }; |
| |
| return ComponentView; |
| }(); |
| enableClassExtend(ComponentView); |
| enableClassManagement(ComponentView); |
| |
| /** |
| * @return {string} If large mode changed, return string 'reset'; |
| */ |
| |
| function createRenderPlanner() { |
| var inner = makeInner(); |
| return function (seriesModel) { |
| var fields = inner(seriesModel); |
| var pipelineContext = seriesModel.pipelineContext; |
| var originalLarge = !!fields.large; |
| var originalProgressive = !!fields.progressiveRender; // FIXME: if the planner works on a filtered series, `pipelineContext` does not |
| // exists. See #11611 . Probably we need to modify this structure, see the comment |
| // on `performRawSeries` in `Schedular.js`. |
| |
| var large = fields.large = !!(pipelineContext && pipelineContext.large); |
| var progressive = fields.progressiveRender = !!(pipelineContext && pipelineContext.progressiveRender); |
| return !!(originalLarge !== large || originalProgressive !== progressive) && 'reset'; |
| }; |
| } |
| |
| var inner$2 = makeInner(); |
| var renderPlanner = createRenderPlanner(); |
| |
| var ChartView = |
| /** @class */ |
| function () { |
| function ChartView() { |
| this.group = new Group(); |
| this.uid = getUID('viewChart'); |
| this.renderTask = createTask({ |
| plan: renderTaskPlan, |
| reset: renderTaskReset |
| }); |
| this.renderTask.context = { |
| view: this |
| }; |
| } |
| |
| ChartView.prototype.init = function (ecModel, api) {}; |
| |
| ChartView.prototype.render = function (seriesModel, ecModel, api, payload) { |
| if ("development" !== 'production') { |
| throw new Error('render method must been implemented'); |
| } |
| }; |
| /** |
| * Highlight series or specified data item. |
| */ |
| |
| |
| ChartView.prototype.highlight = function (seriesModel, ecModel, api, payload) { |
| var data = seriesModel.getData(payload && payload.dataType); |
| |
| if (!data) { |
| if ("development" !== 'production') { |
| error("Unknown dataType " + payload.dataType); |
| } |
| |
| return; |
| } |
| |
| toggleHighlight(data, payload, 'emphasis'); |
| }; |
| /** |
| * Downplay series or specified data item. |
| */ |
| |
| |
| ChartView.prototype.downplay = function (seriesModel, ecModel, api, payload) { |
| var data = seriesModel.getData(payload && payload.dataType); |
| |
| if (!data) { |
| if ("development" !== 'production') { |
| error("Unknown dataType " + payload.dataType); |
| } |
| |
| return; |
| } |
| |
| toggleHighlight(data, payload, 'normal'); |
| }; |
| /** |
| * Remove self. |
| */ |
| |
| |
| ChartView.prototype.remove = function (ecModel, api) { |
| this.group.removeAll(); |
| }; |
| /** |
| * Dispose self. |
| */ |
| |
| |
| ChartView.prototype.dispose = function (ecModel, api) {}; |
| |
| ChartView.prototype.updateView = function (seriesModel, ecModel, api, payload) { |
| this.render(seriesModel, ecModel, api, payload); |
| }; // FIXME never used? |
| |
| |
| ChartView.prototype.updateLayout = function (seriesModel, ecModel, api, payload) { |
| this.render(seriesModel, ecModel, api, payload); |
| }; // FIXME never used? |
| |
| |
| ChartView.prototype.updateVisual = function (seriesModel, ecModel, api, payload) { |
| this.render(seriesModel, ecModel, api, payload); |
| }; |
| /** |
| * Traverse the new rendered elements. |
| * |
| * It will traverse the new added element in progressive rendering. |
| * And traverse all in normal rendering. |
| */ |
| |
| |
| ChartView.prototype.eachRendered = function (cb) { |
| traverseElements(this.group, cb); |
| }; |
| |
| ChartView.markUpdateMethod = function (payload, methodName) { |
| inner$2(payload).updateMethod = methodName; |
| }; |
| |
| ChartView.protoInitialize = function () { |
| var proto = ChartView.prototype; |
| proto.type = 'chart'; |
| }(); |
| |
| return ChartView; |
| }(); |
| /** |
| * Set state of single element |
| */ |
| |
| function elSetState(el, state, highlightDigit) { |
| if (el && isHighDownDispatcher(el)) { |
| (state === 'emphasis' ? enterEmphasis : leaveEmphasis)(el, highlightDigit); |
| } |
| } |
| |
| function toggleHighlight(data, payload, state) { |
| var dataIndex = queryDataIndex(data, payload); |
| var highlightDigit = payload && payload.highlightKey != null ? getHighlightDigit(payload.highlightKey) : null; |
| |
| if (dataIndex != null) { |
| each(normalizeToArray(dataIndex), function (dataIdx) { |
| elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit); |
| }); |
| } else { |
| data.eachItemGraphicEl(function (el) { |
| elSetState(el, state, highlightDigit); |
| }); |
| } |
| } |
| |
| enableClassExtend(ChartView, ['dispose']); |
| enableClassManagement(ChartView); |
| |
| function renderTaskPlan(context) { |
| return renderPlanner(context.model); |
| } |
| |
| function renderTaskReset(context) { |
| var seriesModel = context.model; |
| var ecModel = context.ecModel; |
| var api = context.api; |
| var payload = context.payload; // FIXME: remove updateView updateVisual |
| |
| var progressiveRender = seriesModel.pipelineContext.progressiveRender; |
| var view = context.view; |
| var updateMethod = payload && inner$2(payload).updateMethod; |
| var methodName = progressiveRender ? 'incrementalPrepareRender' : updateMethod && view[updateMethod] ? updateMethod // `appendData` is also supported when data amount |
| // is less than progressive threshold. |
| : 'render'; |
| |
| if (methodName !== 'render') { |
| view[methodName](seriesModel, ecModel, api, payload); |
| } |
| |
| return progressMethodMap[methodName]; |
| } |
| |
| var progressMethodMap = { |
| incrementalPrepareRender: { |
| progress: function (params, context) { |
| context.view.incrementalRender(params, context.model, context.ecModel, context.api, context.payload); |
| } |
| }, |
| render: { |
| // Put view.render in `progress` to support appendData. But in this case |
| // view.render should not be called in reset, otherwise it will be called |
| // twise. Use `forceFirstProgress` to make sure that view.render is called |
| // in any cases. |
| forceFirstProgress: true, |
| progress: function (params, context) { |
| context.view.render(context.model, context.ecModel, context.api, context.payload); |
| } |
| } |
| }; |
| |
| /** |
| * @public |
| * @param {(Function)} fn |
| * @param {number} [delay=0] Unit: ms. |
| * @param {boolean} [debounce=false] |
| * true: If call interval less than `delay`, only the last call works. |
| * false: If call interval less than `delay, call works on fixed rate. |
| * @return {(Function)} throttled fn. |
| */ |
| |
| function throttle(fn, delay, debounce) { |
| var currCall; |
| var lastCall = 0; |
| var lastExec = 0; |
| var timer = null; |
| var diff; |
| var scope; |
| var args; |
| var debounceNextCall; |
| delay = delay || 0; |
| |
| function exec() { |
| lastExec = new Date().getTime(); |
| timer = null; |
| fn.apply(scope, args || []); |
| } |
| |
| var cb = function () { |
| var cbArgs = []; |
| |
| for (var _i = 0; _i < arguments.length; _i++) { |
| cbArgs[_i] = arguments[_i]; |
| } |
| |
| currCall = new Date().getTime(); |
| scope = this; |
| args = cbArgs; |
| var thisDelay = debounceNextCall || delay; |
| var thisDebounce = debounceNextCall || debounce; |
| debounceNextCall = null; |
| diff = currCall - (thisDebounce ? lastCall : lastExec) - thisDelay; |
| clearTimeout(timer); // Here we should make sure that: the `exec` SHOULD NOT be called later |
| // than a new call of `cb`, that is, preserving the command order. Consider |
| // calculating "scale rate" when roaming as an example. When a call of `cb` |
| // happens, either the `exec` is called dierectly, or the call is delayed. |
| // But the delayed call should never be later than next call of `cb`. Under |
| // this assurance, we can simply update view state each time `dispatchAction` |
| // triggered by user roaming, but not need to add extra code to avoid the |
| // state being "rolled-back". |
| |
| if (thisDebounce) { |
| timer = setTimeout(exec, thisDelay); |
| } else { |
| if (diff >= 0) { |
| exec(); |
| } else { |
| timer = setTimeout(exec, -diff); |
| } |
| } |
| |
| lastCall = currCall; |
| }; |
| /** |
| * Clear throttle. |
| * @public |
| */ |
| |
| |
| cb.clear = function () { |
| if (timer) { |
| clearTimeout(timer); |
| timer = null; |
| } |
| }; |
| /** |
| * Enable debounce once. |
| */ |
| |
| |
| cb.debounceNextCall = function (debounceDelay) { |
| debounceNextCall = debounceDelay; |
| }; |
| |
| return cb; |
| } |
| |
| var inner$3 = makeInner(); |
| var defaultStyleMappers = { |
| itemStyle: makeStyleMapper(ITEM_STYLE_KEY_MAP, true), |
| lineStyle: makeStyleMapper(LINE_STYLE_KEY_MAP, true) |
| }; |
| var defaultColorKey = { |
| lineStyle: 'stroke', |
| itemStyle: 'fill' |
| }; |
| |
| function getStyleMapper(seriesModel, stylePath) { |
| var styleMapper = seriesModel.visualStyleMapper || defaultStyleMappers[stylePath]; |
| |
| if (!styleMapper) { |
| console.warn("Unknown style type '" + stylePath + "'."); |
| return defaultStyleMappers.itemStyle; |
| } |
| |
| return styleMapper; |
| } |
| |
| function getDefaultColorKey(seriesModel, stylePath) { |
| // return defaultColorKey[stylePath] || |
| var colorKey = seriesModel.visualDrawType || defaultColorKey[stylePath]; |
| |
| if (!colorKey) { |
| console.warn("Unknown style type '" + stylePath + "'."); |
| return 'fill'; |
| } |
| |
| return colorKey; |
| } |
| |
| var seriesStyleTask = { |
| createOnAllSeries: true, |
| performRawSeries: true, |
| reset: function (seriesModel, ecModel) { |
| var data = seriesModel.getData(); |
| var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle |
| |
| var styleModel = seriesModel.getModel(stylePath); |
| var getStyle = getStyleMapper(seriesModel, stylePath); |
| var globalStyle = getStyle(styleModel); |
| var decalOption = styleModel.getShallow('decal'); |
| |
| if (decalOption) { |
| data.setVisual('decal', decalOption); |
| decalOption.dirty = true; |
| } // TODO |
| |
| |
| var colorKey = getDefaultColorKey(seriesModel, stylePath); |
| var color = globalStyle[colorKey]; // TODO style callback |
| |
| var colorCallback = isFunction(color) ? color : null; |
| var hasAutoColor = globalStyle.fill === 'auto' || globalStyle.stroke === 'auto'; // Get from color palette by default. |
| |
| if (!globalStyle[colorKey] || colorCallback || hasAutoColor) { |
| // Note: If some series has color specified (e.g., by itemStyle.color), we DO NOT |
| // make it effect palette. Because some scenarios users need to make some series |
| // transparent or as background, which should better not effect the palette. |
| var colorPalette = seriesModel.getColorFromPalette( // TODO series count changed. |
| seriesModel.name, null, ecModel.getSeriesCount()); |
| |
| if (!globalStyle[colorKey]) { |
| globalStyle[colorKey] = colorPalette; |
| data.setVisual('colorFromPalette', true); |
| } |
| |
| globalStyle.fill = globalStyle.fill === 'auto' || isFunction(globalStyle.fill) ? colorPalette : globalStyle.fill; |
| globalStyle.stroke = globalStyle.stroke === 'auto' || isFunction(globalStyle.stroke) ? colorPalette : globalStyle.stroke; |
| } |
| |
| data.setVisual('style', globalStyle); |
| data.setVisual('drawType', colorKey); // Only visible series has each data be visual encoded |
| |
| if (!ecModel.isSeriesFiltered(seriesModel) && colorCallback) { |
| data.setVisual('colorFromPalette', false); |
| return { |
| dataEach: function (data, idx) { |
| var dataParams = seriesModel.getDataParams(idx); |
| var itemStyle = extend({}, globalStyle); |
| itemStyle[colorKey] = colorCallback(dataParams); |
| data.setItemVisual(idx, 'style', itemStyle); |
| } |
| }; |
| } |
| } |
| }; |
| var sharedModel = new Model(); |
| var dataStyleTask = { |
| createOnAllSeries: true, |
| performRawSeries: true, |
| reset: function (seriesModel, ecModel) { |
| if (seriesModel.ignoreStyleOnData || ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; // Set in itemStyle |
| |
| var getStyle = getStyleMapper(seriesModel, stylePath); |
| var colorKey = data.getVisual('drawType'); |
| return { |
| dataEach: data.hasItemOption ? function (data, idx) { |
| // Not use getItemModel for performance considuration |
| var rawItem = data.getRawDataItem(idx); |
| |
| if (rawItem && rawItem[stylePath]) { |
| sharedModel.option = rawItem[stylePath]; |
| var style = getStyle(sharedModel); |
| var existsStyle = data.ensureUniqueItemVisual(idx, 'style'); |
| extend(existsStyle, style); |
| |
| if (sharedModel.option.decal) { |
| data.setItemVisual(idx, 'decal', sharedModel.option.decal); |
| sharedModel.option.decal.dirty = true; |
| } |
| |
| if (colorKey in style) { |
| data.setItemVisual(idx, 'colorFromPalette', false); |
| } |
| } |
| } : null |
| }; |
| } |
| }; // Pick color from palette for the data which has not been set with color yet. |
| // Note: do not support stream rendering. No such cases yet. |
| |
| var dataColorPaletteTask = { |
| performRawSeries: true, |
| overallReset: function (ecModel) { |
| // Each type of series uses one scope. |
| // Pie and funnel are using different scopes. |
| var paletteScopeGroupByType = createHashMap(); |
| ecModel.eachSeries(function (seriesModel) { |
| var colorBy = seriesModel.getColorBy(); |
| |
| if (seriesModel.isColorBySeries()) { |
| return; |
| } |
| |
| var key = seriesModel.type + '-' + colorBy; |
| var colorScope = paletteScopeGroupByType.get(key); |
| |
| if (!colorScope) { |
| colorScope = {}; |
| paletteScopeGroupByType.set(key, colorScope); |
| } |
| |
| inner$3(seriesModel).scope = colorScope; |
| }); |
| ecModel.eachSeries(function (seriesModel) { |
| if (seriesModel.isColorBySeries() || ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| var dataAll = seriesModel.getRawData(); |
| var idxMap = {}; |
| var data = seriesModel.getData(); |
| var colorScope = inner$3(seriesModel).scope; |
| var stylePath = seriesModel.visualStyleAccessPath || 'itemStyle'; |
| var colorKey = getDefaultColorKey(seriesModel, stylePath); |
| data.each(function (idx) { |
| var rawIdx = data.getRawIndex(idx); |
| idxMap[rawIdx] = idx; |
| }); // Iterate on data before filtered. To make sure color from palette can be |
| // Consistent when toggling legend. |
| |
| dataAll.each(function (rawIdx) { |
| var idx = idxMap[rawIdx]; |
| var fromPalette = data.getItemVisual(idx, 'colorFromPalette'); // Get color from palette for each data only when the color is inherited from series color, which is |
| // also picked from color palette. So following situation is not in the case: |
| // 1. series.itemStyle.color is set |
| // 2. color is encoded by visualMap |
| |
| if (fromPalette) { |
| var itemStyle = data.ensureUniqueItemVisual(idx, 'style'); |
| var name_1 = dataAll.getName(rawIdx) || rawIdx + ''; |
| var dataCount = dataAll.count(); |
| itemStyle[colorKey] = seriesModel.getColorFromPalette(name_1, colorScope, dataCount); |
| } |
| }); |
| }); |
| } |
| }; |
| |
| var PI$3 = Math.PI; |
| /** |
| * @param {module:echarts/ExtensionAPI} api |
| * @param {Object} [opts] |
| * @param {string} [opts.text] |
| * @param {string} [opts.color] |
| * @param {string} [opts.textColor] |
| * @return {module:zrender/Element} |
| */ |
| |
| function defaultLoading(api, opts) { |
| opts = opts || {}; |
| defaults(opts, { |
| text: 'loading', |
| textColor: '#000', |
| fontSize: 12, |
| fontWeight: 'normal', |
| fontStyle: 'normal', |
| fontFamily: 'sans-serif', |
| maskColor: 'rgba(255, 255, 255, 0.8)', |
| showSpinner: true, |
| color: '#5470c6', |
| spinnerRadius: 10, |
| lineWidth: 5, |
| zlevel: 0 |
| }); |
| var group = new Group(); |
| var mask = new Rect({ |
| style: { |
| fill: opts.maskColor |
| }, |
| zlevel: opts.zlevel, |
| z: 10000 |
| }); |
| group.add(mask); |
| var textContent = new ZRText({ |
| style: { |
| text: opts.text, |
| fill: opts.textColor, |
| fontSize: opts.fontSize, |
| fontWeight: opts.fontWeight, |
| fontStyle: opts.fontStyle, |
| fontFamily: opts.fontFamily |
| }, |
| zlevel: opts.zlevel, |
| z: 10001 |
| }); |
| var labelRect = new Rect({ |
| style: { |
| fill: 'none' |
| }, |
| textContent: textContent, |
| textConfig: { |
| position: 'right', |
| distance: 10 |
| }, |
| zlevel: opts.zlevel, |
| z: 10001 |
| }); |
| group.add(labelRect); |
| var arc; |
| |
| if (opts.showSpinner) { |
| arc = new Arc({ |
| shape: { |
| startAngle: -PI$3 / 2, |
| endAngle: -PI$3 / 2 + 0.1, |
| r: opts.spinnerRadius |
| }, |
| style: { |
| stroke: opts.color, |
| lineCap: 'round', |
| lineWidth: opts.lineWidth |
| }, |
| zlevel: opts.zlevel, |
| z: 10001 |
| }); |
| arc.animateShape(true).when(1000, { |
| endAngle: PI$3 * 3 / 2 |
| }).start('circularInOut'); |
| arc.animateShape(true).when(1000, { |
| startAngle: PI$3 * 3 / 2 |
| }).delay(300).start('circularInOut'); |
| group.add(arc); |
| } // Inject resize |
| |
| |
| group.resize = function () { |
| var textWidth = textContent.getBoundingRect().width; |
| var r = opts.showSpinner ? opts.spinnerRadius : 0; // cx = (containerWidth - arcDiameter - textDistance - textWidth) / 2 |
| // textDistance needs to be calculated when both animation and text exist |
| |
| var cx = (api.getWidth() - r * 2 - (opts.showSpinner && textWidth ? 10 : 0) - textWidth) / 2 - (opts.showSpinner && textWidth ? 0 : 5 + textWidth / 2) // only show the text |
| + (opts.showSpinner ? 0 : textWidth / 2) // only show the spinner |
| + (textWidth ? 0 : r); |
| var cy = api.getHeight() / 2; |
| opts.showSpinner && arc.setShape({ |
| cx: cx, |
| cy: cy |
| }); |
| labelRect.setShape({ |
| x: cx - r, |
| y: cy - r, |
| width: r * 2, |
| height: r * 2 |
| }); |
| mask.setShape({ |
| x: 0, |
| y: 0, |
| width: api.getWidth(), |
| height: api.getHeight() |
| }); |
| }; |
| |
| group.resize(); |
| return group; |
| } |
| |
| var Scheduler = |
| /** @class */ |
| function () { |
| function Scheduler(ecInstance, api, dataProcessorHandlers, visualHandlers) { |
| // key: handlerUID |
| this._stageTaskMap = createHashMap(); |
| this.ecInstance = ecInstance; |
| this.api = api; // Fix current processors in case that in some rear cases that |
| // processors might be registered after echarts instance created. |
| // Register processors incrementally for a echarts instance is |
| // not supported by this stream architecture. |
| |
| dataProcessorHandlers = this._dataProcessorHandlers = dataProcessorHandlers.slice(); |
| visualHandlers = this._visualHandlers = visualHandlers.slice(); |
| this._allHandlers = dataProcessorHandlers.concat(visualHandlers); |
| } |
| |
| Scheduler.prototype.restoreData = function (ecModel, payload) { |
| // TODO: Only restore needed series and components, but not all components. |
| // Currently `restoreData` of all of the series and component will be called. |
| // But some independent components like `title`, `legend`, `graphic`, `toolbox`, |
| // `tooltip`, `axisPointer`, etc, do not need series refresh when `setOption`, |
| // and some components like coordinate system, axes, dataZoom, visualMap only |
| // need their target series refresh. |
| // (1) If we are implementing this feature some day, we should consider these cases: |
| // if a data processor depends on a component (e.g., dataZoomProcessor depends |
| // on the settings of `dataZoom`), it should be re-performed if the component |
| // is modified by `setOption`. |
| // (2) If a processor depends on sevral series, speicified by its `getTargetSeries`, |
| // it should be re-performed when the result array of `getTargetSeries` changed. |
| // We use `dependencies` to cover these issues. |
| // (3) How to update target series when coordinate system related components modified. |
| // TODO: simply the dirty mechanism? Check whether only the case here can set tasks dirty, |
| // and this case all of the tasks will be set as dirty. |
| ecModel.restoreData(payload); // Theoretically an overall task not only depends on each of its target series, but also |
| // depends on all of the series. |
| // The overall task is not in pipeline, and `ecModel.restoreData` only set pipeline tasks |
| // dirty. If `getTargetSeries` of an overall task returns nothing, we should also ensure |
| // that the overall task is set as dirty and to be performed, otherwise it probably cause |
| // state chaos. So we have to set dirty of all of the overall tasks manually, otherwise it |
| // probably cause state chaos (consider `dataZoomProcessor`). |
| |
| this._stageTaskMap.each(function (taskRecord) { |
| var overallTask = taskRecord.overallTask; |
| overallTask && overallTask.dirty(); |
| }); |
| }; // If seriesModel provided, incremental threshold is check by series data. |
| |
| |
| Scheduler.prototype.getPerformArgs = function (task, isBlock) { |
| // For overall task |
| if (!task.__pipeline) { |
| return; |
| } |
| |
| var pipeline = this._pipelineMap.get(task.__pipeline.id); |
| |
| var pCtx = pipeline.context; |
| var incremental = !isBlock && pipeline.progressiveEnabled && (!pCtx || pCtx.progressiveRender) && task.__idxInPipeline > pipeline.blockIndex; |
| var step = incremental ? pipeline.step : null; |
| var modDataCount = pCtx && pCtx.modDataCount; |
| var modBy = modDataCount != null ? Math.ceil(modDataCount / step) : null; |
| return { |
| step: step, |
| modBy: modBy, |
| modDataCount: modDataCount |
| }; |
| }; |
| |
| Scheduler.prototype.getPipeline = function (pipelineId) { |
| return this._pipelineMap.get(pipelineId); |
| }; |
| /** |
| * Current, progressive rendering starts from visual and layout. |
| * Always detect render mode in the same stage, avoiding that incorrect |
| * detection caused by data filtering. |
| * Caution: |
| * `updateStreamModes` use `seriesModel.getData()`. |
| */ |
| |
| |
| Scheduler.prototype.updateStreamModes = function (seriesModel, view) { |
| var pipeline = this._pipelineMap.get(seriesModel.uid); |
| |
| var data = seriesModel.getData(); |
| var dataLen = data.count(); // `progressiveRender` means that can render progressively in each |
| // animation frame. Note that some types of series do not provide |
| // `view.incrementalPrepareRender` but support `chart.appendData`. We |
| // use the term `incremental` but not `progressive` to describe the |
| // case that `chart.appendData`. |
| |
| var progressiveRender = pipeline.progressiveEnabled && view.incrementalPrepareRender && dataLen >= pipeline.threshold; |
| var large = seriesModel.get('large') && dataLen >= seriesModel.get('largeThreshold'); // TODO: modDataCount should not updated if `appendData`, otherwise cause whole repaint. |
| // see `test/candlestick-large3.html` |
| |
| var modDataCount = seriesModel.get('progressiveChunkMode') === 'mod' ? dataLen : null; |
| seriesModel.pipelineContext = pipeline.context = { |
| progressiveRender: progressiveRender, |
| modDataCount: modDataCount, |
| large: large |
| }; |
| }; |
| |
| Scheduler.prototype.restorePipelines = function (ecModel) { |
| var scheduler = this; |
| var pipelineMap = scheduler._pipelineMap = createHashMap(); |
| ecModel.eachSeries(function (seriesModel) { |
| var progressive = seriesModel.getProgressive(); |
| var pipelineId = seriesModel.uid; |
| pipelineMap.set(pipelineId, { |
| id: pipelineId, |
| head: null, |
| tail: null, |
| threshold: seriesModel.getProgressiveThreshold(), |
| progressiveEnabled: progressive && !(seriesModel.preventIncremental && seriesModel.preventIncremental()), |
| blockIndex: -1, |
| step: Math.round(progressive || 700), |
| count: 0 |
| }); |
| |
| scheduler._pipe(seriesModel, seriesModel.dataTask); |
| }); |
| }; |
| |
| Scheduler.prototype.prepareStageTasks = function () { |
| var stageTaskMap = this._stageTaskMap; |
| var ecModel = this.api.getModel(); |
| var api = this.api; |
| each(this._allHandlers, function (handler) { |
| var record = stageTaskMap.get(handler.uid) || stageTaskMap.set(handler.uid, {}); |
| var errMsg = ''; |
| |
| if ("development" !== 'production') { |
| // Currently do not need to support to sepecify them both. |
| errMsg = '"reset" and "overallReset" must not be both specified.'; |
| } |
| |
| assert(!(handler.reset && handler.overallReset), errMsg); |
| handler.reset && this._createSeriesStageTask(handler, record, ecModel, api); |
| handler.overallReset && this._createOverallStageTask(handler, record, ecModel, api); |
| }, this); |
| }; |
| |
| Scheduler.prototype.prepareView = function (view, model, ecModel, api) { |
| var renderTask = view.renderTask; |
| var context = renderTask.context; |
| context.model = model; |
| context.ecModel = ecModel; |
| context.api = api; |
| renderTask.__block = !view.incrementalPrepareRender; |
| |
| this._pipe(model, renderTask); |
| }; |
| |
| Scheduler.prototype.performDataProcessorTasks = function (ecModel, payload) { |
| // If we do not use `block` here, it should be considered when to update modes. |
| this._performStageTasks(this._dataProcessorHandlers, ecModel, payload, { |
| block: true |
| }); |
| }; |
| |
| Scheduler.prototype.performVisualTasks = function (ecModel, payload, opt) { |
| this._performStageTasks(this._visualHandlers, ecModel, payload, opt); |
| }; |
| |
| Scheduler.prototype._performStageTasks = function (stageHandlers, ecModel, payload, opt) { |
| opt = opt || {}; |
| var unfinished = false; |
| var scheduler = this; |
| each(stageHandlers, function (stageHandler, idx) { |
| if (opt.visualType && opt.visualType !== stageHandler.visualType) { |
| return; |
| } |
| |
| var stageHandlerRecord = scheduler._stageTaskMap.get(stageHandler.uid); |
| |
| var seriesTaskMap = stageHandlerRecord.seriesTaskMap; |
| var overallTask = stageHandlerRecord.overallTask; |
| |
| if (overallTask) { |
| var overallNeedDirty_1; |
| var agentStubMap = overallTask.agentStubMap; |
| agentStubMap.each(function (stub) { |
| if (needSetDirty(opt, stub)) { |
| stub.dirty(); |
| overallNeedDirty_1 = true; |
| } |
| }); |
| overallNeedDirty_1 && overallTask.dirty(); |
| scheduler.updatePayload(overallTask, payload); |
| var performArgs_1 = scheduler.getPerformArgs(overallTask, opt.block); // Execute stubs firstly, which may set the overall task dirty, |
| // then execute the overall task. And stub will call seriesModel.setData, |
| // which ensures that in the overallTask seriesModel.getData() will not |
| // return incorrect data. |
| |
| agentStubMap.each(function (stub) { |
| stub.perform(performArgs_1); |
| }); |
| |
| if (overallTask.perform(performArgs_1)) { |
| unfinished = true; |
| } |
| } else if (seriesTaskMap) { |
| seriesTaskMap.each(function (task, pipelineId) { |
| if (needSetDirty(opt, task)) { |
| task.dirty(); |
| } |
| |
| var performArgs = scheduler.getPerformArgs(task, opt.block); // FIXME |
| // if intending to declare `performRawSeries` in handlers, only |
| // stream-independent (specifically, data item independent) operations can be |
| // performed. Because if a series is filtered, most of the tasks will not |
| // be performed. A stream-dependent operation probably cause wrong biz logic. |
| // Perhaps we should not provide a separate callback for this case instead |
| // of providing the config `performRawSeries`. The stream-dependent operations |
| // and stream-independent operations should better not be mixed. |
| |
| performArgs.skip = !stageHandler.performRawSeries && ecModel.isSeriesFiltered(task.context.model); |
| scheduler.updatePayload(task, payload); |
| |
| if (task.perform(performArgs)) { |
| unfinished = true; |
| } |
| }); |
| } |
| }); |
| |
| function needSetDirty(opt, task) { |
| return opt.setDirty && (!opt.dirtyMap || opt.dirtyMap.get(task.__pipeline.id)); |
| } |
| |
| this.unfinished = unfinished || this.unfinished; |
| }; |
| |
| Scheduler.prototype.performSeriesTasks = function (ecModel) { |
| var unfinished; |
| ecModel.eachSeries(function (seriesModel) { |
| // Progress to the end for dataInit and dataRestore. |
| unfinished = seriesModel.dataTask.perform() || unfinished; |
| }); |
| this.unfinished = unfinished || this.unfinished; |
| }; |
| |
| Scheduler.prototype.plan = function () { |
| // Travel pipelines, check block. |
| this._pipelineMap.each(function (pipeline) { |
| var task = pipeline.tail; |
| |
| do { |
| if (task.__block) { |
| pipeline.blockIndex = task.__idxInPipeline; |
| break; |
| } |
| |
| task = task.getUpstream(); |
| } while (task); |
| }); |
| }; |
| |
| Scheduler.prototype.updatePayload = function (task, payload) { |
| payload !== 'remain' && (task.context.payload = payload); |
| }; |
| |
| Scheduler.prototype._createSeriesStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) { |
| var scheduler = this; |
| var oldSeriesTaskMap = stageHandlerRecord.seriesTaskMap; // The count of stages are totally about only several dozen, so |
| // do not need to reuse the map. |
| |
| var newSeriesTaskMap = stageHandlerRecord.seriesTaskMap = createHashMap(); |
| var seriesType = stageHandler.seriesType; |
| var getTargetSeries = stageHandler.getTargetSeries; // If a stageHandler should cover all series, `createOnAllSeries` should be declared mandatorily, |
| // to avoid some typo or abuse. Otherwise if an extension do not specify a `seriesType`, |
| // it works but it may cause other irrelevant charts blocked. |
| |
| if (stageHandler.createOnAllSeries) { |
| ecModel.eachRawSeries(create); |
| } else if (seriesType) { |
| ecModel.eachRawSeriesByType(seriesType, create); |
| } else if (getTargetSeries) { |
| getTargetSeries(ecModel, api).each(create); |
| } |
| |
| function create(seriesModel) { |
| var pipelineId = seriesModel.uid; // Init tasks for each seriesModel only once. |
| // Reuse original task instance. |
| |
| var task = newSeriesTaskMap.set(pipelineId, oldSeriesTaskMap && oldSeriesTaskMap.get(pipelineId) || createTask({ |
| plan: seriesTaskPlan, |
| reset: seriesTaskReset, |
| count: seriesTaskCount |
| })); |
| task.context = { |
| model: seriesModel, |
| ecModel: ecModel, |
| api: api, |
| // PENDING: `useClearVisual` not used? |
| useClearVisual: stageHandler.isVisual && !stageHandler.isLayout, |
| plan: stageHandler.plan, |
| reset: stageHandler.reset, |
| scheduler: scheduler |
| }; |
| |
| scheduler._pipe(seriesModel, task); |
| } |
| }; |
| |
| Scheduler.prototype._createOverallStageTask = function (stageHandler, stageHandlerRecord, ecModel, api) { |
| var scheduler = this; |
| var overallTask = stageHandlerRecord.overallTask = stageHandlerRecord.overallTask // For overall task, the function only be called on reset stage. |
| || createTask({ |
| reset: overallTaskReset |
| }); |
| overallTask.context = { |
| ecModel: ecModel, |
| api: api, |
| overallReset: stageHandler.overallReset, |
| scheduler: scheduler |
| }; |
| var oldAgentStubMap = overallTask.agentStubMap; // The count of stages are totally about only several dozen, so |
| // do not need to reuse the map. |
| |
| var newAgentStubMap = overallTask.agentStubMap = createHashMap(); |
| var seriesType = stageHandler.seriesType; |
| var getTargetSeries = stageHandler.getTargetSeries; |
| var overallProgress = true; |
| var shouldOverallTaskDirty = false; // FIXME:TS never used, so comment it |
| // let modifyOutputEnd = stageHandler.modifyOutputEnd; |
| // An overall task with seriesType detected or has `getTargetSeries`, we add |
| // stub in each pipelines, it will set the overall task dirty when the pipeline |
| // progress. Moreover, to avoid call the overall task each frame (too frequent), |
| // we set the pipeline block. |
| |
| var errMsg = ''; |
| |
| if ("development" !== 'production') { |
| errMsg = '"createOnAllSeries" is not supported for "overallReset", ' + 'because it will block all streams.'; |
| } |
| |
| assert(!stageHandler.createOnAllSeries, errMsg); |
| |
| if (seriesType) { |
| ecModel.eachRawSeriesByType(seriesType, createStub); |
| } else if (getTargetSeries) { |
| getTargetSeries(ecModel, api).each(createStub); |
| } // Otherwise, (usually it is legacy case), the overall task will only be |
| // executed when upstream is dirty. Otherwise the progressive rendering of all |
| // pipelines will be disabled unexpectedly. But it still needs stubs to receive |
| // dirty info from upstream. |
| else { |
| overallProgress = false; |
| each(ecModel.getSeries(), createStub); |
| } |
| |
| function createStub(seriesModel) { |
| var pipelineId = seriesModel.uid; |
| var stub = newAgentStubMap.set(pipelineId, oldAgentStubMap && oldAgentStubMap.get(pipelineId) || ( // When the result of `getTargetSeries` changed, the overallTask |
| // should be set as dirty and re-performed. |
| shouldOverallTaskDirty = true, createTask({ |
| reset: stubReset, |
| onDirty: stubOnDirty |
| }))); |
| stub.context = { |
| model: seriesModel, |
| overallProgress: overallProgress // FIXME:TS never used, so comment it |
| // modifyOutputEnd: modifyOutputEnd |
| |
| }; |
| stub.agent = overallTask; |
| stub.__block = overallProgress; |
| |
| scheduler._pipe(seriesModel, stub); |
| } |
| |
| if (shouldOverallTaskDirty) { |
| overallTask.dirty(); |
| } |
| }; |
| |
| Scheduler.prototype._pipe = function (seriesModel, task) { |
| var pipelineId = seriesModel.uid; |
| |
| var pipeline = this._pipelineMap.get(pipelineId); |
| |
| !pipeline.head && (pipeline.head = task); |
| pipeline.tail && pipeline.tail.pipe(task); |
| pipeline.tail = task; |
| task.__idxInPipeline = pipeline.count++; |
| task.__pipeline = pipeline; |
| }; |
| |
| Scheduler.wrapStageHandler = function (stageHandler, visualType) { |
| if (isFunction(stageHandler)) { |
| stageHandler = { |
| overallReset: stageHandler, |
| seriesType: detectSeriseType(stageHandler) |
| }; |
| } |
| |
| stageHandler.uid = getUID('stageHandler'); |
| visualType && (stageHandler.visualType = visualType); |
| return stageHandler; |
| }; |
| return Scheduler; |
| }(); |
| |
| function overallTaskReset(context) { |
| context.overallReset(context.ecModel, context.api, context.payload); |
| } |
| |
| function stubReset(context) { |
| return context.overallProgress && stubProgress; |
| } |
| |
| function stubProgress() { |
| this.agent.dirty(); |
| this.getDownstream().dirty(); |
| } |
| |
| function stubOnDirty() { |
| this.agent && this.agent.dirty(); |
| } |
| |
| function seriesTaskPlan(context) { |
| return context.plan ? context.plan(context.model, context.ecModel, context.api, context.payload) : null; |
| } |
| |
| function seriesTaskReset(context) { |
| if (context.useClearVisual) { |
| context.data.clearAllVisual(); |
| } |
| |
| var resetDefines = context.resetDefines = normalizeToArray(context.reset(context.model, context.ecModel, context.api, context.payload)); |
| return resetDefines.length > 1 ? map(resetDefines, function (v, idx) { |
| return makeSeriesTaskProgress(idx); |
| }) : singleSeriesTaskProgress; |
| } |
| |
| var singleSeriesTaskProgress = makeSeriesTaskProgress(0); |
| |
| function makeSeriesTaskProgress(resetDefineIdx) { |
| return function (params, context) { |
| var data = context.data; |
| var resetDefine = context.resetDefines[resetDefineIdx]; |
| |
| if (resetDefine && resetDefine.dataEach) { |
| for (var i = params.start; i < params.end; i++) { |
| resetDefine.dataEach(data, i); |
| } |
| } else if (resetDefine && resetDefine.progress) { |
| resetDefine.progress(params, data); |
| } |
| }; |
| } |
| |
| function seriesTaskCount(context) { |
| return context.data.count(); |
| } |
| /** |
| * Only some legacy stage handlers (usually in echarts extensions) are pure function. |
| * To ensure that they can work normally, they should work in block mode, that is, |
| * they should not be started util the previous tasks finished. So they cause the |
| * progressive rendering disabled. We try to detect the series type, to narrow down |
| * the block range to only the series type they concern, but not all series. |
| */ |
| |
| |
| function detectSeriseType(legacyFunc) { |
| seriesType = null; |
| |
| try { |
| // Assume there is no async when calling `eachSeriesByType`. |
| legacyFunc(ecModelMock, apiMock); |
| } catch (e) {} |
| |
| return seriesType; |
| } |
| |
| var ecModelMock = {}; |
| var apiMock = {}; |
| var seriesType; |
| mockMethods(ecModelMock, GlobalModel); |
| mockMethods(apiMock, ExtensionAPI); |
| |
| ecModelMock.eachSeriesByType = ecModelMock.eachRawSeriesByType = function (type) { |
| seriesType = type; |
| }; |
| |
| ecModelMock.eachComponent = function (cond) { |
| if (cond.mainType === 'series' && cond.subType) { |
| seriesType = cond.subType; |
| } |
| }; |
| |
| function mockMethods(target, Clz) { |
| /* eslint-disable */ |
| for (var name_1 in Clz.prototype) { |
| // Do not use hasOwnProperty |
| target[name_1] = noop; |
| } |
| /* eslint-enable */ |
| |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var colorAll = ['#37A2DA', '#32C5E9', '#67E0E3', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#E062AE', '#E690D1', '#e7bcf3', '#9d96f5', '#8378EA', '#96BFFF']; |
| var lightTheme = { |
| color: colorAll, |
| colorLayer: [['#37A2DA', '#ffd85c', '#fd7b5f'], ['#37A2DA', '#67E0E3', '#FFDB5C', '#ff9f7f', '#E062AE', '#9d96f5'], ['#37A2DA', '#32C5E9', '#9FE6B8', '#FFDB5C', '#ff9f7f', '#fb7293', '#e7bcf3', '#8378EA', '#96BFFF'], colorAll] |
| }; |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var contrastColor = '#B9B8CE'; |
| var backgroundColor = '#100C2A'; |
| |
| var axisCommon = function () { |
| return { |
| axisLine: { |
| lineStyle: { |
| color: contrastColor |
| } |
| }, |
| splitLine: { |
| lineStyle: { |
| color: '#484753' |
| } |
| }, |
| splitArea: { |
| areaStyle: { |
| color: ['rgba(255,255,255,0.02)', 'rgba(255,255,255,0.05)'] |
| } |
| }, |
| minorSplitLine: { |
| lineStyle: { |
| color: '#20203B' |
| } |
| } |
| }; |
| }; |
| |
| var colorPalette = ['#4992ff', '#7cffb2', '#fddd60', '#ff6e76', '#58d9f9', '#05c091', '#ff8a45', '#8d48e3', '#dd79ff']; |
| var theme = { |
| darkMode: true, |
| color: colorPalette, |
| backgroundColor: backgroundColor, |
| axisPointer: { |
| lineStyle: { |
| color: '#817f91' |
| }, |
| crossStyle: { |
| color: '#817f91' |
| }, |
| label: { |
| // TODO Contrast of label backgorundColor |
| color: '#fff' |
| } |
| }, |
| legend: { |
| textStyle: { |
| color: contrastColor |
| } |
| }, |
| textStyle: { |
| color: contrastColor |
| }, |
| title: { |
| textStyle: { |
| color: '#EEF1FA' |
| }, |
| subtextStyle: { |
| color: '#B9B8CE' |
| } |
| }, |
| toolbox: { |
| iconStyle: { |
| borderColor: contrastColor |
| } |
| }, |
| dataZoom: { |
| borderColor: '#71708A', |
| textStyle: { |
| color: contrastColor |
| }, |
| brushStyle: { |
| color: 'rgba(135,163,206,0.3)' |
| }, |
| handleStyle: { |
| color: '#353450', |
| borderColor: '#C5CBE3' |
| }, |
| moveHandleStyle: { |
| color: '#B0B6C3', |
| opacity: 0.3 |
| }, |
| fillerColor: 'rgba(135,163,206,0.2)', |
| emphasis: { |
| handleStyle: { |
| borderColor: '#91B7F2', |
| color: '#4D587D' |
| }, |
| moveHandleStyle: { |
| color: '#636D9A', |
| opacity: 0.7 |
| } |
| }, |
| dataBackground: { |
| lineStyle: { |
| color: '#71708A', |
| width: 1 |
| }, |
| areaStyle: { |
| color: '#71708A' |
| } |
| }, |
| selectedDataBackground: { |
| lineStyle: { |
| color: '#87A3CE' |
| }, |
| areaStyle: { |
| color: '#87A3CE' |
| } |
| } |
| }, |
| visualMap: { |
| textStyle: { |
| color: contrastColor |
| } |
| }, |
| timeline: { |
| lineStyle: { |
| color: contrastColor |
| }, |
| label: { |
| color: contrastColor |
| }, |
| controlStyle: { |
| color: contrastColor, |
| borderColor: contrastColor |
| } |
| }, |
| calendar: { |
| itemStyle: { |
| color: backgroundColor |
| }, |
| dayLabel: { |
| color: contrastColor |
| }, |
| monthLabel: { |
| color: contrastColor |
| }, |
| yearLabel: { |
| color: contrastColor |
| } |
| }, |
| timeAxis: axisCommon(), |
| logAxis: axisCommon(), |
| valueAxis: axisCommon(), |
| categoryAxis: axisCommon(), |
| line: { |
| symbol: 'circle' |
| }, |
| graph: { |
| color: colorPalette |
| }, |
| gauge: { |
| title: { |
| color: contrastColor |
| }, |
| axisLine: { |
| lineStyle: { |
| color: [[1, 'rgba(207,212,219,0.2)']] |
| } |
| }, |
| axisLabel: { |
| color: contrastColor |
| }, |
| detail: { |
| color: '#EEF1FA' |
| } |
| }, |
| candlestick: { |
| itemStyle: { |
| color: '#f64e56', |
| color0: '#54ea92', |
| borderColor: '#f64e56', |
| borderColor0: '#54ea92' // borderColor: '#ca2824', |
| // borderColor0: '#09a443' |
| |
| } |
| } |
| }; |
| theme.categoryAxis.splitLine.show = false; |
| |
| /** |
| * Usage of query: |
| * `chart.on('click', query, handler);` |
| * The `query` can be: |
| * + The component type query string, only `mainType` or `mainType.subType`, |
| * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'. |
| * + The component query object, like: |
| * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`, |
| * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`. |
| * + The data query object, like: |
| * `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`. |
| * + The other query object (cmponent customized query), like: |
| * `{element: 'some'}` (only available in custom series). |
| * |
| * Caveat: If a prop in the `query` object is `null/undefined`, it is the |
| * same as there is no such prop in the `query` object. |
| */ |
| |
| var ECEventProcessor = |
| /** @class */ |
| function () { |
| function ECEventProcessor() {} |
| |
| ECEventProcessor.prototype.normalizeQuery = function (query) { |
| var cptQuery = {}; |
| var dataQuery = {}; |
| var otherQuery = {}; // `query` is `mainType` or `mainType.subType` of component. |
| |
| if (isString(query)) { |
| var condCptType = parseClassType(query); // `.main` and `.sub` may be ''. |
| |
| cptQuery.mainType = condCptType.main || null; |
| cptQuery.subType = condCptType.sub || null; |
| } // `query` is an object, convert to {mainType, index, name, id}. |
| else { |
| // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved, |
| // can not be used in `compomentModel.filterForExposedEvent`. |
| var suffixes_1 = ['Index', 'Name', 'Id']; |
| var dataKeys_1 = { |
| name: 1, |
| dataIndex: 1, |
| dataType: 1 |
| }; |
| each(query, function (val, key) { |
| var reserved = false; |
| |
| for (var i = 0; i < suffixes_1.length; i++) { |
| var propSuffix = suffixes_1[i]; |
| var suffixPos = key.lastIndexOf(propSuffix); |
| |
| if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) { |
| var mainType = key.slice(0, suffixPos); // Consider `dataIndex`. |
| |
| if (mainType !== 'data') { |
| cptQuery.mainType = mainType; |
| cptQuery[propSuffix.toLowerCase()] = val; |
| reserved = true; |
| } |
| } |
| } |
| |
| if (dataKeys_1.hasOwnProperty(key)) { |
| dataQuery[key] = val; |
| reserved = true; |
| } |
| |
| if (!reserved) { |
| otherQuery[key] = val; |
| } |
| }); |
| } |
| |
| return { |
| cptQuery: cptQuery, |
| dataQuery: dataQuery, |
| otherQuery: otherQuery |
| }; |
| }; |
| |
| ECEventProcessor.prototype.filter = function (eventType, query) { |
| // They should be assigned before each trigger call. |
| var eventInfo = this.eventInfo; |
| |
| if (!eventInfo) { |
| return true; |
| } |
| |
| var targetEl = eventInfo.targetEl; |
| var packedEvent = eventInfo.packedEvent; |
| var model = eventInfo.model; |
| var view = eventInfo.view; // For event like 'globalout'. |
| |
| if (!model || !view) { |
| return true; |
| } |
| |
| var cptQuery = query.cptQuery; |
| var dataQuery = query.dataQuery; |
| return check(cptQuery, model, 'mainType') && check(cptQuery, model, 'subType') && check(cptQuery, model, 'index', 'componentIndex') && check(cptQuery, model, 'name') && check(cptQuery, model, 'id') && check(dataQuery, packedEvent, 'name') && check(dataQuery, packedEvent, 'dataIndex') && check(dataQuery, packedEvent, 'dataType') && (!view.filterForExposedEvent || view.filterForExposedEvent(eventType, query.otherQuery, targetEl, packedEvent)); |
| |
| function check(query, host, prop, propOnHost) { |
| return query[prop] == null || host[propOnHost || prop] === query[prop]; |
| } |
| }; |
| |
| ECEventProcessor.prototype.afterTrigger = function () { |
| // Make sure the eventInfo won't be used in next trigger. |
| this.eventInfo = null; |
| }; |
| |
| return ECEventProcessor; |
| }(); |
| |
| var SYMBOL_PROPS_WITH_CB = ['symbol', 'symbolSize', 'symbolRotate', 'symbolOffset']; |
| var SYMBOL_PROPS = SYMBOL_PROPS_WITH_CB.concat(['symbolKeepAspect']); // Encoding visual for all series include which is filtered for legend drawing |
| |
| var seriesSymbolTask = { |
| createOnAllSeries: true, |
| // For legend. |
| performRawSeries: true, |
| reset: function (seriesModel, ecModel) { |
| var data = seriesModel.getData(); |
| |
| if (seriesModel.legendIcon) { |
| data.setVisual('legendIcon', seriesModel.legendIcon); |
| } |
| |
| if (!seriesModel.hasSymbolVisual) { |
| return; |
| } |
| |
| var symbolOptions = {}; |
| var symbolOptionsCb = {}; |
| var hasCallback = false; |
| |
| for (var i = 0; i < SYMBOL_PROPS_WITH_CB.length; i++) { |
| var symbolPropName = SYMBOL_PROPS_WITH_CB[i]; |
| var val = seriesModel.get(symbolPropName); |
| |
| if (isFunction(val)) { |
| hasCallback = true; |
| symbolOptionsCb[symbolPropName] = val; |
| } else { |
| symbolOptions[symbolPropName] = val; |
| } |
| } |
| |
| symbolOptions.symbol = symbolOptions.symbol || seriesModel.defaultSymbol; |
| data.setVisual(extend({ |
| legendIcon: seriesModel.legendIcon || symbolOptions.symbol, |
| symbolKeepAspect: seriesModel.get('symbolKeepAspect') |
| }, symbolOptions)); // Only visible series has each data be visual encoded |
| |
| if (ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| var symbolPropsCb = keys(symbolOptionsCb); |
| |
| function dataEach(data, idx) { |
| var rawValue = seriesModel.getRawValue(idx); |
| var params = seriesModel.getDataParams(idx); |
| |
| for (var i = 0; i < symbolPropsCb.length; i++) { |
| var symbolPropName = symbolPropsCb[i]; |
| data.setItemVisual(idx, symbolPropName, symbolOptionsCb[symbolPropName](rawValue, params)); |
| } |
| } |
| |
| return { |
| dataEach: hasCallback ? dataEach : null |
| }; |
| } |
| }; |
| var dataSymbolTask = { |
| createOnAllSeries: true, |
| // For legend. |
| performRawSeries: true, |
| reset: function (seriesModel, ecModel) { |
| if (!seriesModel.hasSymbolVisual) { |
| return; |
| } // Only visible series has each data be visual encoded |
| |
| |
| if (ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| |
| function dataEach(data, idx) { |
| var itemModel = data.getItemModel(idx); |
| |
| for (var i = 0; i < SYMBOL_PROPS.length; i++) { |
| var symbolPropName = SYMBOL_PROPS[i]; |
| var val = itemModel.getShallow(symbolPropName, true); |
| |
| if (val != null) { |
| data.setItemVisual(idx, symbolPropName, val); |
| } |
| } |
| } |
| |
| return { |
| dataEach: data.hasItemOption ? dataEach : null |
| }; |
| } |
| }; |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| function getItemVisualFromData(data, dataIndex, key) { |
| switch (key) { |
| case 'color': |
| var style = data.getItemVisual(dataIndex, 'style'); |
| return style[data.getVisual('drawType')]; |
| |
| case 'opacity': |
| return data.getItemVisual(dataIndex, 'style').opacity; |
| |
| case 'symbol': |
| case 'symbolSize': |
| case 'liftZ': |
| return data.getItemVisual(dataIndex, key); |
| |
| default: |
| if ("development" !== 'production') { |
| console.warn("Unknown visual type " + key); |
| } |
| |
| } |
| } |
| function getVisualFromData(data, key) { |
| switch (key) { |
| case 'color': |
| var style = data.getVisual('style'); |
| return style[data.getVisual('drawType')]; |
| |
| case 'opacity': |
| return data.getVisual('style').opacity; |
| |
| case 'symbol': |
| case 'symbolSize': |
| case 'liftZ': |
| return data.getVisual(key); |
| |
| default: |
| if ("development" !== 'production') { |
| console.warn("Unknown visual type " + key); |
| } |
| |
| } |
| } |
| |
| // Inlucdes: pieSelect, pieUnSelect, pieToggleSelect, mapSelect, mapUnSelect, mapToggleSelect |
| |
| function createLegacyDataSelectAction(seriesType, ecRegisterAction) { |
| function getSeriesIndices(ecModel, payload) { |
| var seriesIndices = []; |
| ecModel.eachComponent({ |
| mainType: 'series', |
| subType: seriesType, |
| query: payload |
| }, function (seriesModel) { |
| seriesIndices.push(seriesModel.seriesIndex); |
| }); |
| return seriesIndices; |
| } |
| |
| each([[seriesType + 'ToggleSelect', 'toggleSelect'], [seriesType + 'Select', 'select'], [seriesType + 'UnSelect', 'unselect']], function (eventsMap) { |
| ecRegisterAction(eventsMap[0], function (payload, ecModel, api) { |
| payload = extend({}, payload); |
| |
| if ("development" !== 'production') { |
| deprecateReplaceLog(payload.type, eventsMap[1]); |
| } |
| |
| api.dispatchAction(extend(payload, { |
| type: eventsMap[1], |
| seriesIndex: getSeriesIndices(ecModel, payload) |
| })); |
| }); |
| }); |
| } |
| |
| function handleSeriesLegacySelectEvents(type, eventPostfix, ecIns, ecModel, payload) { |
| var legacyEventName = type + eventPostfix; |
| |
| if (!ecIns.isSilent(legacyEventName)) { |
| if ("development" !== 'production') { |
| deprecateLog("event " + legacyEventName + " is deprecated."); |
| } |
| |
| ecModel.eachComponent({ |
| mainType: 'series', |
| subType: 'pie' |
| }, function (seriesModel) { |
| var seriesIndex = seriesModel.seriesIndex; |
| var selectedMap = seriesModel.option.selectedMap; |
| var selected = payload.selected; |
| |
| for (var i = 0; i < selected.length; i++) { |
| if (selected[i].seriesIndex === seriesIndex) { |
| var data = seriesModel.getData(); |
| var dataIndex = queryDataIndex(data, payload.fromActionPayload); |
| ecIns.trigger(legacyEventName, { |
| type: legacyEventName, |
| seriesId: seriesModel.id, |
| name: isArray(dataIndex) ? data.getName(dataIndex[0]) : data.getName(dataIndex), |
| selected: isString(selectedMap) ? selectedMap : extend({}, selectedMap) |
| }); |
| } |
| } |
| }); |
| } |
| } |
| |
| function handleLegacySelectEvents(messageCenter, ecIns, api) { |
| messageCenter.on('selectchanged', function (params) { |
| var ecModel = api.getModel(); |
| |
| if (params.isFromClick) { |
| handleSeriesLegacySelectEvents('map', 'selectchanged', ecIns, ecModel, params); |
| handleSeriesLegacySelectEvents('pie', 'selectchanged', ecIns, ecModel, params); |
| } else if (params.fromAction === 'select') { |
| handleSeriesLegacySelectEvents('map', 'selected', ecIns, ecModel, params); |
| handleSeriesLegacySelectEvents('pie', 'selected', ecIns, ecModel, params); |
| } else if (params.fromAction === 'unselect') { |
| handleSeriesLegacySelectEvents('map', 'unselected', ecIns, ecModel, params); |
| handleSeriesLegacySelectEvents('pie', 'unselected', ecIns, ecModel, params); |
| } |
| }); |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| function findEventDispatcher(target, det, returnFirstMatch) { |
| var found; |
| |
| while (target) { |
| if (det(target)) { |
| found = target; |
| |
| if (returnFirstMatch) { |
| break; |
| } |
| } |
| |
| target = target.__hostTarget || target.parent; |
| } |
| |
| return found; |
| } |
| |
| var wmUniqueIndex = Math.round(Math.random() * 9); |
| var supportDefineProperty = typeof Object.defineProperty === 'function'; |
| var WeakMap = (function () { |
| function WeakMap() { |
| this._id = '__ec_inner_' + wmUniqueIndex++; |
| } |
| WeakMap.prototype.get = function (key) { |
| return this._guard(key)[this._id]; |
| }; |
| WeakMap.prototype.set = function (key, value) { |
| var target = this._guard(key); |
| if (supportDefineProperty) { |
| Object.defineProperty(target, this._id, { |
| value: value, |
| enumerable: false, |
| configurable: true |
| }); |
| } |
| else { |
| target[this._id] = value; |
| } |
| return this; |
| }; |
| WeakMap.prototype["delete"] = function (key) { |
| if (this.has(key)) { |
| delete this._guard(key)[this._id]; |
| return true; |
| } |
| return false; |
| }; |
| WeakMap.prototype.has = function (key) { |
| return !!this._guard(key)[this._id]; |
| }; |
| WeakMap.prototype._guard = function (key) { |
| if (key !== Object(key)) { |
| throw TypeError('Value of WeakMap is not a non-null object.'); |
| } |
| return key; |
| }; |
| return WeakMap; |
| }()); |
| |
| /** |
| * Triangle shape |
| * @inner |
| */ |
| |
| var Triangle = Path.extend({ |
| type: 'triangle', |
| shape: { |
| cx: 0, |
| cy: 0, |
| width: 0, |
| height: 0 |
| }, |
| buildPath: function (path, shape) { |
| var cx = shape.cx; |
| var cy = shape.cy; |
| var width = shape.width / 2; |
| var height = shape.height / 2; |
| path.moveTo(cx, cy - height); |
| path.lineTo(cx + width, cy + height); |
| path.lineTo(cx - width, cy + height); |
| path.closePath(); |
| } |
| }); |
| /** |
| * Diamond shape |
| * @inner |
| */ |
| |
| var Diamond = Path.extend({ |
| type: 'diamond', |
| shape: { |
| cx: 0, |
| cy: 0, |
| width: 0, |
| height: 0 |
| }, |
| buildPath: function (path, shape) { |
| var cx = shape.cx; |
| var cy = shape.cy; |
| var width = shape.width / 2; |
| var height = shape.height / 2; |
| path.moveTo(cx, cy - height); |
| path.lineTo(cx + width, cy); |
| path.lineTo(cx, cy + height); |
| path.lineTo(cx - width, cy); |
| path.closePath(); |
| } |
| }); |
| /** |
| * Pin shape |
| * @inner |
| */ |
| |
| var Pin = Path.extend({ |
| type: 'pin', |
| shape: { |
| // x, y on the cusp |
| x: 0, |
| y: 0, |
| width: 0, |
| height: 0 |
| }, |
| buildPath: function (path, shape) { |
| var x = shape.x; |
| var y = shape.y; |
| var w = shape.width / 5 * 3; // Height must be larger than width |
| |
| var h = Math.max(w, shape.height); |
| var r = w / 2; // Dist on y with tangent point and circle center |
| |
| var dy = r * r / (h - r); |
| var cy = y - h + r + dy; |
| var angle = Math.asin(dy / r); // Dist on x with tangent point and circle center |
| |
| var dx = Math.cos(angle) * r; |
| var tanX = Math.sin(angle); |
| var tanY = Math.cos(angle); |
| var cpLen = r * 0.6; |
| var cpLen2 = r * 0.7; |
| path.moveTo(x - dx, cy + dy); |
| path.arc(x, cy, r, Math.PI - angle, Math.PI * 2 + angle); |
| path.bezierCurveTo(x + dx - tanX * cpLen, cy + dy + tanY * cpLen, x, y - cpLen2, x, y); |
| path.bezierCurveTo(x, y - cpLen2, x - dx + tanX * cpLen, cy + dy + tanY * cpLen, x - dx, cy + dy); |
| path.closePath(); |
| } |
| }); |
| /** |
| * Arrow shape |
| * @inner |
| */ |
| |
| var Arrow = Path.extend({ |
| type: 'arrow', |
| shape: { |
| x: 0, |
| y: 0, |
| width: 0, |
| height: 0 |
| }, |
| buildPath: function (ctx, shape) { |
| var height = shape.height; |
| var width = shape.width; |
| var x = shape.x; |
| var y = shape.y; |
| var dx = width / 3 * 2; |
| ctx.moveTo(x, y); |
| ctx.lineTo(x + dx, y + height); |
| ctx.lineTo(x, y + height / 4 * 3); |
| ctx.lineTo(x - dx, y + height); |
| ctx.lineTo(x, y); |
| ctx.closePath(); |
| } |
| }); |
| /** |
| * Map of path constructors |
| */ |
| // TODO Use function to build symbol path. |
| |
| var symbolCtors = { |
| line: Line, |
| rect: Rect, |
| roundRect: Rect, |
| square: Rect, |
| circle: Circle, |
| diamond: Diamond, |
| pin: Pin, |
| arrow: Arrow, |
| triangle: Triangle |
| }; |
| var symbolShapeMakers = { |
| line: function (x, y, w, h, shape) { |
| shape.x1 = x; |
| shape.y1 = y + h / 2; |
| shape.x2 = x + w; |
| shape.y2 = y + h / 2; |
| }, |
| rect: function (x, y, w, h, shape) { |
| shape.x = x; |
| shape.y = y; |
| shape.width = w; |
| shape.height = h; |
| }, |
| roundRect: function (x, y, w, h, shape) { |
| shape.x = x; |
| shape.y = y; |
| shape.width = w; |
| shape.height = h; |
| shape.r = Math.min(w, h) / 4; |
| }, |
| square: function (x, y, w, h, shape) { |
| var size = Math.min(w, h); |
| shape.x = x; |
| shape.y = y; |
| shape.width = size; |
| shape.height = size; |
| }, |
| circle: function (x, y, w, h, shape) { |
| // Put circle in the center of square |
| shape.cx = x + w / 2; |
| shape.cy = y + h / 2; |
| shape.r = Math.min(w, h) / 2; |
| }, |
| diamond: function (x, y, w, h, shape) { |
| shape.cx = x + w / 2; |
| shape.cy = y + h / 2; |
| shape.width = w; |
| shape.height = h; |
| }, |
| pin: function (x, y, w, h, shape) { |
| shape.x = x + w / 2; |
| shape.y = y + h / 2; |
| shape.width = w; |
| shape.height = h; |
| }, |
| arrow: function (x, y, w, h, shape) { |
| shape.x = x + w / 2; |
| shape.y = y + h / 2; |
| shape.width = w; |
| shape.height = h; |
| }, |
| triangle: function (x, y, w, h, shape) { |
| shape.cx = x + w / 2; |
| shape.cy = y + h / 2; |
| shape.width = w; |
| shape.height = h; |
| } |
| }; |
| var symbolBuildProxies = {}; |
| each(symbolCtors, function (Ctor, name) { |
| symbolBuildProxies[name] = new Ctor(); |
| }); |
| var SymbolClz = Path.extend({ |
| type: 'symbol', |
| shape: { |
| symbolType: '', |
| x: 0, |
| y: 0, |
| width: 0, |
| height: 0 |
| }, |
| calculateTextPosition: function (out, config, rect) { |
| var res = calculateTextPosition(out, config, rect); |
| var shape = this.shape; |
| |
| if (shape && shape.symbolType === 'pin' && config.position === 'inside') { |
| res.y = rect.y + rect.height * 0.4; |
| } |
| |
| return res; |
| }, |
| buildPath: function (ctx, shape, inBundle) { |
| var symbolType = shape.symbolType; |
| |
| if (symbolType !== 'none') { |
| var proxySymbol = symbolBuildProxies[symbolType]; |
| |
| if (!proxySymbol) { |
| // Default rect |
| symbolType = 'rect'; |
| proxySymbol = symbolBuildProxies[symbolType]; |
| } |
| |
| symbolShapeMakers[symbolType](shape.x, shape.y, shape.width, shape.height, proxySymbol.shape); |
| proxySymbol.buildPath(ctx, proxySymbol.shape, inBundle); |
| } |
| } |
| }); // Provide setColor helper method to avoid determine if set the fill or stroke outside |
| |
| function symbolPathSetColor(color, innerColor) { |
| if (this.type !== 'image') { |
| var symbolStyle = this.style; |
| |
| if (this.__isEmptyBrush) { |
| symbolStyle.stroke = color; |
| symbolStyle.fill = innerColor || '#fff'; // TODO Same width with lineStyle in LineView |
| |
| symbolStyle.lineWidth = 2; |
| } else if (this.shape.symbolType === 'line') { |
| symbolStyle.stroke = color; |
| } else { |
| symbolStyle.fill = color; |
| } |
| |
| this.markRedraw(); |
| } |
| } |
| /** |
| * Create a symbol element with given symbol configuration: shape, x, y, width, height, color |
| */ |
| |
| |
| function createSymbol(symbolType, x, y, w, h, color, // whether to keep the ratio of w/h, |
| keepAspect) { |
| // TODO Support image object, DynamicImage. |
| var isEmpty = symbolType.indexOf('empty') === 0; |
| |
| if (isEmpty) { |
| symbolType = symbolType.substr(5, 1).toLowerCase() + symbolType.substr(6); |
| } |
| |
| var symbolPath; |
| |
| if (symbolType.indexOf('image://') === 0) { |
| symbolPath = makeImage(symbolType.slice(8), new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover'); |
| } else if (symbolType.indexOf('path://') === 0) { |
| symbolPath = makePath(symbolType.slice(7), {}, new BoundingRect(x, y, w, h), keepAspect ? 'center' : 'cover'); |
| } else { |
| symbolPath = new SymbolClz({ |
| shape: { |
| symbolType: symbolType, |
| x: x, |
| y: y, |
| width: w, |
| height: h |
| } |
| }); |
| } |
| |
| symbolPath.__isEmptyBrush = isEmpty; // TODO Should deprecate setColor |
| |
| symbolPath.setColor = symbolPathSetColor; |
| |
| if (color) { |
| symbolPath.setColor(color); |
| } |
| |
| return symbolPath; |
| } |
| function normalizeSymbolSize(symbolSize) { |
| if (!isArray(symbolSize)) { |
| symbolSize = [+symbolSize, +symbolSize]; |
| } |
| |
| return [symbolSize[0] || 0, symbolSize[1] || 0]; |
| } |
| function normalizeSymbolOffset(symbolOffset, symbolSize) { |
| if (symbolOffset == null) { |
| return; |
| } |
| |
| if (!isArray(symbolOffset)) { |
| symbolOffset = [symbolOffset, symbolOffset]; |
| } |
| |
| return [parsePercent$1(symbolOffset[0], symbolSize[0]) || 0, parsePercent$1(retrieve2(symbolOffset[1], symbolOffset[0]), symbolSize[1]) || 0]; |
| } |
| |
| function isSafeNum(num) { |
| return isFinite(num); |
| } |
| 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; |
| } |
| x = isSafeNum(x) ? x : 0; |
| x2 = isSafeNum(x2) ? x2 : 1; |
| y = isSafeNum(y) ? y : 0; |
| y2 = isSafeNum(y2) ? y2 : 0; |
| 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; |
| } |
| x = isSafeNum(x) ? x : 0.5; |
| y = isSafeNum(y) ? y : 0.5; |
| r = r >= 0 && isSafeNum(r) ? r : 0.5; |
| var canvasGradient = ctx.createRadialGradient(x, y, 0, x, y, r); |
| return canvasGradient; |
| } |
| function getCanvasGradient(ctx, obj, rect) { |
| var canvasGradient = obj.type === 'radial' |
| ? createRadialGradient(ctx, obj, rect) |
| : createLinearGradient(ctx, obj, rect); |
| var colorStops = obj.colorStops; |
| for (var i = 0; i < colorStops.length; i++) { |
| canvasGradient.addColorStop(colorStops[i].offset, colorStops[i].color); |
| } |
| return canvasGradient; |
| } |
| function isClipPathChanged(clipPaths, prevClipPaths) { |
| if (clipPaths === prevClipPaths || (!clipPaths && !prevClipPaths)) { |
| return false; |
| } |
| if (!clipPaths || !prevClipPaths || (clipPaths.length !== prevClipPaths.length)) { |
| return true; |
| } |
| for (var i = 0; i < clipPaths.length; i++) { |
| if (clipPaths[i] !== prevClipPaths[i]) { |
| return true; |
| } |
| } |
| return false; |
| } |
| function parseInt10(val) { |
| return parseInt(val, 10); |
| } |
| function getSize(root, whIdx, opts) { |
| var wh = ['width', 'height'][whIdx]; |
| var cwh = ['clientWidth', 'clientHeight'][whIdx]; |
| var plt = ['paddingLeft', 'paddingTop'][whIdx]; |
| var prb = ['paddingRight', 'paddingBottom'][whIdx]; |
| if (opts[wh] != null && opts[wh] !== 'auto') { |
| return parseFloat(opts[wh]); |
| } |
| var stl = document.defaultView.getComputedStyle(root); |
| return ((root[cwh] || parseInt10(stl[wh]) || parseInt10(root.style[wh])) |
| - (parseInt10(stl[plt]) || 0) |
| - (parseInt10(stl[prb]) || 0)) | 0; |
| } |
| |
| function normalizeLineDash(lineType, lineWidth) { |
| if (!lineType || lineType === 'solid' || !(lineWidth > 0)) { |
| return null; |
| } |
| return lineType === 'dashed' |
| ? [4 * lineWidth, 2 * lineWidth] |
| : lineType === 'dotted' |
| ? [lineWidth] |
| : isNumber(lineType) |
| ? [lineType] : isArray(lineType) ? lineType : null; |
| } |
| function getLineDash(el) { |
| var style = el.style; |
| var lineDash = style.lineDash && style.lineWidth > 0 && normalizeLineDash(style.lineDash, style.lineWidth); |
| var lineDashOffset = style.lineDashOffset; |
| if (lineDash) { |
| var lineScale_1 = (style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1; |
| if (lineScale_1 && lineScale_1 !== 1) { |
| lineDash = map(lineDash, function (rawVal) { |
| return rawVal / lineScale_1; |
| }); |
| lineDashOffset /= lineScale_1; |
| } |
| } |
| return [lineDash, lineDashOffset]; |
| } |
| |
| var pathProxyForDraw = new PathProxy(true); |
| function styleHasStroke(style) { |
| var stroke = style.stroke; |
| return !(stroke == null || stroke === 'none' || !(style.lineWidth > 0)); |
| } |
| function isValidStrokeFillStyle(strokeOrFill) { |
| return typeof strokeOrFill === 'string' && strokeOrFill !== 'none'; |
| } |
| function styleHasFill(style) { |
| var fill = style.fill; |
| return fill != null && fill !== 'none'; |
| } |
| function doFillPath(ctx, style) { |
| if (style.fillOpacity != null && style.fillOpacity !== 1) { |
| var originalGlobalAlpha = ctx.globalAlpha; |
| ctx.globalAlpha = style.fillOpacity * style.opacity; |
| ctx.fill(); |
| ctx.globalAlpha = originalGlobalAlpha; |
| } |
| else { |
| ctx.fill(); |
| } |
| } |
| function doStrokePath(ctx, style) { |
| if (style.strokeOpacity != null && style.strokeOpacity !== 1) { |
| var originalGlobalAlpha = ctx.globalAlpha; |
| ctx.globalAlpha = style.strokeOpacity * style.opacity; |
| ctx.stroke(); |
| ctx.globalAlpha = originalGlobalAlpha; |
| } |
| else { |
| ctx.stroke(); |
| } |
| } |
| function createCanvasPattern(ctx, pattern, el) { |
| var image = createOrUpdateImage(pattern.image, pattern.__image, el); |
| if (isImageReady(image)) { |
| var canvasPattern = ctx.createPattern(image, pattern.repeat || 'repeat'); |
| if (typeof DOMMatrix === 'function' |
| && canvasPattern |
| && canvasPattern.setTransform) { |
| var matrix = new DOMMatrix(); |
| matrix.translateSelf((pattern.x || 0), (pattern.y || 0)); |
| matrix.rotateSelf(0, 0, (pattern.rotation || 0) * RADIAN_TO_DEGREE); |
| matrix.scaleSelf((pattern.scaleX || 1), (pattern.scaleY || 1)); |
| canvasPattern.setTransform(matrix); |
| } |
| return canvasPattern; |
| } |
| } |
| function brushPath(ctx, el, style, inBatch) { |
| var _a; |
| var hasStroke = styleHasStroke(style); |
| var hasFill = styleHasFill(style); |
| var strokePercent = style.strokePercent; |
| var strokePart = strokePercent < 1; |
| var firstDraw = !el.path; |
| if ((!el.silent || strokePart) && firstDraw) { |
| el.createPathProxy(); |
| } |
| var path = el.path || pathProxyForDraw; |
| var dirtyFlag = el.__dirty; |
| if (!inBatch) { |
| 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; |
| var fillGradient = void 0; |
| var strokeGradient = void 0; |
| var fillPattern = void 0; |
| var strokePattern = void 0; |
| var rect = void 0; |
| if (hasFillGradient || hasStrokeGradient) { |
| rect = el.getBoundingRect(); |
| } |
| if (hasFillGradient) { |
| fillGradient = dirtyFlag |
| ? getCanvasGradient(ctx, fill, rect) |
| : el.__canvasFillGradient; |
| el.__canvasFillGradient = fillGradient; |
| } |
| if (hasStrokeGradient) { |
| strokeGradient = dirtyFlag |
| ? getCanvasGradient(ctx, stroke, rect) |
| : el.__canvasStrokeGradient; |
| el.__canvasStrokeGradient = strokeGradient; |
| } |
| if (hasFillPattern) { |
| fillPattern = (dirtyFlag || !el.__canvasFillPattern) |
| ? createCanvasPattern(ctx, fill, el) |
| : el.__canvasFillPattern; |
| el.__canvasFillPattern = fillPattern; |
| } |
| if (hasStrokePattern) { |
| strokePattern = (dirtyFlag || !el.__canvasStrokePattern) |
| ? createCanvasPattern(ctx, stroke, el) |
| : el.__canvasStrokePattern; |
| el.__canvasStrokePattern = fillPattern; |
| } |
| if (hasFillGradient) { |
| ctx.fillStyle = fillGradient; |
| } |
| else if (hasFillPattern) { |
| if (fillPattern) { |
| ctx.fillStyle = fillPattern; |
| } |
| else { |
| hasFill = false; |
| } |
| } |
| if (hasStrokeGradient) { |
| ctx.strokeStyle = strokeGradient; |
| } |
| else if (hasStrokePattern) { |
| if (strokePattern) { |
| ctx.strokeStyle = strokePattern; |
| } |
| else { |
| hasStroke = false; |
| } |
| } |
| } |
| var scale = el.getGlobalScale(); |
| path.setScale(scale[0], scale[1], el.segmentIgnoreThreshold); |
| var lineDash; |
| var lineDashOffset; |
| if (ctx.setLineDash && style.lineDash) { |
| _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1]; |
| } |
| var needsRebuild = true; |
| if (firstDraw || (dirtyFlag & SHAPE_CHANGED_BIT)) { |
| path.setDPR(ctx.dpr); |
| if (strokePart) { |
| path.setContext(null); |
| } |
| else { |
| path.setContext(ctx); |
| needsRebuild = false; |
| } |
| path.reset(); |
| el.buildPath(path, el.shape, inBatch); |
| path.toStatic(); |
| el.pathUpdated(); |
| } |
| if (needsRebuild) { |
| path.rebuildPath(ctx, strokePart ? strokePercent : 1); |
| } |
| if (lineDash) { |
| ctx.setLineDash(lineDash); |
| ctx.lineDashOffset = lineDashOffset; |
| } |
| if (!inBatch) { |
| if (style.strokeFirst) { |
| if (hasStroke) { |
| doStrokePath(ctx, style); |
| } |
| if (hasFill) { |
| doFillPath(ctx, style); |
| } |
| } |
| else { |
| if (hasFill) { |
| doFillPath(ctx, style); |
| } |
| if (hasStroke) { |
| doStrokePath(ctx, style); |
| } |
| } |
| } |
| if (lineDash) { |
| ctx.setLineDash([]); |
| } |
| } |
| function brushImage(ctx, el, style) { |
| var image = el.__image = createOrUpdateImage(style.image, el.__image, el, el.onload); |
| if (!image || !isImageReady(image)) { |
| return; |
| } |
| var x = style.x || 0; |
| var y = style.y || 0; |
| var width = el.getWidth(); |
| var height = el.getHeight(); |
| var aspect = image.width / image.height; |
| if (width == null && height != null) { |
| width = height * aspect; |
| } |
| else if (height == null && width != null) { |
| height = width / aspect; |
| } |
| else if (width == null && height == null) { |
| width = image.width; |
| height = image.height; |
| } |
| 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); |
| } |
| } |
| function brushText(ctx, el, style) { |
| var _a; |
| var text = style.text; |
| text != null && (text += ''); |
| if (text) { |
| ctx.font = style.font || DEFAULT_FONT; |
| ctx.textAlign = style.textAlign; |
| ctx.textBaseline = style.textBaseline; |
| var lineDash = void 0; |
| var lineDashOffset = void 0; |
| if (ctx.setLineDash && style.lineDash) { |
| _a = getLineDash(el), lineDash = _a[0], lineDashOffset = _a[1]; |
| } |
| if (lineDash) { |
| ctx.setLineDash(lineDash); |
| ctx.lineDashOffset = lineDashOffset; |
| } |
| if (style.strokeFirst) { |
| if (styleHasStroke(style)) { |
| ctx.strokeText(text, style.x, style.y); |
| } |
| if (styleHasFill(style)) { |
| ctx.fillText(text, style.x, style.y); |
| } |
| } |
| else { |
| if (styleHasFill(style)) { |
| ctx.fillText(text, style.x, style.y); |
| } |
| if (styleHasStroke(style)) { |
| ctx.strokeText(text, style.x, style.y); |
| } |
| } |
| if (lineDash) { |
| ctx.setLineDash([]); |
| } |
| } |
| } |
| var SHADOW_NUMBER_PROPS = ['shadowBlur', 'shadowOffsetX', 'shadowOffsetY']; |
| var STROKE_PROPS = [ |
| ['lineCap', 'butt'], ['lineJoin', 'miter'], ['miterLimit', 10] |
| ]; |
| function bindCommonProps(ctx, style, prevStyle, forceSetAll, scope) { |
| var styleChanged = false; |
| if (!forceSetAll) { |
| prevStyle = prevStyle || {}; |
| if (style === prevStyle) { |
| return false; |
| } |
| } |
| if (forceSetAll || style.opacity !== prevStyle.opacity) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| var opacity = Math.max(Math.min(style.opacity, 1), 0); |
| ctx.globalAlpha = isNaN(opacity) ? DEFAULT_COMMON_STYLE.opacity : opacity; |
| } |
| if (forceSetAll || style.blend !== prevStyle.blend) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx.globalCompositeOperation = style.blend || DEFAULT_COMMON_STYLE.blend; |
| } |
| for (var i = 0; i < SHADOW_NUMBER_PROPS.length; i++) { |
| var propName = SHADOW_NUMBER_PROPS[i]; |
| if (forceSetAll || style[propName] !== prevStyle[propName]) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx[propName] = ctx.dpr * (style[propName] || 0); |
| } |
| } |
| if (forceSetAll || style.shadowColor !== prevStyle.shadowColor) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx.shadowColor = style.shadowColor || DEFAULT_COMMON_STYLE.shadowColor; |
| } |
| return styleChanged; |
| } |
| function bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetAll, scope) { |
| var style = getStyle(el, scope.inHover); |
| var prevStyle = forceSetAll |
| ? null |
| : (prevEl && getStyle(prevEl, scope.inHover) || {}); |
| if (style === prevStyle) { |
| return false; |
| } |
| var styleChanged = bindCommonProps(ctx, style, prevStyle, forceSetAll, scope); |
| if (forceSetAll || style.fill !== prevStyle.fill) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| isValidStrokeFillStyle(style.fill) && (ctx.fillStyle = style.fill); |
| } |
| if (forceSetAll || style.stroke !== prevStyle.stroke) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| isValidStrokeFillStyle(style.stroke) && (ctx.strokeStyle = style.stroke); |
| } |
| if (forceSetAll || style.opacity !== prevStyle.opacity) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx.globalAlpha = style.opacity == null ? 1 : style.opacity; |
| } |
| if (el.hasStroke()) { |
| var lineWidth = style.lineWidth; |
| var newLineWidth = lineWidth / ((style.strokeNoScale && el.getLineScale) ? el.getLineScale() : 1); |
| if (ctx.lineWidth !== newLineWidth) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx.lineWidth = newLineWidth; |
| } |
| } |
| for (var i = 0; i < STROKE_PROPS.length; i++) { |
| var prop = STROKE_PROPS[i]; |
| var propName = prop[0]; |
| if (forceSetAll || style[propName] !== prevStyle[propName]) { |
| if (!styleChanged) { |
| flushPathDrawn(ctx, scope); |
| styleChanged = true; |
| } |
| ctx[propName] = style[propName] || prop[1]; |
| } |
| } |
| return styleChanged; |
| } |
| function bindImageStyle(ctx, el, prevEl, forceSetAll, scope) { |
| return bindCommonProps(ctx, getStyle(el, scope.inHover), prevEl && getStyle(prevEl, scope.inHover), forceSetAll, scope); |
| } |
| function setContextTransform(ctx, el) { |
| var m = el.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); |
| } |
| } |
| function updateClipStatus(clipPaths, ctx, scope) { |
| var allClipped = false; |
| for (var i = 0; i < clipPaths.length; i++) { |
| var clipPath = clipPaths[i]; |
| allClipped = allClipped || clipPath.isZeroArea(); |
| setContextTransform(ctx, clipPath); |
| ctx.beginPath(); |
| clipPath.buildPath(ctx, clipPath.shape); |
| ctx.clip(); |
| } |
| scope.allClipped = allClipped; |
| } |
| function isTransformChanged(m0, m1) { |
| if (m0 && m1) { |
| return m0[0] !== m1[0] |
| || m0[1] !== m1[1] |
| || m0[2] !== m1[2] |
| || m0[3] !== m1[3] |
| || m0[4] !== m1[4] |
| || m0[5] !== m1[5]; |
| } |
| else if (!m0 && !m1) { |
| return false; |
| } |
| return true; |
| } |
| var DRAW_TYPE_PATH = 1; |
| var DRAW_TYPE_IMAGE = 2; |
| var DRAW_TYPE_TEXT = 3; |
| var DRAW_TYPE_INCREMENTAL = 4; |
| function canPathBatch(style) { |
| var hasFill = styleHasFill(style); |
| var hasStroke = styleHasStroke(style); |
| return !(style.lineDash |
| || !(+hasFill ^ +hasStroke) |
| || (hasFill && typeof style.fill !== 'string') |
| || (hasStroke && typeof style.stroke !== 'string') |
| || style.strokePercent < 1 |
| || style.strokeOpacity < 1 |
| || style.fillOpacity < 1); |
| } |
| function flushPathDrawn(ctx, scope) { |
| scope.batchFill && ctx.fill(); |
| scope.batchStroke && ctx.stroke(); |
| scope.batchFill = ''; |
| scope.batchStroke = ''; |
| } |
| function getStyle(el, inHover) { |
| return inHover ? (el.__hoverStyle || el.style) : el.style; |
| } |
| function brushSingle(ctx, el) { |
| brush(ctx, el, { inHover: false, viewWidth: 0, viewHeight: 0 }, true); |
| } |
| function brush(ctx, el, scope, isLast) { |
| var m = el.transform; |
| if (!el.shouldBePainted(scope.viewWidth, scope.viewHeight, false, false)) { |
| el.__dirty &= ~REDRAW_BIT; |
| el.__isRendered = false; |
| return; |
| } |
| var clipPaths = el.__clipPaths; |
| var prevElClipPaths = scope.prevElClipPaths; |
| var forceSetTransform = false; |
| var forceSetStyle = false; |
| if (!prevElClipPaths || isClipPathChanged(clipPaths, prevElClipPaths)) { |
| if (prevElClipPaths && prevElClipPaths.length) { |
| flushPathDrawn(ctx, scope); |
| ctx.restore(); |
| forceSetStyle = forceSetTransform = true; |
| scope.prevElClipPaths = null; |
| scope.allClipped = false; |
| scope.prevEl = null; |
| } |
| if (clipPaths && clipPaths.length) { |
| flushPathDrawn(ctx, scope); |
| ctx.save(); |
| updateClipStatus(clipPaths, ctx, scope); |
| forceSetTransform = true; |
| } |
| scope.prevElClipPaths = clipPaths; |
| } |
| if (scope.allClipped) { |
| el.__isRendered = false; |
| return; |
| } |
| el.beforeBrush && el.beforeBrush(); |
| el.innerBeforeBrush(); |
| var prevEl = scope.prevEl; |
| if (!prevEl) { |
| forceSetStyle = forceSetTransform = true; |
| } |
| var canBatchPath = el instanceof Path |
| && el.autoBatch |
| && canPathBatch(el.style); |
| if (forceSetTransform || isTransformChanged(m, prevEl.transform)) { |
| flushPathDrawn(ctx, scope); |
| setContextTransform(ctx, el); |
| } |
| else if (!canBatchPath) { |
| flushPathDrawn(ctx, scope); |
| } |
| var style = getStyle(el, scope.inHover); |
| if (el instanceof Path) { |
| if (scope.lastDrawType !== DRAW_TYPE_PATH) { |
| forceSetStyle = true; |
| scope.lastDrawType = DRAW_TYPE_PATH; |
| } |
| bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope); |
| if (!canBatchPath || (!scope.batchFill && !scope.batchStroke)) { |
| ctx.beginPath(); |
| } |
| brushPath(ctx, el, style, canBatchPath); |
| if (canBatchPath) { |
| scope.batchFill = style.fill || ''; |
| scope.batchStroke = style.stroke || ''; |
| } |
| } |
| else { |
| if (el instanceof TSpan) { |
| if (scope.lastDrawType !== DRAW_TYPE_TEXT) { |
| forceSetStyle = true; |
| scope.lastDrawType = DRAW_TYPE_TEXT; |
| } |
| bindPathAndTextCommonStyle(ctx, el, prevEl, forceSetStyle, scope); |
| brushText(ctx, el, style); |
| } |
| else if (el instanceof ZRImage) { |
| if (scope.lastDrawType !== DRAW_TYPE_IMAGE) { |
| forceSetStyle = true; |
| scope.lastDrawType = DRAW_TYPE_IMAGE; |
| } |
| bindImageStyle(ctx, el, prevEl, forceSetStyle, scope); |
| brushImage(ctx, el, style); |
| } |
| else if (el.getTemporalDisplayables) { |
| if (scope.lastDrawType !== DRAW_TYPE_INCREMENTAL) { |
| forceSetStyle = true; |
| scope.lastDrawType = DRAW_TYPE_INCREMENTAL; |
| } |
| brushIncremental(ctx, el, scope); |
| } |
| } |
| if (canBatchPath && isLast) { |
| flushPathDrawn(ctx, scope); |
| } |
| el.innerAfterBrush(); |
| el.afterBrush && el.afterBrush(); |
| scope.prevEl = el; |
| el.__dirty = 0; |
| el.__isRendered = true; |
| } |
| function brushIncremental(ctx, el, scope) { |
| var displayables = el.getDisplayables(); |
| var temporalDisplayables = el.getTemporalDisplayables(); |
| ctx.save(); |
| var innerScope = { |
| prevElClipPaths: null, |
| prevEl: null, |
| allClipped: false, |
| viewWidth: scope.viewWidth, |
| viewHeight: scope.viewHeight, |
| inHover: scope.inHover |
| }; |
| var i; |
| var len; |
| for (i = el.getCursor(), len = displayables.length; i < len; i++) { |
| var displayable = displayables[i]; |
| displayable.beforeBrush && displayable.beforeBrush(); |
| displayable.innerBeforeBrush(); |
| brush(ctx, displayable, innerScope, i === len - 1); |
| displayable.innerAfterBrush(); |
| displayable.afterBrush && displayable.afterBrush(); |
| innerScope.prevEl = displayable; |
| } |
| for (var i_1 = 0, len_1 = temporalDisplayables.length; i_1 < len_1; i_1++) { |
| var displayable = temporalDisplayables[i_1]; |
| displayable.beforeBrush && displayable.beforeBrush(); |
| displayable.innerBeforeBrush(); |
| brush(ctx, displayable, innerScope, i_1 === len_1 - 1); |
| displayable.innerAfterBrush(); |
| displayable.afterBrush && displayable.afterBrush(); |
| innerScope.prevEl = displayable; |
| } |
| el.clearTemporalDisplayables(); |
| el.notClear = true; |
| ctx.restore(); |
| } |
| |
| var decalMap = new WeakMap(); |
| var decalCache = new LRU(100); |
| var decalKeys = ['symbol', 'symbolSize', 'symbolKeepAspect', 'color', 'backgroundColor', 'dashArrayX', 'dashArrayY', 'maxTileWidth', 'maxTileHeight']; |
| /** |
| * Create or update pattern image from decal options |
| * |
| * @param {InnerDecalObject | 'none'} decalObject decal options, 'none' if no decal |
| * @return {Pattern} pattern with generated image, null if no decal |
| */ |
| |
| function createOrUpdatePatternFromDecal(decalObject, api) { |
| if (decalObject === 'none') { |
| return null; |
| } |
| |
| var dpr = api.getDevicePixelRatio(); |
| var zr = api.getZr(); |
| var isSVG = zr.painter.type === 'svg'; |
| |
| if (decalObject.dirty) { |
| decalMap["delete"](decalObject); |
| } |
| |
| var oldPattern = decalMap.get(decalObject); |
| |
| if (oldPattern) { |
| return oldPattern; |
| } |
| |
| var decalOpt = defaults(decalObject, { |
| symbol: 'rect', |
| symbolSize: 1, |
| symbolKeepAspect: true, |
| color: 'rgba(0, 0, 0, 0.2)', |
| backgroundColor: null, |
| dashArrayX: 5, |
| dashArrayY: 5, |
| rotation: 0, |
| maxTileWidth: 512, |
| maxTileHeight: 512 |
| }); |
| |
| if (decalOpt.backgroundColor === 'none') { |
| decalOpt.backgroundColor = null; |
| } |
| |
| var pattern = { |
| repeat: 'repeat' |
| }; |
| setPatternnSource(pattern); |
| pattern.rotation = decalOpt.rotation; |
| pattern.scaleX = pattern.scaleY = isSVG ? 1 : 1 / dpr; |
| decalMap.set(decalObject, pattern); |
| decalObject.dirty = false; |
| return pattern; |
| |
| function setPatternnSource(pattern) { |
| var keys = [dpr]; |
| var isValidKey = true; |
| |
| for (var i = 0; i < decalKeys.length; ++i) { |
| var value = decalOpt[decalKeys[i]]; |
| |
| if (value != null && !isArray(value) && !isString(value) && !isNumber(value) && typeof value !== 'boolean') { |
| isValidKey = false; |
| break; |
| } |
| |
| keys.push(value); |
| } |
| |
| var cacheKey; |
| |
| if (isValidKey) { |
| cacheKey = keys.join(',') + (isSVG ? '-svg' : ''); |
| var cache = decalCache.get(cacheKey); |
| |
| if (cache) { |
| isSVG ? pattern.svgElement = cache : pattern.image = cache; |
| } |
| } |
| |
| var dashArrayX = normalizeDashArrayX(decalOpt.dashArrayX); |
| var dashArrayY = normalizeDashArrayY(decalOpt.dashArrayY); |
| var symbolArray = normalizeSymbolArray(decalOpt.symbol); |
| var lineBlockLengthsX = getLineBlockLengthX(dashArrayX); |
| var lineBlockLengthY = getLineBlockLengthY(dashArrayY); |
| var canvas = !isSVG && platformApi.createCanvas(); |
| var svgRoot = isSVG && { |
| tag: 'g', |
| attrs: {}, |
| key: 'dcl', |
| children: [] |
| }; |
| var pSize = getPatternSize(); |
| var ctx; |
| |
| if (canvas) { |
| canvas.width = pSize.width * dpr; |
| canvas.height = pSize.height * dpr; |
| ctx = canvas.getContext('2d'); |
| } |
| |
| brushDecal(); |
| |
| if (isValidKey) { |
| decalCache.put(cacheKey, canvas || svgRoot); |
| } |
| |
| pattern.image = canvas; |
| pattern.svgElement = svgRoot; |
| pattern.svgWidth = pSize.width; |
| pattern.svgHeight = pSize.height; |
| /** |
| * Get minimum length that can make a repeatable pattern. |
| * |
| * @return {Object} pattern width and height |
| */ |
| |
| function getPatternSize() { |
| /** |
| * For example, if dash is [[3, 2], [2, 1]] for X, it looks like |
| * |--- --- --- --- --- ... |
| * |-- -- -- -- -- -- -- -- ... |
| * |--- --- --- --- --- ... |
| * |-- -- -- -- -- -- -- -- ... |
| * So the minimum length of X is 15, |
| * which is the least common multiple of `3 + 2` and `2 + 1` |
| * |--- --- --- |--- --- ... |
| * |-- -- -- -- -- |-- -- -- ... |
| */ |
| var width = 1; |
| |
| for (var i = 0, xlen = lineBlockLengthsX.length; i < xlen; ++i) { |
| width = getLeastCommonMultiple(width, lineBlockLengthsX[i]); |
| } |
| |
| var symbolRepeats = 1; |
| |
| for (var i = 0, xlen = symbolArray.length; i < xlen; ++i) { |
| symbolRepeats = getLeastCommonMultiple(symbolRepeats, symbolArray[i].length); |
| } |
| |
| width *= symbolRepeats; |
| var height = lineBlockLengthY * lineBlockLengthsX.length * symbolArray.length; |
| |
| if ("development" !== 'production') { |
| var warn = function (attrName) { |
| /* eslint-disable-next-line */ |
| console.warn("Calculated decal size is greater than " + attrName + " due to decal option settings so " + attrName + " is used for the decal size. Please consider changing the decal option to make a smaller decal or set " + attrName + " to be larger to avoid incontinuity."); |
| }; |
| |
| if (width > decalOpt.maxTileWidth) { |
| warn('maxTileWidth'); |
| } |
| |
| if (height > decalOpt.maxTileHeight) { |
| warn('maxTileHeight'); |
| } |
| } |
| |
| return { |
| width: Math.max(1, Math.min(width, decalOpt.maxTileWidth)), |
| height: Math.max(1, Math.min(height, decalOpt.maxTileHeight)) |
| }; |
| } |
| |
| function brushDecal() { |
| if (ctx) { |
| ctx.clearRect(0, 0, canvas.width, canvas.height); |
| |
| if (decalOpt.backgroundColor) { |
| ctx.fillStyle = decalOpt.backgroundColor; |
| ctx.fillRect(0, 0, canvas.width, canvas.height); |
| } |
| } |
| |
| var ySum = 0; |
| |
| for (var i = 0; i < dashArrayY.length; ++i) { |
| ySum += dashArrayY[i]; |
| } |
| |
| if (ySum <= 0) { |
| // dashArrayY is 0, draw nothing |
| return; |
| } |
| |
| var y = -lineBlockLengthY; |
| var yId = 0; |
| var yIdTotal = 0; |
| var xId0 = 0; |
| |
| while (y < pSize.height) { |
| if (yId % 2 === 0) { |
| var symbolYId = yIdTotal / 2 % symbolArray.length; |
| var x = 0; |
| var xId1 = 0; |
| var xId1Total = 0; |
| |
| while (x < pSize.width * 2) { |
| var xSum = 0; |
| |
| for (var i = 0; i < dashArrayX[xId0].length; ++i) { |
| xSum += dashArrayX[xId0][i]; |
| } |
| |
| if (xSum <= 0) { |
| // Skip empty line |
| break; |
| } // E.g., [15, 5, 20, 5] draws only for 15 and 20 |
| |
| |
| if (xId1 % 2 === 0) { |
| var size = (1 - decalOpt.symbolSize) * 0.5; |
| var left = x + dashArrayX[xId0][xId1] * size; |
| var top_1 = y + dashArrayY[yId] * size; |
| var width = dashArrayX[xId0][xId1] * decalOpt.symbolSize; |
| var height = dashArrayY[yId] * decalOpt.symbolSize; |
| var symbolXId = xId1Total / 2 % symbolArray[symbolYId].length; |
| brushSymbol(left, top_1, width, height, symbolArray[symbolYId][symbolXId]); |
| } |
| |
| x += dashArrayX[xId0][xId1]; |
| ++xId1Total; |
| ++xId1; |
| |
| if (xId1 === dashArrayX[xId0].length) { |
| xId1 = 0; |
| } |
| } |
| |
| ++xId0; |
| |
| if (xId0 === dashArrayX.length) { |
| xId0 = 0; |
| } |
| } |
| |
| y += dashArrayY[yId]; |
| ++yIdTotal; |
| ++yId; |
| |
| if (yId === dashArrayY.length) { |
| yId = 0; |
| } |
| } |
| |
| function brushSymbol(x, y, width, height, symbolType) { |
| var scale = isSVG ? 1 : dpr; |
| var symbol = createSymbol(symbolType, x * scale, y * scale, width * scale, height * scale, decalOpt.color, decalOpt.symbolKeepAspect); |
| |
| if (isSVG) { |
| var symbolVNode = zr.painter.renderOneToVNode(symbol); |
| |
| if (symbolVNode) { |
| svgRoot.children.push(symbolVNode); |
| } |
| } else { |
| // Paint to canvas for all other renderers. |
| brushSingle(ctx, symbol); |
| } |
| } |
| } |
| } |
| } |
| /** |
| * Convert symbol array into normalized array |
| * |
| * @param {string | (string | string[])[]} symbol symbol input |
| * @return {string[][]} normolized symbol array |
| */ |
| |
| function normalizeSymbolArray(symbol) { |
| if (!symbol || symbol.length === 0) { |
| return [['rect']]; |
| } |
| |
| if (isString(symbol)) { |
| return [[symbol]]; |
| } |
| |
| var isAllString = true; |
| |
| for (var i = 0; i < symbol.length; ++i) { |
| if (!isString(symbol[i])) { |
| isAllString = false; |
| break; |
| } |
| } |
| |
| if (isAllString) { |
| return normalizeSymbolArray([symbol]); |
| } |
| |
| var result = []; |
| |
| for (var i = 0; i < symbol.length; ++i) { |
| if (isString(symbol[i])) { |
| result.push([symbol[i]]); |
| } else { |
| result.push(symbol[i]); |
| } |
| } |
| |
| return result; |
| } |
| /** |
| * Convert dash input into dashArray |
| * |
| * @param {DecalDashArrayX} dash dash input |
| * @return {number[][]} normolized dash array |
| */ |
| |
| |
| function normalizeDashArrayX(dash) { |
| if (!dash || dash.length === 0) { |
| return [[0, 0]]; |
| } |
| |
| if (isNumber(dash)) { |
| var dashValue = Math.ceil(dash); |
| return [[dashValue, dashValue]]; |
| } |
| /** |
| * [20, 5] should be normalized into [[20, 5]], |
| * while [20, [5, 10]] should be normalized into [[20, 20], [5, 10]] |
| */ |
| |
| |
| var isAllNumber = true; |
| |
| for (var i = 0; i < dash.length; ++i) { |
| if (!isNumber(dash[i])) { |
| isAllNumber = false; |
| break; |
| } |
| } |
| |
| if (isAllNumber) { |
| return normalizeDashArrayX([dash]); |
| } |
| |
| var result = []; |
| |
| for (var i = 0; i < dash.length; ++i) { |
| if (isNumber(dash[i])) { |
| var dashValue = Math.ceil(dash[i]); |
| result.push([dashValue, dashValue]); |
| } else { |
| var dashValue = map(dash[i], function (n) { |
| return Math.ceil(n); |
| }); |
| |
| if (dashValue.length % 2 === 1) { |
| // [4, 2, 1] means |---- - -- |---- - -- | |
| // so normalize it to be [4, 2, 1, 4, 2, 1] |
| result.push(dashValue.concat(dashValue)); |
| } else { |
| result.push(dashValue); |
| } |
| } |
| } |
| |
| return result; |
| } |
| /** |
| * Convert dash input into dashArray |
| * |
| * @param {DecalDashArrayY} dash dash input |
| * @return {number[]} normolized dash array |
| */ |
| |
| |
| function normalizeDashArrayY(dash) { |
| if (!dash || typeof dash === 'object' && dash.length === 0) { |
| return [0, 0]; |
| } |
| |
| if (isNumber(dash)) { |
| var dashValue_1 = Math.ceil(dash); |
| return [dashValue_1, dashValue_1]; |
| } |
| |
| var dashValue = map(dash, function (n) { |
| return Math.ceil(n); |
| }); |
| return dash.length % 2 ? dashValue.concat(dashValue) : dashValue; |
| } |
| /** |
| * Get block length of each line. A block is the length of dash line and space. |
| * For example, a line with [4, 1] has a dash line of 4 and a space of 1 after |
| * that, so the block length of this line is 5. |
| * |
| * @param {number[][]} dash dash array of X or Y |
| * @return {number[]} block length of each line |
| */ |
| |
| |
| function getLineBlockLengthX(dash) { |
| return map(dash, function (line) { |
| return getLineBlockLengthY(line); |
| }); |
| } |
| |
| function getLineBlockLengthY(dash) { |
| var blockLength = 0; |
| |
| for (var i = 0; i < dash.length; ++i) { |
| blockLength += dash[i]; |
| } |
| |
| if (dash.length % 2 === 1) { |
| // [4, 2, 1] means |---- - -- |---- - -- | |
| // So total length is (4 + 2 + 1) * 2 |
| return blockLength * 2; |
| } |
| |
| return blockLength; |
| } |
| |
| function decalVisual(ecModel, api) { |
| ecModel.eachRawSeries(function (seriesModel) { |
| if (ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| |
| if (data.hasItemVisual()) { |
| data.each(function (idx) { |
| var decal = data.getItemVisual(idx, 'decal'); |
| |
| if (decal) { |
| var itemStyle = data.ensureUniqueItemVisual(idx, 'style'); |
| itemStyle.decal = createOrUpdatePatternFromDecal(decal, api); |
| } |
| }); |
| } |
| |
| var decal = data.getVisual('decal'); |
| |
| if (decal) { |
| var style = data.getVisual('style'); |
| style.decal = createOrUpdatePatternFromDecal(decal, api); |
| } |
| }); |
| } |
| |
| var lifecycle = new Eventful(); |
| |
| // The implementations will be registered when installing the component. |
| // Avoid these code being bundled to the core module. |
| |
| var implsStore = {}; // TODO Type |
| |
| function registerImpl(name, impl) { |
| if ("development" !== 'production') { |
| if (implsStore[name]) { |
| error("Already has an implementation of " + name + "."); |
| } |
| } |
| |
| implsStore[name] = impl; |
| } |
| function getImpl(name) { |
| if ("development" !== 'production') { |
| if (!implsStore[name]) { |
| error("Implementation of " + name + " doesn't exists."); |
| } |
| } |
| |
| return implsStore[name]; |
| } |
| |
| var hasWindow = typeof window !== 'undefined'; |
| var version$1 = '5.4.0'; |
| var dependencies = { |
| zrender: '5.4.0' |
| }; |
| var TEST_FRAME_REMAIN_TIME = 1; |
| var PRIORITY_PROCESSOR_SERIES_FILTER = 800; // Some data processors depends on the stack result dimension (to calculate data extent). |
| // So data stack stage should be in front of data processing stage. |
| |
| var PRIORITY_PROCESSOR_DATASTACK = 900; // "Data filter" will block the stream, so it should be |
| // put at the beginning of data processing. |
| |
| var PRIORITY_PROCESSOR_FILTER = 1000; |
| var PRIORITY_PROCESSOR_DEFAULT = 2000; |
| var PRIORITY_PROCESSOR_STATISTIC = 5000; |
| var PRIORITY_VISUAL_LAYOUT = 1000; |
| var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100; |
| var PRIORITY_VISUAL_GLOBAL = 2000; |
| var PRIORITY_VISUAL_CHART = 3000; |
| var PRIORITY_VISUAL_COMPONENT = 4000; // Visual property in data. Greater than `PRIORITY_VISUAL_COMPONENT` to enable to |
| // overwrite the viusal result of component (like `visualMap`) |
| // using data item specific setting (like itemStyle.xxx on data item) |
| |
| var PRIORITY_VISUAL_CHART_DATA_CUSTOM = 4500; // Greater than `PRIORITY_VISUAL_CHART_DATA_CUSTOM` to enable to layout based on |
| // visual result like `symbolSize`. |
| |
| var PRIORITY_VISUAL_POST_CHART_LAYOUT = 4600; |
| var PRIORITY_VISUAL_BRUSH = 5000; |
| var PRIORITY_VISUAL_ARIA = 6000; |
| var PRIORITY_VISUAL_DECAL = 7000; |
| var PRIORITY = { |
| PROCESSOR: { |
| FILTER: PRIORITY_PROCESSOR_FILTER, |
| SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER, |
| STATISTIC: PRIORITY_PROCESSOR_STATISTIC |
| }, |
| VISUAL: { |
| LAYOUT: PRIORITY_VISUAL_LAYOUT, |
| PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT, |
| GLOBAL: PRIORITY_VISUAL_GLOBAL, |
| CHART: PRIORITY_VISUAL_CHART, |
| POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT, |
| COMPONENT: PRIORITY_VISUAL_COMPONENT, |
| BRUSH: PRIORITY_VISUAL_BRUSH, |
| CHART_ITEM: PRIORITY_VISUAL_CHART_DATA_CUSTOM, |
| ARIA: PRIORITY_VISUAL_ARIA, |
| DECAL: PRIORITY_VISUAL_DECAL |
| } |
| }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`, |
| // where they must not be invoked nestedly, except the only case: invoke |
| // dispatchAction with updateMethod "none" in main process. |
| // This flag is used to carry out this rule. |
| // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]). |
| |
| var IN_MAIN_PROCESS_KEY = '__flagInMainProcess'; |
| var PENDING_UPDATE = '__pendingUpdate'; |
| var STATUS_NEEDS_UPDATE_KEY = '__needsUpdateStatus'; |
| var ACTION_REG = /^[a-zA-Z0-9_]+$/; |
| var CONNECT_STATUS_KEY = '__connectUpdateStatus'; |
| var CONNECT_STATUS_PENDING = 0; |
| var CONNECT_STATUS_UPDATING = 1; |
| var CONNECT_STATUS_UPDATED = 2; |
| |
| function createRegisterEventWithLowercaseECharts(method) { |
| return function () { |
| var args = []; |
| |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args[_i] = arguments[_i]; |
| } |
| |
| if (this.isDisposed()) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| return toLowercaseNameAndCallEventful(this, method, args); |
| }; |
| } |
| |
| function createRegisterEventWithLowercaseMessageCenter(method) { |
| return function () { |
| var args = []; |
| |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args[_i] = arguments[_i]; |
| } |
| |
| return toLowercaseNameAndCallEventful(this, method, args); |
| }; |
| } |
| |
| function toLowercaseNameAndCallEventful(host, method, args) { |
| // `args[0]` is event name. Event name is all lowercase. |
| args[0] = args[0] && args[0].toLowerCase(); |
| return Eventful.prototype[method].apply(host, args); |
| } |
| |
| var MessageCenter = |
| /** @class */ |
| function (_super) { |
| __extends(MessageCenter, _super); |
| |
| function MessageCenter() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| return MessageCenter; |
| }(Eventful); |
| |
| var messageCenterProto = MessageCenter.prototype; |
| messageCenterProto.on = createRegisterEventWithLowercaseMessageCenter('on'); |
| messageCenterProto.off = createRegisterEventWithLowercaseMessageCenter('off'); // --------------------------------------- |
| // Internal method names for class ECharts |
| // --------------------------------------- |
| |
| var prepare; |
| var prepareView; |
| var updateDirectly; |
| var updateMethods; |
| var doConvertPixel; |
| var updateStreamModes; |
| var doDispatchAction; |
| var flushPendingActions; |
| var triggerUpdatedEvent; |
| var bindRenderedEvent; |
| var bindMouseEvent; |
| var render; |
| var renderComponents; |
| var renderSeries; |
| var createExtensionAPI; |
| var enableConnect; |
| var markStatusToUpdate; |
| var applyChangedStates; |
| |
| var ECharts = |
| /** @class */ |
| function (_super) { |
| __extends(ECharts, _super); |
| |
| function ECharts(dom, // Theme name or themeOption. |
| theme, opts) { |
| var _this = _super.call(this, new ECEventProcessor()) || this; |
| |
| _this._chartsViews = []; |
| _this._chartsMap = {}; |
| _this._componentsViews = []; |
| _this._componentsMap = {}; // Can't dispatch action during rendering procedure |
| |
| _this._pendingActions = []; |
| opts = opts || {}; // Get theme by name |
| |
| if (isString(theme)) { |
| theme = themeStorage[theme]; |
| } |
| |
| _this._dom = dom; |
| var defaultRenderer = 'canvas'; |
| var defaultCoarsePointer = 'auto'; |
| var defaultUseDirtyRect = false; |
| |
| if ("development" !== 'production') { |
| var root = |
| /* eslint-disable-next-line */ |
| hasWindow ? window : global; |
| defaultRenderer = root.__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer; |
| defaultCoarsePointer = retrieve2(root.__ECHARTS__DEFAULT__COARSE_POINTER, defaultCoarsePointer); |
| var devUseDirtyRect = root.__ECHARTS__DEFAULT__USE_DIRTY_RECT__; |
| defaultUseDirtyRect = devUseDirtyRect == null ? defaultUseDirtyRect : devUseDirtyRect; |
| } |
| |
| var zr = _this._zr = init(dom, { |
| renderer: opts.renderer || defaultRenderer, |
| devicePixelRatio: opts.devicePixelRatio, |
| width: opts.width, |
| height: opts.height, |
| ssr: opts.ssr, |
| useDirtyRect: retrieve2(opts.useDirtyRect, defaultUseDirtyRect), |
| useCoarsePointer: retrieve2(opts.useCoarsePointer, defaultCoarsePointer), |
| pointerSize: opts.pointerSize |
| }); |
| _this._ssr = opts.ssr; // Expect 60 fps. |
| |
| _this._throttledZrFlush = throttle(bind(zr.flush, zr), 17); |
| theme = clone(theme); |
| theme && globalBackwardCompat(theme, true); |
| _this._theme = theme; |
| _this._locale = createLocaleObject(opts.locale || SYSTEM_LANG); |
| _this._coordSysMgr = new CoordinateSystemManager(); |
| var api = _this._api = createExtensionAPI(_this); // Sort on demand |
| |
| function prioritySortFunc(a, b) { |
| return a.__prio - b.__prio; |
| } |
| |
| sort(visualFuncs, prioritySortFunc); |
| sort(dataProcessorFuncs, prioritySortFunc); |
| _this._scheduler = new Scheduler(_this, api, dataProcessorFuncs, visualFuncs); |
| _this._messageCenter = new MessageCenter(); // Init mouse events |
| |
| _this._initEvents(); // In case some people write `window.onresize = chart.resize` |
| |
| |
| _this.resize = bind(_this.resize, _this); |
| zr.animation.on('frame', _this._onframe, _this); |
| bindRenderedEvent(zr, _this); |
| bindMouseEvent(zr, _this); // ECharts instance can be used as value. |
| |
| setAsPrimitive(_this); |
| return _this; |
| } |
| |
| ECharts.prototype._onframe = function () { |
| if (this._disposed) { |
| return; |
| } |
| |
| applyChangedStates(this); |
| var scheduler = this._scheduler; // Lazy update |
| |
| if (this[PENDING_UPDATE]) { |
| var silent = this[PENDING_UPDATE].silent; |
| this[IN_MAIN_PROCESS_KEY] = true; |
| |
| try { |
| prepare(this); |
| updateMethods.update.call(this, null, this[PENDING_UPDATE].updateParams); |
| } catch (e) { |
| this[IN_MAIN_PROCESS_KEY] = false; |
| this[PENDING_UPDATE] = null; |
| throw e; |
| } // At present, in each frame, zrender performs: |
| // (1) animation step forward. |
| // (2) trigger('frame') (where this `_onframe` is called) |
| // (3) zrender flush (render). |
| // If we do nothing here, since we use `setToFinal: true`, the step (3) above |
| // will render the final state of the elements before the real animation started. |
| |
| |
| this._zr.flush(); |
| |
| this[IN_MAIN_PROCESS_KEY] = false; |
| this[PENDING_UPDATE] = null; |
| flushPendingActions.call(this, silent); |
| triggerUpdatedEvent.call(this, silent); |
| } // Avoid do both lazy update and progress in one frame. |
| else if (scheduler.unfinished) { |
| // Stream progress. |
| var remainTime = TEST_FRAME_REMAIN_TIME; |
| var ecModel = this._model; |
| var api = this._api; |
| scheduler.unfinished = false; |
| |
| do { |
| var startTime = +new Date(); |
| scheduler.performSeriesTasks(ecModel); // Currently dataProcessorFuncs do not check threshold. |
| |
| scheduler.performDataProcessorTasks(ecModel); |
| updateStreamModes(this, ecModel); // Do not update coordinate system here. Because that coord system update in |
| // each frame is not a good user experience. So we follow the rule that |
| // the extent of the coordinate system is determined in the first frame (the |
| // frame is executed immediately after task reset. |
| // this._coordSysMgr.update(ecModel, api); |
| // console.log('--- ec frame visual ---', remainTime); |
| |
| scheduler.performVisualTasks(ecModel); |
| renderSeries(this, this._model, api, 'remain', {}); |
| remainTime -= +new Date() - startTime; |
| } while (remainTime > 0 && scheduler.unfinished); // Call flush explicitly for trigger finished event. |
| |
| |
| if (!scheduler.unfinished) { |
| this._zr.flush(); |
| } // Else, zr flushing be ensue within the same frame, |
| // because zr flushing is after onframe event. |
| |
| } |
| }; |
| |
| ECharts.prototype.getDom = function () { |
| return this._dom; |
| }; |
| |
| ECharts.prototype.getId = function () { |
| return this.id; |
| }; |
| |
| ECharts.prototype.getZr = function () { |
| return this._zr; |
| }; |
| |
| ECharts.prototype.isSSR = function () { |
| return this._ssr; |
| }; |
| /* eslint-disable-next-line */ |
| |
| |
| ECharts.prototype.setOption = function (option, notMerge, lazyUpdate) { |
| if (this[IN_MAIN_PROCESS_KEY]) { |
| if ("development" !== 'production') { |
| error('`setOption` should not be called during main process.'); |
| } |
| |
| return; |
| } |
| |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| var silent; |
| var replaceMerge; |
| var transitionOpt; |
| |
| if (isObject(notMerge)) { |
| lazyUpdate = notMerge.lazyUpdate; |
| silent = notMerge.silent; |
| replaceMerge = notMerge.replaceMerge; |
| transitionOpt = notMerge.transition; |
| notMerge = notMerge.notMerge; |
| } |
| |
| this[IN_MAIN_PROCESS_KEY] = true; |
| |
| if (!this._model || notMerge) { |
| var optionManager = new OptionManager(this._api); |
| var theme = this._theme; |
| var ecModel = this._model = new GlobalModel(); |
| ecModel.scheduler = this._scheduler; |
| ecModel.ssr = this._ssr; |
| ecModel.init(null, null, null, theme, this._locale, optionManager); |
| } |
| |
| this._model.setOption(option, { |
| replaceMerge: replaceMerge |
| }, optionPreprocessorFuncs); |
| |
| var updateParams = { |
| seriesTransition: transitionOpt, |
| optionChanged: true |
| }; |
| |
| if (lazyUpdate) { |
| this[PENDING_UPDATE] = { |
| silent: silent, |
| updateParams: updateParams |
| }; |
| this[IN_MAIN_PROCESS_KEY] = false; // `setOption(option, {lazyMode: true})` may be called when zrender has been slept. |
| // It should wake it up to make sure zrender start to render at the next frame. |
| |
| this.getZr().wakeUp(); |
| } else { |
| try { |
| prepare(this); |
| updateMethods.update.call(this, null, updateParams); |
| } catch (e) { |
| this[PENDING_UPDATE] = null; |
| this[IN_MAIN_PROCESS_KEY] = false; |
| throw e; |
| } // Ensure zr refresh sychronously, and then pixel in canvas can be |
| // fetched after `setOption`. |
| |
| |
| if (!this._ssr) { |
| // not use flush when using ssr mode. |
| this._zr.flush(); |
| } |
| |
| this[PENDING_UPDATE] = null; |
| this[IN_MAIN_PROCESS_KEY] = false; |
| flushPendingActions.call(this, silent); |
| triggerUpdatedEvent.call(this, silent); |
| } |
| }; |
| /** |
| * @deprecated |
| */ |
| |
| |
| ECharts.prototype.setTheme = function () { |
| deprecateLog('ECharts#setTheme() is DEPRECATED in ECharts 3.0'); |
| }; // We don't want developers to use getModel directly. |
| |
| |
| ECharts.prototype.getModel = function () { |
| return this._model; |
| }; |
| |
| ECharts.prototype.getOption = function () { |
| return this._model && this._model.getOption(); |
| }; |
| |
| ECharts.prototype.getWidth = function () { |
| return this._zr.getWidth(); |
| }; |
| |
| ECharts.prototype.getHeight = function () { |
| return this._zr.getHeight(); |
| }; |
| |
| ECharts.prototype.getDevicePixelRatio = function () { |
| return this._zr.painter.dpr |
| /* eslint-disable-next-line */ |
| || hasWindow && window.devicePixelRatio || 1; |
| }; |
| /** |
| * Get canvas which has all thing rendered |
| * @deprecated Use renderToCanvas instead. |
| */ |
| |
| |
| ECharts.prototype.getRenderedCanvas = function (opts) { |
| if ("development" !== 'production') { |
| deprecateReplaceLog('getRenderedCanvas', 'renderToCanvas'); |
| } |
| |
| return this.renderToCanvas(opts); |
| }; |
| |
| ECharts.prototype.renderToCanvas = function (opts) { |
| opts = opts || {}; |
| var painter = this._zr.painter; |
| |
| if ("development" !== 'production') { |
| if (painter.type !== 'canvas') { |
| throw new Error('renderToCanvas can only be used in the canvas renderer.'); |
| } |
| } |
| |
| return painter.getRenderedCanvas({ |
| backgroundColor: opts.backgroundColor || this._model.get('backgroundColor'), |
| pixelRatio: opts.pixelRatio || this.getDevicePixelRatio() |
| }); |
| }; |
| |
| ECharts.prototype.renderToSVGString = function (opts) { |
| opts = opts || {}; |
| var painter = this._zr.painter; |
| |
| if ("development" !== 'production') { |
| if (painter.type !== 'svg') { |
| throw new Error('renderToSVGString can only be used in the svg renderer.'); |
| } |
| } |
| |
| return painter.renderToString({ |
| useViewBox: opts.useViewBox |
| }); |
| }; |
| /** |
| * Get svg data url |
| */ |
| |
| |
| ECharts.prototype.getSvgDataURL = function () { |
| if (!env.svgSupported) { |
| return; |
| } |
| |
| var zr = this._zr; |
| var list = zr.storage.getDisplayList(); // Stop animations |
| |
| each(list, function (el) { |
| el.stopAnimation(null, true); |
| }); |
| return zr.painter.toDataURL(); |
| }; |
| |
| ECharts.prototype.getDataURL = function (opts) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| opts = opts || {}; |
| var excludeComponents = opts.excludeComponents; |
| var ecModel = this._model; |
| var excludesComponentViews = []; |
| var self = this; |
| each(excludeComponents, function (componentType) { |
| ecModel.eachComponent({ |
| mainType: componentType |
| }, function (component) { |
| var view = self._componentsMap[component.__viewId]; |
| |
| if (!view.group.ignore) { |
| excludesComponentViews.push(view); |
| view.group.ignore = true; |
| } |
| }); |
| }); |
| var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataURL() : this.renderToCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png')); |
| each(excludesComponentViews, function (view) { |
| view.group.ignore = false; |
| }); |
| return url; |
| }; |
| |
| ECharts.prototype.getConnectedDataURL = function (opts) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| var isSvg = opts.type === 'svg'; |
| var groupId = this.group; |
| var mathMin = Math.min; |
| var mathMax = Math.max; |
| var MAX_NUMBER = Infinity; |
| |
| if (connectedGroups[groupId]) { |
| var left_1 = MAX_NUMBER; |
| var top_1 = MAX_NUMBER; |
| var right_1 = -MAX_NUMBER; |
| var bottom_1 = -MAX_NUMBER; |
| var canvasList_1 = []; |
| var dpr_1 = opts && opts.pixelRatio || this.getDevicePixelRatio(); |
| each(instances$1, function (chart, id) { |
| if (chart.group === groupId) { |
| var canvas = isSvg ? chart.getZr().painter.getSvgDom().innerHTML : chart.renderToCanvas(clone(opts)); |
| var boundingRect = chart.getDom().getBoundingClientRect(); |
| left_1 = mathMin(boundingRect.left, left_1); |
| top_1 = mathMin(boundingRect.top, top_1); |
| right_1 = mathMax(boundingRect.right, right_1); |
| bottom_1 = mathMax(boundingRect.bottom, bottom_1); |
| canvasList_1.push({ |
| dom: canvas, |
| left: boundingRect.left, |
| top: boundingRect.top |
| }); |
| } |
| }); |
| left_1 *= dpr_1; |
| top_1 *= dpr_1; |
| right_1 *= dpr_1; |
| bottom_1 *= dpr_1; |
| var width = right_1 - left_1; |
| var height = bottom_1 - top_1; |
| var targetCanvas = platformApi.createCanvas(); |
| var zr_1 = init(targetCanvas, { |
| renderer: isSvg ? 'svg' : 'canvas' |
| }); |
| zr_1.resize({ |
| width: width, |
| height: height |
| }); |
| |
| if (isSvg) { |
| var content_1 = ''; |
| each(canvasList_1, function (item) { |
| var x = item.left - left_1; |
| var y = item.top - top_1; |
| content_1 += '<g transform="translate(' + x + ',' + y + ')">' + item.dom + '</g>'; |
| }); |
| zr_1.painter.getSvgRoot().innerHTML = content_1; |
| |
| if (opts.connectedBackgroundColor) { |
| zr_1.painter.setBackgroundColor(opts.connectedBackgroundColor); |
| } |
| |
| zr_1.refreshImmediately(); |
| return zr_1.painter.toDataURL(); |
| } else { |
| // Background between the charts |
| if (opts.connectedBackgroundColor) { |
| zr_1.add(new Rect({ |
| shape: { |
| x: 0, |
| y: 0, |
| width: width, |
| height: height |
| }, |
| style: { |
| fill: opts.connectedBackgroundColor |
| } |
| })); |
| } |
| |
| each(canvasList_1, function (item) { |
| var img = new ZRImage({ |
| style: { |
| x: item.left * dpr_1 - left_1, |
| y: item.top * dpr_1 - top_1, |
| image: item.dom |
| } |
| }); |
| zr_1.add(img); |
| }); |
| zr_1.refreshImmediately(); |
| return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png')); |
| } |
| } else { |
| return this.getDataURL(opts); |
| } |
| }; |
| |
| ECharts.prototype.convertToPixel = function (finder, value) { |
| return doConvertPixel(this, 'convertToPixel', finder, value); |
| }; |
| |
| ECharts.prototype.convertFromPixel = function (finder, value) { |
| return doConvertPixel(this, 'convertFromPixel', finder, value); |
| }; |
| /** |
| * Is the specified coordinate systems or components contain the given pixel point. |
| * @param {Array|number} value |
| * @return {boolean} result |
| */ |
| |
| |
| ECharts.prototype.containPixel = function (finder, value) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| var ecModel = this._model; |
| var result; |
| var findResult = parseFinder(ecModel, finder); |
| each(findResult, function (models, key) { |
| key.indexOf('Models') >= 0 && each(models, function (model) { |
| var coordSys = model.coordinateSystem; |
| |
| if (coordSys && coordSys.containPoint) { |
| result = result || !!coordSys.containPoint(value); |
| } else if (key === 'seriesModels') { |
| var view = this._chartsMap[model.__viewId]; |
| |
| if (view && view.containPoint) { |
| result = result || view.containPoint(value, model); |
| } else { |
| if ("development" !== 'production') { |
| console.warn(key + ': ' + (view ? 'The found component do not support containPoint.' : 'No view mapping to the found component.')); |
| } |
| } |
| } else { |
| if ("development" !== 'production') { |
| console.warn(key + ': containPoint is not supported'); |
| } |
| } |
| }, this); |
| }, this); |
| return !!result; |
| }; |
| /** |
| * Get visual from series or data. |
| * @param finder |
| * If string, e.g., 'series', means {seriesIndex: 0}. |
| * If Object, could contain some of these properties below: |
| * { |
| * seriesIndex / seriesId / seriesName, |
| * dataIndex / dataIndexInside |
| * } |
| * If dataIndex is not specified, series visual will be fetched, |
| * but not data item visual. |
| * If all of seriesIndex, seriesId, seriesName are not specified, |
| * visual will be fetched from first series. |
| * @param visualType 'color', 'symbol', 'symbolSize' |
| */ |
| |
| |
| ECharts.prototype.getVisual = function (finder, visualType) { |
| var ecModel = this._model; |
| var parsedFinder = parseFinder(ecModel, finder, { |
| defaultMainType: 'series' |
| }); |
| var seriesModel = parsedFinder.seriesModel; |
| |
| if ("development" !== 'production') { |
| if (!seriesModel) { |
| console.warn('There is no specified seires model'); |
| } |
| } |
| |
| var data = seriesModel.getData(); |
| var dataIndexInside = parsedFinder.hasOwnProperty('dataIndexInside') ? parsedFinder.dataIndexInside : parsedFinder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(parsedFinder.dataIndex) : null; |
| return dataIndexInside != null ? getItemVisualFromData(data, dataIndexInside, visualType) : getVisualFromData(data, visualType); |
| }; |
| /** |
| * Get view of corresponding component model |
| */ |
| |
| |
| ECharts.prototype.getViewOfComponentModel = function (componentModel) { |
| return this._componentsMap[componentModel.__viewId]; |
| }; |
| /** |
| * Get view of corresponding series model |
| */ |
| |
| |
| ECharts.prototype.getViewOfSeriesModel = function (seriesModel) { |
| return this._chartsMap[seriesModel.__viewId]; |
| }; |
| |
| ECharts.prototype._initEvents = function () { |
| var _this = this; |
| |
| each(MOUSE_EVENT_NAMES, function (eveName) { |
| var handler = function (e) { |
| var ecModel = _this.getModel(); |
| |
| var el = e.target; |
| var params; |
| var isGlobalOut = eveName === 'globalout'; // no e.target when 'globalout'. |
| |
| if (isGlobalOut) { |
| params = {}; |
| } else { |
| el && findEventDispatcher(el, function (parent) { |
| var ecData = getECData(parent); |
| |
| if (ecData && ecData.dataIndex != null) { |
| var dataModel = ecData.dataModel || ecModel.getSeriesByIndex(ecData.seriesIndex); |
| params = dataModel && dataModel.getDataParams(ecData.dataIndex, ecData.dataType) || {}; |
| return true; |
| } // If element has custom eventData of components |
| else if (ecData.eventData) { |
| params = extend({}, ecData.eventData); |
| return true; |
| } |
| }, true); |
| } // Contract: if params prepared in mouse event, |
| // these properties must be specified: |
| // { |
| // componentType: string (component main type) |
| // componentIndex: number |
| // } |
| // Otherwise event query can not work. |
| |
| |
| if (params) { |
| var componentType = params.componentType; |
| var componentIndex = params.componentIndex; // Special handling for historic reason: when trigger by |
| // markLine/markPoint/markArea, the componentType is |
| // 'markLine'/'markPoint'/'markArea', but we should better |
| // enable them to be queried by seriesIndex, since their |
| // option is set in each series. |
| |
| if (componentType === 'markLine' || componentType === 'markPoint' || componentType === 'markArea') { |
| componentType = 'series'; |
| componentIndex = params.seriesIndex; |
| } |
| |
| var model = componentType && componentIndex != null && ecModel.getComponent(componentType, componentIndex); |
| var view = model && _this[model.mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]; |
| |
| if ("development" !== 'production') { |
| // `event.componentType` and `event[componentTpype + 'Index']` must not |
| // be missed, otherwise there is no way to distinguish source component. |
| // See `dataFormat.getDataParams`. |
| if (!isGlobalOut && !(model && view)) { |
| console.warn('model or view can not be found by params'); |
| } |
| } |
| |
| params.event = e; |
| params.type = eveName; |
| _this._$eventProcessor.eventInfo = { |
| targetEl: el, |
| packedEvent: params, |
| model: model, |
| view: view |
| }; |
| |
| _this.trigger(eveName, params); |
| } |
| }; // Consider that some component (like tooltip, brush, ...) |
| // register zr event handler, but user event handler might |
| // do anything, such as call `setOption` or `dispatchAction`, |
| // which probably update any of the content and probably |
| // cause problem if it is called previous other inner handlers. |
| |
| |
| handler.zrEventfulCallAtLast = true; |
| |
| _this._zr.on(eveName, handler, _this); |
| }); |
| each(eventActionMap, function (actionType, eventType) { |
| _this._messageCenter.on(eventType, function (event) { |
| this.trigger(eventType, event); |
| }, _this); |
| }); // Extra events |
| // TODO register? |
| |
| each(['selectchanged'], function (eventType) { |
| _this._messageCenter.on(eventType, function (event) { |
| this.trigger(eventType, event); |
| }, _this); |
| }); |
| handleLegacySelectEvents(this._messageCenter, this, this._api); |
| }; |
| |
| ECharts.prototype.isDisposed = function () { |
| return this._disposed; |
| }; |
| |
| ECharts.prototype.clear = function () { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| this.setOption({ |
| series: [] |
| }, true); |
| }; |
| |
| ECharts.prototype.dispose = function () { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| this._disposed = true; |
| var dom = this.getDom(); |
| |
| if (dom) { |
| setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, ''); |
| } |
| |
| var chart = this; |
| var api = chart._api; |
| var ecModel = chart._model; |
| each(chart._componentsViews, function (component) { |
| component.dispose(ecModel, api); |
| }); |
| each(chart._chartsViews, function (chart) { |
| chart.dispose(ecModel, api); |
| }); // Dispose after all views disposed |
| |
| chart._zr.dispose(); // Set properties to null. |
| // To reduce the memory cost in case the top code still holds this instance unexpectedly. |
| |
| |
| chart._dom = chart._model = chart._chartsMap = chart._componentsMap = chart._chartsViews = chart._componentsViews = chart._scheduler = chart._api = chart._zr = chart._throttledZrFlush = chart._theme = chart._coordSysMgr = chart._messageCenter = null; |
| delete instances$1[chart.id]; |
| }; |
| /** |
| * Resize the chart |
| */ |
| |
| |
| ECharts.prototype.resize = function (opts) { |
| if (this[IN_MAIN_PROCESS_KEY]) { |
| if ("development" !== 'production') { |
| error('`resize` should not be called during main process.'); |
| } |
| |
| return; |
| } |
| |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| this._zr.resize(opts); |
| |
| var ecModel = this._model; // Resize loading effect |
| |
| this._loadingFX && this._loadingFX.resize(); |
| |
| if (!ecModel) { |
| return; |
| } |
| |
| var needPrepare = ecModel.resetOption('media'); |
| var silent = opts && opts.silent; // There is some real cases that: |
| // chart.setOption(option, { lazyUpdate: true }); |
| // chart.resize(); |
| |
| if (this[PENDING_UPDATE]) { |
| if (silent == null) { |
| silent = this[PENDING_UPDATE].silent; |
| } |
| |
| needPrepare = true; |
| this[PENDING_UPDATE] = null; |
| } |
| |
| this[IN_MAIN_PROCESS_KEY] = true; |
| |
| try { |
| needPrepare && prepare(this); |
| updateMethods.update.call(this, { |
| type: 'resize', |
| animation: extend({ |
| // Disable animation |
| duration: 0 |
| }, opts && opts.animation) |
| }); |
| } catch (e) { |
| this[IN_MAIN_PROCESS_KEY] = false; |
| throw e; |
| } |
| |
| this[IN_MAIN_PROCESS_KEY] = false; |
| flushPendingActions.call(this, silent); |
| triggerUpdatedEvent.call(this, silent); |
| }; |
| |
| ECharts.prototype.showLoading = function (name, cfg) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| if (isObject(name)) { |
| cfg = name; |
| name = ''; |
| } |
| |
| name = name || 'default'; |
| this.hideLoading(); |
| |
| if (!loadingEffects[name]) { |
| if ("development" !== 'production') { |
| console.warn('Loading effects ' + name + ' not exists.'); |
| } |
| |
| return; |
| } |
| |
| var el = loadingEffects[name](this._api, cfg); |
| var zr = this._zr; |
| this._loadingFX = el; |
| zr.add(el); |
| }; |
| /** |
| * Hide loading effect |
| */ |
| |
| |
| ECharts.prototype.hideLoading = function () { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| this._loadingFX && this._zr.remove(this._loadingFX); |
| this._loadingFX = null; |
| }; |
| |
| ECharts.prototype.makeActionFromEvent = function (eventObj) { |
| var payload = extend({}, eventObj); |
| payload.type = eventActionMap[eventObj.type]; |
| return payload; |
| }; |
| /** |
| * @param opt If pass boolean, means opt.silent |
| * @param opt.silent Default `false`. Whether trigger events. |
| * @param opt.flush Default `undefined`. |
| * true: Flush immediately, and then pixel in canvas can be fetched |
| * immediately. Caution: it might affect performance. |
| * false: Not flush. |
| * undefined: Auto decide whether perform flush. |
| */ |
| |
| |
| ECharts.prototype.dispatchAction = function (payload, opt) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| if (!isObject(opt)) { |
| opt = { |
| silent: !!opt |
| }; |
| } |
| |
| if (!actions[payload.type]) { |
| return; |
| } // Avoid dispatch action before setOption. Especially in `connect`. |
| |
| |
| if (!this._model) { |
| return; |
| } // May dispatchAction in rendering procedure |
| |
| |
| if (this[IN_MAIN_PROCESS_KEY]) { |
| this._pendingActions.push(payload); |
| |
| return; |
| } |
| |
| var silent = opt.silent; |
| doDispatchAction.call(this, payload, silent); |
| var flush = opt.flush; |
| |
| if (flush) { |
| this._zr.flush(); |
| } else if (flush !== false && env.browser.weChat) { |
| // In WeChat embedded browser, `requestAnimationFrame` and `setInterval` |
| // hang when sliding page (on touch event), which cause that zr does not |
| // refresh until user interaction finished, which is not expected. |
| // But `dispatchAction` may be called too frequently when pan on touch |
| // screen, which impacts performance if do not throttle them. |
| this._throttledZrFlush(); |
| } |
| |
| flushPendingActions.call(this, silent); |
| triggerUpdatedEvent.call(this, silent); |
| }; |
| |
| ECharts.prototype.updateLabelLayout = function () { |
| lifecycle.trigger('series:layoutlabels', this._model, this._api, { |
| // Not adding series labels. |
| // TODO |
| updatedSeries: [] |
| }); |
| }; |
| |
| ECharts.prototype.appendData = function (params) { |
| if (this._disposed) { |
| disposedWarning(this.id); |
| return; |
| } |
| |
| var seriesIndex = params.seriesIndex; |
| var ecModel = this.getModel(); |
| var seriesModel = ecModel.getSeriesByIndex(seriesIndex); |
| |
| if ("development" !== 'production') { |
| assert(params.data && seriesModel); |
| } |
| |
| seriesModel.appendData(params); // Note: `appendData` does not support that update extent of coordinate |
| // system, util some scenario require that. In the expected usage of |
| // `appendData`, the initial extent of coordinate system should better |
| // be fixed by axis `min`/`max` setting or initial data, otherwise if |
| // the extent changed while `appendData`, the location of the painted |
| // graphic elements have to be changed, which make the usage of |
| // `appendData` meaningless. |
| |
| this._scheduler.unfinished = true; |
| this.getZr().wakeUp(); |
| }; // A work around for no `internal` modifier in ts yet but |
| // need to strictly hide private methods to JS users. |
| |
| |
| ECharts.internalField = function () { |
| prepare = function (ecIns) { |
| var scheduler = ecIns._scheduler; |
| scheduler.restorePipelines(ecIns._model); |
| scheduler.prepareStageTasks(); |
| prepareView(ecIns, true); |
| prepareView(ecIns, false); |
| scheduler.plan(); |
| }; |
| /** |
| * Prepare view instances of charts and components |
| */ |
| |
| |
| prepareView = function (ecIns, isComponent) { |
| var ecModel = ecIns._model; |
| var scheduler = ecIns._scheduler; |
| var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews; |
| var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap; |
| var zr = ecIns._zr; |
| var api = ecIns._api; |
| |
| for (var i = 0; i < viewList.length; i++) { |
| viewList[i].__alive = false; |
| } |
| |
| isComponent ? ecModel.eachComponent(function (componentType, model) { |
| componentType !== 'series' && doPrepare(model); |
| }) : ecModel.eachSeries(doPrepare); |
| |
| function doPrepare(model) { |
| // By default view will be reused if possible for the case that `setOption` with "notMerge" |
| // mode and need to enable transition animation. (Usually, when they have the same id, or |
| // especially no id but have the same type & name & index. See the `model.id` generation |
| // rule in `makeIdAndName` and `viewId` generation rule here). |
| // But in `replaceMerge` mode, this feature should be able to disabled when it is clear that |
| // the new model has nothing to do with the old model. |
| var requireNewView = model.__requireNewView; // This command should not work twice. |
| |
| model.__requireNewView = false; // Consider: id same and type changed. |
| |
| var viewId = '_ec_' + model.id + '_' + model.type; |
| var view = !requireNewView && viewMap[viewId]; |
| |
| if (!view) { |
| var classType = parseClassType(model.type); |
| var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : // FIXME:TS |
| // (ChartView as ChartViewConstructor).getClass('series', classType.sub) |
| // For backward compat, still support a chart type declared as only subType |
| // like "liquidfill", but recommend "series.liquidfill" |
| // But need a base class to make a type series. |
| ChartView.getClass(classType.sub); |
| |
| if ("development" !== 'production') { |
| assert(Clazz, classType.sub + ' does not exist.'); |
| } |
| |
| view = new Clazz(); |
| view.init(ecModel, api); |
| viewMap[viewId] = view; |
| viewList.push(view); |
| zr.add(view.group); |
| } |
| |
| model.__viewId = view.__id = viewId; |
| view.__alive = true; |
| view.__model = model; |
| view.group.__ecComponentInfo = { |
| mainType: model.mainType, |
| index: model.componentIndex |
| }; |
| !isComponent && scheduler.prepareView(view, model, ecModel, api); |
| } |
| |
| for (var i = 0; i < viewList.length;) { |
| var view = viewList[i]; |
| |
| if (!view.__alive) { |
| !isComponent && view.renderTask.dispose(); |
| zr.remove(view.group); |
| view.dispose(ecModel, api); |
| viewList.splice(i, 1); |
| |
| if (viewMap[view.__id] === view) { |
| delete viewMap[view.__id]; |
| } |
| |
| view.__id = view.group.__ecComponentInfo = null; |
| } else { |
| i++; |
| } |
| } |
| }; |
| |
| updateDirectly = function (ecIns, method, payload, mainType, subType) { |
| var ecModel = ecIns._model; |
| ecModel.setUpdatePayload(payload); // broadcast |
| |
| if (!mainType) { |
| // FIXME |
| // Chart will not be update directly here, except set dirty. |
| // But there is no such scenario now. |
| each([].concat(ecIns._componentsViews).concat(ecIns._chartsViews), callView); |
| return; |
| } |
| |
| var query = {}; |
| query[mainType + 'Id'] = payload[mainType + 'Id']; |
| query[mainType + 'Index'] = payload[mainType + 'Index']; |
| query[mainType + 'Name'] = payload[mainType + 'Name']; |
| var condition = { |
| mainType: mainType, |
| query: query |
| }; |
| subType && (condition.subType = subType); // subType may be '' by parseClassType; |
| |
| var excludeSeriesId = payload.excludeSeriesId; |
| var excludeSeriesIdMap; |
| |
| if (excludeSeriesId != null) { |
| excludeSeriesIdMap = createHashMap(); |
| each(normalizeToArray(excludeSeriesId), function (id) { |
| var modelId = convertOptionIdName(id, null); |
| |
| if (modelId != null) { |
| excludeSeriesIdMap.set(modelId, true); |
| } |
| }); |
| } // If dispatchAction before setOption, do nothing. |
| |
| |
| ecModel && ecModel.eachComponent(condition, function (model) { |
| var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) !== null; |
| |
| if (isExcluded) { |
| return; |
| } |
| |
| if (isHighDownPayload(payload)) { |
| if (model instanceof SeriesModel) { |
| if (payload.type === HIGHLIGHT_ACTION_TYPE && !payload.notBlur && !model.get(['emphasis', 'disabled'])) { |
| blurSeriesFromHighlightPayload(model, payload, ecIns._api); |
| } |
| } else { |
| var _a = findComponentHighDownDispatchers(model.mainType, model.componentIndex, payload.name, ecIns._api), |
| focusSelf = _a.focusSelf, |
| dispatchers = _a.dispatchers; |
| |
| if (payload.type === HIGHLIGHT_ACTION_TYPE && focusSelf && !payload.notBlur) { |
| blurComponent(model.mainType, model.componentIndex, ecIns._api); |
| } // PENDING: |
| // Whether to put this "enter emphasis" code in `ComponentView`, |
| // which will be the same as `ChartView` but might be not necessary |
| // and will be far from this logic. |
| |
| |
| if (dispatchers) { |
| each(dispatchers, function (dispatcher) { |
| payload.type === HIGHLIGHT_ACTION_TYPE ? enterEmphasis(dispatcher) : leaveEmphasis(dispatcher); |
| }); |
| } |
| } |
| } else if (isSelectChangePayload(payload)) { |
| // TODO geo |
| if (model instanceof SeriesModel) { |
| toggleSelectionFromPayload(model, payload, ecIns._api); |
| updateSeriesElementSelection(model); |
| markStatusToUpdate(ecIns); |
| } |
| } |
| }, ecIns); |
| ecModel && ecModel.eachComponent(condition, function (model) { |
| var isExcluded = excludeSeriesIdMap && excludeSeriesIdMap.get(model.id) !== null; |
| |
| if (isExcluded) { |
| return; |
| } |
| callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]); |
| }, ecIns); |
| |
| function callView(view) { |
| view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload); |
| } |
| }; |
| |
| updateMethods = { |
| prepareAndUpdate: function (payload) { |
| prepare(this); |
| updateMethods.update.call(this, payload, { |
| // Needs to mark option changed if newOption is given. |
| // It's from MagicType. |
| // TODO If use a separate flag optionChanged in payload? |
| optionChanged: payload.newOption != null |
| }); |
| }, |
| update: function (payload, updateParams) { |
| var ecModel = this._model; |
| var api = this._api; |
| var zr = this._zr; |
| var coordSysMgr = this._coordSysMgr; |
| var scheduler = this._scheduler; // update before setOption |
| |
| if (!ecModel) { |
| return; |
| } |
| |
| ecModel.setUpdatePayload(payload); |
| scheduler.restoreData(ecModel, payload); |
| scheduler.performSeriesTasks(ecModel); // TODO |
| // Save total ecModel here for undo/redo (after restoring data and before processing data). |
| // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call. |
| // Create new coordinate system each update |
| // In LineView may save the old coordinate system and use it to get the original point. |
| |
| coordSysMgr.create(ecModel, api); |
| scheduler.performDataProcessorTasks(ecModel, payload); // Current stream render is not supported in data process. So we can update |
| // stream modes after data processing, where the filtered data is used to |
| // determine whether to use progressive rendering. |
| |
| updateStreamModes(this, ecModel); // We update stream modes before coordinate system updated, then the modes info |
| // can be fetched when coord sys updating (consider the barGrid extent fix). But |
| // the drawback is the full coord info can not be fetched. Fortunately this full |
| // coord is not required in stream mode updater currently. |
| |
| coordSysMgr.update(ecModel, api); |
| clearColorPalette(ecModel); |
| scheduler.performVisualTasks(ecModel, payload); |
| render(this, ecModel, api, payload, updateParams); // Set background |
| |
| var backgroundColor = ecModel.get('backgroundColor') || 'transparent'; |
| var darkMode = ecModel.get('darkMode'); |
| zr.setBackgroundColor(backgroundColor); // Force set dark mode. |
| |
| if (darkMode != null && darkMode !== 'auto') { |
| zr.setDarkMode(darkMode); |
| } |
| |
| lifecycle.trigger('afterupdate', ecModel, api); |
| }, |
| updateTransform: function (payload) { |
| var _this = this; |
| |
| var ecModel = this._model; |
| var api = this._api; // update before setOption |
| |
| if (!ecModel) { |
| return; |
| } |
| |
| ecModel.setUpdatePayload(payload); // ChartView.markUpdateMethod(payload, 'updateTransform'); |
| |
| var componentDirtyList = []; |
| ecModel.eachComponent(function (componentType, componentModel) { |
| if (componentType === 'series') { |
| return; |
| } |
| |
| var componentView = _this.getViewOfComponentModel(componentModel); |
| |
| if (componentView && componentView.__alive) { |
| if (componentView.updateTransform) { |
| var result = componentView.updateTransform(componentModel, ecModel, api, payload); |
| result && result.update && componentDirtyList.push(componentView); |
| } else { |
| componentDirtyList.push(componentView); |
| } |
| } |
| }); |
| var seriesDirtyMap = createHashMap(); |
| ecModel.eachSeries(function (seriesModel) { |
| var chartView = _this._chartsMap[seriesModel.__viewId]; |
| |
| if (chartView.updateTransform) { |
| var result = chartView.updateTransform(seriesModel, ecModel, api, payload); |
| result && result.update && seriesDirtyMap.set(seriesModel.uid, 1); |
| } else { |
| seriesDirtyMap.set(seriesModel.uid, 1); |
| } |
| }); |
| clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. |
| // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true); |
| |
| this._scheduler.performVisualTasks(ecModel, payload, { |
| setDirty: true, |
| dirtyMap: seriesDirtyMap |
| }); // Currently, not call render of components. Geo render cost a lot. |
| // renderComponents(ecIns, ecModel, api, payload, componentDirtyList); |
| |
| |
| renderSeries(this, ecModel, api, payload, {}, seriesDirtyMap); |
| lifecycle.trigger('afterupdate', ecModel, api); |
| }, |
| updateView: function (payload) { |
| var ecModel = this._model; // update before setOption |
| |
| if (!ecModel) { |
| return; |
| } |
| |
| ecModel.setUpdatePayload(payload); |
| ChartView.markUpdateMethod(payload, 'updateView'); |
| clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. |
| |
| this._scheduler.performVisualTasks(ecModel, payload, { |
| setDirty: true |
| }); |
| |
| render(this, ecModel, this._api, payload, {}); |
| lifecycle.trigger('afterupdate', ecModel, this._api); |
| }, |
| updateVisual: function (payload) { |
| // updateMethods.update.call(this, payload); |
| var _this = this; |
| |
| var ecModel = this._model; // update before setOption |
| |
| if (!ecModel) { |
| return; |
| } |
| |
| ecModel.setUpdatePayload(payload); // clear all visual |
| |
| ecModel.eachSeries(function (seriesModel) { |
| seriesModel.getData().clearAllVisual(); |
| }); // Perform visual |
| |
| ChartView.markUpdateMethod(payload, 'updateVisual'); |
| clearColorPalette(ecModel); // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline. |
| |
| this._scheduler.performVisualTasks(ecModel, payload, { |
| visualType: 'visual', |
| setDirty: true |
| }); |
| |
| ecModel.eachComponent(function (componentType, componentModel) { |
| if (componentType !== 'series') { |
| var componentView = _this.getViewOfComponentModel(componentModel); |
| |
| componentView && componentView.__alive && componentView.updateVisual(componentModel, ecModel, _this._api, payload); |
| } |
| }); |
| ecModel.eachSeries(function (seriesModel) { |
| var chartView = _this._chartsMap[seriesModel.__viewId]; |
| chartView.updateVisual(seriesModel, ecModel, _this._api, payload); |
| }); |
| lifecycle.trigger('afterupdate', ecModel, this._api); |
| }, |
| updateLayout: function (payload) { |
| updateMethods.update.call(this, payload); |
| } |
| }; |
| |
| doConvertPixel = function (ecIns, methodName, finder, value) { |
| if (ecIns._disposed) { |
| disposedWarning(ecIns.id); |
| return; |
| } |
| |
| var ecModel = ecIns._model; |
| |
| var coordSysList = ecIns._coordSysMgr.getCoordinateSystems(); |
| |
| var result; |
| var parsedFinder = parseFinder(ecModel, finder); |
| |
| for (var i = 0; i < coordSysList.length; i++) { |
| var coordSys = coordSysList[i]; |
| |
| if (coordSys[methodName] && (result = coordSys[methodName](ecModel, parsedFinder, value)) != null) { |
| return result; |
| } |
| } |
| |
| if ("development" !== 'production') { |
| console.warn('No coordinate system that supports ' + methodName + ' found by the given finder.'); |
| } |
| }; |
| |
| updateStreamModes = function (ecIns, ecModel) { |
| var chartsMap = ecIns._chartsMap; |
| var scheduler = ecIns._scheduler; |
| ecModel.eachSeries(function (seriesModel) { |
| scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]); |
| }); |
| }; |
| |
| doDispatchAction = function (payload, silent) { |
| var _this = this; |
| |
| var ecModel = this.getModel(); |
| var payloadType = payload.type; |
| var escapeConnect = payload.escapeConnect; |
| var actionWrap = actions[payloadType]; |
| var actionInfo = actionWrap.actionInfo; |
| var cptTypeTmp = (actionInfo.update || 'update').split(':'); |
| var updateMethod = cptTypeTmp.pop(); |
| var cptType = cptTypeTmp[0] != null && parseClassType(cptTypeTmp[0]); |
| this[IN_MAIN_PROCESS_KEY] = true; |
| var payloads = [payload]; |
| var batched = false; // Batch action |
| |
| if (payload.batch) { |
| batched = true; |
| payloads = map(payload.batch, function (item) { |
| item = defaults(extend({}, item), payload); |
| item.batch = null; |
| return item; |
| }); |
| } |
| |
| var eventObjBatch = []; |
| var eventObj; |
| var isSelectChange = isSelectChangePayload(payload); |
| var isHighDown = isHighDownPayload(payload); // Only leave blur once if there are multiple batches. |
| |
| if (isHighDown) { |
| allLeaveBlur(this._api); |
| } |
| |
| each(payloads, function (batchItem) { |
| // Action can specify the event by return it. |
| eventObj = actionWrap.action(batchItem, _this._model, _this._api); // Emit event outside |
| |
| eventObj = eventObj || extend({}, batchItem); // Convert type to eventType |
| |
| eventObj.type = actionInfo.event || eventObj.type; |
| eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual. |
| |
| if (isHighDown) { |
| var _a = preParseFinder(payload), |
| queryOptionMap = _a.queryOptionMap, |
| mainTypeSpecified = _a.mainTypeSpecified; |
| |
| var componentMainType = mainTypeSpecified ? queryOptionMap.keys()[0] : 'series'; |
| updateDirectly(_this, updateMethod, batchItem, componentMainType); |
| markStatusToUpdate(_this); |
| } else if (isSelectChange) { |
| // At present `dispatchAction({ type: 'select', ... })` is not supported on components. |
| // geo still use 'geoselect'. |
| updateDirectly(_this, updateMethod, batchItem, 'series'); |
| markStatusToUpdate(_this); |
| } else if (cptType) { |
| updateDirectly(_this, updateMethod, batchItem, cptType.main, cptType.sub); |
| } |
| }); |
| |
| if (updateMethod !== 'none' && !isHighDown && !isSelectChange && !cptType) { |
| try { |
| // Still dirty |
| if (this[PENDING_UPDATE]) { |
| prepare(this); |
| updateMethods.update.call(this, payload); |
| this[PENDING_UPDATE] = null; |
| } else { |
| updateMethods[updateMethod].call(this, payload); |
| } |
| } catch (e) { |
| this[IN_MAIN_PROCESS_KEY] = false; |
| throw e; |
| } |
| } // Follow the rule of action batch |
| |
| |
| if (batched) { |
| eventObj = { |
| type: actionInfo.event || payloadType, |
| escapeConnect: escapeConnect, |
| batch: eventObjBatch |
| }; |
| } else { |
| eventObj = eventObjBatch[0]; |
| } |
| |
| this[IN_MAIN_PROCESS_KEY] = false; |
| |
| if (!silent) { |
| var messageCenter = this._messageCenter; |
| messageCenter.trigger(eventObj.type, eventObj); // Extra triggered 'selectchanged' event |
| |
| if (isSelectChange) { |
| var newObj = { |
| type: 'selectchanged', |
| escapeConnect: escapeConnect, |
| selected: getAllSelectedIndices(ecModel), |
| isFromClick: payload.isFromClick || false, |
| fromAction: payload.type, |
| fromActionPayload: payload |
| }; |
| messageCenter.trigger(newObj.type, newObj); |
| } |
| } |
| }; |
| |
| flushPendingActions = function (silent) { |
| var pendingActions = this._pendingActions; |
| |
| while (pendingActions.length) { |
| var payload = pendingActions.shift(); |
| doDispatchAction.call(this, payload, silent); |
| } |
| }; |
| |
| triggerUpdatedEvent = function (silent) { |
| !silent && this.trigger('updated'); |
| }; |
| /** |
| * Event `rendered` is triggered when zr |
| * rendered. It is useful for realtime |
| * snapshot (reflect animation). |
| * |
| * Event `finished` is triggered when: |
| * (1) zrender rendering finished. |
| * (2) initial animation finished. |
| * (3) progressive rendering finished. |
| * (4) no pending action. |
| * (5) no delayed setOption needs to be processed. |
| */ |
| |
| |
| bindRenderedEvent = function (zr, ecIns) { |
| zr.on('rendered', function (params) { |
| ecIns.trigger('rendered', params); // The `finished` event should not be triggered repeatedly, |
| // so it should only be triggered when rendering indeed happens |
| // in zrender. (Consider the case that dipatchAction is keep |
| // triggering when mouse move). |
| |
| if ( // Although zr is dirty if initial animation is not finished |
| // and this checking is called on frame, we also check |
| // animation finished for robustness. |
| zr.animation.isFinished() && !ecIns[PENDING_UPDATE] && !ecIns._scheduler.unfinished && !ecIns._pendingActions.length) { |
| ecIns.trigger('finished'); |
| } |
| }); |
| }; |
| |
| bindMouseEvent = function (zr, ecIns) { |
| zr.on('mouseover', function (e) { |
| var el = e.target; |
| var dispatcher = findEventDispatcher(el, isHighDownDispatcher); |
| |
| if (dispatcher) { |
| handleGlobalMouseOverForHighDown(dispatcher, e, ecIns._api); |
| markStatusToUpdate(ecIns); |
| } |
| }).on('mouseout', function (e) { |
| var el = e.target; |
| var dispatcher = findEventDispatcher(el, isHighDownDispatcher); |
| |
| if (dispatcher) { |
| handleGlobalMouseOutForHighDown(dispatcher, e, ecIns._api); |
| markStatusToUpdate(ecIns); |
| } |
| }).on('click', function (e) { |
| var el = e.target; |
| var dispatcher = findEventDispatcher(el, function (target) { |
| return getECData(target).dataIndex != null; |
| }, true); |
| |
| if (dispatcher) { |
| var actionType = dispatcher.selected ? 'unselect' : 'select'; |
| var ecData = getECData(dispatcher); |
| |
| ecIns._api.dispatchAction({ |
| type: actionType, |
| dataType: ecData.dataType, |
| dataIndexInside: ecData.dataIndex, |
| seriesIndex: ecData.seriesIndex, |
| isFromClick: true |
| }); |
| } |
| }); |
| }; |
| |
| function clearColorPalette(ecModel) { |
| ecModel.clearColorPalette(); |
| ecModel.eachSeries(function (seriesModel) { |
| seriesModel.clearColorPalette(); |
| }); |
| } |
| |
| function allocateZlevels(ecModel) { |
| var componentZLevels = []; |
| var seriesZLevels = []; |
| var hasSeperateZLevel = false; |
| ecModel.eachComponent(function (componentType, componentModel) { |
| var zlevel = componentModel.get('zlevel') || 0; |
| var z = componentModel.get('z') || 0; |
| var zlevelKey = componentModel.getZLevelKey(); |
| hasSeperateZLevel = hasSeperateZLevel || !!zlevelKey; |
| (componentType === 'series' ? seriesZLevels : componentZLevels).push({ |
| zlevel: zlevel, |
| z: z, |
| idx: componentModel.componentIndex, |
| type: componentType, |
| key: zlevelKey |
| }); |
| }); |
| |
| if (hasSeperateZLevel) { |
| // Series after component |
| var zLevels = componentZLevels.concat(seriesZLevels); |
| var lastSeriesZLevel_1; |
| var lastSeriesKey_1; |
| sort(zLevels, function (a, b) { |
| if (a.zlevel === b.zlevel) { |
| return a.z - b.z; |
| } |
| |
| return a.zlevel - b.zlevel; |
| }); |
| each(zLevels, function (item) { |
| var componentModel = ecModel.getComponent(item.type, item.idx); |
| var zlevel = item.zlevel; |
| var key = item.key; |
| |
| if (lastSeriesZLevel_1 != null) { |
| zlevel = Math.max(lastSeriesZLevel_1, zlevel); |
| } |
| |
| if (key) { |
| if (zlevel === lastSeriesZLevel_1 && key !== lastSeriesKey_1) { |
| zlevel++; |
| } |
| |
| lastSeriesKey_1 = key; |
| } else if (lastSeriesKey_1) { |
| if (zlevel === lastSeriesZLevel_1) { |
| zlevel++; |
| } |
| |
| lastSeriesKey_1 = ''; |
| } |
| |
| lastSeriesZLevel_1 = zlevel; |
| componentModel.setZLevel(zlevel); |
| }); |
| } |
| } |
| |
| render = function (ecIns, ecModel, api, payload, updateParams) { |
| allocateZlevels(ecModel); |
| renderComponents(ecIns, ecModel, api, payload, updateParams); |
| each(ecIns._chartsViews, function (chart) { |
| chart.__alive = false; |
| }); |
| renderSeries(ecIns, ecModel, api, payload, updateParams); // Remove groups of unrendered charts |
| |
| each(ecIns._chartsViews, function (chart) { |
| if (!chart.__alive) { |
| chart.remove(ecModel, api); |
| } |
| }); |
| }; |
| |
| renderComponents = function (ecIns, ecModel, api, payload, updateParams, dirtyList) { |
| each(dirtyList || ecIns._componentsViews, function (componentView) { |
| var componentModel = componentView.__model; |
| clearStates(componentModel, componentView); |
| componentView.render(componentModel, ecModel, api, payload); |
| updateZ(componentModel, componentView); |
| updateStates(componentModel, componentView); |
| }); |
| }; |
| /** |
| * Render each chart and component |
| */ |
| |
| |
| renderSeries = function (ecIns, ecModel, api, payload, updateParams, dirtyMap) { |
| // Render all charts |
| var scheduler = ecIns._scheduler; |
| updateParams = extend(updateParams || {}, { |
| updatedSeries: ecModel.getSeries() |
| }); // TODO progressive? |
| |
| lifecycle.trigger('series:beforeupdate', ecModel, api, updateParams); |
| var unfinished = false; |
| ecModel.eachSeries(function (seriesModel) { |
| var chartView = ecIns._chartsMap[seriesModel.__viewId]; |
| chartView.__alive = true; |
| var renderTask = chartView.renderTask; |
| scheduler.updatePayload(renderTask, payload); // TODO states on marker. |
| |
| clearStates(seriesModel, chartView); |
| |
| if (dirtyMap && dirtyMap.get(seriesModel.uid)) { |
| renderTask.dirty(); |
| } |
| |
| if (renderTask.perform(scheduler.getPerformArgs(renderTask))) { |
| unfinished = true; |
| } |
| |
| chartView.group.silent = !!seriesModel.get('silent'); // Should not call markRedraw on group, because it will disable zrender |
| // incremental render (always render from the __startIndex each frame) |
| // chartView.group.markRedraw(); |
| |
| updateBlend(seriesModel, chartView); |
| updateSeriesElementSelection(seriesModel); |
| }); |
| scheduler.unfinished = unfinished || scheduler.unfinished; |
| lifecycle.trigger('series:layoutlabels', ecModel, api, updateParams); // transition after label is layouted. |
| |
| lifecycle.trigger('series:transition', ecModel, api, updateParams); |
| ecModel.eachSeries(function (seriesModel) { |
| var chartView = ecIns._chartsMap[seriesModel.__viewId]; // Update Z after labels updated. Before applying states. |
| |
| updateZ(seriesModel, chartView); // NOTE: Update states after label is updated. |
| // label should be in normal status when layouting. |
| |
| updateStates(seriesModel, chartView); |
| }); // If use hover layer |
| |
| updateHoverLayerStatus(ecIns, ecModel); |
| lifecycle.trigger('series:afterupdate', ecModel, api, updateParams); |
| }; |
| |
| markStatusToUpdate = function (ecIns) { |
| ecIns[STATUS_NEEDS_UPDATE_KEY] = true; // Wake up zrender if it's sleep. Let it update states in the next frame. |
| |
| ecIns.getZr().wakeUp(); |
| }; |
| |
| applyChangedStates = function (ecIns) { |
| if (!ecIns[STATUS_NEEDS_UPDATE_KEY]) { |
| return; |
| } |
| |
| ecIns.getZr().storage.traverse(function (el) { |
| // Not applied on removed elements, it may still in fading. |
| if (isElementRemoved(el)) { |
| return; |
| } |
| |
| applyElementStates(el); |
| }); |
| ecIns[STATUS_NEEDS_UPDATE_KEY] = false; |
| }; |
| |
| function applyElementStates(el) { |
| var newStates = []; |
| var oldStates = el.currentStates; // Keep other states. |
| |
| for (var i = 0; i < oldStates.length; i++) { |
| var stateName = oldStates[i]; |
| |
| if (!(stateName === 'emphasis' || stateName === 'blur' || stateName === 'select')) { |
| newStates.push(stateName); |
| } |
| } // Only use states when it's exists. |
| |
| |
| if (el.selected && el.states.select) { |
| newStates.push('select'); |
| } |
| |
| if (el.hoverState === HOVER_STATE_EMPHASIS && el.states.emphasis) { |
| newStates.push('emphasis'); |
| } else if (el.hoverState === HOVER_STATE_BLUR && el.states.blur) { |
| newStates.push('blur'); |
| } |
| |
| el.useStates(newStates); |
| } |
| |
| function updateHoverLayerStatus(ecIns, ecModel) { |
| var zr = ecIns._zr; |
| var storage = zr.storage; |
| var elCount = 0; |
| storage.traverse(function (el) { |
| if (!el.isGroup) { |
| elCount++; |
| } |
| }); |
| |
| if (elCount > ecModel.get('hoverLayerThreshold') && !env.node && !env.worker) { |
| ecModel.eachSeries(function (seriesModel) { |
| if (seriesModel.preventUsingHoverLayer) { |
| return; |
| } |
| |
| var chartView = ecIns._chartsMap[seriesModel.__viewId]; |
| |
| if (chartView.__alive) { |
| chartView.eachRendered(function (el) { |
| if (el.states.emphasis) { |
| el.states.emphasis.hoverLayer = true; |
| } |
| }); |
| } |
| }); |
| } |
| } |
| /** |
| * Update chart and blend. |
| */ |
| |
| function updateBlend(seriesModel, chartView) { |
| var blendMode = seriesModel.get('blendMode') || null; |
| chartView.eachRendered(function (el) { |
| // FIXME marker and other components |
| if (!el.isGroup) { |
| // DON'T mark the element dirty. In case element is incremental and don't want to rerender. |
| el.style.blend = blendMode; |
| } |
| }); |
| } |
| |
| function updateZ(model, view) { |
| if (model.preventAutoZ) { |
| return; |
| } |
| |
| var z = model.get('z') || 0; |
| var zlevel = model.get('zlevel') || 0; // Set z and zlevel |
| |
| view.eachRendered(function (el) { |
| doUpdateZ(el, z, zlevel, -Infinity); // Don't traverse the children because it has been traversed in _updateZ. |
| |
| return true; |
| }); |
| } |
| |
| function doUpdateZ(el, z, zlevel, maxZ2) { |
| // Group may also have textContent |
| var label = el.getTextContent(); |
| var labelLine = el.getTextGuideLine(); |
| var isGroup = el.isGroup; |
| |
| if (isGroup) { |
| // set z & zlevel of children elements of Group |
| var children = el.childrenRef(); |
| |
| for (var i = 0; i < children.length; i++) { |
| maxZ2 = Math.max(doUpdateZ(children[i], z, zlevel, maxZ2), maxZ2); |
| } |
| } else { |
| // not Group |
| el.z = z; |
| el.zlevel = zlevel; |
| maxZ2 = Math.max(el.z2, maxZ2); |
| } // always set z and zlevel if label/labelLine exists |
| |
| |
| if (label) { |
| label.z = z; |
| label.zlevel = zlevel; // lift z2 of text content |
| // TODO if el.emphasis.z2 is spcefied, what about textContent. |
| |
| isFinite(maxZ2) && (label.z2 = maxZ2 + 2); |
| } |
| |
| if (labelLine) { |
| var textGuideLineConfig = el.textGuideLineConfig; |
| labelLine.z = z; |
| labelLine.zlevel = zlevel; |
| isFinite(maxZ2) && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1)); |
| } |
| |
| return maxZ2; |
| } // Clear states without animation. |
| // TODO States on component. |
| |
| |
| function clearStates(model, view) { |
| view.eachRendered(function (el) { |
| // Not applied on removed elements, it may still in fading. |
| if (isElementRemoved(el)) { |
| return; |
| } |
| |
| var textContent = el.getTextContent(); |
| var textGuide = el.getTextGuideLine(); |
| |
| if (el.stateTransition) { |
| el.stateTransition = null; |
| } |
| |
| if (textContent && textContent.stateTransition) { |
| textContent.stateTransition = null; |
| } |
| |
| if (textGuide && textGuide.stateTransition) { |
| textGuide.stateTransition = null; |
| } // TODO If el is incremental. |
| |
| |
| if (el.hasState()) { |
| el.prevStates = el.currentStates; |
| el.clearStates(); |
| } else if (el.prevStates) { |
| el.prevStates = null; |
| } |
| }); |
| } |
| |
| function updateStates(model, view) { |
| var stateAnimationModel = model.getModel('stateAnimation'); |
| var enableAnimation = model.isAnimationEnabled(); |
| var duration = stateAnimationModel.get('duration'); |
| var stateTransition = duration > 0 ? { |
| duration: duration, |
| delay: stateAnimationModel.get('delay'), |
| easing: stateAnimationModel.get('easing') // additive: stateAnimationModel.get('additive') |
| |
| } : null; |
| view.eachRendered(function (el) { |
| if (el.states && el.states.emphasis) { |
| // Not applied on removed elements, it may still in fading. |
| if (isElementRemoved(el)) { |
| return; |
| } |
| |
| if (el instanceof Path) { |
| savePathStates(el); |
| } // Only updated on changed element. In case element is incremental and don't want to rerender. |
| // TODO, a more proper way? |
| |
| |
| if (el.__dirty) { |
| var prevStates = el.prevStates; // Restore states without animation |
| |
| if (prevStates) { |
| el.useStates(prevStates); |
| } |
| } // Update state transition and enable animation again. |
| |
| |
| if (enableAnimation) { |
| el.stateTransition = stateTransition; |
| var textContent = el.getTextContent(); |
| var textGuide = el.getTextGuideLine(); // TODO Is it necessary to animate label? |
| |
| if (textContent) { |
| textContent.stateTransition = stateTransition; |
| } |
| |
| if (textGuide) { |
| textGuide.stateTransition = stateTransition; |
| } |
| } // Use highlighted and selected flag to toggle states. |
| |
| |
| if (el.__dirty) { |
| applyElementStates(el); |
| } |
| } |
| }); |
| } |
| |
| createExtensionAPI = function (ecIns) { |
| return new ( |
| /** @class */ |
| function (_super) { |
| __extends(class_1, _super); |
| |
| function class_1() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| class_1.prototype.getCoordinateSystems = function () { |
| return ecIns._coordSysMgr.getCoordinateSystems(); |
| }; |
| |
| class_1.prototype.getComponentByElement = function (el) { |
| while (el) { |
| var modelInfo = el.__ecComponentInfo; |
| |
| if (modelInfo != null) { |
| return ecIns._model.getComponent(modelInfo.mainType, modelInfo.index); |
| } |
| |
| el = el.parent; |
| } |
| }; |
| |
| class_1.prototype.enterEmphasis = function (el, highlightDigit) { |
| enterEmphasis(el, highlightDigit); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.leaveEmphasis = function (el, highlightDigit) { |
| leaveEmphasis(el, highlightDigit); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.enterBlur = function (el) { |
| enterBlur(el); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.leaveBlur = function (el) { |
| leaveBlur(el); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.enterSelect = function (el) { |
| enterSelect(el); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.leaveSelect = function (el) { |
| leaveSelect(el); |
| markStatusToUpdate(ecIns); |
| }; |
| |
| class_1.prototype.getModel = function () { |
| return ecIns.getModel(); |
| }; |
| |
| class_1.prototype.getViewOfComponentModel = function (componentModel) { |
| return ecIns.getViewOfComponentModel(componentModel); |
| }; |
| |
| class_1.prototype.getViewOfSeriesModel = function (seriesModel) { |
| return ecIns.getViewOfSeriesModel(seriesModel); |
| }; |
| |
| return class_1; |
| }(ExtensionAPI))(ecIns); |
| }; |
| |
| enableConnect = function (chart) { |
| function updateConnectedChartsStatus(charts, status) { |
| for (var i = 0; i < charts.length; i++) { |
| var otherChart = charts[i]; |
| otherChart[CONNECT_STATUS_KEY] = status; |
| } |
| } |
| |
| each(eventActionMap, function (actionType, eventType) { |
| chart._messageCenter.on(eventType, function (event) { |
| if (connectedGroups[chart.group] && chart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_PENDING) { |
| if (event && event.escapeConnect) { |
| return; |
| } |
| |
| var action_1 = chart.makeActionFromEvent(event); |
| var otherCharts_1 = []; |
| each(instances$1, function (otherChart) { |
| if (otherChart !== chart && otherChart.group === chart.group) { |
| otherCharts_1.push(otherChart); |
| } |
| }); |
| updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_PENDING); |
| each(otherCharts_1, function (otherChart) { |
| if (otherChart[CONNECT_STATUS_KEY] !== CONNECT_STATUS_UPDATING) { |
| otherChart.dispatchAction(action_1); |
| } |
| }); |
| updateConnectedChartsStatus(otherCharts_1, CONNECT_STATUS_UPDATED); |
| } |
| }); |
| }); |
| }; |
| }(); |
| |
| return ECharts; |
| }(Eventful); |
| |
| var echartsProto = ECharts.prototype; |
| echartsProto.on = createRegisterEventWithLowercaseECharts('on'); |
| echartsProto.off = createRegisterEventWithLowercaseECharts('off'); |
| /** |
| * @deprecated |
| */ |
| // @ts-ignore |
| |
| echartsProto.one = function (eventName, cb, ctx) { |
| var self = this; |
| deprecateLog('ECharts#one is deprecated.'); |
| |
| function wrapped() { |
| var args2 = []; |
| |
| for (var _i = 0; _i < arguments.length; _i++) { |
| args2[_i] = arguments[_i]; |
| } |
| |
| cb && cb.apply && cb.apply(this, args2); // @ts-ignore |
| |
| self.off(eventName, wrapped); |
| } |
| |
| this.on.call(this, eventName, wrapped, ctx); |
| }; |
| |
| var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu']; |
| |
| function disposedWarning(id) { |
| if ("development" !== 'production') { |
| console.warn('Instance ' + id + ' has been disposed'); |
| } |
| } |
| |
| var actions = {}; |
| /** |
| * Map eventType to actionType |
| */ |
| |
| var eventActionMap = {}; |
| var dataProcessorFuncs = []; |
| var optionPreprocessorFuncs = []; |
| var visualFuncs = []; |
| var themeStorage = {}; |
| var loadingEffects = {}; |
| var instances$1 = {}; |
| var connectedGroups = {}; |
| var idBase = +new Date() - 0; |
| var groupIdBase = +new Date() - 0; |
| var DOM_ATTRIBUTE_KEY = '_echarts_instance_'; |
| /** |
| * @param opts.devicePixelRatio Use window.devicePixelRatio by default |
| * @param opts.renderer Can choose 'canvas' or 'svg' to render the chart. |
| * @param opts.width Use clientWidth of the input `dom` by default. |
| * Can be 'auto' (the same as null/undefined) |
| * @param opts.height Use clientHeight of the input `dom` by default. |
| * Can be 'auto' (the same as null/undefined) |
| * @param opts.locale Specify the locale. |
| * @param opts.useDirtyRect Enable dirty rectangle rendering or not. |
| */ |
| |
| function init$1(dom, theme, opts) { |
| var isClient = !(opts && opts.ssr); |
| |
| if (isClient) { |
| if ("development" !== 'production') { |
| if (!dom) { |
| throw new Error('Initialize failed: invalid dom.'); |
| } |
| } |
| |
| var existInstance = getInstanceByDom(dom); |
| |
| if (existInstance) { |
| if ("development" !== 'production') { |
| console.warn('There is a chart instance already initialized on the dom.'); |
| } |
| |
| return existInstance; |
| } |
| |
| if ("development" !== 'production') { |
| if (isDom(dom) && dom.nodeName.toUpperCase() !== 'CANVAS' && (!dom.clientWidth && (!opts || opts.width == null) || !dom.clientHeight && (!opts || opts.height == null))) { |
| console.warn('Can\'t get DOM width or height. Please check ' + 'dom.clientWidth and dom.clientHeight. They should not be 0.' + 'For example, you may need to call this in the callback ' + 'of window.onload.'); |
| } |
| } |
| } |
| |
| var chart = new ECharts(dom, theme, opts); |
| chart.id = 'ec_' + idBase++; |
| instances$1[chart.id] = chart; |
| isClient && setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id); |
| enableConnect(chart); |
| lifecycle.trigger('afterinit', chart); |
| return chart; |
| } |
| /** |
| * @usage |
| * (A) |
| * ```js |
| * let chart1 = echarts.init(dom1); |
| * let chart2 = echarts.init(dom2); |
| * chart1.group = 'xxx'; |
| * chart2.group = 'xxx'; |
| * echarts.connect('xxx'); |
| * ``` |
| * (B) |
| * ```js |
| * let chart1 = echarts.init(dom1); |
| * let chart2 = echarts.init(dom2); |
| * echarts.connect('xxx', [chart1, chart2]); |
| * ``` |
| */ |
| |
| function connect(groupId) { |
| // Is array of charts |
| if (isArray(groupId)) { |
| var charts = groupId; |
| groupId = null; // If any chart has group |
| |
| each(charts, function (chart) { |
| if (chart.group != null) { |
| groupId = chart.group; |
| } |
| }); |
| groupId = groupId || 'g_' + groupIdBase++; |
| each(charts, function (chart) { |
| chart.group = groupId; |
| }); |
| } |
| |
| connectedGroups[groupId] = true; |
| return groupId; |
| } |
| /** |
| * @deprecated |
| */ |
| |
| function disConnect(groupId) { |
| connectedGroups[groupId] = false; |
| } |
| /** |
| * Alias and backward compatibility |
| */ |
| |
| var disconnect = disConnect; |
| /** |
| * Dispose a chart instance |
| */ |
| |
| function dispose$1(chart) { |
| if (isString(chart)) { |
| chart = instances$1[chart]; |
| } else if (!(chart instanceof ECharts)) { |
| // Try to treat as dom |
| chart = getInstanceByDom(chart); |
| } |
| |
| if (chart instanceof ECharts && !chart.isDisposed()) { |
| chart.dispose(); |
| } |
| } |
| function getInstanceByDom(dom) { |
| return instances$1[getAttribute(dom, DOM_ATTRIBUTE_KEY)]; |
| } |
| function getInstanceById(key) { |
| return instances$1[key]; |
| } |
| /** |
| * Register theme |
| */ |
| |
| function registerTheme(name, theme) { |
| themeStorage[name] = theme; |
| } |
| /** |
| * Register option preprocessor |
| */ |
| |
| function registerPreprocessor(preprocessorFunc) { |
| if (indexOf(optionPreprocessorFuncs, preprocessorFunc) < 0) { |
| optionPreprocessorFuncs.push(preprocessorFunc); |
| } |
| } |
| function registerProcessor(priority, processor) { |
| normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_DEFAULT); |
| } |
| /** |
| * Register postIniter |
| * @param {Function} postInitFunc |
| */ |
| |
| function registerPostInit(postInitFunc) { |
| registerUpdateLifecycle('afterinit', postInitFunc); |
| } |
| /** |
| * Register postUpdater |
| * @param {Function} postUpdateFunc |
| */ |
| |
| function registerPostUpdate(postUpdateFunc) { |
| registerUpdateLifecycle('afterupdate', postUpdateFunc); |
| } |
| function registerUpdateLifecycle(name, cb) { |
| lifecycle.on(name, cb); |
| } |
| function registerAction(actionInfo, eventName, action) { |
| if (isFunction(eventName)) { |
| action = eventName; |
| eventName = ''; |
| } |
| |
| var actionType = isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = { |
| event: eventName |
| }][0]; // Event name is all lowercase |
| |
| actionInfo.event = (actionInfo.event || actionType).toLowerCase(); |
| eventName = actionInfo.event; |
| |
| if (eventActionMap[eventName]) { |
| // Already registered. |
| return; |
| } // Validate action type and event name. |
| |
| |
| assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName)); |
| |
| if (!actions[actionType]) { |
| actions[actionType] = { |
| action: action, |
| actionInfo: actionInfo |
| }; |
| } |
| |
| eventActionMap[eventName] = actionType; |
| } |
| function registerCoordinateSystem(type, coordSysCreator) { |
| CoordinateSystemManager.register(type, coordSysCreator); |
| } |
| /** |
| * Get dimensions of specified coordinate system. |
| * @param {string} type |
| * @return {Array.<string|Object>} |
| */ |
| |
| function getCoordinateSystemDimensions(type) { |
| var coordSysCreator = CoordinateSystemManager.get(type); |
| |
| if (coordSysCreator) { |
| return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice(); |
| } |
| } |
| |
| function registerLayout(priority, layoutTask) { |
| normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout'); |
| } |
| |
| function registerVisual(priority, visualTask) { |
| normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual'); |
| } |
| var registeredTasks = []; |
| |
| function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) { |
| if (isFunction(priority) || isObject(priority)) { |
| fn = priority; |
| priority = defaultPriority; |
| } |
| |
| if ("development" !== 'production') { |
| if (isNaN(priority) || priority == null) { |
| throw new Error('Illegal priority'); |
| } // Check duplicate |
| |
| |
| each(targetList, function (wrap) { |
| assert(wrap.__raw !== fn); |
| }); |
| } // Already registered |
| |
| |
| if (indexOf(registeredTasks, fn) >= 0) { |
| return; |
| } |
| |
| registeredTasks.push(fn); |
| var stageHandler = Scheduler.wrapStageHandler(fn, visualType); |
| stageHandler.__prio = priority; |
| stageHandler.__raw = fn; |
| targetList.push(stageHandler); |
| } |
| |
| function registerLoading(name, loadingFx) { |
| loadingEffects[name] = loadingFx; |
| } |
| /** |
| * ZRender need a canvas context to do measureText. |
| * But in node environment canvas may be created by node-canvas. |
| * So we need to specify how to create a canvas instead of using document.createElement('canvas') |
| * |
| * |
| * @deprecated use setPlatformAPI({ createCanvas }) instead. |
| * |
| * @example |
| * let Canvas = require('canvas'); |
| * let echarts = require('echarts'); |
| * echarts.setCanvasCreator(function () { |
| * // Small size is enough. |
| * return new Canvas(32, 32); |
| * }); |
| */ |
| |
| function setCanvasCreator(creator) { |
| if ("development" !== 'production') { |
| deprecateLog('setCanvasCreator is deprecated. Use setPlatformAPI({ createCanvas }) instead.'); |
| } |
| |
| setPlatformAPI({ |
| createCanvas: creator |
| }); |
| } |
| /** |
| * The parameters and usage: see `geoSourceManager.registerMap`. |
| * Compatible with previous `echarts.registerMap`. |
| */ |
| |
| function registerMap(mapName, geoJson, specialAreas) { |
| var registerMap = getImpl('registerMap'); |
| registerMap && registerMap(mapName, geoJson, specialAreas); |
| } |
| function getMap(mapName) { |
| var getMap = getImpl('getMap'); |
| return getMap && getMap(mapName); |
| } |
| var registerTransform = registerExternalTransform; |
| /** |
| * Globa dispatchAction to a specified chart instance. |
| */ |
| // export function dispatchAction(payload: { chartId: string } & Payload, opt?: Parameters<ECharts['dispatchAction']>[1]) { |
| // if (!payload || !payload.chartId) { |
| // // Must have chartId to find chart |
| // return; |
| // } |
| // const chart = instances[payload.chartId]; |
| // if (chart) { |
| // chart.dispatchAction(payload, opt); |
| // } |
| // } |
| // Builtin global visual |
| |
| registerVisual(PRIORITY_VISUAL_GLOBAL, seriesStyleTask); |
| registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataStyleTask); |
| registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataColorPaletteTask); |
| registerVisual(PRIORITY_VISUAL_GLOBAL, seriesSymbolTask); |
| registerVisual(PRIORITY_VISUAL_CHART_DATA_CUSTOM, dataSymbolTask); |
| registerVisual(PRIORITY_VISUAL_DECAL, decalVisual); |
| registerPreprocessor(globalBackwardCompat); |
| registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack); |
| registerLoading('default', defaultLoading); // Default actions |
| |
| registerAction({ |
| type: HIGHLIGHT_ACTION_TYPE, |
| event: HIGHLIGHT_ACTION_TYPE, |
| update: HIGHLIGHT_ACTION_TYPE |
| }, noop); |
| registerAction({ |
| type: DOWNPLAY_ACTION_TYPE, |
| event: DOWNPLAY_ACTION_TYPE, |
| update: DOWNPLAY_ACTION_TYPE |
| }, noop); |
| registerAction({ |
| type: SELECT_ACTION_TYPE, |
| event: SELECT_ACTION_TYPE, |
| update: SELECT_ACTION_TYPE |
| }, noop); |
| registerAction({ |
| type: UNSELECT_ACTION_TYPE, |
| event: UNSELECT_ACTION_TYPE, |
| update: UNSELECT_ACTION_TYPE |
| }, noop); |
| registerAction({ |
| type: TOGGLE_SELECT_ACTION_TYPE, |
| event: TOGGLE_SELECT_ACTION_TYPE, |
| update: TOGGLE_SELECT_ACTION_TYPE |
| }, noop); // Default theme |
| |
| registerTheme('light', lightTheme); |
| registerTheme('dark', theme); // For backward compatibility, where the namespace `dataTool` will |
| // be mounted on `echarts` is the extension `dataTool` is imported. |
| |
| var dataTool = {}; |
| |
| var extensions = []; |
| var extensionRegisters = { |
| registerPreprocessor: registerPreprocessor, |
| registerProcessor: registerProcessor, |
| registerPostInit: registerPostInit, |
| registerPostUpdate: registerPostUpdate, |
| registerUpdateLifecycle: registerUpdateLifecycle, |
| registerAction: registerAction, |
| registerCoordinateSystem: registerCoordinateSystem, |
| registerLayout: registerLayout, |
| registerVisual: registerVisual, |
| registerTransform: registerTransform, |
| registerLoading: registerLoading, |
| registerMap: registerMap, |
| registerImpl: registerImpl, |
| PRIORITY: PRIORITY, |
| ComponentModel: ComponentModel, |
| ComponentView: ComponentView, |
| SeriesModel: SeriesModel, |
| ChartView: ChartView, |
| // TODO Use ComponentModel and SeriesModel instead of Constructor |
| registerComponentModel: function (ComponentModelClass) { |
| ComponentModel.registerClass(ComponentModelClass); |
| }, |
| registerComponentView: function (ComponentViewClass) { |
| ComponentView.registerClass(ComponentViewClass); |
| }, |
| registerSeriesModel: function (SeriesModelClass) { |
| SeriesModel.registerClass(SeriesModelClass); |
| }, |
| registerChartView: function (ChartViewClass) { |
| ChartView.registerClass(ChartViewClass); |
| }, |
| registerSubTypeDefaulter: function (componentType, defaulter) { |
| ComponentModel.registerSubTypeDefaulter(componentType, defaulter); |
| }, |
| registerPainter: function (painterType, PainterCtor) { |
| registerPainter(painterType, PainterCtor); |
| } |
| }; |
| function use(ext) { |
| if (isArray(ext)) { |
| // use([ChartLine, ChartBar]); |
| each(ext, function (singleExt) { |
| use(singleExt); |
| }); |
| return; |
| } |
| |
| if (indexOf(extensions, ext) >= 0) { |
| return; |
| } |
| |
| extensions.push(ext); |
| |
| if (isFunction(ext)) { |
| ext = { |
| install: ext |
| }; |
| } |
| |
| ext.install(extensionRegisters); |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) { |
| return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1; |
| } |
| |
| function defaultKeyGetter(item) { |
| return item; |
| } |
| |
| var DataDiffer = |
| /** @class */ |
| function () { |
| /** |
| * @param context Can be visited by this.context in callback. |
| */ |
| function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'. |
| diffMode) { |
| this._old = oldArr; |
| this._new = newArr; |
| this._oldKeyGetter = oldKeyGetter || defaultKeyGetter; |
| this._newKeyGetter = newKeyGetter || defaultKeyGetter; // Visible in callback via `this.context`; |
| |
| this.context = context; |
| this._diffModeMultiple = diffMode === 'multiple'; |
| } |
| /** |
| * Callback function when add a data |
| */ |
| |
| |
| DataDiffer.prototype.add = function (func) { |
| this._add = func; |
| return this; |
| }; |
| /** |
| * Callback function when update a data |
| */ |
| |
| |
| DataDiffer.prototype.update = function (func) { |
| this._update = func; |
| return this; |
| }; |
| /** |
| * Callback function when update a data and only work in `cbMode: 'byKey'`. |
| */ |
| |
| |
| DataDiffer.prototype.updateManyToOne = function (func) { |
| this._updateManyToOne = func; |
| return this; |
| }; |
| /** |
| * Callback function when update a data and only work in `cbMode: 'byKey'`. |
| */ |
| |
| |
| DataDiffer.prototype.updateOneToMany = function (func) { |
| this._updateOneToMany = func; |
| return this; |
| }; |
| /** |
| * Callback function when update a data and only work in `cbMode: 'byKey'`. |
| */ |
| |
| |
| DataDiffer.prototype.updateManyToMany = function (func) { |
| this._updateManyToMany = func; |
| return this; |
| }; |
| /** |
| * Callback function when remove a data |
| */ |
| |
| |
| DataDiffer.prototype.remove = function (func) { |
| this._remove = func; |
| return this; |
| }; |
| |
| DataDiffer.prototype.execute = function () { |
| this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne'](); |
| }; |
| |
| DataDiffer.prototype._executeOneToOne = function () { |
| var oldArr = this._old; |
| var newArr = this._new; |
| var newDataIndexMap = {}; |
| var oldDataKeyArr = new Array(oldArr.length); |
| var newDataKeyArr = new Array(newArr.length); |
| |
| this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter'); |
| |
| this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); |
| |
| for (var i = 0; i < oldArr.length; i++) { |
| var oldKey = oldDataKeyArr[i]; |
| var newIdxMapVal = newDataIndexMap[oldKey]; |
| var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); // idx can never be empty array here. see 'set null' logic below. |
| |
| if (newIdxMapValLen > 1) { |
| // Consider there is duplicate key (for example, use dataItem.name as key). |
| // We should make sure every item in newArr and oldArr can be visited. |
| var newIdx = newIdxMapVal.shift(); |
| |
| if (newIdxMapVal.length === 1) { |
| newDataIndexMap[oldKey] = newIdxMapVal[0]; |
| } |
| |
| this._update && this._update(newIdx, i); |
| } else if (newIdxMapValLen === 1) { |
| newDataIndexMap[oldKey] = null; |
| this._update && this._update(newIdxMapVal, i); |
| } else { |
| this._remove && this._remove(i); |
| } |
| } |
| |
| this._performRestAdd(newDataKeyArr, newDataIndexMap); |
| }; |
| /** |
| * For example, consider the case: |
| * oldData: [o0, o1, o2, o3, o4, o5, o6, o7], |
| * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8], |
| * Where: |
| * o0, o1, n0 has key 'a' (many to one) |
| * o5, n4, n5, n6 has key 'b' (one to many) |
| * o2, n1 has key 'c' (one to one) |
| * n2, n3 has key 'd' (add) |
| * o3, o4 has key 'e' (remove) |
| * o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove) |
| * Then: |
| * (The order of the following directives are not ensured.) |
| * this._updateManyToOne(n0, [o0, o1]); |
| * this._updateOneToMany([n4, n5, n6], o5); |
| * this._update(n1, o2); |
| * this._remove(o3); |
| * this._remove(o4); |
| * this._remove(o6); |
| * this._remove(o7); |
| * this._add(n2); |
| * this._add(n3); |
| * this._add(n7); |
| * this._add(n8); |
| */ |
| |
| |
| DataDiffer.prototype._executeMultiple = function () { |
| var oldArr = this._old; |
| var newArr = this._new; |
| var oldDataIndexMap = {}; |
| var newDataIndexMap = {}; |
| var oldDataKeyArr = []; |
| var newDataKeyArr = []; |
| |
| this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter'); |
| |
| this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter'); |
| |
| for (var i = 0; i < oldDataKeyArr.length; i++) { |
| var oldKey = oldDataKeyArr[i]; |
| var oldIdxMapVal = oldDataIndexMap[oldKey]; |
| var newIdxMapVal = newDataIndexMap[oldKey]; |
| var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal); |
| var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal); |
| |
| if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) { |
| this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal); |
| newDataIndexMap[oldKey] = null; |
| } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) { |
| this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal); |
| newDataIndexMap[oldKey] = null; |
| } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) { |
| this._update && this._update(newIdxMapVal, oldIdxMapVal); |
| newDataIndexMap[oldKey] = null; |
| } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) { |
| this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal); |
| newDataIndexMap[oldKey] = null; |
| } else if (oldIdxMapValLen > 1) { |
| for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) { |
| this._remove && this._remove(oldIdxMapVal[i_1]); |
| } |
| } else { |
| this._remove && this._remove(oldIdxMapVal); |
| } |
| } |
| |
| this._performRestAdd(newDataKeyArr, newDataIndexMap); |
| }; |
| |
| DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) { |
| for (var i = 0; i < newDataKeyArr.length; i++) { |
| var newKey = newDataKeyArr[i]; |
| var newIdxMapVal = newDataIndexMap[newKey]; |
| var idxMapValLen = dataIndexMapValueLength(newIdxMapVal); |
| |
| if (idxMapValLen > 1) { |
| for (var j = 0; j < idxMapValLen; j++) { |
| this._add && this._add(newIdxMapVal[j]); |
| } |
| } else if (idxMapValLen === 1) { |
| this._add && this._add(newIdxMapVal); |
| } // Support both `newDataKeyArr` are duplication removed or not removed. |
| |
| |
| newDataIndexMap[newKey] = null; |
| } |
| }; |
| |
| DataDiffer.prototype._initIndexMap = function (arr, // Can be null. |
| map, // In 'byKey', the output `keyArr` is duplication removed. |
| // In 'byIndex', the output `keyArr` is not duplication removed and |
| // its indices are accurately corresponding to `arr`. |
| keyArr, keyGetterName) { |
| var cbModeMultiple = this._diffModeMultiple; |
| |
| for (var i = 0; i < arr.length; i++) { |
| // Add prefix to avoid conflict with Object.prototype. |
| var key = '_ec_' + this[keyGetterName](arr[i], i); |
| |
| if (!cbModeMultiple) { |
| keyArr[i] = key; |
| } |
| |
| if (!map) { |
| continue; |
| } |
| |
| var idxMapVal = map[key]; |
| var idxMapValLen = dataIndexMapValueLength(idxMapVal); |
| |
| if (idxMapValLen === 0) { |
| // Simple optimize: in most cases, one index has one key, |
| // do not need array. |
| map[key] = i; |
| |
| if (cbModeMultiple) { |
| keyArr.push(key); |
| } |
| } else if (idxMapValLen === 1) { |
| map[key] = [idxMapVal, i]; |
| } else { |
| idxMapVal.push(i); |
| } |
| } |
| }; |
| |
| return DataDiffer; |
| }(); |
| |
| var DimensionUserOuput = |
| /** @class */ |
| function () { |
| function DimensionUserOuput(encode, dimRequest) { |
| this._encode = encode; |
| this._schema = dimRequest; |
| } |
| |
| DimensionUserOuput.prototype.get = function () { |
| return { |
| // Do not generate full dimension name until fist used. |
| fullDimensions: this._getFullDimensionNames(), |
| encode: this._encode |
| }; |
| }; |
| /** |
| * Get all data store dimension names. |
| * Theoretically a series data store is defined both by series and used dataset (if any). |
| * If some dimensions are omitted for performance reason in `this.dimensions`, |
| * the dimension name may not be auto-generated if user does not specify a dimension name. |
| * In this case, the dimension name is `null`/`undefined`. |
| */ |
| |
| |
| DimensionUserOuput.prototype._getFullDimensionNames = function () { |
| if (!this._cachedDimNames) { |
| this._cachedDimNames = this._schema ? this._schema.makeOutputDimensionNames() : []; |
| } |
| |
| return this._cachedDimNames; |
| }; |
| |
| return DimensionUserOuput; |
| }(); |
| function summarizeDimensions(data, schema) { |
| var summary = {}; |
| var encode = summary.encode = {}; |
| var notExtraCoordDimMap = createHashMap(); |
| var defaultedLabel = []; |
| var defaultedTooltip = []; |
| var userOutputEncode = {}; |
| each(data.dimensions, function (dimName) { |
| var dimItem = data.getDimensionInfo(dimName); |
| var coordDim = dimItem.coordDim; |
| |
| if (coordDim) { |
| if ("development" !== 'production') { |
| assert(VISUAL_DIMENSIONS.get(coordDim) == null); |
| } |
| |
| var coordDimIndex = dimItem.coordDimIndex; |
| getOrCreateEncodeArr(encode, coordDim)[coordDimIndex] = dimName; |
| |
| if (!dimItem.isExtraCoord) { |
| notExtraCoordDimMap.set(coordDim, 1); // Use the last coord dim (and label friendly) as default label, |
| // because when dataset is used, it is hard to guess which dimension |
| // can be value dimension. If both show x, y on label is not look good, |
| // and conventionally y axis is focused more. |
| |
| if (mayLabelDimType(dimItem.type)) { |
| defaultedLabel[0] = dimName; |
| } // User output encode do not contain generated coords. |
| // And it only has index. User can use index to retrieve value from the raw item array. |
| |
| |
| getOrCreateEncodeArr(userOutputEncode, coordDim)[coordDimIndex] = data.getDimensionIndex(dimItem.name); |
| } |
| |
| if (dimItem.defaultTooltip) { |
| defaultedTooltip.push(dimName); |
| } |
| } |
| |
| VISUAL_DIMENSIONS.each(function (v, otherDim) { |
| var encodeArr = getOrCreateEncodeArr(encode, otherDim); |
| var dimIndex = dimItem.otherDims[otherDim]; |
| |
| if (dimIndex != null && dimIndex !== false) { |
| encodeArr[dimIndex] = dimItem.name; |
| } |
| }); |
| }); |
| var dataDimsOnCoord = []; |
| var encodeFirstDimNotExtra = {}; |
| notExtraCoordDimMap.each(function (v, coordDim) { |
| var dimArr = encode[coordDim]; |
| encodeFirstDimNotExtra[coordDim] = dimArr[0]; // Not necessary to remove duplicate, because a data |
| // dim canot on more than one coordDim. |
| |
| dataDimsOnCoord = dataDimsOnCoord.concat(dimArr); |
| }); |
| summary.dataDimsOnCoord = dataDimsOnCoord; |
| summary.dataDimIndicesOnCoord = map(dataDimsOnCoord, function (dimName) { |
| return data.getDimensionInfo(dimName).storeDimIndex; |
| }); |
| summary.encodeFirstDimNotExtra = encodeFirstDimNotExtra; |
| var encodeLabel = encode.label; // FIXME `encode.label` is not recommended, because formatter cannot be set |
| // in this way. Use label.formatter instead. Maybe remove this approach someday. |
| |
| if (encodeLabel && encodeLabel.length) { |
| defaultedLabel = encodeLabel.slice(); |
| } |
| |
| var encodeTooltip = encode.tooltip; |
| |
| if (encodeTooltip && encodeTooltip.length) { |
| defaultedTooltip = encodeTooltip.slice(); |
| } else if (!defaultedTooltip.length) { |
| defaultedTooltip = defaultedLabel.slice(); |
| } |
| |
| encode.defaultedLabel = defaultedLabel; |
| encode.defaultedTooltip = defaultedTooltip; |
| summary.userOutput = new DimensionUserOuput(userOutputEncode, schema); |
| return summary; |
| } |
| |
| function getOrCreateEncodeArr(encode, dim) { |
| if (!encode.hasOwnProperty(dim)) { |
| encode[dim] = []; |
| } |
| |
| return encode[dim]; |
| } // FIXME:TS should be type `AxisType` |
| |
| |
| function getDimensionTypeByAxis(axisType) { |
| return axisType === 'category' ? 'ordinal' : axisType === 'time' ? 'time' : 'float'; |
| } |
| |
| function mayLabelDimType(dimType) { |
| // In most cases, ordinal and time do not suitable for label. |
| // Ordinal info can be displayed on axis. Time is too long. |
| return !(dimType === 'ordinal' || dimType === 'time'); |
| } // function findTheLastDimMayLabel(data) { |
| // // Get last value dim |
| // let dimensions = data.dimensions.slice(); |
| // let valueType; |
| // let valueDim; |
| // while (dimensions.length && ( |
| // valueDim = dimensions.pop(), |
| // valueType = data.getDimensionInfo(valueDim).type, |
| // valueType === 'ordinal' || valueType === 'time' |
| // )) {} // jshint ignore:line |
| // return valueDim; |
| // } |
| |
| var SeriesDimensionDefine = |
| /** @class */ |
| function () { |
| /** |
| * @param opt All of the fields will be shallow copied. |
| */ |
| function SeriesDimensionDefine(opt) { |
| /** |
| * The format of `otherDims` is: |
| * ```js |
| * { |
| * tooltip?: number |
| * label?: number |
| * itemName?: number |
| * seriesName?: number |
| * } |
| * ``` |
| * |
| * A `series.encode` can specified these fields: |
| * ```js |
| * encode: { |
| * // "3, 1, 5" is the index of data dimension. |
| * tooltip: [3, 1, 5], |
| * label: [0, 3], |
| * ... |
| * } |
| * ``` |
| * `otherDims` is the parse result of the `series.encode` above, like: |
| * ```js |
| * // Suppose the index of this data dimension is `3`. |
| * this.otherDims = { |
| * // `3` is at the index `0` of the `encode.tooltip` |
| * tooltip: 0, |
| * // `3` is at the index `1` of the `encode.label` |
| * label: 1 |
| * }; |
| * ``` |
| * |
| * This prop should never be `null`/`undefined` after initialized. |
| */ |
| this.otherDims = {}; |
| |
| if (opt != null) { |
| extend(this, opt); |
| } |
| } |
| |
| return SeriesDimensionDefine; |
| }(); |
| |
| var inner$4 = makeInner(); |
| var dimTypeShort = { |
| float: 'f', |
| int: 'i', |
| ordinal: 'o', |
| number: 'n', |
| time: 't' |
| }; |
| /** |
| * Represents the dimension requirement of a series. |
| * |
| * NOTICE: |
| * When there are too many dimensions in dataset and many series, only the used dimensions |
| * (i.e., used by coord sys and declared in `series.encode`) are add to `dimensionDefineList`. |
| * But users may query data by other unused dimension names. |
| * In this case, users can only query data if and only if they have defined dimension names |
| * via ec option, so we provide `getDimensionIndexFromSource`, which only query them from |
| * `source` dimensions. |
| */ |
| |
| var SeriesDataSchema = |
| /** @class */ |
| function () { |
| function SeriesDataSchema(opt) { |
| this.dimensions = opt.dimensions; |
| this._dimOmitted = opt.dimensionOmitted; |
| this.source = opt.source; |
| this._fullDimCount = opt.fullDimensionCount; |
| |
| this._updateDimOmitted(opt.dimensionOmitted); |
| } |
| |
| SeriesDataSchema.prototype.isDimensionOmitted = function () { |
| return this._dimOmitted; |
| }; |
| |
| SeriesDataSchema.prototype._updateDimOmitted = function (dimensionOmitted) { |
| this._dimOmitted = dimensionOmitted; |
| |
| if (!dimensionOmitted) { |
| return; |
| } |
| |
| if (!this._dimNameMap) { |
| this._dimNameMap = ensureSourceDimNameMap(this.source); |
| } |
| }; |
| /** |
| * @caution Can only be used when `dimensionOmitted: true`. |
| * |
| * Get index by user defined dimension name (i.e., not internal generate name). |
| * That is, get index from `dimensionsDefine`. |
| * If no `dimensionsDefine`, or no name get, return -1. |
| */ |
| |
| |
| SeriesDataSchema.prototype.getSourceDimensionIndex = function (dimName) { |
| return retrieve2(this._dimNameMap.get(dimName), -1); |
| }; |
| /** |
| * @caution Can only be used when `dimensionOmitted: true`. |
| * |
| * Notice: may return `null`/`undefined` if user not specify dimension names. |
| */ |
| |
| |
| SeriesDataSchema.prototype.getSourceDimension = function (dimIndex) { |
| var dimensionsDefine = this.source.dimensionsDefine; |
| |
| if (dimensionsDefine) { |
| return dimensionsDefine[dimIndex]; |
| } |
| }; |
| |
| SeriesDataSchema.prototype.makeStoreSchema = function () { |
| var dimCount = this._fullDimCount; |
| var willRetrieveDataByName = shouldRetrieveDataByName(this.source); |
| var makeHashStrict = !shouldOmitUnusedDimensions(dimCount); // If source don't have dimensions or series don't omit unsed dimensions. |
| // Generate from seriesDimList directly |
| |
| var dimHash = ''; |
| var dims = []; |
| |
| for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < dimCount; fullDimIdx++) { |
| var property = void 0; |
| var type = void 0; |
| var ordinalMeta = void 0; |
| var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc. |
| |
| if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) { |
| property = willRetrieveDataByName ? seriesDimDef.name : null; |
| type = seriesDimDef.type; |
| ordinalMeta = seriesDimDef.ordinalMeta; |
| seriesDimIdx++; |
| } else { |
| var sourceDimDef = this.getSourceDimension(fullDimIdx); |
| |
| if (sourceDimDef) { |
| property = willRetrieveDataByName ? sourceDimDef.name : null; |
| type = sourceDimDef.type; |
| } |
| } |
| |
| dims.push({ |
| property: property, |
| type: type, |
| ordinalMeta: ordinalMeta |
| }); // If retrieving data by index, |
| // use <index, type, ordinalMeta> to determine whether data can be shared. |
| // (Because in this case there might be no dimension name defined in dataset, but indices always exists). |
| // (Indices are always 0, 1, 2, ..., so we can ignore them to shorten the hash). |
| // Otherwise if retrieving data by property name (like `data: [{aa: 123, bb: 765}, ...]`), |
| // use <property, type, ordinalMeta> in hash. |
| |
| if (willRetrieveDataByName && property != null // For data stack, we have make sure each series has its own dim on this store. |
| // So we do not add property to hash to make sure they can share this store. |
| && (!seriesDimDef || !seriesDimDef.isCalculationCoord)) { |
| dimHash += makeHashStrict // Use escape character '`' in case that property name contains '$'. |
| ? property.replace(/\`/g, '`1').replace(/\$/g, '`2') // For better performance, when there are large dimensions, tolerant this defects that hardly meet. |
| : property; |
| } |
| |
| dimHash += '$'; |
| dimHash += dimTypeShort[type] || 'f'; |
| |
| if (ordinalMeta) { |
| dimHash += ordinalMeta.uid; |
| } |
| |
| dimHash += '$'; |
| } // Source from endpoint(usually series) will be read differently |
| // when seriesLayoutBy or startIndex(which is affected by sourceHeader) are different. |
| // So we use this three props as key. |
| |
| |
| var source = this.source; |
| var hash = [source.seriesLayoutBy, source.startIndex, dimHash].join('$$'); |
| return { |
| dimensions: dims, |
| hash: hash |
| }; |
| }; |
| |
| SeriesDataSchema.prototype.makeOutputDimensionNames = function () { |
| var result = []; |
| |
| for (var fullDimIdx = 0, seriesDimIdx = 0; fullDimIdx < this._fullDimCount; fullDimIdx++) { |
| var name_1 = void 0; |
| var seriesDimDef = this.dimensions[seriesDimIdx]; // The list has been sorted by `storeDimIndex` asc. |
| |
| if (seriesDimDef && seriesDimDef.storeDimIndex === fullDimIdx) { |
| if (!seriesDimDef.isCalculationCoord) { |
| name_1 = seriesDimDef.name; |
| } |
| |
| seriesDimIdx++; |
| } else { |
| var sourceDimDef = this.getSourceDimension(fullDimIdx); |
| |
| if (sourceDimDef) { |
| name_1 = sourceDimDef.name; |
| } |
| } |
| |
| result.push(name_1); |
| } |
| |
| return result; |
| }; |
| |
| SeriesDataSchema.prototype.appendCalculationDimension = function (dimDef) { |
| this.dimensions.push(dimDef); |
| dimDef.isCalculationCoord = true; |
| this._fullDimCount++; // If append dimension on a data store, consider the store |
| // might be shared by different series, series dimensions not |
| // really map to store dimensions. |
| |
| this._updateDimOmitted(true); |
| }; |
| |
| return SeriesDataSchema; |
| }(); |
| function isSeriesDataSchema(schema) { |
| return schema instanceof SeriesDataSchema; |
| } |
| function createDimNameMap(dimsDef) { |
| var dataDimNameMap = createHashMap(); |
| |
| for (var i = 0; i < (dimsDef || []).length; i++) { |
| var dimDefItemRaw = dimsDef[i]; |
| var userDimName = isObject(dimDefItemRaw) ? dimDefItemRaw.name : dimDefItemRaw; |
| |
| if (userDimName != null && dataDimNameMap.get(userDimName) == null) { |
| dataDimNameMap.set(userDimName, i); |
| } |
| } |
| |
| return dataDimNameMap; |
| } |
| function ensureSourceDimNameMap(source) { |
| var innerSource = inner$4(source); |
| return innerSource.dimNameMap || (innerSource.dimNameMap = createDimNameMap(source.dimensionsDefine)); |
| } |
| function shouldOmitUnusedDimensions(dimCount) { |
| return dimCount > 30; |
| } |
| |
| var isObject$2 = isObject; |
| var map$1 = map; |
| var CtorInt32Array$1 = typeof Int32Array === 'undefined' ? Array : Int32Array; // Use prefix to avoid index to be the same as otherIdList[idx], |
| // which will cause weird update animation. |
| |
| var ID_PREFIX = 'e\0\0'; |
| var INDEX_NOT_FOUND = -1; // type SeriesDimensionIndex = DimensionIndex; |
| |
| var TRANSFERABLE_PROPERTIES = ['hasItemOption', '_nameList', '_idList', '_invertedIndicesMap', '_dimSummary', 'userOutput', '_rawData', '_dimValueGetter', '_nameDimIdx', '_idDimIdx', '_nameRepeatCount']; |
| var CLONE_PROPERTIES = ['_approximateExtent']; // ----------------------------- |
| // Internal method declarations: |
| // ----------------------------- |
| |
| var prepareInvertedIndex; |
| var getId; |
| var getIdNameFromStore; |
| var normalizeDimensions; |
| var transferProperties; |
| var cloneListForMapAndSample; |
| var makeIdFromName; |
| |
| var SeriesData = |
| /** @class */ |
| function () { |
| /** |
| * @param dimensionsInput.dimensions |
| * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...]. |
| * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius |
| */ |
| function SeriesData(dimensionsInput, hostModel) { |
| this.type = 'list'; |
| this._dimOmitted = false; |
| this._nameList = []; |
| this._idList = []; // Models of data option is stored sparse for optimizing memory cost |
| // Never used yet (not used yet). |
| // private _optionModels: Model[] = []; |
| // Global visual properties after visual coding |
| |
| this._visual = {}; // Global layout properties. |
| |
| this._layout = {}; // Item visual properties after visual coding |
| |
| this._itemVisuals = []; // Item layout properties after layout |
| |
| this._itemLayouts = []; // Graphic elements |
| |
| this._graphicEls = []; // key: dim, value: extent |
| |
| this._approximateExtent = {}; |
| this._calculationInfo = {}; // Having detected that there is data item is non primitive type |
| // (in type `OptionDataItemObject`). |
| // Like `data: [ { value: xx, itemStyle: {...} }, ...]` |
| // At present it only happen in `SOURCE_FORMAT_ORIGINAL`. |
| |
| this.hasItemOption = false; // Methods that create a new list based on this list should be listed here. |
| // Notice that those method should `RETURN` the new list. |
| |
| this.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'lttbDownSample', 'map']; // Methods that change indices of this list should be listed here. |
| |
| this.CHANGABLE_METHODS = ['filterSelf', 'selectRange']; |
| this.DOWNSAMPLE_METHODS = ['downSample', 'lttbDownSample']; |
| var dimensions; |
| var assignStoreDimIdx = false; |
| |
| if (isSeriesDataSchema(dimensionsInput)) { |
| dimensions = dimensionsInput.dimensions; |
| this._dimOmitted = dimensionsInput.isDimensionOmitted(); |
| this._schema = dimensionsInput; |
| } else { |
| assignStoreDimIdx = true; |
| dimensions = dimensionsInput; |
| } |
| |
| dimensions = dimensions || ['x', 'y']; |
| var dimensionInfos = {}; |
| var dimensionNames = []; |
| var invertedIndicesMap = {}; |
| var needsHasOwn = false; |
| var emptyObj = {}; |
| |
| for (var i = 0; i < dimensions.length; i++) { |
| // Use the original dimensions[i], where other flag props may exists. |
| var dimInfoInput = dimensions[i]; |
| var dimensionInfo = isString(dimInfoInput) ? new SeriesDimensionDefine({ |
| name: dimInfoInput |
| }) : !(dimInfoInput instanceof SeriesDimensionDefine) ? new SeriesDimensionDefine(dimInfoInput) : dimInfoInput; |
| var dimensionName = dimensionInfo.name; |
| dimensionInfo.type = dimensionInfo.type || 'float'; |
| |
| if (!dimensionInfo.coordDim) { |
| dimensionInfo.coordDim = dimensionName; |
| dimensionInfo.coordDimIndex = 0; |
| } |
| |
| var otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {}; |
| dimensionNames.push(dimensionName); |
| dimensionInfos[dimensionName] = dimensionInfo; |
| |
| if (emptyObj[dimensionName] != null) { |
| needsHasOwn = true; |
| } |
| |
| if (dimensionInfo.createInvertedIndices) { |
| invertedIndicesMap[dimensionName] = []; |
| } |
| |
| if (otherDims.itemName === 0) { |
| this._nameDimIdx = i; |
| } |
| |
| if (otherDims.itemId === 0) { |
| this._idDimIdx = i; |
| } |
| |
| if ("development" !== 'production') { |
| assert(assignStoreDimIdx || dimensionInfo.storeDimIndex >= 0); |
| } |
| |
| if (assignStoreDimIdx) { |
| dimensionInfo.storeDimIndex = i; |
| } |
| } |
| |
| this.dimensions = dimensionNames; |
| this._dimInfos = dimensionInfos; |
| |
| this._initGetDimensionInfo(needsHasOwn); |
| |
| this.hostModel = hostModel; |
| this._invertedIndicesMap = invertedIndicesMap; |
| |
| if (this._dimOmitted) { |
| var dimIdxToName_1 = this._dimIdxToName = createHashMap(); |
| each(dimensionNames, function (dimName) { |
| dimIdxToName_1.set(dimensionInfos[dimName].storeDimIndex, dimName); |
| }); |
| } |
| } |
| /** |
| * |
| * Get concrete dimension name by dimension name or dimension index. |
| * If input a dimension name, do not validate whether the dimension name exits. |
| * |
| * @caution |
| * @param dim Must make sure the dimension is `SeriesDimensionLoose`. |
| * Because only those dimensions will have auto-generated dimension names if not |
| * have a user-specified name, and other dimensions will get a return of null/undefined. |
| * |
| * @notice Because of this reason, should better use `getDimensionIndex` instead, for examples: |
| * ```js |
| * const val = data.getStore().get(data.getDimensionIndex(dim), dataIdx); |
| * ``` |
| * |
| * @return Concrete dim name. |
| */ |
| |
| |
| SeriesData.prototype.getDimension = function (dim) { |
| var dimIdx = this._recognizeDimIndex(dim); |
| |
| if (dimIdx == null) { |
| return dim; |
| } |
| |
| dimIdx = dim; |
| |
| if (!this._dimOmitted) { |
| return this.dimensions[dimIdx]; |
| } // Retrieve from series dimension definition because it probably contains |
| // generated dimension name (like 'x', 'y'). |
| |
| |
| var dimName = this._dimIdxToName.get(dimIdx); |
| |
| if (dimName != null) { |
| return dimName; |
| } |
| |
| var sourceDimDef = this._schema.getSourceDimension(dimIdx); |
| |
| if (sourceDimDef) { |
| return sourceDimDef.name; |
| } |
| }; |
| /** |
| * Get dimension index in data store. Return -1 if not found. |
| * Can be used to index value from getRawValue. |
| */ |
| |
| |
| SeriesData.prototype.getDimensionIndex = function (dim) { |
| var dimIdx = this._recognizeDimIndex(dim); |
| |
| if (dimIdx != null) { |
| return dimIdx; |
| } |
| |
| if (dim == null) { |
| return -1; |
| } |
| |
| var dimInfo = this._getDimInfo(dim); |
| |
| return dimInfo ? dimInfo.storeDimIndex : this._dimOmitted ? this._schema.getSourceDimensionIndex(dim) : -1; |
| }; |
| /** |
| * The meanings of the input parameter `dim`: |
| * |
| * + If dim is a number (e.g., `1`), it means the index of the dimension. |
| * For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'. |
| * + If dim is a number-like string (e.g., `"1"`): |
| * + If there is the same concrete dim name defined in `series.dimensions` or `dataset.dimensions`, |
| * it means that concrete name. |
| * + If not, it will be converted to a number, which means the index of the dimension. |
| * (why? because of the backward compatibility. We have been tolerating number-like string in |
| * dimension setting, although now it seems that it is not a good idea.) |
| * For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`, |
| * if no dimension name is defined as `"1"`. |
| * + If dim is a not-number-like string, it means the concrete dim name. |
| * For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`, |
| * or customized in `dimensions` property of option like `"age"`. |
| * |
| * @return recognized `DimensionIndex`. Otherwise return null/undefined (means that dim is `DimensionName`). |
| */ |
| |
| |
| SeriesData.prototype._recognizeDimIndex = function (dim) { |
| if (isNumber(dim) // If being a number-like string but not being defined as a dimension name. |
| || dim != null && !isNaN(dim) && !this._getDimInfo(dim) && (!this._dimOmitted || this._schema.getSourceDimensionIndex(dim) < 0)) { |
| return +dim; |
| } |
| }; |
| |
| SeriesData.prototype._getStoreDimIndex = function (dim) { |
| var dimIdx = this.getDimensionIndex(dim); |
| |
| if ("development" !== 'production') { |
| if (dimIdx == null) { |
| throw new Error('Unknown dimension ' + dim); |
| } |
| } |
| |
| return dimIdx; |
| }; |
| /** |
| * Get type and calculation info of particular dimension |
| * @param dim |
| * Dimension can be concrete names like x, y, z, lng, lat, angle, radius |
| * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius' |
| */ |
| |
| |
| SeriesData.prototype.getDimensionInfo = function (dim) { |
| // Do not clone, because there may be categories in dimInfo. |
| return this._getDimInfo(this.getDimension(dim)); |
| }; |
| |
| SeriesData.prototype._initGetDimensionInfo = function (needsHasOwn) { |
| var dimensionInfos = this._dimInfos; |
| this._getDimInfo = needsHasOwn ? function (dimName) { |
| return dimensionInfos.hasOwnProperty(dimName) ? dimensionInfos[dimName] : undefined; |
| } : function (dimName) { |
| return dimensionInfos[dimName]; |
| }; |
| }; |
| /** |
| * concrete dimension name list on coord. |
| */ |
| |
| |
| SeriesData.prototype.getDimensionsOnCoord = function () { |
| return this._dimSummary.dataDimsOnCoord.slice(); |
| }; |
| |
| SeriesData.prototype.mapDimension = function (coordDim, idx) { |
| var dimensionsSummary = this._dimSummary; |
| |
| if (idx == null) { |
| return dimensionsSummary.encodeFirstDimNotExtra[coordDim]; |
| } |
| |
| var dims = dimensionsSummary.encode[coordDim]; |
| return dims ? dims[idx] : null; |
| }; |
| |
| SeriesData.prototype.mapDimensionsAll = function (coordDim) { |
| var dimensionsSummary = this._dimSummary; |
| var dims = dimensionsSummary.encode[coordDim]; |
| return (dims || []).slice(); |
| }; |
| |
| SeriesData.prototype.getStore = function () { |
| return this._store; |
| }; |
| /** |
| * Initialize from data |
| * @param data source or data or data store. |
| * @param nameList The name of a datum is used on data diff and |
| * default label/tooltip. |
| * A name can be specified in encode.itemName, |
| * or dataItem.name (only for series option data), |
| * or provided in nameList from outside. |
| */ |
| |
| |
| SeriesData.prototype.initData = function (data, nameList, dimValueGetter) { |
| var _this = this; |
| |
| var store; |
| |
| if (data instanceof DataStore) { |
| store = data; |
| } |
| |
| if (!store) { |
| var dimensions = this.dimensions; |
| var provider = isSourceInstance(data) || isArrayLike(data) ? new DefaultDataProvider(data, dimensions.length) : data; |
| store = new DataStore(); |
| var dimensionInfos = map$1(dimensions, function (dimName) { |
| return { |
| type: _this._dimInfos[dimName].type, |
| property: dimName |
| }; |
| }); |
| store.initData(provider, dimensionInfos, dimValueGetter); |
| } |
| |
| this._store = store; // Reset |
| |
| this._nameList = (nameList || []).slice(); |
| this._idList = []; |
| this._nameRepeatCount = {}; |
| |
| this._doInit(0, store.count()); // Cache summary info for fast visit. See "dimensionHelper". |
| // Needs to be initialized after store is prepared. |
| |
| |
| this._dimSummary = summarizeDimensions(this, this._schema); |
| this.userOutput = this._dimSummary.userOutput; |
| }; |
| /** |
| * Caution: Can be only called on raw data (before `this._indices` created). |
| */ |
| |
| |
| SeriesData.prototype.appendData = function (data) { |
| var range = this._store.appendData(data); |
| |
| this._doInit(range[0], range[1]); |
| }; |
| /** |
| * Caution: Can be only called on raw data (before `this._indices` created). |
| * This method does not modify `rawData` (`dataProvider`), but only |
| * add values to store. |
| * |
| * The final count will be increased by `Math.max(values.length, names.length)`. |
| * |
| * @param values That is the SourceType: 'arrayRows', like |
| * [ |
| * [12, 33, 44], |
| * [NaN, 43, 1], |
| * ['-', 'asdf', 0] |
| * ] |
| * Each item is exactly corresponding to a dimension. |
| */ |
| |
| |
| SeriesData.prototype.appendValues = function (values, names) { |
| var _a = this._store.appendValues(values, names.length), |
| start = _a.start, |
| end = _a.end; |
| |
| var shouldMakeIdFromName = this._shouldMakeIdFromName(); |
| |
| this._updateOrdinalMeta(); |
| |
| if (names) { |
| for (var idx = start; idx < end; idx++) { |
| var sourceIdx = idx - start; |
| this._nameList[idx] = names[sourceIdx]; |
| |
| if (shouldMakeIdFromName) { |
| makeIdFromName(this, idx); |
| } |
| } |
| } |
| }; |
| |
| SeriesData.prototype._updateOrdinalMeta = function () { |
| var store = this._store; |
| var dimensions = this.dimensions; |
| |
| for (var i = 0; i < dimensions.length; i++) { |
| var dimInfo = this._dimInfos[dimensions[i]]; |
| |
| if (dimInfo.ordinalMeta) { |
| store.collectOrdinalMeta(dimInfo.storeDimIndex, dimInfo.ordinalMeta); |
| } |
| } |
| }; |
| |
| SeriesData.prototype._shouldMakeIdFromName = function () { |
| var provider = this._store.getProvider(); |
| |
| return this._idDimIdx == null && provider.getSource().sourceFormat !== SOURCE_FORMAT_TYPED_ARRAY && !provider.fillStorage; |
| }; |
| |
| SeriesData.prototype._doInit = function (start, end) { |
| if (start >= end) { |
| return; |
| } |
| |
| var store = this._store; |
| var provider = store.getProvider(); |
| |
| this._updateOrdinalMeta(); |
| |
| var nameList = this._nameList; |
| var idList = this._idList; |
| var sourceFormat = provider.getSource().sourceFormat; |
| var isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL; // Each data item is value |
| // [1, 2] |
| // 2 |
| // Bar chart, line chart which uses category axis |
| // only gives the 'y' value. 'x' value is the indices of category |
| // Use a tempValue to normalize the value to be a (x, y) value |
| // If dataItem is {name: ...} or {id: ...}, it has highest priority. |
| // This kind of ids and names are always stored `_nameList` and `_idList`. |
| |
| if (isFormatOriginal && !provider.pure) { |
| var sharedDataItem = []; |
| |
| for (var idx = start; idx < end; idx++) { |
| // NOTICE: Try not to write things into dataItem |
| var dataItem = provider.getItem(idx, sharedDataItem); |
| |
| if (!this.hasItemOption && isDataItemOption(dataItem)) { |
| this.hasItemOption = true; |
| } |
| |
| if (dataItem) { |
| var itemName = dataItem.name; |
| |
| if (nameList[idx] == null && itemName != null) { |
| nameList[idx] = convertOptionIdName(itemName, null); |
| } |
| |
| var itemId = dataItem.id; |
| |
| if (idList[idx] == null && itemId != null) { |
| idList[idx] = convertOptionIdName(itemId, null); |
| } |
| } |
| } |
| } |
| |
| if (this._shouldMakeIdFromName()) { |
| for (var idx = start; idx < end; idx++) { |
| makeIdFromName(this, idx); |
| } |
| } |
| |
| prepareInvertedIndex(this); |
| }; |
| /** |
| * PENDING: In fact currently this function is only used to short-circuit |
| * the calling of `scale.unionExtentFromData` when data have been filtered by modules |
| * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on |
| * an axis, but if a "axis related data filter module" is used, the extent of the axis have |
| * been fixed and no need to calling `scale.unionExtentFromData` actually. |
| * But if we add "custom data filter" in future, which is not "axis related", this method may |
| * be still needed. |
| * |
| * Optimize for the scenario that data is filtered by a given extent. |
| * Consider that if data amount is more than hundreds of thousand, |
| * extent calculation will cost more than 10ms and the cache will |
| * be erased because of the filtering. |
| */ |
| |
| |
| SeriesData.prototype.getApproximateExtent = function (dim) { |
| return this._approximateExtent[dim] || this._store.getDataExtent(this._getStoreDimIndex(dim)); |
| }; |
| /** |
| * Calculate extent on a filtered data might be time consuming. |
| * Approximate extent is only used for: calculate extent of filtered data outside. |
| */ |
| |
| |
| SeriesData.prototype.setApproximateExtent = function (extent, dim) { |
| dim = this.getDimension(dim); |
| this._approximateExtent[dim] = extent.slice(); |
| }; |
| |
| SeriesData.prototype.getCalculationInfo = function (key) { |
| return this._calculationInfo[key]; |
| }; |
| |
| SeriesData.prototype.setCalculationInfo = function (key, value) { |
| isObject$2(key) ? extend(this._calculationInfo, key) : this._calculationInfo[key] = value; |
| }; |
| /** |
| * @return Never be null/undefined. `number` will be converted to string. Because: |
| * In most cases, name is used in display, where returning a string is more convenient. |
| * In other cases, name is used in query (see `indexOfName`), where we can keep the |
| * rule that name `2` equals to name `'2'`. |
| */ |
| |
| |
| SeriesData.prototype.getName = function (idx) { |
| var rawIndex = this.getRawIndex(idx); |
| var name = this._nameList[rawIndex]; |
| |
| if (name == null && this._nameDimIdx != null) { |
| name = getIdNameFromStore(this, this._nameDimIdx, rawIndex); |
| } |
| |
| if (name == null) { |
| name = ''; |
| } |
| |
| return name; |
| }; |
| |
| SeriesData.prototype._getCategory = function (dimIdx, idx) { |
| var ordinal = this._store.get(dimIdx, idx); |
| |
| var ordinalMeta = this._store.getOrdinalMeta(dimIdx); |
| |
| if (ordinalMeta) { |
| return ordinalMeta.categories[ordinal]; |
| } |
| |
| return ordinal; |
| }; |
| /** |
| * @return Never null/undefined. `number` will be converted to string. Because: |
| * In all cases having encountered at present, id is used in making diff comparison, which |
| * are usually based on hash map. We can keep the rule that the internal id are always string |
| * (treat `2` is the same as `'2'`) to make the related logic simple. |
| */ |
| |
| |
| SeriesData.prototype.getId = function (idx) { |
| return getId(this, this.getRawIndex(idx)); |
| }; |
| |
| SeriesData.prototype.count = function () { |
| return this._store.count(); |
| }; |
| /** |
| * Get value. Return NaN if idx is out of range. |
| * |
| * @notice Should better to use `data.getStore().get(dimIndex, dataIdx)` instead. |
| */ |
| |
| |
| SeriesData.prototype.get = function (dim, idx) { |
| var store = this._store; |
| var dimInfo = this._dimInfos[dim]; |
| |
| if (dimInfo) { |
| return store.get(dimInfo.storeDimIndex, idx); |
| } |
| }; |
| /** |
| * @notice Should better to use `data.getStore().getByRawIndex(dimIndex, dataIdx)` instead. |
| */ |
| |
| |
| SeriesData.prototype.getByRawIndex = function (dim, rawIdx) { |
| var store = this._store; |
| var dimInfo = this._dimInfos[dim]; |
| |
| if (dimInfo) { |
| return store.getByRawIndex(dimInfo.storeDimIndex, rawIdx); |
| } |
| }; |
| |
| SeriesData.prototype.getIndices = function () { |
| return this._store.getIndices(); |
| }; |
| |
| SeriesData.prototype.getDataExtent = function (dim) { |
| return this._store.getDataExtent(this._getStoreDimIndex(dim)); |
| }; |
| |
| SeriesData.prototype.getSum = function (dim) { |
| return this._store.getSum(this._getStoreDimIndex(dim)); |
| }; |
| |
| SeriesData.prototype.getMedian = function (dim) { |
| return this._store.getMedian(this._getStoreDimIndex(dim)); |
| }; |
| |
| SeriesData.prototype.getValues = function (dimensions, idx) { |
| var _this = this; |
| |
| var store = this._store; |
| return isArray(dimensions) ? store.getValues(map$1(dimensions, function (dim) { |
| return _this._getStoreDimIndex(dim); |
| }), idx) : store.getValues(dimensions); |
| }; |
| /** |
| * If value is NaN. Including '-' |
| * Only check the coord dimensions. |
| */ |
| |
| |
| SeriesData.prototype.hasValue = function (idx) { |
| var dataDimIndicesOnCoord = this._dimSummary.dataDimIndicesOnCoord; |
| |
| for (var i = 0, len = dataDimIndicesOnCoord.length; i < len; i++) { |
| // Ordinal type originally can be string or number. |
| // But when an ordinal type is used on coord, it can |
| // not be string but only number. So we can also use isNaN. |
| if (isNaN(this._store.get(dataDimIndicesOnCoord[i], idx))) { |
| return false; |
| } |
| } |
| |
| return true; |
| }; |
| /** |
| * Retrieve the index with given name |
| */ |
| |
| |
| SeriesData.prototype.indexOfName = function (name) { |
| for (var i = 0, len = this._store.count(); i < len; i++) { |
| if (this.getName(i) === name) { |
| return i; |
| } |
| } |
| |
| return -1; |
| }; |
| |
| SeriesData.prototype.getRawIndex = function (idx) { |
| return this._store.getRawIndex(idx); |
| }; |
| |
| SeriesData.prototype.indexOfRawIndex = function (rawIndex) { |
| return this._store.indexOfRawIndex(rawIndex); |
| }; |
| /** |
| * Only support the dimension which inverted index created. |
| * Do not support other cases until required. |
| * @param dim concrete dim |
| * @param value ordinal index |
| * @return rawIndex |
| */ |
| |
| |
| SeriesData.prototype.rawIndexOf = function (dim, value) { |
| var invertedIndices = dim && this._invertedIndicesMap[dim]; |
| |
| if ("development" !== 'production') { |
| if (!invertedIndices) { |
| throw new Error('Do not supported yet'); |
| } |
| } |
| |
| var rawIndex = invertedIndices[value]; |
| |
| if (rawIndex == null || isNaN(rawIndex)) { |
| return INDEX_NOT_FOUND; |
| } |
| |
| return rawIndex; |
| }; |
| /** |
| * Retrieve the index of nearest value |
| * @param dim |
| * @param value |
| * @param [maxDistance=Infinity] |
| * @return If and only if multiple indices has |
| * the same value, they are put to the result. |
| */ |
| |
| |
| SeriesData.prototype.indicesOfNearest = function (dim, value, maxDistance) { |
| return this._store.indicesOfNearest(this._getStoreDimIndex(dim), value, maxDistance); |
| }; |
| |
| SeriesData.prototype.each = function (dims, cb, ctx) { |
| |
| if (isFunction(dims)) { |
| ctx = cb; |
| cb = dims; |
| dims = []; |
| } // ctxCompat just for compat echarts3 |
| |
| |
| var fCtx = ctx || this; |
| var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); |
| |
| this._store.each(dimIndices, fCtx ? bind(cb, fCtx) : cb); |
| }; |
| |
| SeriesData.prototype.filterSelf = function (dims, cb, ctx) { |
| |
| if (isFunction(dims)) { |
| ctx = cb; |
| cb = dims; |
| dims = []; |
| } // ctxCompat just for compat echarts3 |
| |
| |
| var fCtx = ctx || this; |
| var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); |
| this._store = this._store.filter(dimIndices, fCtx ? bind(cb, fCtx) : cb); |
| return this; |
| }; |
| /** |
| * Select data in range. (For optimization of filter) |
| * (Manually inline code, support 5 million data filtering in data zoom.) |
| */ |
| |
| |
| SeriesData.prototype.selectRange = function (range) { |
| |
| var _this = this; |
| |
| var innerRange = {}; |
| var dims = keys(range); |
| each(dims, function (dim) { |
| var dimIdx = _this._getStoreDimIndex(dim); |
| |
| innerRange[dimIdx] = range[dim]; |
| }); |
| this._store = this._store.selectRange(innerRange); |
| return this; |
| }; |
| /* eslint-enable max-len */ |
| |
| |
| SeriesData.prototype.mapArray = function (dims, cb, ctx) { |
| |
| if (isFunction(dims)) { |
| ctx = cb; |
| cb = dims; |
| dims = []; |
| } // ctxCompat just for compat echarts3 |
| |
| |
| ctx = ctx || this; |
| var result = []; |
| this.each(dims, function () { |
| result.push(cb && cb.apply(this, arguments)); |
| }, ctx); |
| return result; |
| }; |
| |
| SeriesData.prototype.map = function (dims, cb, ctx, ctxCompat) { |
| |
| var fCtx = ctx || ctxCompat || this; |
| var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); |
| var list = cloneListForMapAndSample(this); |
| list._store = this._store.map(dimIndices, fCtx ? bind(cb, fCtx) : cb); |
| return list; |
| }; |
| |
| SeriesData.prototype.modify = function (dims, cb, ctx, ctxCompat) { |
| var _this = this; // ctxCompat just for compat echarts3 |
| |
| |
| var fCtx = ctx || ctxCompat || this; |
| |
| if ("development" !== 'production') { |
| each(normalizeDimensions(dims), function (dim) { |
| var dimInfo = _this.getDimensionInfo(dim); |
| |
| if (!dimInfo.isCalculationCoord) { |
| console.error('Danger: only stack dimension can be modified'); |
| } |
| }); |
| } |
| |
| var dimIndices = map$1(normalizeDimensions(dims), this._getStoreDimIndex, this); // If do shallow clone here, if there are too many stacked series, |
| // it still cost lots of memory, because `_store.dimensions` are not shared. |
| // We should consider there probably be shallow clone happen in each series |
| // in consequent filter/map. |
| |
| this._store.modify(dimIndices, fCtx ? bind(cb, fCtx) : cb); |
| }; |
| /** |
| * Large data down sampling on given dimension |
| * @param sampleIndex Sample index for name and id |
| */ |
| |
| |
| SeriesData.prototype.downSample = function (dimension, rate, sampleValue, sampleIndex) { |
| var list = cloneListForMapAndSample(this); |
| list._store = this._store.downSample(this._getStoreDimIndex(dimension), rate, sampleValue, sampleIndex); |
| return list; |
| }; |
| /** |
| * Large data down sampling using largest-triangle-three-buckets |
| * @param {string} valueDimension |
| * @param {number} targetCount |
| */ |
| |
| |
| SeriesData.prototype.lttbDownSample = function (valueDimension, rate) { |
| var list = cloneListForMapAndSample(this); |
| list._store = this._store.lttbDownSample(this._getStoreDimIndex(valueDimension), rate); |
| return list; |
| }; |
| |
| SeriesData.prototype.getRawDataItem = function (idx) { |
| return this._store.getRawDataItem(idx); |
| }; |
| /** |
| * Get model of one data item. |
| */ |
| // TODO: Type of data item |
| |
| |
| SeriesData.prototype.getItemModel = function (idx) { |
| var hostModel = this.hostModel; |
| var dataItem = this.getRawDataItem(idx); |
| return new Model(dataItem, hostModel, hostModel && hostModel.ecModel); |
| }; |
| /** |
| * Create a data differ |
| */ |
| |
| |
| SeriesData.prototype.diff = function (otherList) { |
| var thisList = this; |
| return new DataDiffer(otherList ? otherList.getStore().getIndices() : [], this.getStore().getIndices(), function (idx) { |
| return getId(otherList, idx); |
| }, function (idx) { |
| return getId(thisList, idx); |
| }); |
| }; |
| /** |
| * Get visual property. |
| */ |
| |
| |
| SeriesData.prototype.getVisual = function (key) { |
| var visual = this._visual; |
| return visual && visual[key]; |
| }; |
| |
| SeriesData.prototype.setVisual = function (kvObj, val) { |
| this._visual = this._visual || {}; |
| |
| if (isObject$2(kvObj)) { |
| extend(this._visual, kvObj); |
| } else { |
| this._visual[kvObj] = val; |
| } |
| }; |
| /** |
| * Get visual property of single data item |
| */ |
| // eslint-disable-next-line |
| |
| |
| SeriesData.prototype.getItemVisual = function (idx, key) { |
| var itemVisual = this._itemVisuals[idx]; |
| var val = itemVisual && itemVisual[key]; |
| |
| if (val == null) { |
| // Use global visual property |
| return this.getVisual(key); |
| } |
| |
| return val; |
| }; |
| /** |
| * If exists visual property of single data item |
| */ |
| |
| |
| SeriesData.prototype.hasItemVisual = function () { |
| return this._itemVisuals.length > 0; |
| }; |
| /** |
| * Make sure itemVisual property is unique |
| */ |
| // TODO: use key to save visual to reduce memory. |
| |
| |
| SeriesData.prototype.ensureUniqueItemVisual = function (idx, key) { |
| var itemVisuals = this._itemVisuals; |
| var itemVisual = itemVisuals[idx]; |
| |
| if (!itemVisual) { |
| itemVisual = itemVisuals[idx] = {}; |
| } |
| |
| var val = itemVisual[key]; |
| |
| if (val == null) { |
| val = this.getVisual(key); // TODO Performance? |
| |
| if (isArray(val)) { |
| val = val.slice(); |
| } else if (isObject$2(val)) { |
| val = extend({}, val); |
| } |
| |
| itemVisual[key] = val; |
| } |
| |
| return val; |
| }; // eslint-disable-next-line |
| |
| |
| SeriesData.prototype.setItemVisual = function (idx, key, value) { |
| var itemVisual = this._itemVisuals[idx] || {}; |
| this._itemVisuals[idx] = itemVisual; |
| |
| if (isObject$2(key)) { |
| extend(itemVisual, key); |
| } else { |
| itemVisual[key] = value; |
| } |
| }; |
| /** |
| * Clear itemVisuals and list visual. |
| */ |
| |
| |
| SeriesData.prototype.clearAllVisual = function () { |
| this._visual = {}; |
| this._itemVisuals = []; |
| }; |
| |
| SeriesData.prototype.setLayout = function (key, val) { |
| isObject$2(key) ? extend(this._layout, key) : this._layout[key] = val; |
| }; |
| /** |
| * Get layout property. |
| */ |
| |
| |
| SeriesData.prototype.getLayout = function (key) { |
| return this._layout[key]; |
| }; |
| /** |
| * Get layout of single data item |
| */ |
| |
| |
| SeriesData.prototype.getItemLayout = function (idx) { |
| return this._itemLayouts[idx]; |
| }; |
| /** |
| * Set layout of single data item |
| */ |
| |
| |
| SeriesData.prototype.setItemLayout = function (idx, layout, merge) { |
| this._itemLayouts[idx] = merge ? extend(this._itemLayouts[idx] || {}, layout) : layout; |
| }; |
| /** |
| * Clear all layout of single data item |
| */ |
| |
| |
| SeriesData.prototype.clearItemLayouts = function () { |
| this._itemLayouts.length = 0; |
| }; |
| /** |
| * Set graphic element relative to data. It can be set as null |
| */ |
| |
| |
| SeriesData.prototype.setItemGraphicEl = function (idx, el) { |
| var seriesIndex = this.hostModel && this.hostModel.seriesIndex; |
| setCommonECData(seriesIndex, this.dataType, idx, el); |
| this._graphicEls[idx] = el; |
| }; |
| |
| SeriesData.prototype.getItemGraphicEl = function (idx) { |
| return this._graphicEls[idx]; |
| }; |
| |
| SeriesData.prototype.eachItemGraphicEl = function (cb, context) { |
| each(this._graphicEls, function (el, idx) { |
| if (el) { |
| cb && cb.call(context, el, idx); |
| } |
| }); |
| }; |
| /** |
| * Shallow clone a new list except visual and layout properties, and graph elements. |
| * New list only change the indices. |
| */ |
| |
| |
| SeriesData.prototype.cloneShallow = function (list) { |
| if (!list) { |
| list = new SeriesData(this._schema ? this._schema : map$1(this.dimensions, this._getDimInfo, this), this.hostModel); |
| } |
| |
| transferProperties(list, this); |
| list._store = this._store; |
| return list; |
| }; |
| /** |
| * Wrap some method to add more feature |
| */ |
| |
| |
| SeriesData.prototype.wrapMethod = function (methodName, injectFunction) { |
| var originalMethod = this[methodName]; |
| |
| if (!isFunction(originalMethod)) { |
| return; |
| } |
| |
| this.__wrappedMethods = this.__wrappedMethods || []; |
| |
| this.__wrappedMethods.push(methodName); |
| |
| this[methodName] = function () { |
| var res = originalMethod.apply(this, arguments); |
| return injectFunction.apply(this, [res].concat(slice(arguments))); |
| }; |
| }; // ---------------------------------------------------------- |
| // A work around for internal method visiting private member. |
| // ---------------------------------------------------------- |
| |
| |
| SeriesData.internalField = function () { |
| prepareInvertedIndex = function (data) { |
| var invertedIndicesMap = data._invertedIndicesMap; |
| each(invertedIndicesMap, function (invertedIndices, dim) { |
| var dimInfo = data._dimInfos[dim]; // Currently, only dimensions that has ordinalMeta can create inverted indices. |
| |
| var ordinalMeta = dimInfo.ordinalMeta; |
| var store = data._store; |
| |
| if (ordinalMeta) { |
| invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array$1(ordinalMeta.categories.length); // The default value of TypedArray is 0. To avoid miss |
| // mapping to 0, we should set it as INDEX_NOT_FOUND. |
| |
| for (var i = 0; i < invertedIndices.length; i++) { |
| invertedIndices[i] = INDEX_NOT_FOUND; |
| } |
| |
| for (var i = 0; i < store.count(); i++) { |
| // Only support the case that all values are distinct. |
| invertedIndices[store.get(dimInfo.storeDimIndex, i)] = i; |
| } |
| } |
| }); |
| }; |
| |
| getIdNameFromStore = function (data, dimIdx, idx) { |
| return convertOptionIdName(data._getCategory(dimIdx, idx), null); |
| }; |
| /** |
| * @see the comment of `List['getId']`. |
| */ |
| |
| |
| getId = function (data, rawIndex) { |
| var id = data._idList[rawIndex]; |
| |
| if (id == null && data._idDimIdx != null) { |
| id = getIdNameFromStore(data, data._idDimIdx, rawIndex); |
| } |
| |
| if (id == null) { |
| id = ID_PREFIX + rawIndex; |
| } |
| |
| return id; |
| }; |
| |
| normalizeDimensions = function (dimensions) { |
| if (!isArray(dimensions)) { |
| dimensions = dimensions != null ? [dimensions] : []; |
| } |
| |
| return dimensions; |
| }; |
| /** |
| * Data in excludeDimensions is copied, otherwise transferred. |
| */ |
| |
| |
| cloneListForMapAndSample = function (original) { |
| var list = new SeriesData(original._schema ? original._schema : map$1(original.dimensions, original._getDimInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked |
| |
| transferProperties(list, original); |
| return list; |
| }; |
| |
| transferProperties = function (target, source) { |
| each(TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []), function (propName) { |
| if (source.hasOwnProperty(propName)) { |
| target[propName] = source[propName]; |
| } |
| }); |
| target.__wrappedMethods = source.__wrappedMethods; |
| each(CLONE_PROPERTIES, function (propName) { |
| target[propName] = clone(source[propName]); |
| }); |
| target._calculationInfo = extend({}, source._calculationInfo); |
| }; |
| |
| makeIdFromName = function (data, idx) { |
| var nameList = data._nameList; |
| var idList = data._idList; |
| var nameDimIdx = data._nameDimIdx; |
| var idDimIdx = data._idDimIdx; |
| var name = nameList[idx]; |
| var id = idList[idx]; |
| |
| if (name == null && nameDimIdx != null) { |
| nameList[idx] = name = getIdNameFromStore(data, nameDimIdx, idx); |
| } |
| |
| if (id == null && idDimIdx != null) { |
| idList[idx] = id = getIdNameFromStore(data, idDimIdx, idx); |
| } |
| |
| if (id == null && name != null) { |
| var nameRepeatCount = data._nameRepeatCount; |
| var nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1; |
| id = name; |
| |
| if (nmCnt > 1) { |
| id += '__ec__' + nmCnt; |
| } |
| |
| idList[idx] = id; |
| } |
| }; |
| }(); |
| |
| return SeriesData; |
| }(); |
| |
| /** |
| * For outside usage compat (like echarts-gl are using it). |
| */ |
| |
| function createDimensions(source, opt) { |
| return prepareSeriesDataSchema(source, opt).dimensions; |
| } |
| /** |
| * This method builds the relationship between: |
| * + "what the coord sys or series requires (see `coordDimensions`)", |
| * + "what the user defines (in `encode` and `dimensions`, see `opt.dimensionsDefine` and `opt.encodeDefine`)" |
| * + "what the data source provids (see `source`)". |
| * |
| * Some guess strategy will be adapted if user does not define something. |
| * If no 'value' dimension specified, the first no-named dimension will be |
| * named as 'value'. |
| * |
| * @return The results are always sorted by `storeDimIndex` asc. |
| */ |
| |
| function prepareSeriesDataSchema( // TODO: TYPE completeDimensions type |
| source, opt) { |
| if (!isSourceInstance(source)) { |
| source = createSourceFromSeriesDataOption(source); |
| } |
| |
| opt = opt || {}; |
| var sysDims = opt.coordDimensions || []; |
| var dimsDef = opt.dimensionsDefine || source.dimensionsDefine || []; |
| var coordDimNameMap = createHashMap(); |
| var resultList = []; |
| var dimCount = getDimCount(source, sysDims, dimsDef, opt.dimensionsCount); // Try to ignore unused dimensions if sharing a high dimension datastore |
| // 30 is an experience value. |
| |
| var omitUnusedDimensions = opt.canOmitUnusedDimensions && shouldOmitUnusedDimensions(dimCount); |
| var isUsingSourceDimensionsDef = dimsDef === source.dimensionsDefine; |
| var dataDimNameMap = isUsingSourceDimensionsDef ? ensureSourceDimNameMap(source) : createDimNameMap(dimsDef); |
| var encodeDef = opt.encodeDefine; |
| |
| if (!encodeDef && opt.encodeDefaulter) { |
| encodeDef = opt.encodeDefaulter(source, dimCount); |
| } |
| |
| var encodeDefMap = createHashMap(encodeDef); |
| var indicesMap = new CtorInt32Array(dimCount); |
| |
| for (var i = 0; i < indicesMap.length; i++) { |
| indicesMap[i] = -1; |
| } |
| |
| function getResultItem(dimIdx) { |
| var idx = indicesMap[dimIdx]; |
| |
| if (idx < 0) { |
| var dimDefItemRaw = dimsDef[dimIdx]; |
| var dimDefItem = isObject(dimDefItemRaw) ? dimDefItemRaw : { |
| name: dimDefItemRaw |
| }; |
| var resultItem = new SeriesDimensionDefine(); |
| var userDimName = dimDefItem.name; |
| |
| if (userDimName != null && dataDimNameMap.get(userDimName) != null) { |
| // Only if `series.dimensions` is defined in option |
| // displayName, will be set, and dimension will be displayed vertically in |
| // tooltip by default. |
| resultItem.name = resultItem.displayName = userDimName; |
| } |
| |
| dimDefItem.type != null && (resultItem.type = dimDefItem.type); |
| dimDefItem.displayName != null && (resultItem.displayName = dimDefItem.displayName); |
| var newIdx = resultList.length; |
| indicesMap[dimIdx] = newIdx; |
| resultItem.storeDimIndex = dimIdx; |
| resultList.push(resultItem); |
| return resultItem; |
| } |
| |
| return resultList[idx]; |
| } |
| |
| if (!omitUnusedDimensions) { |
| for (var i = 0; i < dimCount; i++) { |
| getResultItem(i); |
| } |
| } // Set `coordDim` and `coordDimIndex` by `encodeDefMap` and normalize `encodeDefMap`. |
| |
| |
| encodeDefMap.each(function (dataDimsRaw, coordDim) { |
| var dataDims = normalizeToArray(dataDimsRaw).slice(); // Note: It is allowed that `dataDims.length` is `0`, e.g., options is |
| // `{encode: {x: -1, y: 1}}`. Should not filter anything in |
| // this case. |
| |
| if (dataDims.length === 1 && !isString(dataDims[0]) && dataDims[0] < 0) { |
| encodeDefMap.set(coordDim, false); |
| return; |
| } |
| |
| var validDataDims = encodeDefMap.set(coordDim, []); |
| each(dataDims, function (resultDimIdxOrName, idx) { |
| // The input resultDimIdx can be dim name or index. |
| var resultDimIdx = isString(resultDimIdxOrName) ? dataDimNameMap.get(resultDimIdxOrName) : resultDimIdxOrName; |
| |
| if (resultDimIdx != null && resultDimIdx < dimCount) { |
| validDataDims[idx] = resultDimIdx; |
| applyDim(getResultItem(resultDimIdx), coordDim, idx); |
| } |
| }); |
| }); // Apply templates and default order from `sysDims`. |
| |
| var availDimIdx = 0; |
| each(sysDims, function (sysDimItemRaw) { |
| var coordDim; |
| var sysDimItemDimsDef; |
| var sysDimItemOtherDims; |
| var sysDimItem; |
| |
| if (isString(sysDimItemRaw)) { |
| coordDim = sysDimItemRaw; |
| sysDimItem = {}; |
| } else { |
| sysDimItem = sysDimItemRaw; |
| coordDim = sysDimItem.name; |
| var ordinalMeta = sysDimItem.ordinalMeta; |
| sysDimItem.ordinalMeta = null; |
| sysDimItem = extend({}, sysDimItem); |
| sysDimItem.ordinalMeta = ordinalMeta; // `coordDimIndex` should not be set directly. |
| |
| sysDimItemDimsDef = sysDimItem.dimsDef; |
| sysDimItemOtherDims = sysDimItem.otherDims; |
| sysDimItem.name = sysDimItem.coordDim = sysDimItem.coordDimIndex = sysDimItem.dimsDef = sysDimItem.otherDims = null; |
| } |
| |
| var dataDims = encodeDefMap.get(coordDim); // negative resultDimIdx means no need to mapping. |
| |
| if (dataDims === false) { |
| return; |
| } |
| |
| dataDims = normalizeToArray(dataDims); // dimensions provides default dim sequences. |
| |
| if (!dataDims.length) { |
| for (var i = 0; i < (sysDimItemDimsDef && sysDimItemDimsDef.length || 1); i++) { |
| while (availDimIdx < dimCount && getResultItem(availDimIdx).coordDim != null) { |
| availDimIdx++; |
| } |
| |
| availDimIdx < dimCount && dataDims.push(availDimIdx++); |
| } |
| } // Apply templates. |
| |
| |
| each(dataDims, function (resultDimIdx, coordDimIndex) { |
| var resultItem = getResultItem(resultDimIdx); // Coordinate system has a higher priority on dim type than source. |
| |
| if (isUsingSourceDimensionsDef && sysDimItem.type != null) { |
| resultItem.type = sysDimItem.type; |
| } |
| |
| applyDim(defaults(resultItem, sysDimItem), coordDim, coordDimIndex); |
| |
| if (resultItem.name == null && sysDimItemDimsDef) { |
| var sysDimItemDimsDefItem = sysDimItemDimsDef[coordDimIndex]; |
| !isObject(sysDimItemDimsDefItem) && (sysDimItemDimsDefItem = { |
| name: sysDimItemDimsDefItem |
| }); |
| resultItem.name = resultItem.displayName = sysDimItemDimsDefItem.name; |
| resultItem.defaultTooltip = sysDimItemDimsDefItem.defaultTooltip; |
| } // FIXME refactor, currently only used in case: {otherDims: {tooltip: false}} |
| |
| |
| sysDimItemOtherDims && defaults(resultItem.otherDims, sysDimItemOtherDims); |
| }); |
| }); |
| |
| function applyDim(resultItem, coordDim, coordDimIndex) { |
| if (VISUAL_DIMENSIONS.get(coordDim) != null) { |
| resultItem.otherDims[coordDim] = coordDimIndex; |
| } else { |
| resultItem.coordDim = coordDim; |
| resultItem.coordDimIndex = coordDimIndex; |
| coordDimNameMap.set(coordDim, true); |
| } |
| } // Make sure the first extra dim is 'value'. |
| |
| |
| var generateCoord = opt.generateCoord; |
| var generateCoordCount = opt.generateCoordCount; |
| var fromZero = generateCoordCount != null; |
| generateCoordCount = generateCoord ? generateCoordCount || 1 : 0; |
| var extra = generateCoord || 'value'; |
| |
| function ifNoNameFillWithCoordName(resultItem) { |
| if (resultItem.name == null) { |
| // Duplication will be removed in the next step. |
| resultItem.name = resultItem.coordDim; |
| } |
| } // Set dim `name` and other `coordDim` and other props. |
| |
| |
| if (!omitUnusedDimensions) { |
| for (var resultDimIdx = 0; resultDimIdx < dimCount; resultDimIdx++) { |
| var resultItem = getResultItem(resultDimIdx); |
| var coordDim = resultItem.coordDim; |
| |
| if (coordDim == null) { |
| // TODO no need to generate coordDim for isExtraCoord? |
| resultItem.coordDim = genCoordDimName(extra, coordDimNameMap, fromZero); |
| resultItem.coordDimIndex = 0; // Series specified generateCoord is using out. |
| |
| if (!generateCoord || generateCoordCount <= 0) { |
| resultItem.isExtraCoord = true; |
| } |
| |
| generateCoordCount--; |
| } |
| |
| ifNoNameFillWithCoordName(resultItem); |
| |
| if (resultItem.type == null && (guessOrdinal(source, resultDimIdx) === BE_ORDINAL.Must // Consider the case: |
| // { |
| // dataset: {source: [ |
| // ['2001', 123], |
| // ['2002', 456], |
| // ... |
| // ['The others', 987], |
| // ]}, |
| // series: {type: 'pie'} |
| // } |
| // The first column should better be treated as a "ordinal" although it |
| // might not be detected as an "ordinal" by `guessOrdinal`. |
| || resultItem.isExtraCoord && (resultItem.otherDims.itemName != null || resultItem.otherDims.seriesName != null))) { |
| resultItem.type = 'ordinal'; |
| } |
| } |
| } else { |
| each(resultList, function (resultItem) { |
| // PENDING: guessOrdinal or let user specify type: 'ordinal' manually? |
| ifNoNameFillWithCoordName(resultItem); |
| }); // Sort dimensions: there are some rule that use the last dim as label, |
| // and for some latter travel process easier. |
| |
| resultList.sort(function (item0, item1) { |
| return item0.storeDimIndex - item1.storeDimIndex; |
| }); |
| } |
| |
| removeDuplication(resultList); |
| return new SeriesDataSchema({ |
| source: source, |
| dimensions: resultList, |
| fullDimensionCount: dimCount, |
| dimensionOmitted: omitUnusedDimensions |
| }); |
| } |
| |
| function removeDuplication(result) { |
| var duplicationMap = createHashMap(); |
| |
| for (var i = 0; i < result.length; i++) { |
| var dim = result[i]; |
| var dimOriginalName = dim.name; |
| var count = duplicationMap.get(dimOriginalName) || 0; |
| |
| if (count > 0) { |
| // Starts from 0. |
| dim.name = dimOriginalName + (count - 1); |
| } |
| |
| count++; |
| duplicationMap.set(dimOriginalName, count); |
| } |
| } // ??? TODO |
| // Originally detect dimCount by data[0]. Should we |
| // optimize it to only by sysDims and dimensions and encode. |
| // So only necessary dims will be initialized. |
| // But |
| // (1) custom series should be considered. where other dims |
| // may be visited. |
| // (2) sometimes user need to calculate bubble size or use visualMap |
| // on other dimensions besides coordSys needed. |
| // So, dims that is not used by system, should be shared in data store? |
| |
| |
| function getDimCount(source, sysDims, dimsDef, optDimCount) { |
| // Note that the result dimCount should not small than columns count |
| // of data, otherwise `dataDimNameMap` checking will be incorrect. |
| var dimCount = Math.max(source.dimensionsDetectedCount || 1, sysDims.length, dimsDef.length, optDimCount || 0); |
| each(sysDims, function (sysDimItem) { |
| var sysDimItemDimsDef; |
| |
| if (isObject(sysDimItem) && (sysDimItemDimsDef = sysDimItem.dimsDef)) { |
| dimCount = Math.max(dimCount, sysDimItemDimsDef.length); |
| } |
| }); |
| return dimCount; |
| } |
| |
| function genCoordDimName(name, map, fromZero) { |
| if (fromZero || map.hasKey(name)) { |
| var i = 0; |
| |
| while (map.hasKey(name + i)) { |
| i++; |
| } |
| |
| name += i; |
| } |
| |
| map.set(name, true); |
| return name; |
| } |
| |
| /** |
| * @class |
| * For example: |
| * { |
| * coordSysName: 'cartesian2d', |
| * coordSysDims: ['x', 'y', ...], |
| * axisMap: HashMap({ |
| * x: xAxisModel, |
| * y: yAxisModel |
| * }), |
| * categoryAxisMap: HashMap({ |
| * x: xAxisModel, |
| * y: undefined |
| * }), |
| * // The index of the first category axis in `coordSysDims`. |
| * // `null/undefined` means no category axis exists. |
| * firstCategoryDimIndex: 1, |
| * // To replace user specified encode. |
| * } |
| */ |
| |
| var CoordSysInfo = |
| /** @class */ |
| function () { |
| function CoordSysInfo(coordSysName) { |
| this.coordSysDims = []; |
| this.axisMap = createHashMap(); |
| this.categoryAxisMap = createHashMap(); |
| this.coordSysName = coordSysName; |
| } |
| |
| return CoordSysInfo; |
| }(); |
| |
| function getCoordSysInfoBySeries(seriesModel) { |
| var coordSysName = seriesModel.get('coordinateSystem'); |
| var result = new CoordSysInfo(coordSysName); |
| var fetch = fetchers[coordSysName]; |
| |
| if (fetch) { |
| fetch(seriesModel, result, result.axisMap, result.categoryAxisMap); |
| return result; |
| } |
| } |
| var fetchers = { |
| cartesian2d: function (seriesModel, result, axisMap, categoryAxisMap) { |
| var xAxisModel = seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0]; |
| var yAxisModel = seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0]; |
| |
| if ("development" !== 'production') { |
| if (!xAxisModel) { |
| throw new Error('xAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('xAxisId'), 0) + '" not found'); |
| } |
| |
| if (!yAxisModel) { |
| throw new Error('yAxis "' + retrieve(seriesModel.get('xAxisIndex'), seriesModel.get('yAxisId'), 0) + '" not found'); |
| } |
| } |
| |
| result.coordSysDims = ['x', 'y']; |
| axisMap.set('x', xAxisModel); |
| axisMap.set('y', yAxisModel); |
| |
| if (isCategory(xAxisModel)) { |
| categoryAxisMap.set('x', xAxisModel); |
| result.firstCategoryDimIndex = 0; |
| } |
| |
| if (isCategory(yAxisModel)) { |
| categoryAxisMap.set('y', yAxisModel); |
| result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1); |
| } |
| }, |
| singleAxis: function (seriesModel, result, axisMap, categoryAxisMap) { |
| var singleAxisModel = seriesModel.getReferringComponents('singleAxis', SINGLE_REFERRING).models[0]; |
| |
| if ("development" !== 'production') { |
| if (!singleAxisModel) { |
| throw new Error('singleAxis should be specified.'); |
| } |
| } |
| |
| result.coordSysDims = ['single']; |
| axisMap.set('single', singleAxisModel); |
| |
| if (isCategory(singleAxisModel)) { |
| categoryAxisMap.set('single', singleAxisModel); |
| result.firstCategoryDimIndex = 0; |
| } |
| }, |
| polar: function (seriesModel, result, axisMap, categoryAxisMap) { |
| var polarModel = seriesModel.getReferringComponents('polar', SINGLE_REFERRING).models[0]; |
| var radiusAxisModel = polarModel.findAxisModel('radiusAxis'); |
| var angleAxisModel = polarModel.findAxisModel('angleAxis'); |
| |
| if ("development" !== 'production') { |
| if (!angleAxisModel) { |
| throw new Error('angleAxis option not found'); |
| } |
| |
| if (!radiusAxisModel) { |
| throw new Error('radiusAxis option not found'); |
| } |
| } |
| |
| result.coordSysDims = ['radius', 'angle']; |
| axisMap.set('radius', radiusAxisModel); |
| axisMap.set('angle', angleAxisModel); |
| |
| if (isCategory(radiusAxisModel)) { |
| categoryAxisMap.set('radius', radiusAxisModel); |
| result.firstCategoryDimIndex = 0; |
| } |
| |
| if (isCategory(angleAxisModel)) { |
| categoryAxisMap.set('angle', angleAxisModel); |
| result.firstCategoryDimIndex == null && (result.firstCategoryDimIndex = 1); |
| } |
| }, |
| geo: function (seriesModel, result, axisMap, categoryAxisMap) { |
| result.coordSysDims = ['lng', 'lat']; |
| }, |
| parallel: function (seriesModel, result, axisMap, categoryAxisMap) { |
| var ecModel = seriesModel.ecModel; |
| var parallelModel = ecModel.getComponent('parallel', seriesModel.get('parallelIndex')); |
| var coordSysDims = result.coordSysDims = parallelModel.dimensions.slice(); |
| each(parallelModel.parallelAxisIndex, function (axisIndex, index) { |
| var axisModel = ecModel.getComponent('parallelAxis', axisIndex); |
| var axisDim = coordSysDims[index]; |
| axisMap.set(axisDim, axisModel); |
| |
| if (isCategory(axisModel)) { |
| categoryAxisMap.set(axisDim, axisModel); |
| |
| if (result.firstCategoryDimIndex == null) { |
| result.firstCategoryDimIndex = index; |
| } |
| } |
| }); |
| } |
| }; |
| |
| function isCategory(axisModel) { |
| return axisModel.get('type') === 'category'; |
| } |
| |
| /** |
| * Note that it is too complicated to support 3d stack by value |
| * (have to create two-dimension inverted index), so in 3d case |
| * we just support that stacked by index. |
| * |
| * @param seriesModel |
| * @param dimensionsInput The same as the input of <module:echarts/data/SeriesData>. |
| * The input will be modified. |
| * @param opt |
| * @param opt.stackedCoordDimension Specify a coord dimension if needed. |
| * @param opt.byIndex=false |
| * @return calculationInfo |
| * { |
| * stackedDimension: string |
| * stackedByDimension: string |
| * isStackedByIndex: boolean |
| * stackedOverDimension: string |
| * stackResultDimension: string |
| * } |
| */ |
| |
| function enableDataStack(seriesModel, dimensionsInput, opt) { |
| opt = opt || {}; |
| var byIndex = opt.byIndex; |
| var stackedCoordDimension = opt.stackedCoordDimension; |
| var dimensionDefineList; |
| var schema; |
| var store; |
| |
| if (isLegacyDimensionsInput(dimensionsInput)) { |
| dimensionDefineList = dimensionsInput; |
| } else { |
| schema = dimensionsInput.schema; |
| dimensionDefineList = schema.dimensions; |
| store = dimensionsInput.store; |
| } // Compatibal: when `stack` is set as '', do not stack. |
| |
| |
| var mayStack = !!(seriesModel && seriesModel.get('stack')); |
| var stackedByDimInfo; |
| var stackedDimInfo; |
| var stackResultDimension; |
| var stackedOverDimension; |
| each(dimensionDefineList, function (dimensionInfo, index) { |
| if (isString(dimensionInfo)) { |
| dimensionDefineList[index] = dimensionInfo = { |
| name: dimensionInfo |
| }; |
| } |
| |
| if (mayStack && !dimensionInfo.isExtraCoord) { |
| // Find the first ordinal dimension as the stackedByDimInfo. |
| if (!byIndex && !stackedByDimInfo && dimensionInfo.ordinalMeta) { |
| stackedByDimInfo = dimensionInfo; |
| } // Find the first stackable dimension as the stackedDimInfo. |
| |
| |
| if (!stackedDimInfo && dimensionInfo.type !== 'ordinal' && dimensionInfo.type !== 'time' && (!stackedCoordDimension || stackedCoordDimension === dimensionInfo.coordDim)) { |
| stackedDimInfo = dimensionInfo; |
| } |
| } |
| }); |
| |
| if (stackedDimInfo && !byIndex && !stackedByDimInfo) { |
| // Compatible with previous design, value axis (time axis) only stack by index. |
| // It may make sense if the user provides elaborately constructed data. |
| byIndex = true; |
| } // Add stack dimension, they can be both calculated by coordinate system in `unionExtent`. |
| // That put stack logic in List is for using conveniently in echarts extensions, but it |
| // might not be a good way. |
| |
| |
| if (stackedDimInfo) { |
| // Use a weird name that not duplicated with other names. |
| // Also need to use seriesModel.id as postfix because different |
| // series may share same data store. The stack dimension needs to be distinguished. |
| stackResultDimension = '__\0ecstackresult_' + seriesModel.id; |
| stackedOverDimension = '__\0ecstackedover_' + seriesModel.id; // Create inverted index to fast query index by value. |
| |
| if (stackedByDimInfo) { |
| stackedByDimInfo.createInvertedIndices = true; |
| } |
| |
| var stackedDimCoordDim_1 = stackedDimInfo.coordDim; |
| var stackedDimType = stackedDimInfo.type; |
| var stackedDimCoordIndex_1 = 0; |
| each(dimensionDefineList, function (dimensionInfo) { |
| if (dimensionInfo.coordDim === stackedDimCoordDim_1) { |
| stackedDimCoordIndex_1++; |
| } |
| }); |
| var stackedOverDimensionDefine = { |
| name: stackResultDimension, |
| coordDim: stackedDimCoordDim_1, |
| coordDimIndex: stackedDimCoordIndex_1, |
| type: stackedDimType, |
| isExtraCoord: true, |
| isCalculationCoord: true, |
| storeDimIndex: dimensionDefineList.length |
| }; |
| var stackResultDimensionDefine = { |
| name: stackedOverDimension, |
| // This dimension contains stack base (generally, 0), so do not set it as |
| // `stackedDimCoordDim` to avoid extent calculation, consider log scale. |
| coordDim: stackedOverDimension, |
| coordDimIndex: stackedDimCoordIndex_1 + 1, |
| type: stackedDimType, |
| isExtraCoord: true, |
| isCalculationCoord: true, |
| storeDimIndex: dimensionDefineList.length + 1 |
| }; |
| |
| if (schema) { |
| if (store) { |
| stackedOverDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackedOverDimension, stackedDimType); |
| stackResultDimensionDefine.storeDimIndex = store.ensureCalculationDimension(stackResultDimension, stackedDimType); |
| } |
| |
| schema.appendCalculationDimension(stackedOverDimensionDefine); |
| schema.appendCalculationDimension(stackResultDimensionDefine); |
| } else { |
| dimensionDefineList.push(stackedOverDimensionDefine); |
| dimensionDefineList.push(stackResultDimensionDefine); |
| } |
| } |
| |
| return { |
| stackedDimension: stackedDimInfo && stackedDimInfo.name, |
| stackedByDimension: stackedByDimInfo && stackedByDimInfo.name, |
| isStackedByIndex: byIndex, |
| stackedOverDimension: stackedOverDimension, |
| stackResultDimension: stackResultDimension |
| }; |
| } |
| |
| function isLegacyDimensionsInput(dimensionsInput) { |
| return !isSeriesDataSchema(dimensionsInput.schema); |
| } |
| |
| function isDimensionStacked(data, stackedDim) { |
| // Each single series only maps to one pair of axis. So we do not need to |
| // check stackByDim, whatever stacked by a dimension or stacked by index. |
| return !!stackedDim && stackedDim === data.getCalculationInfo('stackedDimension'); |
| } |
| function getStackedDimension(data, targetDim) { |
| return isDimensionStacked(data, targetDim) ? data.getCalculationInfo('stackResultDimension') : targetDim; |
| } |
| |
| function getCoordSysDimDefs(seriesModel, coordSysInfo) { |
| var coordSysName = seriesModel.get('coordinateSystem'); |
| var registeredCoordSys = CoordinateSystemManager.get(coordSysName); |
| var coordSysDimDefs; |
| |
| if (coordSysInfo && coordSysInfo.coordSysDims) { |
| coordSysDimDefs = map(coordSysInfo.coordSysDims, function (dim) { |
| var dimInfo = { |
| name: dim |
| }; |
| var axisModel = coordSysInfo.axisMap.get(dim); |
| |
| if (axisModel) { |
| var axisType = axisModel.get('type'); |
| dimInfo.type = getDimensionTypeByAxis(axisType); |
| } |
| |
| return dimInfo; |
| }); |
| } |
| |
| if (!coordSysDimDefs) { |
| // Get dimensions from registered coordinate system |
| coordSysDimDefs = registeredCoordSys && (registeredCoordSys.getDimensionsInfo ? registeredCoordSys.getDimensionsInfo() : registeredCoordSys.dimensions.slice()) || ['x', 'y']; |
| } |
| |
| return coordSysDimDefs; |
| } |
| |
| function injectOrdinalMeta(dimInfoList, createInvertedIndices, coordSysInfo) { |
| var firstCategoryDimIndex; |
| var hasNameEncode; |
| coordSysInfo && each(dimInfoList, function (dimInfo, dimIndex) { |
| var coordDim = dimInfo.coordDim; |
| var categoryAxisModel = coordSysInfo.categoryAxisMap.get(coordDim); |
| |
| if (categoryAxisModel) { |
| if (firstCategoryDimIndex == null) { |
| firstCategoryDimIndex = dimIndex; |
| } |
| |
| dimInfo.ordinalMeta = categoryAxisModel.getOrdinalMeta(); |
| |
| if (createInvertedIndices) { |
| dimInfo.createInvertedIndices = true; |
| } |
| } |
| |
| if (dimInfo.otherDims.itemName != null) { |
| hasNameEncode = true; |
| } |
| }); |
| |
| if (!hasNameEncode && firstCategoryDimIndex != null) { |
| dimInfoList[firstCategoryDimIndex].otherDims.itemName = 0; |
| } |
| |
| return firstCategoryDimIndex; |
| } |
| /** |
| * Caution: there are side effects to `sourceManager` in this method. |
| * Should better only be called in `Series['getInitialData']`. |
| */ |
| |
| |
| function createSeriesData(sourceRaw, seriesModel, opt) { |
| opt = opt || {}; |
| var sourceManager = seriesModel.getSourceManager(); |
| var source; |
| var isOriginalSource = false; |
| |
| if (sourceRaw) { |
| isOriginalSource = true; |
| source = createSourceFromSeriesDataOption(sourceRaw); |
| } else { |
| source = sourceManager.getSource(); // Is series.data. not dataset. |
| |
| isOriginalSource = source.sourceFormat === SOURCE_FORMAT_ORIGINAL; |
| } |
| |
| var coordSysInfo = getCoordSysInfoBySeries(seriesModel); |
| var coordSysDimDefs = getCoordSysDimDefs(seriesModel, coordSysInfo); |
| var useEncodeDefaulter = opt.useEncodeDefaulter; |
| var encodeDefaulter = isFunction(useEncodeDefaulter) ? useEncodeDefaulter : useEncodeDefaulter ? curry(makeSeriesEncodeForAxisCoordSys, coordSysDimDefs, seriesModel) : null; |
| var createDimensionOptions = { |
| coordDimensions: coordSysDimDefs, |
| generateCoord: opt.generateCoord, |
| encodeDefine: seriesModel.getEncode(), |
| encodeDefaulter: encodeDefaulter, |
| canOmitUnusedDimensions: !isOriginalSource |
| }; |
| var schema = prepareSeriesDataSchema(source, createDimensionOptions); |
| var firstCategoryDimIndex = injectOrdinalMeta(schema.dimensions, opt.createInvertedIndices, coordSysInfo); |
| var store = !isOriginalSource ? sourceManager.getSharedDataStore(schema) : null; |
| var stackCalculationInfo = enableDataStack(seriesModel, { |
| schema: schema, |
| store: store |
| }); |
| var data = new SeriesData(schema, seriesModel); |
| data.setCalculationInfo(stackCalculationInfo); |
| var dimValueGetter = firstCategoryDimIndex != null && isNeedCompleteOrdinalData(source) ? function (itemOpt, dimName, dataIndex, dimIndex) { |
| // Use dataIndex as ordinal value in categoryAxis |
| return dimIndex === firstCategoryDimIndex ? dataIndex : this.defaultDimValueGetter(itemOpt, dimName, dataIndex, dimIndex); |
| } : null; |
| data.hasItemOption = false; |
| data.initData( // Try to reuse the data store in sourceManager if using dataset. |
| isOriginalSource ? source : store, null, dimValueGetter); |
| return data; |
| } |
| |
| function isNeedCompleteOrdinalData(source) { |
| if (source.sourceFormat === SOURCE_FORMAT_ORIGINAL) { |
| var sampleItem = firstDataNotNull(source.data || []); |
| return !isArray(getDataItemValue(sampleItem)); |
| } |
| } |
| |
| function firstDataNotNull(arr) { |
| var i = 0; |
| |
| while (i < arr.length && arr[i] == null) { |
| i++; |
| } |
| |
| return arr[i]; |
| } |
| |
| var Scale = |
| /** @class */ |
| function () { |
| function Scale(setting) { |
| this._setting = setting || {}; |
| this._extent = [Infinity, -Infinity]; |
| } |
| |
| Scale.prototype.getSetting = function (name) { |
| return this._setting[name]; |
| }; |
| /** |
| * Set extent from data |
| */ |
| |
| |
| Scale.prototype.unionExtent = function (other) { |
| var extent = this._extent; |
| other[0] < extent[0] && (extent[0] = other[0]); |
| other[1] > extent[1] && (extent[1] = other[1]); // not setExtent because in log axis it may transformed to power |
| // this.setExtent(extent[0], extent[1]); |
| }; |
| /** |
| * Set extent from data |
| */ |
| |
| |
| Scale.prototype.unionExtentFromData = function (data, dim) { |
| this.unionExtent(data.getApproximateExtent(dim)); |
| }; |
| /** |
| * Get extent |
| * |
| * Extent is always in increase order. |
| */ |
| |
| |
| Scale.prototype.getExtent = function () { |
| return this._extent.slice(); |
| }; |
| /** |
| * Set extent |
| */ |
| |
| |
| Scale.prototype.setExtent = function (start, end) { |
| var thisExtent = this._extent; |
| |
| if (!isNaN(start)) { |
| thisExtent[0] = start; |
| } |
| |
| if (!isNaN(end)) { |
| thisExtent[1] = end; |
| } |
| }; |
| /** |
| * If value is in extent range |
| */ |
| |
| |
| Scale.prototype.isInExtentRange = function (value) { |
| return this._extent[0] <= value && this._extent[1] >= value; |
| }; |
| /** |
| * When axis extent depends on data and no data exists, |
| * axis ticks should not be drawn, which is named 'blank'. |
| */ |
| |
| |
| Scale.prototype.isBlank = function () { |
| return this._isBlank; |
| }; |
| /** |
| * When axis extent depends on data and no data exists, |
| * axis ticks should not be drawn, which is named 'blank'. |
| */ |
| |
| |
| Scale.prototype.setBlank = function (isBlank) { |
| this._isBlank = isBlank; |
| }; |
| |
| return Scale; |
| }(); |
| |
| enableClassManagement(Scale); |
| |
| var uidBase = 0; |
| |
| var OrdinalMeta = |
| /** @class */ |
| function () { |
| function OrdinalMeta(opt) { |
| this.categories = opt.categories || []; |
| this._needCollect = opt.needCollect; |
| this._deduplication = opt.deduplication; |
| this.uid = ++uidBase; |
| } |
| |
| OrdinalMeta.createByAxisModel = function (axisModel) { |
| var option = axisModel.option; |
| var data = option.data; |
| var categories = data && map(data, getName); |
| return new OrdinalMeta({ |
| categories: categories, |
| needCollect: !categories, |
| // deduplication is default in axis. |
| deduplication: option.dedplication !== false |
| }); |
| }; |
| |
| OrdinalMeta.prototype.getOrdinal = function (category) { |
| // @ts-ignore |
| return this._getOrCreateMap().get(category); |
| }; |
| /** |
| * @return The ordinal. If not found, return NaN. |
| */ |
| |
| |
| OrdinalMeta.prototype.parseAndCollect = function (category) { |
| var index; |
| var needCollect = this._needCollect; // The value of category dim can be the index of the given category set. |
| // This feature is only supported when !needCollect, because we should |
| // consider a common case: a value is 2017, which is a number but is |
| // expected to be tread as a category. This case usually happen in dataset, |
| // where it happent to be no need of the index feature. |
| |
| if (!isString(category) && !needCollect) { |
| return category; |
| } // Optimize for the scenario: |
| // category is ['2012-01-01', '2012-01-02', ...], where the input |
| // data has been ensured not duplicate and is large data. |
| // Notice, if a dataset dimension provide categroies, usually echarts |
| // should remove duplication except user tell echarts dont do that |
| // (set axis.deduplication = false), because echarts do not know whether |
| // the values in the category dimension has duplication (consider the |
| // parallel-aqi example) |
| |
| |
| if (needCollect && !this._deduplication) { |
| index = this.categories.length; |
| this.categories[index] = category; |
| return index; |
| } |
| |
| var map = this._getOrCreateMap(); // @ts-ignore |
| |
| |
| index = map.get(category); |
| |
| if (index == null) { |
| if (needCollect) { |
| index = this.categories.length; |
| this.categories[index] = category; // @ts-ignore |
| |
| map.set(category, index); |
| } else { |
| index = NaN; |
| } |
| } |
| |
| return index; |
| }; // Consider big data, do not create map until needed. |
| |
| |
| OrdinalMeta.prototype._getOrCreateMap = function () { |
| return this._map || (this._map = createHashMap(this.categories)); |
| }; |
| |
| return OrdinalMeta; |
| }(); |
| |
| function getName(obj) { |
| if (isObject(obj) && obj.value != null) { |
| return obj.value; |
| } else { |
| return obj + ''; |
| } |
| } |
| |
| function isValueNice(val) { |
| var exp10 = Math.pow(10, quantityExponent(Math.abs(val))); |
| var f = Math.abs(val / exp10); |
| return f === 0 || f === 1 || f === 2 || f === 3 || f === 5; |
| } |
| function isIntervalOrLogScale(scale) { |
| return scale.type === 'interval' || scale.type === 'log'; |
| } |
| /** |
| * @param extent Both extent[0] and extent[1] should be valid number. |
| * Should be extent[0] < extent[1]. |
| * @param splitNumber splitNumber should be >= 1. |
| */ |
| |
| function intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval) { |
| var result = {}; |
| var span = extent[1] - extent[0]; |
| var interval = result.interval = nice(span / splitNumber, true); |
| |
| if (minInterval != null && interval < minInterval) { |
| interval = result.interval = minInterval; |
| } |
| |
| if (maxInterval != null && interval > maxInterval) { |
| interval = result.interval = maxInterval; |
| } // Tow more digital for tick. |
| |
| |
| var precision = result.intervalPrecision = getIntervalPrecision(interval); // Niced extent inside original extent |
| |
| var niceTickExtent = result.niceTickExtent = [round(Math.ceil(extent[0] / interval) * interval, precision), round(Math.floor(extent[1] / interval) * interval, precision)]; |
| fixExtent(niceTickExtent, extent); |
| return result; |
| } |
| function increaseInterval(interval) { |
| var exp10 = Math.pow(10, quantityExponent(interval)); // Increase interval |
| |
| var f = interval / exp10; |
| |
| if (!f) { |
| f = 1; |
| } else if (f === 2) { |
| f = 3; |
| } else if (f === 3) { |
| f = 5; |
| } else { |
| // f is 1 or 5 |
| f *= 2; |
| } |
| |
| return round(f * exp10); |
| } |
| /** |
| * @return interval precision |
| */ |
| |
| function getIntervalPrecision(interval) { |
| // Tow more digital for tick. |
| return getPrecision(interval) + 2; |
| } |
| |
| function clamp(niceTickExtent, idx, extent) { |
| niceTickExtent[idx] = Math.max(Math.min(niceTickExtent[idx], extent[1]), extent[0]); |
| } // In some cases (e.g., splitNumber is 1), niceTickExtent may be out of extent. |
| |
| |
| function fixExtent(niceTickExtent, extent) { |
| !isFinite(niceTickExtent[0]) && (niceTickExtent[0] = extent[0]); |
| !isFinite(niceTickExtent[1]) && (niceTickExtent[1] = extent[1]); |
| clamp(niceTickExtent, 0, extent); |
| clamp(niceTickExtent, 1, extent); |
| |
| if (niceTickExtent[0] > niceTickExtent[1]) { |
| niceTickExtent[0] = niceTickExtent[1]; |
| } |
| } |
| function contain$1(val, extent) { |
| return val >= extent[0] && val <= extent[1]; |
| } |
| function normalize$1(val, extent) { |
| if (extent[1] === extent[0]) { |
| return 0.5; |
| } |
| |
| return (val - extent[0]) / (extent[1] - extent[0]); |
| } |
| function scale$2(val, extent) { |
| return val * (extent[1] - extent[0]) + extent[0]; |
| } |
| |
| var OrdinalScale = |
| /** @class */ |
| function (_super) { |
| __extends(OrdinalScale, _super); |
| |
| function OrdinalScale(setting) { |
| var _this = _super.call(this, setting) || this; |
| |
| _this.type = 'ordinal'; |
| |
| var ordinalMeta = _this.getSetting('ordinalMeta'); // Caution: Should not use instanceof, consider ec-extensions using |
| // import approach to get OrdinalMeta class. |
| |
| |
| if (!ordinalMeta) { |
| ordinalMeta = new OrdinalMeta({}); |
| } |
| |
| if (isArray(ordinalMeta)) { |
| ordinalMeta = new OrdinalMeta({ |
| categories: map(ordinalMeta, function (item) { |
| return isObject(item) ? item.value : item; |
| }) |
| }); |
| } |
| |
| _this._ordinalMeta = ordinalMeta; |
| _this._extent = _this.getSetting('extent') || [0, ordinalMeta.categories.length - 1]; |
| return _this; |
| } |
| |
| OrdinalScale.prototype.parse = function (val) { |
| // Caution: Math.round(null) will return `0` rather than `NaN` |
| if (val == null) { |
| return NaN; |
| } |
| |
| return isString(val) ? this._ordinalMeta.getOrdinal(val) // val might be float. |
| : Math.round(val); |
| }; |
| |
| OrdinalScale.prototype.contain = function (rank) { |
| rank = this.parse(rank); |
| return contain$1(rank, this._extent) && this._ordinalMeta.categories[rank] != null; |
| }; |
| /** |
| * Normalize given rank or name to linear [0, 1] |
| * @param val raw ordinal number. |
| * @return normalized value in [0, 1]. |
| */ |
| |
| |
| OrdinalScale.prototype.normalize = function (val) { |
| val = this._getTickNumber(this.parse(val)); |
| return normalize$1(val, this._extent); |
| }; |
| /** |
| * @param val normalized value in [0, 1]. |
| * @return raw ordinal number. |
| */ |
| |
| |
| OrdinalScale.prototype.scale = function (val) { |
| val = Math.round(scale$2(val, this._extent)); |
| return this.getRawOrdinalNumber(val); |
| }; |
| |
| OrdinalScale.prototype.getTicks = function () { |
| var ticks = []; |
| var extent = this._extent; |
| var rank = extent[0]; |
| |
| while (rank <= extent[1]) { |
| ticks.push({ |
| value: rank |
| }); |
| rank++; |
| } |
| |
| return ticks; |
| }; |
| |
| OrdinalScale.prototype.getMinorTicks = function (splitNumber) { |
| // Not support. |
| return; |
| }; |
| /** |
| * @see `Ordinal['_ordinalNumbersByTick']` |
| */ |
| |
| |
| OrdinalScale.prototype.setSortInfo = function (info) { |
| if (info == null) { |
| this._ordinalNumbersByTick = this._ticksByOrdinalNumber = null; |
| return; |
| } |
| |
| var infoOrdinalNumbers = info.ordinalNumbers; |
| var ordinalsByTick = this._ordinalNumbersByTick = []; |
| var ticksByOrdinal = this._ticksByOrdinalNumber = []; // Unnecessary support negative tick in `realtimeSort`. |
| |
| var tickNum = 0; |
| var allCategoryLen = this._ordinalMeta.categories.length; |
| |
| for (var len = Math.min(allCategoryLen, infoOrdinalNumbers.length); tickNum < len; ++tickNum) { |
| var ordinalNumber = infoOrdinalNumbers[tickNum]; |
| ordinalsByTick[tickNum] = ordinalNumber; |
| ticksByOrdinal[ordinalNumber] = tickNum; |
| } // Handle that `series.data` only covers part of the `axis.category.data`. |
| |
| |
| var unusedOrdinal = 0; |
| |
| for (; tickNum < allCategoryLen; ++tickNum) { |
| while (ticksByOrdinal[unusedOrdinal] != null) { |
| unusedOrdinal++; |
| } |
| ordinalsByTick.push(unusedOrdinal); |
| ticksByOrdinal[unusedOrdinal] = tickNum; |
| } |
| }; |
| |
| OrdinalScale.prototype._getTickNumber = function (ordinal) { |
| var ticksByOrdinalNumber = this._ticksByOrdinalNumber; // also support ordinal out of range of `ordinalMeta.categories.length`, |
| // where ordinal numbers are used as tick value directly. |
| |
| return ticksByOrdinalNumber && ordinal >= 0 && ordinal < ticksByOrdinalNumber.length ? ticksByOrdinalNumber[ordinal] : ordinal; |
| }; |
| /** |
| * @usage |
| * ```js |
| * const ordinalNumber = ordinalScale.getRawOrdinalNumber(tickVal); |
| * |
| * // case0 |
| * const rawOrdinalValue = axisModel.getCategories()[ordinalNumber]; |
| * // case1 |
| * const rawOrdinalValue = this._ordinalMeta.categories[ordinalNumber]; |
| * // case2 |
| * const coord = axis.dataToCoord(ordinalNumber); |
| * ``` |
| * |
| * @param {OrdinalNumber} tickNumber index of display |
| */ |
| |
| |
| OrdinalScale.prototype.getRawOrdinalNumber = function (tickNumber) { |
| var ordinalNumbersByTick = this._ordinalNumbersByTick; // tickNumber may be out of range, e.g., when axis max is larger than `ordinalMeta.categories.length`., |
| // where ordinal numbers are used as tick value directly. |
| |
| return ordinalNumbersByTick && tickNumber >= 0 && tickNumber < ordinalNumbersByTick.length ? ordinalNumbersByTick[tickNumber] : tickNumber; |
| }; |
| /** |
| * Get item on tick |
| */ |
| |
| |
| OrdinalScale.prototype.getLabel = function (tick) { |
| if (!this.isBlank()) { |
| var ordinalNumber = this.getRawOrdinalNumber(tick.value); |
| var cateogry = this._ordinalMeta.categories[ordinalNumber]; // Note that if no data, ordinalMeta.categories is an empty array. |
| // Return empty if it's not exist. |
| |
| return cateogry == null ? '' : cateogry + ''; |
| } |
| }; |
| |
| OrdinalScale.prototype.count = function () { |
| return this._extent[1] - this._extent[0] + 1; |
| }; |
| |
| OrdinalScale.prototype.unionExtentFromData = function (data, dim) { |
| this.unionExtent(data.getApproximateExtent(dim)); |
| }; |
| /** |
| * @override |
| * If value is in extent range |
| */ |
| |
| |
| OrdinalScale.prototype.isInExtentRange = function (value) { |
| value = this._getTickNumber(value); |
| return this._extent[0] <= value && this._extent[1] >= value; |
| }; |
| |
| OrdinalScale.prototype.getOrdinalMeta = function () { |
| return this._ordinalMeta; |
| }; |
| |
| OrdinalScale.prototype.calcNiceTicks = function () {}; |
| |
| OrdinalScale.prototype.calcNiceExtent = function () {}; |
| |
| OrdinalScale.type = 'ordinal'; |
| return OrdinalScale; |
| }(Scale); |
| |
| Scale.registerClass(OrdinalScale); |
| |
| var roundNumber = round; |
| |
| var IntervalScale = |
| /** @class */ |
| function (_super) { |
| __extends(IntervalScale, _super); |
| |
| function IntervalScale() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'interval'; // Step is calculated in adjustExtent. |
| |
| _this._interval = 0; |
| _this._intervalPrecision = 2; |
| return _this; |
| } |
| |
| IntervalScale.prototype.parse = function (val) { |
| return val; |
| }; |
| |
| IntervalScale.prototype.contain = function (val) { |
| return contain$1(val, this._extent); |
| }; |
| |
| IntervalScale.prototype.normalize = function (val) { |
| return normalize$1(val, this._extent); |
| }; |
| |
| IntervalScale.prototype.scale = function (val) { |
| return scale$2(val, this._extent); |
| }; |
| |
| IntervalScale.prototype.setExtent = function (start, end) { |
| var thisExtent = this._extent; // start,end may be a Number like '25',so... |
| |
| if (!isNaN(start)) { |
| thisExtent[0] = parseFloat(start); |
| } |
| |
| if (!isNaN(end)) { |
| thisExtent[1] = parseFloat(end); |
| } |
| }; |
| |
| IntervalScale.prototype.unionExtent = function (other) { |
| var extent = this._extent; |
| other[0] < extent[0] && (extent[0] = other[0]); |
| other[1] > extent[1] && (extent[1] = other[1]); // unionExtent may called by it's sub classes |
| |
| this.setExtent(extent[0], extent[1]); |
| }; |
| |
| IntervalScale.prototype.getInterval = function () { |
| return this._interval; |
| }; |
| |
| IntervalScale.prototype.setInterval = function (interval) { |
| this._interval = interval; // Dropped auto calculated niceExtent and use user-set extent. |
| // We assume user wants to set both interval, min, max to get a better result. |
| |
| this._niceExtent = this._extent.slice(); |
| this._intervalPrecision = getIntervalPrecision(interval); |
| }; |
| /** |
| * @param expandToNicedExtent Whether expand the ticks to niced extent. |
| */ |
| |
| |
| IntervalScale.prototype.getTicks = function (expandToNicedExtent) { |
| var interval = this._interval; |
| var extent = this._extent; |
| var niceTickExtent = this._niceExtent; |
| var intervalPrecision = this._intervalPrecision; |
| var ticks = []; // If interval is 0, return []; |
| |
| if (!interval) { |
| return ticks; |
| } // Consider this case: using dataZoom toolbox, zoom and zoom. |
| |
| |
| var safeLimit = 10000; |
| |
| if (extent[0] < niceTickExtent[0]) { |
| if (expandToNicedExtent) { |
| ticks.push({ |
| value: roundNumber(niceTickExtent[0] - interval, intervalPrecision) |
| }); |
| } else { |
| ticks.push({ |
| value: extent[0] |
| }); |
| } |
| } |
| |
| var tick = niceTickExtent[0]; |
| |
| while (tick <= niceTickExtent[1]) { |
| ticks.push({ |
| value: tick |
| }); // Avoid rounding error |
| |
| tick = roundNumber(tick + interval, intervalPrecision); |
| |
| if (tick === ticks[ticks.length - 1].value) { |
| // Consider out of safe float point, e.g., |
| // -3711126.9907707 + 2e-10 === -3711126.9907707 |
| break; |
| } |
| |
| if (ticks.length > safeLimit) { |
| return []; |
| } |
| } // Consider this case: the last item of ticks is smaller |
| // than niceTickExtent[1] and niceTickExtent[1] === extent[1]. |
| |
| |
| var lastNiceTick = ticks.length ? ticks[ticks.length - 1].value : niceTickExtent[1]; |
| |
| if (extent[1] > lastNiceTick) { |
| if (expandToNicedExtent) { |
| ticks.push({ |
| value: roundNumber(lastNiceTick + interval, intervalPrecision) |
| }); |
| } else { |
| ticks.push({ |
| value: extent[1] |
| }); |
| } |
| } |
| |
| return ticks; |
| }; |
| |
| IntervalScale.prototype.getMinorTicks = function (splitNumber) { |
| var ticks = this.getTicks(true); |
| var minorTicks = []; |
| var extent = this.getExtent(); |
| |
| for (var i = 1; i < ticks.length; i++) { |
| var nextTick = ticks[i]; |
| var prevTick = ticks[i - 1]; |
| var count = 0; |
| var minorTicksGroup = []; |
| var interval = nextTick.value - prevTick.value; |
| var minorInterval = interval / splitNumber; |
| |
| while (count < splitNumber - 1) { |
| var minorTick = roundNumber(prevTick.value + (count + 1) * minorInterval); // For the first and last interval. The count may be less than splitNumber. |
| |
| if (minorTick > extent[0] && minorTick < extent[1]) { |
| minorTicksGroup.push(minorTick); |
| } |
| |
| count++; |
| } |
| |
| minorTicks.push(minorTicksGroup); |
| } |
| |
| return minorTicks; |
| }; |
| /** |
| * @param opt.precision If 'auto', use nice presision. |
| * @param opt.pad returns 1.50 but not 1.5 if precision is 2. |
| */ |
| |
| |
| IntervalScale.prototype.getLabel = function (data, opt) { |
| if (data == null) { |
| return ''; |
| } |
| |
| var precision = opt && opt.precision; |
| |
| if (precision == null) { |
| precision = getPrecision(data.value) || 0; |
| } else if (precision === 'auto') { |
| // Should be more precise then tick. |
| precision = this._intervalPrecision; |
| } // (1) If `precision` is set, 12.005 should be display as '12.00500'. |
| // (2) Use roundNumber (toFixed) to avoid scientific notation like '3.5e-7'. |
| |
| |
| var dataNum = roundNumber(data.value, precision, true); |
| return addCommas(dataNum); |
| }; |
| /** |
| * @param splitNumber By default `5`. |
| */ |
| |
| |
| IntervalScale.prototype.calcNiceTicks = function (splitNumber, minInterval, maxInterval) { |
| splitNumber = splitNumber || 5; |
| var extent = this._extent; |
| var span = extent[1] - extent[0]; |
| |
| if (!isFinite(span)) { |
| return; |
| } // User may set axis min 0 and data are all negative |
| // FIXME If it needs to reverse ? |
| |
| |
| if (span < 0) { |
| span = -span; |
| extent.reverse(); |
| } |
| |
| var result = intervalScaleNiceTicks(extent, splitNumber, minInterval, maxInterval); |
| this._intervalPrecision = result.intervalPrecision; |
| this._interval = result.interval; |
| this._niceExtent = result.niceTickExtent; |
| }; |
| |
| IntervalScale.prototype.calcNiceExtent = function (opt) { |
| var extent = this._extent; // If extent start and end are same, expand them |
| |
| if (extent[0] === extent[1]) { |
| if (extent[0] !== 0) { |
| // Expand extent |
| // Note that extents can be both negative. See #13154 |
| var expandSize = Math.abs(extent[0]); // In the fowllowing case |
| // Axis has been fixed max 100 |
| // Plus data are all 100 and axis extent are [100, 100]. |
| // Extend to the both side will cause expanded max is larger than fixed max. |
| // So only expand to the smaller side. |
| |
| if (!opt.fixMax) { |
| extent[1] += expandSize / 2; |
| extent[0] -= expandSize / 2; |
| } else { |
| extent[0] -= expandSize / 2; |
| } |
| } else { |
| extent[1] = 1; |
| } |
| } |
| |
| var span = extent[1] - extent[0]; // If there are no data and extent are [Infinity, -Infinity] |
| |
| if (!isFinite(span)) { |
| extent[0] = 0; |
| extent[1] = 1; |
| } |
| |
| this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); // let extent = this._extent; |
| |
| var interval = this._interval; |
| |
| if (!opt.fixMin) { |
| extent[0] = roundNumber(Math.floor(extent[0] / interval) * interval); |
| } |
| |
| if (!opt.fixMax) { |
| extent[1] = roundNumber(Math.ceil(extent[1] / interval) * interval); |
| } |
| }; |
| |
| IntervalScale.prototype.setNiceExtent = function (min, max) { |
| this._niceExtent = [min, max]; |
| }; |
| |
| IntervalScale.type = 'interval'; |
| return IntervalScale; |
| }(Scale); |
| |
| Scale.registerClass(IntervalScale); |
| |
| /* global Float32Array */ |
| |
| var supportFloat32Array = typeof Float32Array !== 'undefined'; |
| var Float32ArrayCtor = !supportFloat32Array ? Array : Float32Array; |
| function createFloat32Array(arg) { |
| if (isArray(arg)) { |
| // Return self directly if don't support TypedArray. |
| return supportFloat32Array ? new Float32Array(arg) : arg; |
| } // Else is number |
| |
| |
| return new Float32ArrayCtor(arg); |
| } |
| |
| var STACK_PREFIX = '__ec_stack_'; |
| |
| function getSeriesStackId(seriesModel) { |
| return seriesModel.get('stack') || STACK_PREFIX + seriesModel.seriesIndex; |
| } |
| |
| function getAxisKey(axis) { |
| return axis.dim + axis.index; |
| } |
| function prepareLayoutBarSeries(seriesType, ecModel) { |
| var seriesModels = []; |
| ecModel.eachSeriesByType(seriesType, function (seriesModel) { |
| // Check series coordinate, do layout for cartesian2d only |
| if (isOnCartesian(seriesModel)) { |
| seriesModels.push(seriesModel); |
| } |
| }); |
| return seriesModels; |
| } |
| /** |
| * Map from (baseAxis.dim + '_' + baseAxis.index) to min gap of two adjacent |
| * values. |
| * This works for time axes, value axes, and log axes. |
| * For a single time axis, return value is in the form like |
| * {'x_0': [1000000]}. |
| * The value of 1000000 is in milliseconds. |
| */ |
| |
| function getValueAxesMinGaps(barSeries) { |
| /** |
| * Map from axis.index to values. |
| * For a single time axis, axisValues is in the form like |
| * {'x_0': [1495555200000, 1495641600000, 1495728000000]}. |
| * Items in axisValues[x], e.g. 1495555200000, are time values of all |
| * series. |
| */ |
| var axisValues = {}; |
| each(barSeries, function (seriesModel) { |
| var cartesian = seriesModel.coordinateSystem; |
| var baseAxis = cartesian.getBaseAxis(); |
| |
| if (baseAxis.type !== 'time' && baseAxis.type !== 'value') { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| var key = baseAxis.dim + '_' + baseAxis.index; |
| var dimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); |
| var store = data.getStore(); |
| |
| for (var i = 0, cnt = store.count(); i < cnt; ++i) { |
| var value = store.get(dimIdx, i); |
| |
| if (!axisValues[key]) { |
| // No previous data for the axis |
| axisValues[key] = [value]; |
| } else { |
| // No value in previous series |
| axisValues[key].push(value); |
| } // Ignore duplicated time values in the same axis |
| |
| } |
| }); |
| var axisMinGaps = {}; |
| |
| for (var key in axisValues) { |
| if (axisValues.hasOwnProperty(key)) { |
| var valuesInAxis = axisValues[key]; |
| |
| if (valuesInAxis) { |
| // Sort axis values into ascending order to calculate gaps |
| valuesInAxis.sort(function (a, b) { |
| return a - b; |
| }); |
| var min = null; |
| |
| for (var j = 1; j < valuesInAxis.length; ++j) { |
| var delta = valuesInAxis[j] - valuesInAxis[j - 1]; |
| |
| if (delta > 0) { |
| // Ignore 0 delta because they are of the same axis value |
| min = min === null ? delta : Math.min(min, delta); |
| } |
| } // Set to null if only have one data |
| |
| |
| axisMinGaps[key] = min; |
| } |
| } |
| } |
| |
| return axisMinGaps; |
| } |
| |
| function makeColumnLayout(barSeries) { |
| var axisMinGaps = getValueAxesMinGaps(barSeries); |
| var seriesInfoList = []; |
| each(barSeries, function (seriesModel) { |
| var cartesian = seriesModel.coordinateSystem; |
| var baseAxis = cartesian.getBaseAxis(); |
| var axisExtent = baseAxis.getExtent(); |
| var bandWidth; |
| |
| if (baseAxis.type === 'category') { |
| bandWidth = baseAxis.getBandWidth(); |
| } else if (baseAxis.type === 'value' || baseAxis.type === 'time') { |
| var key = baseAxis.dim + '_' + baseAxis.index; |
| var minGap = axisMinGaps[key]; |
| var extentSpan = Math.abs(axisExtent[1] - axisExtent[0]); |
| var scale = baseAxis.scale.getExtent(); |
| var scaleSpan = Math.abs(scale[1] - scale[0]); |
| bandWidth = minGap ? extentSpan / scaleSpan * minGap : extentSpan; // When there is only one data value |
| } else { |
| var data = seriesModel.getData(); |
| bandWidth = Math.abs(axisExtent[1] - axisExtent[0]) / data.count(); |
| } |
| |
| var barWidth = parsePercent$1(seriesModel.get('barWidth'), bandWidth); |
| var barMaxWidth = parsePercent$1(seriesModel.get('barMaxWidth'), bandWidth); |
| var barMinWidth = parsePercent$1( // barMinWidth by default is 0.5 / 1 in cartesian. Because in value axis, |
| // the auto-calculated bar width might be less than 0.5 / 1. |
| seriesModel.get('barMinWidth') || (isInLargeMode(seriesModel) ? 0.5 : 1), bandWidth); |
| var barGap = seriesModel.get('barGap'); |
| var barCategoryGap = seriesModel.get('barCategoryGap'); |
| seriesInfoList.push({ |
| bandWidth: bandWidth, |
| barWidth: barWidth, |
| barMaxWidth: barMaxWidth, |
| barMinWidth: barMinWidth, |
| barGap: barGap, |
| barCategoryGap: barCategoryGap, |
| axisKey: getAxisKey(baseAxis), |
| stackId: getSeriesStackId(seriesModel) |
| }); |
| }); |
| return doCalBarWidthAndOffset(seriesInfoList); |
| } |
| |
| function doCalBarWidthAndOffset(seriesInfoList) { |
| // Columns info on each category axis. Key is cartesian name |
| var columnsMap = {}; |
| each(seriesInfoList, function (seriesInfo, idx) { |
| var axisKey = seriesInfo.axisKey; |
| var bandWidth = seriesInfo.bandWidth; |
| var columnsOnAxis = columnsMap[axisKey] || { |
| bandWidth: bandWidth, |
| remainedWidth: bandWidth, |
| autoWidthCount: 0, |
| categoryGap: null, |
| gap: '20%', |
| stacks: {} |
| }; |
| var stacks = columnsOnAxis.stacks; |
| columnsMap[axisKey] = columnsOnAxis; |
| var stackId = seriesInfo.stackId; |
| |
| if (!stacks[stackId]) { |
| columnsOnAxis.autoWidthCount++; |
| } |
| |
| stacks[stackId] = stacks[stackId] || { |
| width: 0, |
| maxWidth: 0 |
| }; // Caution: In a single coordinate system, these barGrid attributes |
| // will be shared by series. Consider that they have default values, |
| // only the attributes set on the last series will work. |
| // Do not change this fact unless there will be a break change. |
| |
| var barWidth = seriesInfo.barWidth; |
| |
| if (barWidth && !stacks[stackId].width) { |
| // See #6312, do not restrict width. |
| stacks[stackId].width = barWidth; |
| barWidth = Math.min(columnsOnAxis.remainedWidth, barWidth); |
| columnsOnAxis.remainedWidth -= barWidth; |
| } |
| |
| var barMaxWidth = seriesInfo.barMaxWidth; |
| barMaxWidth && (stacks[stackId].maxWidth = barMaxWidth); |
| var barMinWidth = seriesInfo.barMinWidth; |
| barMinWidth && (stacks[stackId].minWidth = barMinWidth); |
| var barGap = seriesInfo.barGap; |
| barGap != null && (columnsOnAxis.gap = barGap); |
| var barCategoryGap = seriesInfo.barCategoryGap; |
| barCategoryGap != null && (columnsOnAxis.categoryGap = barCategoryGap); |
| }); |
| var result = {}; |
| each(columnsMap, function (columnsOnAxis, coordSysName) { |
| result[coordSysName] = {}; |
| var stacks = columnsOnAxis.stacks; |
| var bandWidth = columnsOnAxis.bandWidth; |
| var categoryGapPercent = columnsOnAxis.categoryGap; |
| |
| if (categoryGapPercent == null) { |
| var columnCount = keys(stacks).length; // More columns in one group |
| // the spaces between group is smaller. Or the column will be too thin. |
| |
| categoryGapPercent = Math.max(35 - columnCount * 4, 15) + '%'; |
| } |
| |
| var categoryGap = parsePercent$1(categoryGapPercent, bandWidth); |
| var barGapPercent = parsePercent$1(columnsOnAxis.gap, 1); |
| var remainedWidth = columnsOnAxis.remainedWidth; |
| var autoWidthCount = columnsOnAxis.autoWidthCount; |
| var autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); |
| autoWidth = Math.max(autoWidth, 0); // Find if any auto calculated bar exceeded maxBarWidth |
| |
| each(stacks, function (column) { |
| var maxWidth = column.maxWidth; |
| var minWidth = column.minWidth; |
| |
| if (!column.width) { |
| var finalWidth = autoWidth; |
| |
| if (maxWidth && maxWidth < finalWidth) { |
| finalWidth = Math.min(maxWidth, remainedWidth); |
| } // `minWidth` has higher priority. `minWidth` decide that whether the |
| // bar is able to be visible. So `minWidth` should not be restricted |
| // by `maxWidth` or `remainedWidth` (which is from `bandWidth`). In |
| // the extreme cases for `value` axis, bars are allowed to overlap |
| // with each other if `minWidth` specified. |
| |
| |
| if (minWidth && minWidth > finalWidth) { |
| finalWidth = minWidth; |
| } |
| |
| if (finalWidth !== autoWidth) { |
| column.width = finalWidth; |
| remainedWidth -= finalWidth + barGapPercent * finalWidth; |
| autoWidthCount--; |
| } |
| } else { |
| // `barMinWidth/barMaxWidth` has higher priority than `barWidth`, as |
| // CSS does. Because barWidth can be a percent value, where |
| // `barMaxWidth` can be used to restrict the final width. |
| var finalWidth = column.width; |
| |
| if (maxWidth) { |
| finalWidth = Math.min(finalWidth, maxWidth); |
| } // `minWidth` has higher priority, as described above |
| |
| |
| if (minWidth) { |
| finalWidth = Math.max(finalWidth, minWidth); |
| } |
| |
| column.width = finalWidth; |
| remainedWidth -= finalWidth + barGapPercent * finalWidth; |
| autoWidthCount--; |
| } |
| }); // Recalculate width again |
| |
| autoWidth = (remainedWidth - categoryGap) / (autoWidthCount + (autoWidthCount - 1) * barGapPercent); |
| autoWidth = Math.max(autoWidth, 0); |
| var widthSum = 0; |
| var lastColumn; |
| each(stacks, function (column, idx) { |
| if (!column.width) { |
| column.width = autoWidth; |
| } |
| |
| lastColumn = column; |
| widthSum += column.width * (1 + barGapPercent); |
| }); |
| |
| if (lastColumn) { |
| widthSum -= lastColumn.width * barGapPercent; |
| } |
| |
| var offset = -widthSum / 2; |
| each(stacks, function (column, stackId) { |
| result[coordSysName][stackId] = result[coordSysName][stackId] || { |
| bandWidth: bandWidth, |
| offset: offset, |
| width: column.width |
| }; |
| offset += column.width * (1 + barGapPercent); |
| }); |
| }); |
| return result; |
| } |
| |
| function retrieveColumnLayout(barWidthAndOffset, axis, seriesModel) { |
| if (barWidthAndOffset && axis) { |
| var result = barWidthAndOffset[getAxisKey(axis)]; |
| |
| if (result != null && seriesModel != null) { |
| return result[getSeriesStackId(seriesModel)]; |
| } |
| |
| return result; |
| } |
| } |
| function layout(seriesType, ecModel) { |
| var seriesModels = prepareLayoutBarSeries(seriesType, ecModel); |
| var barWidthAndOffset = makeColumnLayout(seriesModels); |
| each(seriesModels, function (seriesModel) { |
| var data = seriesModel.getData(); |
| var cartesian = seriesModel.coordinateSystem; |
| var baseAxis = cartesian.getBaseAxis(); |
| var stackId = getSeriesStackId(seriesModel); |
| var columnLayoutInfo = barWidthAndOffset[getAxisKey(baseAxis)][stackId]; |
| var columnOffset = columnLayoutInfo.offset; |
| var columnWidth = columnLayoutInfo.width; |
| data.setLayout({ |
| bandWidth: columnLayoutInfo.bandWidth, |
| offset: columnOffset, |
| size: columnWidth |
| }); |
| }); |
| } // TODO: Do not support stack in large mode yet. |
| |
| function createProgressiveLayout(seriesType) { |
| return { |
| seriesType: seriesType, |
| plan: createRenderPlanner(), |
| reset: function (seriesModel) { |
| if (!isOnCartesian(seriesModel)) { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| var cartesian = seriesModel.coordinateSystem; |
| var baseAxis = cartesian.getBaseAxis(); |
| var valueAxis = cartesian.getOtherAxis(baseAxis); |
| var valueDimIdx = data.getDimensionIndex(data.mapDimension(valueAxis.dim)); |
| var baseDimIdx = data.getDimensionIndex(data.mapDimension(baseAxis.dim)); |
| var drawBackground = seriesModel.get('showBackground', true); |
| var valueDim = data.mapDimension(valueAxis.dim); |
| var stackResultDim = data.getCalculationInfo('stackResultDimension'); |
| var stacked = isDimensionStacked(data, valueDim) && !!data.getCalculationInfo('stackedOnSeries'); |
| var isValueAxisH = valueAxis.isHorizontal(); |
| var valueAxisStart = getValueAxisStart(baseAxis, valueAxis); |
| var isLarge = isInLargeMode(seriesModel); |
| var barMinHeight = seriesModel.get('barMinHeight') || 0; |
| var stackedDimIdx = stackResultDim && data.getDimensionIndex(stackResultDim); // Layout info. |
| |
| var columnWidth = data.getLayout('size'); |
| var columnOffset = data.getLayout('offset'); |
| return { |
| progress: function (params, data) { |
| var count = params.count; |
| var largePoints = isLarge && createFloat32Array(count * 3); |
| var largeBackgroundPoints = isLarge && drawBackground && createFloat32Array(count * 3); |
| var largeDataIndices = isLarge && createFloat32Array(count); |
| var coordLayout = cartesian.master.getRect(); |
| var bgSize = isValueAxisH ? coordLayout.width : coordLayout.height; |
| var dataIndex; |
| var store = data.getStore(); |
| var idxOffset = 0; |
| |
| while ((dataIndex = params.next()) != null) { |
| var value = store.get(stacked ? stackedDimIdx : valueDimIdx, dataIndex); |
| var baseValue = store.get(baseDimIdx, dataIndex); |
| var baseCoord = valueAxisStart; |
| var startValue = void 0; // Because of the barMinHeight, we can not use the value in |
| // stackResultDimension directly. |
| |
| if (stacked) { |
| startValue = +value - store.get(valueDimIdx, dataIndex); |
| } |
| |
| var x = void 0; |
| var y = void 0; |
| var width = void 0; |
| var height = void 0; |
| |
| if (isValueAxisH) { |
| var coord = cartesian.dataToPoint([value, baseValue]); |
| |
| if (stacked) { |
| var startCoord = cartesian.dataToPoint([startValue, baseValue]); |
| baseCoord = startCoord[0]; |
| } |
| |
| x = baseCoord; |
| y = coord[1] + columnOffset; |
| width = coord[0] - baseCoord; |
| height = columnWidth; |
| |
| if (Math.abs(width) < barMinHeight) { |
| width = (width < 0 ? -1 : 1) * barMinHeight; |
| } |
| } else { |
| var coord = cartesian.dataToPoint([baseValue, value]); |
| |
| if (stacked) { |
| var startCoord = cartesian.dataToPoint([baseValue, startValue]); |
| baseCoord = startCoord[1]; |
| } |
| |
| x = coord[0] + columnOffset; |
| y = baseCoord; |
| width = columnWidth; |
| height = coord[1] - baseCoord; |
| |
| if (Math.abs(height) < barMinHeight) { |
| // Include zero to has a positive bar |
| height = (height <= 0 ? -1 : 1) * barMinHeight; |
| } |
| } |
| |
| if (!isLarge) { |
| data.setItemLayout(dataIndex, { |
| x: x, |
| y: y, |
| width: width, |
| height: height |
| }); |
| } else { |
| largePoints[idxOffset] = x; |
| largePoints[idxOffset + 1] = y; |
| largePoints[idxOffset + 2] = isValueAxisH ? width : height; |
| |
| if (largeBackgroundPoints) { |
| largeBackgroundPoints[idxOffset] = isValueAxisH ? coordLayout.x : x; |
| largeBackgroundPoints[idxOffset + 1] = isValueAxisH ? y : coordLayout.y; |
| largeBackgroundPoints[idxOffset + 2] = bgSize; |
| } |
| |
| largeDataIndices[dataIndex] = dataIndex; |
| } |
| |
| idxOffset += 3; |
| } |
| |
| if (isLarge) { |
| data.setLayout({ |
| largePoints: largePoints, |
| largeDataIndices: largeDataIndices, |
| largeBackgroundPoints: largeBackgroundPoints, |
| valueAxisHorizontal: isValueAxisH |
| }); |
| } |
| } |
| }; |
| } |
| }; |
| } |
| |
| function isOnCartesian(seriesModel) { |
| return seriesModel.coordinateSystem && seriesModel.coordinateSystem.type === 'cartesian2d'; |
| } |
| |
| function isInLargeMode(seriesModel) { |
| return seriesModel.pipelineContext && seriesModel.pipelineContext.large; |
| } // See cases in `test/bar-start.html` and `#7412`, `#8747`. |
| |
| |
| function getValueAxisStart(baseAxis, valueAxis) { |
| return valueAxis.toGlobalCoord(valueAxis.dataToCoord(valueAxis.type === 'log' ? 1 : 0)); |
| } |
| |
| var bisect = function (a, x, lo, hi) { |
| while (lo < hi) { |
| var mid = lo + hi >>> 1; |
| |
| if (a[mid][1] < x) { |
| lo = mid + 1; |
| } else { |
| hi = mid; |
| } |
| } |
| |
| return lo; |
| }; |
| |
| var TimeScale = |
| /** @class */ |
| function (_super) { |
| __extends(TimeScale, _super); |
| |
| function TimeScale(settings) { |
| var _this = _super.call(this, settings) || this; |
| |
| _this.type = 'time'; |
| return _this; |
| } |
| /** |
| * Get label is mainly for other components like dataZoom, tooltip. |
| */ |
| |
| |
| TimeScale.prototype.getLabel = function (tick) { |
| var useUTC = this.getSetting('useUTC'); |
| return format(tick.value, fullLeveledFormatter[getDefaultFormatPrecisionOfInterval(getPrimaryTimeUnit(this._minLevelUnit))] || fullLeveledFormatter.second, useUTC, this.getSetting('locale')); |
| }; |
| |
| TimeScale.prototype.getFormattedLabel = function (tick, idx, labelFormatter) { |
| var isUTC = this.getSetting('useUTC'); |
| var lang = this.getSetting('locale'); |
| return leveledFormat(tick, idx, labelFormatter, lang, isUTC); |
| }; |
| /** |
| * @override |
| */ |
| |
| |
| TimeScale.prototype.getTicks = function () { |
| var interval = this._interval; |
| var extent = this._extent; |
| var ticks = []; // If interval is 0, return []; |
| |
| if (!interval) { |
| return ticks; |
| } |
| |
| ticks.push({ |
| value: extent[0], |
| level: 0 |
| }); |
| var useUTC = this.getSetting('useUTC'); |
| var innerTicks = getIntervalTicks(this._minLevelUnit, this._approxInterval, useUTC, extent); |
| ticks = ticks.concat(innerTicks); |
| ticks.push({ |
| value: extent[1], |
| level: 0 |
| }); |
| return ticks; |
| }; |
| |
| TimeScale.prototype.calcNiceExtent = function (opt) { |
| var extent = this._extent; // If extent start and end are same, expand them |
| |
| if (extent[0] === extent[1]) { |
| // Expand extent |
| extent[0] -= ONE_DAY; |
| extent[1] += ONE_DAY; |
| } // If there are no data and extent are [Infinity, -Infinity] |
| |
| |
| if (extent[1] === -Infinity && extent[0] === Infinity) { |
| var d = new Date(); |
| extent[1] = +new Date(d.getFullYear(), d.getMonth(), d.getDate()); |
| extent[0] = extent[1] - ONE_DAY; |
| } |
| |
| this.calcNiceTicks(opt.splitNumber, opt.minInterval, opt.maxInterval); |
| }; |
| |
| TimeScale.prototype.calcNiceTicks = function (approxTickNum, minInterval, maxInterval) { |
| approxTickNum = approxTickNum || 10; |
| var extent = this._extent; |
| var span = extent[1] - extent[0]; |
| this._approxInterval = span / approxTickNum; |
| |
| if (minInterval != null && this._approxInterval < minInterval) { |
| this._approxInterval = minInterval; |
| } |
| |
| if (maxInterval != null && this._approxInterval > maxInterval) { |
| this._approxInterval = maxInterval; |
| } |
| |
| var scaleIntervalsLen = scaleIntervals.length; |
| var idx = Math.min(bisect(scaleIntervals, this._approxInterval, 0, scaleIntervalsLen), scaleIntervalsLen - 1); // Interval that can be used to calculate ticks |
| |
| this._interval = scaleIntervals[idx][1]; // Min level used when picking ticks from top down. |
| // We check one more level to avoid the ticks are to sparse in some case. |
| |
| this._minLevelUnit = scaleIntervals[Math.max(idx - 1, 0)][0]; |
| }; |
| |
| TimeScale.prototype.parse = function (val) { |
| // val might be float. |
| return isNumber(val) ? val : +parseDate(val); |
| }; |
| |
| TimeScale.prototype.contain = function (val) { |
| return contain$1(this.parse(val), this._extent); |
| }; |
| |
| TimeScale.prototype.normalize = function (val) { |
| return normalize$1(this.parse(val), this._extent); |
| }; |
| |
| TimeScale.prototype.scale = function (val) { |
| return scale$2(val, this._extent); |
| }; |
| |
| TimeScale.type = 'time'; |
| return TimeScale; |
| }(IntervalScale); |
| /** |
| * This implementation was originally copied from "d3.js" |
| * <https://github.com/d3/d3/blob/b516d77fb8566b576088e73410437494717ada26/src/time/scale.js> |
| * with some modifications made for this program. |
| * See the license statement at the head of this file. |
| */ |
| |
| |
| var scaleIntervals = [// Format interval |
| ['second', ONE_SECOND], ['minute', ONE_MINUTE], ['hour', ONE_HOUR], ['quarter-day', ONE_HOUR * 6], ['half-day', ONE_HOUR * 12], ['day', ONE_DAY * 1.2], ['half-week', ONE_DAY * 3.5], ['week', ONE_DAY * 7], ['month', ONE_DAY * 31], ['quarter', ONE_DAY * 95], ['half-year', ONE_YEAR / 2], ['year', ONE_YEAR] // 1Y |
| ]; |
| |
| function isUnitValueSame(unit, valueA, valueB, isUTC) { |
| var dateA = parseDate(valueA); |
| var dateB = parseDate(valueB); |
| |
| var isSame = function (unit) { |
| return getUnitValue(dateA, unit, isUTC) === getUnitValue(dateB, unit, isUTC); |
| }; |
| |
| var isSameYear = function () { |
| return isSame('year'); |
| }; // const isSameHalfYear = () => isSameYear() && isSame('half-year'); |
| // const isSameQuater = () => isSameYear() && isSame('quarter'); |
| |
| |
| var isSameMonth = function () { |
| return isSameYear() && isSame('month'); |
| }; |
| |
| var isSameDay = function () { |
| return isSameMonth() && isSame('day'); |
| }; // const isSameHalfDay = () => isSameDay() && isSame('half-day'); |
| |
| |
| var isSameHour = function () { |
| return isSameDay() && isSame('hour'); |
| }; |
| |
| var isSameMinute = function () { |
| return isSameHour() && isSame('minute'); |
| }; |
| |
| var isSameSecond = function () { |
| return isSameMinute() && isSame('second'); |
| }; |
| |
| var isSameMilliSecond = function () { |
| return isSameSecond() && isSame('millisecond'); |
| }; |
| |
| switch (unit) { |
| case 'year': |
| return isSameYear(); |
| |
| case 'month': |
| return isSameMonth(); |
| |
| case 'day': |
| return isSameDay(); |
| |
| case 'hour': |
| return isSameHour(); |
| |
| case 'minute': |
| return isSameMinute(); |
| |
| case 'second': |
| return isSameSecond(); |
| |
| case 'millisecond': |
| return isSameMilliSecond(); |
| } |
| } // const primaryUnitGetters = { |
| // year: fullYearGetterName(), |
| // month: monthGetterName(), |
| // day: dateGetterName(), |
| // hour: hoursGetterName(), |
| // minute: minutesGetterName(), |
| // second: secondsGetterName(), |
| // millisecond: millisecondsGetterName() |
| // }; |
| // const primaryUnitUTCGetters = { |
| // year: fullYearGetterName(true), |
| // month: monthGetterName(true), |
| // day: dateGetterName(true), |
| // hour: hoursGetterName(true), |
| // minute: minutesGetterName(true), |
| // second: secondsGetterName(true), |
| // millisecond: millisecondsGetterName(true) |
| // }; |
| // function moveTick(date: Date, unitName: TimeUnit, step: number, isUTC: boolean) { |
| // step = step || 1; |
| // switch (getPrimaryTimeUnit(unitName)) { |
| // case 'year': |
| // date[fullYearSetterName(isUTC)](date[fullYearGetterName(isUTC)]() + step); |
| // break; |
| // case 'month': |
| // date[monthSetterName(isUTC)](date[monthGetterName(isUTC)]() + step); |
| // break; |
| // case 'day': |
| // date[dateSetterName(isUTC)](date[dateGetterName(isUTC)]() + step); |
| // break; |
| // case 'hour': |
| // date[hoursSetterName(isUTC)](date[hoursGetterName(isUTC)]() + step); |
| // break; |
| // case 'minute': |
| // date[minutesSetterName(isUTC)](date[minutesGetterName(isUTC)]() + step); |
| // break; |
| // case 'second': |
| // date[secondsSetterName(isUTC)](date[secondsGetterName(isUTC)]() + step); |
| // break; |
| // case 'millisecond': |
| // date[millisecondsSetterName(isUTC)](date[millisecondsGetterName(isUTC)]() + step); |
| // break; |
| // } |
| // return date.getTime(); |
| // } |
| // const DATE_INTERVALS = [[8, 7.5], [4, 3.5], [2, 1.5]]; |
| // const MONTH_INTERVALS = [[6, 5.5], [3, 2.5], [2, 1.5]]; |
| // const MINUTES_SECONDS_INTERVALS = [[30, 30], [20, 20], [15, 15], [10, 10], [5, 5], [2, 2]]; |
| |
| |
| function getDateInterval(approxInterval, daysInMonth) { |
| approxInterval /= ONE_DAY; |
| return approxInterval > 16 ? 16 // Math.floor(daysInMonth / 2) + 1 // In this case we only want one tick between two months. |
| : approxInterval > 7.5 ? 7 // TODO week 7 or day 8? |
| : approxInterval > 3.5 ? 4 : approxInterval > 1.5 ? 2 : 1; |
| } |
| |
| function getMonthInterval(approxInterval) { |
| var APPROX_ONE_MONTH = 30 * ONE_DAY; |
| approxInterval /= APPROX_ONE_MONTH; |
| return approxInterval > 6 ? 6 : approxInterval > 3 ? 3 : approxInterval > 2 ? 2 : 1; |
| } |
| |
| function getHourInterval(approxInterval) { |
| approxInterval /= ONE_HOUR; |
| return approxInterval > 12 ? 12 : approxInterval > 6 ? 6 : approxInterval > 3.5 ? 4 : approxInterval > 2 ? 2 : 1; |
| } |
| |
| function getMinutesAndSecondsInterval(approxInterval, isMinutes) { |
| approxInterval /= isMinutes ? ONE_MINUTE : ONE_SECOND; |
| return approxInterval > 30 ? 30 : approxInterval > 20 ? 20 : approxInterval > 15 ? 15 : approxInterval > 10 ? 10 : approxInterval > 5 ? 5 : approxInterval > 2 ? 2 : 1; |
| } |
| |
| function getMillisecondsInterval(approxInterval) { |
| return nice(approxInterval, true); |
| } |
| |
| function getFirstTimestampOfUnit(date, unitName, isUTC) { |
| var outDate = new Date(date); |
| |
| switch (getPrimaryTimeUnit(unitName)) { |
| case 'year': |
| case 'month': |
| outDate[monthSetterName(isUTC)](0); |
| |
| case 'day': |
| outDate[dateSetterName(isUTC)](1); |
| |
| case 'hour': |
| outDate[hoursSetterName(isUTC)](0); |
| |
| case 'minute': |
| outDate[minutesSetterName(isUTC)](0); |
| |
| case 'second': |
| outDate[secondsSetterName(isUTC)](0); |
| outDate[millisecondsSetterName(isUTC)](0); |
| } |
| |
| return outDate.getTime(); |
| } |
| |
| function getIntervalTicks(bottomUnitName, approxInterval, isUTC, extent) { |
| var safeLimit = 10000; |
| var unitNames = timeUnits; |
| var iter = 0; |
| |
| function addTicksInSpan(interval, minTimestamp, maxTimestamp, getMethodName, setMethodName, isDate, out) { |
| var date = new Date(minTimestamp); |
| var dateTime = minTimestamp; |
| var d = date[getMethodName](); // if (isDate) { |
| // d -= 1; // Starts with 0; PENDING |
| // } |
| |
| while (dateTime < maxTimestamp && dateTime <= extent[1]) { |
| out.push({ |
| value: dateTime |
| }); |
| d += interval; |
| date[setMethodName](d); |
| dateTime = date.getTime(); |
| } // This extra tick is for calcuating ticks of next level. Will not been added to the final result |
| |
| |
| out.push({ |
| value: dateTime, |
| notAdd: true |
| }); |
| } |
| |
| function addLevelTicks(unitName, lastLevelTicks, levelTicks) { |
| var newAddedTicks = []; |
| var isFirstLevel = !lastLevelTicks.length; |
| |
| if (isUnitValueSame(getPrimaryTimeUnit(unitName), extent[0], extent[1], isUTC)) { |
| return; |
| } |
| |
| if (isFirstLevel) { |
| lastLevelTicks = [{ |
| // TODO Optimize. Not include so may ticks. |
| value: getFirstTimestampOfUnit(new Date(extent[0]), unitName, isUTC) |
| }, { |
| value: extent[1] |
| }]; |
| } |
| |
| for (var i = 0; i < lastLevelTicks.length - 1; i++) { |
| var startTick = lastLevelTicks[i].value; |
| var endTick = lastLevelTicks[i + 1].value; |
| |
| if (startTick === endTick) { |
| continue; |
| } |
| |
| var interval = void 0; |
| var getterName = void 0; |
| var setterName = void 0; |
| var isDate = false; |
| |
| switch (unitName) { |
| case 'year': |
| interval = Math.max(1, Math.round(approxInterval / ONE_DAY / 365)); |
| getterName = fullYearGetterName(isUTC); |
| setterName = fullYearSetterName(isUTC); |
| break; |
| |
| case 'half-year': |
| case 'quarter': |
| case 'month': |
| interval = getMonthInterval(approxInterval); |
| getterName = monthGetterName(isUTC); |
| setterName = monthSetterName(isUTC); |
| break; |
| |
| case 'week': // PENDING If week is added. Ignore day. |
| |
| case 'half-week': |
| case 'day': |
| interval = getDateInterval(approxInterval); // Use 32 days and let interval been 16 |
| |
| getterName = dateGetterName(isUTC); |
| setterName = dateSetterName(isUTC); |
| isDate = true; |
| break; |
| |
| case 'half-day': |
| case 'quarter-day': |
| case 'hour': |
| interval = getHourInterval(approxInterval); |
| getterName = hoursGetterName(isUTC); |
| setterName = hoursSetterName(isUTC); |
| break; |
| |
| case 'minute': |
| interval = getMinutesAndSecondsInterval(approxInterval, true); |
| getterName = minutesGetterName(isUTC); |
| setterName = minutesSetterName(isUTC); |
| break; |
| |
| case 'second': |
| interval = getMinutesAndSecondsInterval(approxInterval, false); |
| getterName = secondsGetterName(isUTC); |
| setterName = secondsSetterName(isUTC); |
| break; |
| |
| case 'millisecond': |
| interval = getMillisecondsInterval(approxInterval); |
| getterName = millisecondsGetterName(isUTC); |
| setterName = millisecondsSetterName(isUTC); |
| break; |
| } |
| |
| addTicksInSpan(interval, startTick, endTick, getterName, setterName, isDate, newAddedTicks); |
| |
| if (unitName === 'year' && levelTicks.length > 1 && i === 0) { |
| // Add nearest years to the left extent. |
| levelTicks.unshift({ |
| value: levelTicks[0].value - interval |
| }); |
| } |
| } |
| |
| for (var i = 0; i < newAddedTicks.length; i++) { |
| levelTicks.push(newAddedTicks[i]); |
| } // newAddedTicks.length && console.log(unitName, newAddedTicks); |
| |
| |
| return newAddedTicks; |
| } |
| |
| var levelsTicks = []; |
| var currentLevelTicks = []; |
| var tickCount = 0; |
| var lastLevelTickCount = 0; |
| |
| for (var i = 0; i < unitNames.length && iter++ < safeLimit; ++i) { |
| var primaryTimeUnit = getPrimaryTimeUnit(unitNames[i]); |
| |
| if (!isPrimaryTimeUnit(unitNames[i])) { |
| // TODO |
| continue; |
| } |
| |
| addLevelTicks(unitNames[i], levelsTicks[levelsTicks.length - 1] || [], currentLevelTicks); |
| var nextPrimaryTimeUnit = unitNames[i + 1] ? getPrimaryTimeUnit(unitNames[i + 1]) : null; |
| |
| if (primaryTimeUnit !== nextPrimaryTimeUnit) { |
| if (currentLevelTicks.length) { |
| lastLevelTickCount = tickCount; // Remove the duplicate so the tick count can be precisely. |
| |
| currentLevelTicks.sort(function (a, b) { |
| return a.value - b.value; |
| }); |
| var levelTicksRemoveDuplicated = []; |
| |
| for (var i_1 = 0; i_1 < currentLevelTicks.length; ++i_1) { |
| var tickValue = currentLevelTicks[i_1].value; |
| |
| if (i_1 === 0 || currentLevelTicks[i_1 - 1].value !== tickValue) { |
| levelTicksRemoveDuplicated.push(currentLevelTicks[i_1]); |
| |
| if (tickValue >= extent[0] && tickValue <= extent[1]) { |
| tickCount++; |
| } |
| } |
| } |
| |
| var targetTickNum = (extent[1] - extent[0]) / approxInterval; // Added too much in this level and not too less in last level |
| |
| if (tickCount > targetTickNum * 1.5 && lastLevelTickCount > targetTickNum / 1.5) { |
| break; |
| } // Only treat primary time unit as one level. |
| |
| |
| levelsTicks.push(levelTicksRemoveDuplicated); |
| |
| if (tickCount > targetTickNum || bottomUnitName === unitNames[i]) { |
| break; |
| } |
| } // Reset if next unitName is primary |
| |
| |
| currentLevelTicks = []; |
| } |
| } |
| |
| if ("development" !== 'production') { |
| if (iter >= safeLimit) { |
| warn('Exceed safe limit.'); |
| } |
| } |
| |
| var levelsTicksInExtent = filter(map(levelsTicks, function (levelTicks) { |
| return filter(levelTicks, function (tick) { |
| return tick.value >= extent[0] && tick.value <= extent[1] && !tick.notAdd; |
| }); |
| }), function (levelTicks) { |
| return levelTicks.length > 0; |
| }); |
| var ticks = []; |
| var maxLevel = levelsTicksInExtent.length - 1; |
| |
| for (var i = 0; i < levelsTicksInExtent.length; ++i) { |
| var levelTicks = levelsTicksInExtent[i]; |
| |
| for (var k = 0; k < levelTicks.length; ++k) { |
| ticks.push({ |
| value: levelTicks[k].value, |
| level: maxLevel - i |
| }); |
| } |
| } |
| |
| ticks.sort(function (a, b) { |
| return a.value - b.value; |
| }); // Remove duplicates |
| |
| var result = []; |
| |
| for (var i = 0; i < ticks.length; ++i) { |
| if (i === 0 || ticks[i].value !== ticks[i - 1].value) { |
| result.push(ticks[i]); |
| } |
| } |
| |
| return result; |
| } |
| |
| Scale.registerClass(TimeScale); |
| |
| var scaleProto = Scale.prototype; // FIXME:TS refactor: not good to call it directly with `this`? |
| |
| var intervalScaleProto = IntervalScale.prototype; |
| var roundingErrorFix = round; |
| var mathFloor = Math.floor; |
| var mathCeil = Math.ceil; |
| var mathPow$1 = Math.pow; |
| var mathLog = Math.log; |
| |
| var LogScale = |
| /** @class */ |
| function (_super) { |
| __extends(LogScale, _super); |
| |
| function LogScale() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'log'; |
| _this.base = 10; |
| _this._originalScale = new IntervalScale(); // FIXME:TS actually used by `IntervalScale` |
| |
| _this._interval = 0; |
| return _this; |
| } |
| /** |
| * @param Whether expand the ticks to niced extent. |
| */ |
| |
| |
| LogScale.prototype.getTicks = function (expandToNicedExtent) { |
| var originalScale = this._originalScale; |
| var extent = this._extent; |
| var originalExtent = originalScale.getExtent(); |
| var ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent); |
| return map(ticks, function (tick) { |
| var val = tick.value; |
| var powVal = round(mathPow$1(this.base, val)); // Fix #4158 |
| |
| powVal = val === extent[0] && this._fixMin ? fixRoundingError(powVal, originalExtent[0]) : powVal; |
| powVal = val === extent[1] && this._fixMax ? fixRoundingError(powVal, originalExtent[1]) : powVal; |
| return { |
| value: powVal |
| }; |
| }, this); |
| }; |
| |
| LogScale.prototype.setExtent = function (start, end) { |
| var base = mathLog(this.base); // log(-Infinity) is NaN, so safe guard here |
| |
| start = mathLog(Math.max(0, start)) / base; |
| end = mathLog(Math.max(0, end)) / base; |
| intervalScaleProto.setExtent.call(this, start, end); |
| }; |
| /** |
| * @return {number} end |
| */ |
| |
| |
| LogScale.prototype.getExtent = function () { |
| var base = this.base; |
| var extent = scaleProto.getExtent.call(this); |
| extent[0] = mathPow$1(base, extent[0]); |
| extent[1] = mathPow$1(base, extent[1]); // Fix #4158 |
| |
| var originalScale = this._originalScale; |
| var originalExtent = originalScale.getExtent(); |
| this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); |
| this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); |
| return extent; |
| }; |
| |
| LogScale.prototype.unionExtent = function (extent) { |
| this._originalScale.unionExtent(extent); |
| |
| var base = this.base; |
| extent[0] = mathLog(extent[0]) / mathLog(base); |
| extent[1] = mathLog(extent[1]) / mathLog(base); |
| scaleProto.unionExtent.call(this, extent); |
| }; |
| |
| LogScale.prototype.unionExtentFromData = function (data, dim) { |
| // TODO |
| // filter value that <= 0 |
| this.unionExtent(data.getApproximateExtent(dim)); |
| }; |
| /** |
| * Update interval and extent of intervals for nice ticks |
| * @param approxTickNum default 10 Given approx tick number |
| */ |
| |
| |
| LogScale.prototype.calcNiceTicks = function (approxTickNum) { |
| approxTickNum = approxTickNum || 10; |
| var extent = this._extent; |
| var span = extent[1] - extent[0]; |
| |
| if (span === Infinity || span <= 0) { |
| return; |
| } |
| |
| var interval = quantity(span); |
| var err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count. |
| |
| if (err <= 0.5) { |
| interval *= 10; |
| } // Interval should be integer |
| |
| |
| while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { |
| interval *= 10; |
| } |
| |
| var niceExtent = [round(mathCeil(extent[0] / interval) * interval), round(mathFloor(extent[1] / interval) * interval)]; |
| this._interval = interval; |
| this._niceExtent = niceExtent; |
| }; |
| |
| LogScale.prototype.calcNiceExtent = function (opt) { |
| intervalScaleProto.calcNiceExtent.call(this, opt); |
| this._fixMin = opt.fixMin; |
| this._fixMax = opt.fixMax; |
| }; |
| |
| LogScale.prototype.parse = function (val) { |
| return val; |
| }; |
| |
| LogScale.prototype.contain = function (val) { |
| val = mathLog(val) / mathLog(this.base); |
| return contain$1(val, this._extent); |
| }; |
| |
| LogScale.prototype.normalize = function (val) { |
| val = mathLog(val) / mathLog(this.base); |
| return normalize$1(val, this._extent); |
| }; |
| |
| LogScale.prototype.scale = function (val) { |
| val = scale$2(val, this._extent); |
| return mathPow$1(this.base, val); |
| }; |
| |
| LogScale.type = 'log'; |
| return LogScale; |
| }(Scale); |
| |
| var proto = LogScale.prototype; |
| proto.getMinorTicks = intervalScaleProto.getMinorTicks; |
| proto.getLabel = intervalScaleProto.getLabel; |
| |
| function fixRoundingError(val, originalVal) { |
| return roundingErrorFix(val, getPrecision(originalVal)); |
| } |
| |
| Scale.registerClass(LogScale); |
| |
| var ScaleRawExtentInfo = |
| /** @class */ |
| function () { |
| function ScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis. |
| originalExtent) { |
| this._prepareParams(scale, model, originalExtent); |
| } |
| /** |
| * Parameters depending on outside (like model, user callback) |
| * are prepared and fixed here. |
| */ |
| |
| |
| ScaleRawExtentInfo.prototype._prepareParams = function (scale, model, // Usually: data extent from all series on this axis. |
| dataExtent) { |
| if (dataExtent[1] < dataExtent[0]) { |
| dataExtent = [NaN, NaN]; |
| } |
| |
| this._dataMin = dataExtent[0]; |
| this._dataMax = dataExtent[1]; |
| var isOrdinal = this._isOrdinal = scale.type === 'ordinal'; |
| this._needCrossZero = scale.type === 'interval' && model.getNeedCrossZero && model.getNeedCrossZero(); |
| var modelMinRaw = this._modelMinRaw = model.get('min', true); |
| |
| if (isFunction(modelMinRaw)) { |
| // This callback always provides users the full data extent (before data is filtered). |
| this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw({ |
| min: dataExtent[0], |
| max: dataExtent[1] |
| })); |
| } else if (modelMinRaw !== 'dataMin') { |
| this._modelMinNum = parseAxisModelMinMax(scale, modelMinRaw); |
| } |
| |
| var modelMaxRaw = this._modelMaxRaw = model.get('max', true); |
| |
| if (isFunction(modelMaxRaw)) { |
| // This callback always provides users the full data extent (before data is filtered). |
| this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw({ |
| min: dataExtent[0], |
| max: dataExtent[1] |
| })); |
| } else if (modelMaxRaw !== 'dataMax') { |
| this._modelMaxNum = parseAxisModelMinMax(scale, modelMaxRaw); |
| } |
| |
| if (isOrdinal) { |
| // FIXME: there is a flaw here: if there is no "block" data processor like `dataZoom`, |
| // and progressive rendering is using, here the category result might just only contain |
| // the processed chunk rather than the entire result. |
| this._axisDataLen = model.getCategories().length; |
| } else { |
| var boundaryGap = model.get('boundaryGap'); |
| var boundaryGapArr = isArray(boundaryGap) ? boundaryGap : [boundaryGap || 0, boundaryGap || 0]; |
| |
| if (typeof boundaryGapArr[0] === 'boolean' || typeof boundaryGapArr[1] === 'boolean') { |
| if ("development" !== 'production') { |
| console.warn('Boolean type for boundaryGap is only ' + 'allowed for ordinal axis. Please use string in ' + 'percentage instead, e.g., "20%". Currently, ' + 'boundaryGap is set to be 0.'); |
| } |
| |
| this._boundaryGapInner = [0, 0]; |
| } else { |
| this._boundaryGapInner = [parsePercent(boundaryGapArr[0], 1), parsePercent(boundaryGapArr[1], 1)]; |
| } |
| } |
| }; |
| /** |
| * Calculate extent by prepared parameters. |
| * This method has no external dependency and can be called duplicatedly, |
| * getting the same result. |
| * If parameters changed, should call this method to recalcuate. |
| */ |
| |
| |
| ScaleRawExtentInfo.prototype.calculate = function () { |
| // Notice: When min/max is not set (that is, when there are null/undefined, |
| // which is the most common case), these cases should be ensured: |
| // (1) For 'ordinal', show all axis.data. |
| // (2) For others: |
| // + `boundaryGap` is applied (if min/max set, boundaryGap is |
| // disabled). |
| // + If `needCrossZero`, min/max should be zero, otherwise, min/max should |
| // be the result that originalExtent enlarged by boundaryGap. |
| // (3) If no data, it should be ensured that `scale.setBlank` is set. |
| var isOrdinal = this._isOrdinal; |
| var dataMin = this._dataMin; |
| var dataMax = this._dataMax; |
| var axisDataLen = this._axisDataLen; |
| var boundaryGapInner = this._boundaryGapInner; |
| var span = !isOrdinal ? dataMax - dataMin || Math.abs(dataMin) : null; // Currently if a `'value'` axis model min is specified as 'dataMin'/'dataMax', |
| // `boundaryGap` will not be used. It's the different from specifying as `null`/`undefined`. |
| |
| var min = this._modelMinRaw === 'dataMin' ? dataMin : this._modelMinNum; |
| var max = this._modelMaxRaw === 'dataMax' ? dataMax : this._modelMaxNum; // If `_modelMinNum`/`_modelMaxNum` is `null`/`undefined`, should not be fixed. |
| |
| var minFixed = min != null; |
| var maxFixed = max != null; |
| |
| if (min == null) { |
| min = isOrdinal ? axisDataLen ? 0 : NaN : dataMin - boundaryGapInner[0] * span; |
| } |
| |
| if (max == null) { |
| max = isOrdinal ? axisDataLen ? axisDataLen - 1 : NaN : dataMax + boundaryGapInner[1] * span; |
| } |
| |
| (min == null || !isFinite(min)) && (min = NaN); |
| (max == null || !isFinite(max)) && (max = NaN); |
| var isBlank = eqNaN(min) || eqNaN(max) || isOrdinal && !axisDataLen; // If data extent modified, need to recalculated to ensure cross zero. |
| |
| if (this._needCrossZero) { |
| // Axis is over zero and min is not set |
| if (min > 0 && max > 0 && !minFixed) { |
| min = 0; // minFixed = true; |
| } // Axis is under zero and max is not set |
| |
| |
| if (min < 0 && max < 0 && !maxFixed) { |
| max = 0; // maxFixed = true; |
| } // PENDING: |
| // When `needCrossZero` and all data is positive/negative, should it be ensured |
| // that the results processed by boundaryGap are positive/negative? |
| // If so, here `minFixed`/`maxFixed` need to be set. |
| |
| } |
| |
| var determinedMin = this._determinedMin; |
| var determinedMax = this._determinedMax; |
| |
| if (determinedMin != null) { |
| min = determinedMin; |
| minFixed = true; |
| } |
| |
| if (determinedMax != null) { |
| max = determinedMax; |
| maxFixed = true; |
| } // Ensure min/max be finite number or NaN here. (not to be null/undefined) |
| // `NaN` means min/max axis is blank. |
| |
| |
| return { |
| min: min, |
| max: max, |
| minFixed: minFixed, |
| maxFixed: maxFixed, |
| isBlank: isBlank |
| }; |
| }; |
| |
| ScaleRawExtentInfo.prototype.modifyDataMinMax = function (minMaxName, val) { |
| if ("development" !== 'production') { |
| assert(!this.frozen); |
| } |
| |
| this[DATA_MIN_MAX_ATTR[minMaxName]] = val; |
| }; |
| |
| ScaleRawExtentInfo.prototype.setDeterminedMinMax = function (minMaxName, val) { |
| var attr = DETERMINED_MIN_MAX_ATTR[minMaxName]; |
| |
| if ("development" !== 'production') { |
| assert(!this.frozen // Earse them usually means logic flaw. |
| && this[attr] == null); |
| } |
| |
| this[attr] = val; |
| }; |
| |
| ScaleRawExtentInfo.prototype.freeze = function () { |
| // @ts-ignore |
| this.frozen = true; |
| }; |
| |
| return ScaleRawExtentInfo; |
| }(); |
| var DETERMINED_MIN_MAX_ATTR = { |
| min: '_determinedMin', |
| max: '_determinedMax' |
| }; |
| var DATA_MIN_MAX_ATTR = { |
| min: '_dataMin', |
| max: '_dataMax' |
| }; |
| /** |
| * Get scale min max and related info only depends on model settings. |
| * This method can be called after coordinate system created. |
| * For example, in data processing stage. |
| * |
| * Scale extent info probably be required multiple times during a workflow. |
| * For example: |
| * (1) `dataZoom` depends it to get the axis extent in "100%" state. |
| * (2) `processor/extentCalculator` depends it to make sure whether axis extent is specified. |
| * (3) `coordSys.update` use it to finally decide the scale extent. |
| * But the callback of `min`/`max` should not be called multiple times. |
| * The code below should not be implemented repeatedly either. |
| * So we cache the result in the scale instance, which will be recreated at the beginning |
| * of the workflow (because `scale` instance will be recreated each round of the workflow). |
| */ |
| |
| function ensureScaleRawExtentInfo(scale, model, // Usually: data extent from all series on this axis. |
| originalExtent) { |
| // Do not permit to recreate. |
| var rawExtentInfo = scale.rawExtentInfo; |
| |
| if (rawExtentInfo) { |
| return rawExtentInfo; |
| } |
| |
| rawExtentInfo = new ScaleRawExtentInfo(scale, model, originalExtent); // @ts-ignore |
| |
| scale.rawExtentInfo = rawExtentInfo; |
| return rawExtentInfo; |
| } |
| function parseAxisModelMinMax(scale, minMax) { |
| return minMax == null ? null : eqNaN(minMax) ? NaN : scale.parse(minMax); |
| } |
| |
| /** |
| * Get axis scale extent before niced. |
| * Item of returned array can only be number (including Infinity and NaN). |
| * |
| * Caution: |
| * Precondition of calling this method: |
| * The scale extent has been initialized using series data extent via |
| * `scale.setExtent` or `scale.unionExtentFromData`; |
| */ |
| |
| function getScaleExtent(scale, model) { |
| var scaleType = scale.type; |
| var rawExtentResult = ensureScaleRawExtentInfo(scale, model, scale.getExtent()).calculate(); |
| scale.setBlank(rawExtentResult.isBlank); |
| var min = rawExtentResult.min; |
| var max = rawExtentResult.max; // If bars are placed on a base axis of type time or interval account for axis boundary overflow and current axis |
| // is base axis |
| // FIXME |
| // (1) Consider support value axis, where below zero and axis `onZero` should be handled properly. |
| // (2) Refactor the logic with `barGrid`. Is it not need to `makeBarWidthAndOffsetInfo` twice with different extent? |
| // Should not depend on series type `bar`? |
| // (3) Fix that might overlap when using dataZoom. |
| // (4) Consider other chart types using `barGrid`? |
| // See #6728, #4862, `test/bar-overflow-time-plot.html` |
| |
| var ecModel = model.ecModel; |
| |
| if (ecModel && scaleType === 'time' |
| /* || scaleType === 'interval' */ |
| ) { |
| var barSeriesModels = prepareLayoutBarSeries('bar', ecModel); |
| var isBaseAxisAndHasBarSeries_1 = false; |
| each(barSeriesModels, function (seriesModel) { |
| isBaseAxisAndHasBarSeries_1 = isBaseAxisAndHasBarSeries_1 || seriesModel.getBaseAxis() === model.axis; |
| }); |
| |
| if (isBaseAxisAndHasBarSeries_1) { |
| // Calculate placement of bars on axis. TODO should be decoupled |
| // with barLayout |
| var barWidthAndOffset = makeColumnLayout(barSeriesModels); // Adjust axis min and max to account for overflow |
| |
| var adjustedScale = adjustScaleForOverflow(min, max, model, barWidthAndOffset); |
| min = adjustedScale.min; |
| max = adjustedScale.max; |
| } |
| } |
| |
| return { |
| extent: [min, max], |
| // "fix" means "fixed", the value should not be |
| // changed in the subsequent steps. |
| fixMin: rawExtentResult.minFixed, |
| fixMax: rawExtentResult.maxFixed |
| }; |
| } |
| |
| function adjustScaleForOverflow(min, max, model, // Only support cartesian coord yet. |
| barWidthAndOffset) { |
| // Get Axis Length |
| var axisExtent = model.axis.getExtent(); |
| var axisLength = axisExtent[1] - axisExtent[0]; // Get bars on current base axis and calculate min and max overflow |
| |
| var barsOnCurrentAxis = retrieveColumnLayout(barWidthAndOffset, model.axis); |
| |
| if (barsOnCurrentAxis === undefined) { |
| return { |
| min: min, |
| max: max |
| }; |
| } |
| |
| var minOverflow = Infinity; |
| each(barsOnCurrentAxis, function (item) { |
| minOverflow = Math.min(item.offset, minOverflow); |
| }); |
| var maxOverflow = -Infinity; |
| each(barsOnCurrentAxis, function (item) { |
| maxOverflow = Math.max(item.offset + item.width, maxOverflow); |
| }); |
| minOverflow = Math.abs(minOverflow); |
| maxOverflow = Math.abs(maxOverflow); |
| var totalOverFlow = minOverflow + maxOverflow; // Calculate required buffer based on old range and overflow |
| |
| var oldRange = max - min; |
| var oldRangePercentOfNew = 1 - (minOverflow + maxOverflow) / axisLength; |
| var overflowBuffer = oldRange / oldRangePercentOfNew - oldRange; |
| max += overflowBuffer * (maxOverflow / totalOverFlow); |
| min -= overflowBuffer * (minOverflow / totalOverFlow); |
| return { |
| min: min, |
| max: max |
| }; |
| } // Precondition of calling this method: |
| // The scale extent has been initialized using series data extent via |
| // `scale.setExtent` or `scale.unionExtentFromData`; |
| |
| |
| function niceScaleExtent(scale, inModel) { |
| var model = inModel; |
| var extentInfo = getScaleExtent(scale, model); |
| var extent = extentInfo.extent; |
| var splitNumber = model.get('splitNumber'); |
| |
| if (scale instanceof LogScale) { |
| scale.base = model.get('logBase'); |
| } |
| |
| var scaleType = scale.type; |
| var interval = model.get('interval'); |
| var isIntervalOrTime = scaleType === 'interval' || scaleType === 'time'; |
| scale.setExtent(extent[0], extent[1]); |
| scale.calcNiceExtent({ |
| splitNumber: splitNumber, |
| fixMin: extentInfo.fixMin, |
| fixMax: extentInfo.fixMax, |
| minInterval: isIntervalOrTime ? model.get('minInterval') : null, |
| maxInterval: isIntervalOrTime ? model.get('maxInterval') : null |
| }); // If some one specified the min, max. And the default calculated interval |
| // is not good enough. He can specify the interval. It is often appeared |
| // in angle axis with angle 0 - 360. Interval calculated in interval scale is hard |
| // to be 60. |
| // FIXME |
| |
| if (interval != null) { |
| scale.setInterval && scale.setInterval(interval); |
| } |
| } |
| /** |
| * @param axisType Default retrieve from model.type |
| */ |
| |
| function createScaleByModel(model, axisType) { |
| axisType = axisType || model.get('type'); |
| |
| if (axisType) { |
| switch (axisType) { |
| // Buildin scale |
| case 'category': |
| return new OrdinalScale({ |
| ordinalMeta: model.getOrdinalMeta ? model.getOrdinalMeta() : model.getCategories(), |
| extent: [Infinity, -Infinity] |
| }); |
| |
| case 'time': |
| return new TimeScale({ |
| locale: model.ecModel.getLocaleModel(), |
| useUTC: model.ecModel.get('useUTC') |
| }); |
| |
| default: |
| // case 'value'/'interval', 'log', or others. |
| return new (Scale.getClass(axisType) || IntervalScale)(); |
| } |
| } |
| } |
| /** |
| * Check if the axis cross 0 |
| */ |
| |
| function ifAxisCrossZero(axis) { |
| var dataExtent = axis.scale.getExtent(); |
| var min = dataExtent[0]; |
| var max = dataExtent[1]; |
| return !(min > 0 && max > 0 || min < 0 && max < 0); |
| } |
| /** |
| * @param axis |
| * @return Label formatter function. |
| * param: {number} tickValue, |
| * param: {number} idx, the index in all ticks. |
| * If category axis, this param is not required. |
| * return: {string} label string. |
| */ |
| |
| function makeLabelFormatter(axis) { |
| var labelFormatter = axis.getLabelModel().get('formatter'); |
| var categoryTickStart = axis.type === 'category' ? axis.scale.getExtent()[0] : null; |
| |
| if (axis.scale.type === 'time') { |
| return function (tpl) { |
| return function (tick, idx) { |
| return axis.scale.getFormattedLabel(tick, idx, tpl); |
| }; |
| }(labelFormatter); |
| } else if (isString(labelFormatter)) { |
| return function (tpl) { |
| return function (tick) { |
| // For category axis, get raw value; for numeric axis, |
| // get formatted label like '1,333,444'. |
| var label = axis.scale.getLabel(tick); |
| var text = tpl.replace('{value}', label != null ? label : ''); |
| return text; |
| }; |
| }(labelFormatter); |
| } else if (isFunction(labelFormatter)) { |
| return function (cb) { |
| return function (tick, idx) { |
| // The original intention of `idx` is "the index of the tick in all ticks". |
| // But the previous implementation of category axis do not consider the |
| // `axisLabel.interval`, which cause that, for example, the `interval` is |
| // `1`, then the ticks "name5", "name7", "name9" are displayed, where the |
| // corresponding `idx` are `0`, `2`, `4`, but not `0`, `1`, `2`. So we keep |
| // the definition here for back compatibility. |
| if (categoryTickStart != null) { |
| idx = tick.value - categoryTickStart; |
| } |
| |
| return cb(getAxisRawValue(axis, tick), idx, tick.level != null ? { |
| level: tick.level |
| } : null); |
| }; |
| }(labelFormatter); |
| } else { |
| return function (tick) { |
| return axis.scale.getLabel(tick); |
| }; |
| } |
| } |
| function getAxisRawValue(axis, tick) { |
| // In category axis with data zoom, tick is not the original |
| // index of axis.data. So tick should not be exposed to user |
| // in category axis. |
| return axis.type === 'category' ? axis.scale.getLabel(tick) : tick.value; |
| } |
| /** |
| * @param axis |
| * @return Be null/undefined if no labels. |
| */ |
| |
| function estimateLabelUnionRect(axis) { |
| var axisModel = axis.model; |
| var scale = axis.scale; |
| |
| if (!axisModel.get(['axisLabel', 'show']) || scale.isBlank()) { |
| return; |
| } |
| |
| var realNumberScaleTicks; |
| var tickCount; |
| var categoryScaleExtent = scale.getExtent(); // Optimize for large category data, avoid call `getTicks()`. |
| |
| if (scale instanceof OrdinalScale) { |
| tickCount = scale.count(); |
| } else { |
| realNumberScaleTicks = scale.getTicks(); |
| tickCount = realNumberScaleTicks.length; |
| } |
| |
| var axisLabelModel = axis.getLabelModel(); |
| var labelFormatter = makeLabelFormatter(axis); |
| var rect; |
| var step = 1; // Simple optimization for large amount of labels |
| |
| if (tickCount > 40) { |
| step = Math.ceil(tickCount / 40); |
| } |
| |
| for (var i = 0; i < tickCount; i += step) { |
| var tick = realNumberScaleTicks ? realNumberScaleTicks[i] : { |
| value: categoryScaleExtent[0] + i |
| }; |
| var label = labelFormatter(tick, i); |
| var unrotatedSingleRect = axisLabelModel.getTextRect(label); |
| var singleRect = rotateTextRect(unrotatedSingleRect, axisLabelModel.get('rotate') || 0); |
| rect ? rect.union(singleRect) : rect = singleRect; |
| } |
| |
| return rect; |
| } |
| |
| function rotateTextRect(textRect, rotate) { |
| var rotateRadians = rotate * Math.PI / 180; |
| var beforeWidth = textRect.width; |
| var beforeHeight = textRect.height; |
| var afterWidth = beforeWidth * Math.abs(Math.cos(rotateRadians)) + Math.abs(beforeHeight * Math.sin(rotateRadians)); |
| var afterHeight = beforeWidth * Math.abs(Math.sin(rotateRadians)) + Math.abs(beforeHeight * Math.cos(rotateRadians)); |
| var rotatedRect = new BoundingRect(textRect.x, textRect.y, afterWidth, afterHeight); |
| return rotatedRect; |
| } |
| /** |
| * @param model axisLabelModel or axisTickModel |
| * @return {number|String} Can be null|'auto'|number|function |
| */ |
| |
| |
| function getOptionCategoryInterval(model) { |
| var interval = model.get('interval'); |
| return interval == null ? 'auto' : interval; |
| } |
| /** |
| * Set `categoryInterval` as 0 implicitly indicates that |
| * show all labels regardless of overlap. |
| * @param {Object} axis axisModel.axis |
| */ |
| |
| function shouldShowAllLabels(axis) { |
| return axis.type === 'category' && getOptionCategoryInterval(axis.getLabelModel()) === 0; |
| } |
| function getDataDimensionsOnAxis(data, axisDim) { |
| // Remove duplicated dat dimensions caused by `getStackedDimension`. |
| var dataDimMap = {}; // Currently `mapDimensionsAll` will contain stack result dimension ('__\0ecstackresult'). |
| // PENDING: is it reasonable? Do we need to remove the original dim from "coord dim" since |
| // there has been stacked result dim? |
| |
| each(data.mapDimensionsAll(axisDim), function (dataDim) { |
| // For example, the extent of the original dimension |
| // is [0.1, 0.5], the extent of the `stackResultDimension` |
| // is [7, 9], the final extent should NOT include [0.1, 0.5], |
| // because there is no graphic corresponding to [0.1, 0.5]. |
| // See the case in `test/area-stack.html` `main1`, where area line |
| // stack needs `yAxis` not start from 0. |
| dataDimMap[getStackedDimension(data, dataDim)] = true; |
| }); |
| return keys(dataDimMap); |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| // eslint-disable-next-line @typescript-eslint/no-unused-vars |
| var AxisModelCommonMixin = |
| /** @class */ |
| function () { |
| function AxisModelCommonMixin() {} |
| |
| AxisModelCommonMixin.prototype.getNeedCrossZero = function () { |
| var option = this.option; |
| return !option.scale; |
| }; |
| /** |
| * Should be implemented by each axis model if necessary. |
| * @return coordinate system model |
| */ |
| |
| |
| AxisModelCommonMixin.prototype.getCoordSysModel = function () { |
| return; |
| }; |
| |
| return AxisModelCommonMixin; |
| }(); |
| |
| /** |
| * Create a multi dimension List structure from seriesModel. |
| */ |
| |
| function createList(seriesModel) { |
| return createSeriesData(null, seriesModel); |
| } // export function createGraph(seriesModel) { |
| var dataStack$1 = { |
| isDimensionStacked: isDimensionStacked, |
| enableDataStack: enableDataStack, |
| getStackedDimension: getStackedDimension |
| }; |
| /** |
| * Create scale |
| * @param {Array.<number>} dataExtent |
| * @param {Object|module:echarts/Model} option If `optoin.type` |
| * is secified, it can only be `'value'` currently. |
| */ |
| |
| function createScale(dataExtent, option) { |
| var axisModel = option; |
| |
| if (!(option instanceof Model)) { |
| axisModel = new Model(option); // FIXME |
| // Currently AxisModelCommonMixin has nothing to do with the |
| // the requirements of `axisHelper.createScaleByModel`. For |
| // example the methods `getCategories` and `getOrdinalMeta` |
| // are required for `'category'` axis, and ecModel is required |
| // for `'time'` axis. But occasionally echarts-gl happened |
| // to only use `'value'` axis. |
| // zrUtil.mixin(axisModel, AxisModelCommonMixin); |
| } |
| |
| var scale = createScaleByModel(axisModel); |
| scale.setExtent(dataExtent[0], dataExtent[1]); |
| niceScaleExtent(scale, axisModel); |
| return scale; |
| } |
| /** |
| * Mixin common methods to axis model, |
| * |
| * Include methods |
| * `getFormattedLabels() => Array.<string>` |
| * `getCategories() => Array.<string>` |
| * `getMin(origin: boolean) => number` |
| * `getMax(origin: boolean) => number` |
| * `getNeedCrossZero() => boolean` |
| */ |
| |
| function mixinAxisModelCommonMethods(Model) { |
| mixin(Model, AxisModelCommonMixin); |
| } |
| function createTextStyle$1(textStyleModel, opts) { |
| opts = opts || {}; |
| return createTextStyle(textStyleModel, null, null, opts.state !== 'normal'); |
| } |
| |
| var helper = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| createList: createList, |
| getLayoutRect: getLayoutRect, |
| dataStack: dataStack$1, |
| createScale: createScale, |
| mixinAxisModelCommonMethods: mixinAxisModelCommonMethods, |
| getECData: getECData, |
| createTextStyle: createTextStyle$1, |
| createDimensions: createDimensions, |
| createSymbol: createSymbol, |
| enableHoverEmphasis: enableHoverEmphasis |
| }); |
| |
| var EPSILON$3 = 1e-8; |
| function isAroundEqual$1(a, b) { |
| return Math.abs(a - b) < EPSILON$3; |
| } |
| function contain$2(points, x, y) { |
| var w = 0; |
| var p = points[0]; |
| if (!p) { |
| return false; |
| } |
| for (var i = 1; i < points.length; i++) { |
| var p2 = points[i]; |
| w += windingLine(p[0], p[1], p2[0], p2[1], x, y); |
| p = p2; |
| } |
| var p0 = points[0]; |
| if (!isAroundEqual$1(p[0], p0[0]) || !isAroundEqual$1(p[1], p0[1])) { |
| w += windingLine(p[0], p[1], p0[0], p0[1], x, y); |
| } |
| return w !== 0; |
| } |
| |
| var TMP_TRANSFORM = []; |
| |
| function transformPoints(points, transform) { |
| for (var p = 0; p < points.length; p++) { |
| applyTransform(points[p], points[p], transform); |
| } |
| } |
| |
| function updateBBoxFromPoints(points, min$1, max$1, projection) { |
| for (var i = 0; i < points.length; i++) { |
| var p = points[i]; |
| |
| if (projection) { |
| // projection may return null point. |
| p = projection.project(p); |
| } |
| |
| if (p && isFinite(p[0]) && isFinite(p[1])) { |
| min(min$1, min$1, p); |
| max(max$1, max$1, p); |
| } |
| } |
| } |
| |
| function centroid(points) { |
| var signedArea = 0; |
| var cx = 0; |
| var cy = 0; |
| var len = points.length; |
| var x0 = points[len - 1][0]; |
| var y0 = points[len - 1][1]; // Polygon should been closed. |
| |
| for (var i = 0; i < len; i++) { |
| var x1 = points[i][0]; |
| var y1 = points[i][1]; |
| var a = x0 * y1 - x1 * y0; |
| signedArea += a; |
| cx += (x0 + x1) * a; |
| cy += (y0 + y1) * a; |
| x0 = x1; |
| y0 = y1; |
| } |
| |
| return signedArea ? [cx / signedArea / 3, cy / signedArea / 3, signedArea] : [points[0][0] || 0, points[0][1] || 0]; |
| } |
| |
| var Region = |
| /** @class */ |
| function () { |
| function Region(name) { |
| this.name = name; |
| } |
| |
| Region.prototype.setCenter = function (center) { |
| this._center = center; |
| }; |
| /** |
| * Get center point in data unit. That is, |
| * for GeoJSONRegion, the unit is lat/lng, |
| * for GeoSVGRegion, the unit is SVG local coord. |
| */ |
| |
| |
| Region.prototype.getCenter = function () { |
| var center = this._center; |
| |
| if (!center) { |
| // In most cases there are no need to calculate this center. |
| // So calculate only when called. |
| center = this._center = this.calcCenter(); |
| } |
| |
| return center; |
| }; |
| |
| return Region; |
| }(); |
| |
| var GeoJSONPolygonGeometry = |
| /** @class */ |
| function () { |
| function GeoJSONPolygonGeometry(exterior, interiors) { |
| this.type = 'polygon'; |
| this.exterior = exterior; |
| this.interiors = interiors; |
| } |
| |
| return GeoJSONPolygonGeometry; |
| }(); |
| |
| var GeoJSONLineStringGeometry = |
| /** @class */ |
| function () { |
| function GeoJSONLineStringGeometry(points) { |
| this.type = 'linestring'; |
| this.points = points; |
| } |
| |
| return GeoJSONLineStringGeometry; |
| }(); |
| |
| var GeoJSONRegion = |
| /** @class */ |
| function (_super) { |
| __extends(GeoJSONRegion, _super); |
| |
| function GeoJSONRegion(name, geometries, cp) { |
| var _this = _super.call(this, name) || this; |
| |
| _this.type = 'geoJSON'; |
| _this.geometries = geometries; |
| _this._center = cp && [cp[0], cp[1]]; |
| return _this; |
| } |
| |
| GeoJSONRegion.prototype.calcCenter = function () { |
| var geometries = this.geometries; |
| var largestGeo; |
| var largestGeoSize = 0; |
| |
| for (var i = 0; i < geometries.length; i++) { |
| var geo = geometries[i]; |
| var exterior = geo.exterior; // Simple trick to use points count instead of polygon area as region size. |
| // Ignore linestring |
| |
| var size = exterior && exterior.length; |
| |
| if (size > largestGeoSize) { |
| largestGeo = geo; |
| largestGeoSize = size; |
| } |
| } |
| |
| if (largestGeo) { |
| return centroid(largestGeo.exterior); |
| } // from bounding rect by default. |
| |
| |
| var rect = this.getBoundingRect(); |
| return [rect.x + rect.width / 2, rect.y + rect.height / 2]; |
| }; |
| |
| GeoJSONRegion.prototype.getBoundingRect = function (projection) { |
| var rect = this._rect; // Always recalculate if using projection. |
| |
| if (rect && !projection) { |
| return rect; |
| } |
| |
| var min = [Infinity, Infinity]; |
| var max = [-Infinity, -Infinity]; |
| var geometries = this.geometries; |
| each(geometries, function (geo) { |
| if (geo.type === 'polygon') { |
| // Doesn't consider hole |
| updateBBoxFromPoints(geo.exterior, min, max, projection); |
| } else { |
| each(geo.points, function (points) { |
| updateBBoxFromPoints(points, min, max, projection); |
| }); |
| } |
| }); // Normalie invalid bounding. |
| |
| if (!(isFinite(min[0]) && isFinite(min[1]) && isFinite(max[0]) && isFinite(max[1]))) { |
| min[0] = min[1] = max[0] = max[1] = 0; |
| } |
| |
| rect = new BoundingRect(min[0], min[1], max[0] - min[0], max[1] - min[1]); |
| |
| if (!projection) { |
| this._rect = rect; |
| } |
| |
| return rect; |
| }; |
| |
| GeoJSONRegion.prototype.contain = function (coord) { |
| var rect = this.getBoundingRect(); |
| var geometries = this.geometries; |
| |
| if (!rect.contain(coord[0], coord[1])) { |
| return false; |
| } |
| |
| loopGeo: for (var i = 0, len = geometries.length; i < len; i++) { |
| var geo = geometries[i]; // Only support polygon. |
| |
| if (geo.type !== 'polygon') { |
| continue; |
| } |
| |
| var exterior = geo.exterior; |
| var interiors = geo.interiors; |
| |
| if (contain$2(exterior, coord[0], coord[1])) { |
| // Not in the region if point is in the hole. |
| for (var k = 0; k < (interiors ? interiors.length : 0); k++) { |
| if (contain$2(interiors[k], coord[0], coord[1])) { |
| continue loopGeo; |
| } |
| } |
| |
| return true; |
| } |
| } |
| |
| return false; |
| }; |
| /** |
| * Transform the raw coords to target bounding. |
| * @param x |
| * @param y |
| * @param width |
| * @param height |
| */ |
| |
| |
| GeoJSONRegion.prototype.transformTo = function (x, y, width, height) { |
| var rect = this.getBoundingRect(); |
| var aspect = rect.width / rect.height; |
| |
| if (!width) { |
| width = aspect * height; |
| } else if (!height) { |
| height = width / aspect; |
| } |
| |
| var target = new BoundingRect(x, y, width, height); |
| var transform = rect.calculateTransform(target); |
| var geometries = this.geometries; |
| |
| for (var i = 0; i < geometries.length; i++) { |
| var geo = geometries[i]; |
| |
| if (geo.type === 'polygon') { |
| transformPoints(geo.exterior, transform); |
| each(geo.interiors, function (interior) { |
| transformPoints(interior, transform); |
| }); |
| } else { |
| each(geo.points, function (points) { |
| transformPoints(points, transform); |
| }); |
| } |
| } |
| |
| rect = this._rect; |
| rect.copy(target); // Update center |
| |
| this._center = [rect.x + rect.width / 2, rect.y + rect.height / 2]; |
| }; |
| |
| GeoJSONRegion.prototype.cloneShallow = function (name) { |
| name == null && (name = this.name); |
| var newRegion = new GeoJSONRegion(name, this.geometries, this._center); |
| newRegion._rect = this._rect; |
| newRegion.transformTo = null; // Simply avoid to be called. |
| |
| return newRegion; |
| }; |
| |
| return GeoJSONRegion; |
| }(Region); |
| |
| var GeoSVGRegion = |
| /** @class */ |
| function (_super) { |
| __extends(GeoSVGRegion, _super); |
| |
| function GeoSVGRegion(name, elOnlyForCalculate) { |
| var _this = _super.call(this, name) || this; |
| |
| _this.type = 'geoSVG'; |
| _this._elOnlyForCalculate = elOnlyForCalculate; |
| return _this; |
| } |
| |
| GeoSVGRegion.prototype.calcCenter = function () { |
| var el = this._elOnlyForCalculate; |
| var rect = el.getBoundingRect(); |
| var center = [rect.x + rect.width / 2, rect.y + rect.height / 2]; |
| var mat = identity(TMP_TRANSFORM); |
| var target = el; |
| |
| while (target && !target.isGeoSVGGraphicRoot) { |
| mul$1(mat, target.getLocalTransform(), mat); |
| target = target.parent; |
| } |
| |
| invert(mat, mat); |
| applyTransform(center, center, mat); |
| return center; |
| }; |
| |
| return GeoSVGRegion; |
| }(Region); |
| |
| function decode(json) { |
| if (!json.UTF8Encoding) { |
| return json; |
| } |
| |
| var jsonCompressed = json; |
| var encodeScale = jsonCompressed.UTF8Scale; |
| |
| if (encodeScale == null) { |
| encodeScale = 1024; |
| } |
| |
| var features = jsonCompressed.features; |
| each(features, function (feature) { |
| var geometry = feature.geometry; |
| var encodeOffsets = geometry.encodeOffsets; |
| var coordinates = geometry.coordinates; // Geometry may be appeded manually in the script after json loaded. |
| // In this case this geometry is usually not encoded. |
| |
| if (!encodeOffsets) { |
| return; |
| } |
| |
| switch (geometry.type) { |
| case 'LineString': |
| geometry.coordinates = decodeRing(coordinates, encodeOffsets, encodeScale); |
| break; |
| |
| case 'Polygon': |
| decodeRings(coordinates, encodeOffsets, encodeScale); |
| break; |
| |
| case 'MultiLineString': |
| decodeRings(coordinates, encodeOffsets, encodeScale); |
| break; |
| |
| case 'MultiPolygon': |
| each(coordinates, function (rings, idx) { |
| return decodeRings(rings, encodeOffsets[idx], encodeScale); |
| }); |
| } |
| }); // Has been decoded |
| |
| jsonCompressed.UTF8Encoding = false; |
| return jsonCompressed; |
| } |
| |
| function decodeRings(rings, encodeOffsets, encodeScale) { |
| for (var c = 0; c < rings.length; c++) { |
| rings[c] = decodeRing(rings[c], encodeOffsets[c], encodeScale); |
| } |
| } |
| |
| function decodeRing(coordinate, encodeOffsets, encodeScale) { |
| var result = []; |
| var prevX = encodeOffsets[0]; |
| var prevY = encodeOffsets[1]; |
| |
| for (var i = 0; i < coordinate.length; i += 2) { |
| var x = coordinate.charCodeAt(i) - 64; |
| var y = coordinate.charCodeAt(i + 1) - 64; // ZigZag decoding |
| |
| x = x >> 1 ^ -(x & 1); |
| y = y >> 1 ^ -(y & 1); // Delta deocding |
| |
| x += prevX; |
| y += prevY; |
| prevX = x; |
| prevY = y; // Dequantize |
| |
| result.push([x / encodeScale, y / encodeScale]); |
| } |
| |
| return result; |
| } |
| |
| function parseGeoJSON(geoJson, nameProperty) { |
| geoJson = decode(geoJson); |
| return map(filter(geoJson.features, function (featureObj) { |
| // Output of mapshaper may have geometry null |
| return featureObj.geometry && featureObj.properties && featureObj.geometry.coordinates.length > 0; |
| }), function (featureObj) { |
| var properties = featureObj.properties; |
| var geo = featureObj.geometry; |
| var geometries = []; |
| |
| switch (geo.type) { |
| case 'Polygon': |
| var coordinates = geo.coordinates; // According to the GeoJSON specification. |
| // First must be exterior, and the rest are all interior(holes). |
| |
| geometries.push(new GeoJSONPolygonGeometry(coordinates[0], coordinates.slice(1))); |
| break; |
| |
| case 'MultiPolygon': |
| each(geo.coordinates, function (item) { |
| if (item[0]) { |
| geometries.push(new GeoJSONPolygonGeometry(item[0], item.slice(1))); |
| } |
| }); |
| break; |
| |
| case 'LineString': |
| geometries.push(new GeoJSONLineStringGeometry([geo.coordinates])); |
| break; |
| |
| case 'MultiLineString': |
| geometries.push(new GeoJSONLineStringGeometry(geo.coordinates)); |
| } |
| |
| var region = new GeoJSONRegion(properties[nameProperty || 'name'], geometries, properties.cp); |
| region.properties = properties; |
| return region; |
| }); |
| } |
| |
| var number = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| linearMap: linearMap, |
| round: round, |
| 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, |
| numericToNumber: numericToNumber |
| }); |
| |
| var time = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| parse: parseDate, |
| format: format |
| }); |
| |
| var graphic = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| extendShape: extendShape, |
| extendPath: extendPath, |
| makePath: makePath, |
| makeImage: makeImage, |
| mergePath: mergePath$1, |
| resizePath: resizePath, |
| createIcon: createIcon, |
| updateProps: updateProps, |
| initProps: initProps, |
| getTransform: getTransform, |
| clipPointsByRect: clipPointsByRect, |
| clipRectByRect: clipRectByRect, |
| registerShape: registerShape, |
| getShapeClass: getShapeClass, |
| Group: Group, |
| Image: ZRImage, |
| Text: ZRText, |
| Circle: Circle, |
| Ellipse: Ellipse, |
| Sector: Sector, |
| Ring: Ring, |
| Polygon: Polygon, |
| Polyline: Polyline, |
| Rect: Rect, |
| Line: Line, |
| BezierCurve: BezierCurve, |
| Arc: Arc, |
| IncrementalDisplayable: IncrementalDisplayable, |
| CompoundPath: CompoundPath, |
| LinearGradient: LinearGradient, |
| RadialGradient: RadialGradient, |
| BoundingRect: BoundingRect |
| }); |
| |
| var format$1 = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| addCommas: addCommas, |
| toCamelCase: toCamelCase, |
| normalizeCssArray: normalizeCssArray$1, |
| encodeHTML: encodeHTML, |
| formatTpl: formatTpl, |
| getTooltipMarker: getTooltipMarker, |
| formatTime: formatTime, |
| capitalFirst: capitalFirst, |
| truncateText: truncateText, |
| getTextRect: getTextRect |
| }); |
| |
| var util$1 = /*#__PURE__*/Object.freeze({ |
| __proto__: null, |
| map: map, |
| each: each, |
| indexOf: indexOf, |
| inherits: inherits, |
| reduce: reduce, |
| filter: filter, |
| bind: bind, |
| curry: curry, |
| isArray: isArray, |
| isString: isString, |
| isObject: isObject, |
| isFunction: isFunction, |
| extend: extend, |
| defaults: defaults, |
| clone: clone, |
| merge: merge |
| }); |
| |
| var inner$5 = makeInner(); |
| function createAxisLabels(axis) { |
| // Only ordinal scale support tick interval |
| return axis.type === 'category' ? makeCategoryLabels(axis) : makeRealNumberLabels(axis); |
| } |
| /** |
| * @param {module:echats/coord/Axis} axis |
| * @param {module:echarts/model/Model} tickModel For example, can be axisTick, splitLine, splitArea. |
| * @return {Object} { |
| * ticks: Array.<number> |
| * tickCategoryInterval: number |
| * } |
| */ |
| |
| function createAxisTicks(axis, tickModel) { |
| // Only ordinal scale support tick interval |
| return axis.type === 'category' ? makeCategoryTicks(axis, tickModel) : { |
| ticks: map(axis.scale.getTicks(), function (tick) { |
| return tick.value; |
| }) |
| }; |
| } |
| |
| function makeCategoryLabels(axis) { |
| var labelModel = axis.getLabelModel(); |
| var result = makeCategoryLabelsActually(axis, labelModel); |
| return !labelModel.get('show') || axis.scale.isBlank() ? { |
| labels: [], |
| labelCategoryInterval: result.labelCategoryInterval |
| } : result; |
| } |
| |
| function makeCategoryLabelsActually(axis, labelModel) { |
| var labelsCache = getListCache(axis, 'labels'); |
| var optionLabelInterval = getOptionCategoryInterval(labelModel); |
| var result = listCacheGet(labelsCache, optionLabelInterval); |
| |
| if (result) { |
| return result; |
| } |
| |
| var labels; |
| var numericLabelInterval; |
| |
| if (isFunction(optionLabelInterval)) { |
| labels = makeLabelsByCustomizedCategoryInterval(axis, optionLabelInterval); |
| } else { |
| numericLabelInterval = optionLabelInterval === 'auto' ? makeAutoCategoryInterval(axis) : optionLabelInterval; |
| labels = makeLabelsByNumericCategoryInterval(axis, numericLabelInterval); |
| } // Cache to avoid calling interval function repeatedly. |
| |
| |
| return listCacheSet(labelsCache, optionLabelInterval, { |
| labels: labels, |
| labelCategoryInterval: numericLabelInterval |
| }); |
| } |
| |
| function makeCategoryTicks(axis, tickModel) { |
| var ticksCache = getListCache(axis, 'ticks'); |
| var optionTickInterval = getOptionCategoryInterval(tickModel); |
| var result = listCacheGet(ticksCache, optionTickInterval); |
| |
| if (result) { |
| return result; |
| } |
| |
| var ticks; |
| var tickCategoryInterval; // Optimize for the case that large category data and no label displayed, |
| // we should not return all ticks. |
| |
| if (!tickModel.get('show') || axis.scale.isBlank()) { |
| ticks = []; |
| } |
| |
| if (isFunction(optionTickInterval)) { |
| ticks = makeLabelsByCustomizedCategoryInterval(axis, optionTickInterval, true); |
| } // Always use label interval by default despite label show. Consider this |
| // scenario, Use multiple grid with the xAxis sync, and only one xAxis shows |
| // labels. `splitLine` and `axisTick` should be consistent in this case. |
| else if (optionTickInterval === 'auto') { |
| var labelsResult = makeCategoryLabelsActually(axis, axis.getLabelModel()); |
| tickCategoryInterval = labelsResult.labelCategoryInterval; |
| ticks = map(labelsResult.labels, function (labelItem) { |
| return labelItem.tickValue; |
| }); |
| } else { |
| tickCategoryInterval = optionTickInterval; |
| ticks = makeLabelsByNumericCategoryInterval(axis, tickCategoryInterval, true); |
| } // Cache to avoid calling interval function repeatedly. |
| |
| |
| return listCacheSet(ticksCache, optionTickInterval, { |
| ticks: ticks, |
| tickCategoryInterval: tickCategoryInterval |
| }); |
| } |
| |
| function makeRealNumberLabels(axis) { |
| var ticks = axis.scale.getTicks(); |
| var labelFormatter = makeLabelFormatter(axis); |
| return { |
| labels: map(ticks, function (tick, idx) { |
| return { |
| level: tick.level, |
| formattedLabel: labelFormatter(tick, idx), |
| rawLabel: axis.scale.getLabel(tick), |
| tickValue: tick.value |
| }; |
| }) |
| }; |
| } |
| |
| function getListCache(axis, prop) { |
| // Because key can be a function, and cache size always is small, we use array cache. |
| return inner$5(axis)[prop] || (inner$5(axis)[prop] = []); |
| } |
| |
| function listCacheGet(cache, key) { |
| for (var i = 0; i < cache.length; i++) { |
| if (cache[i].key === key) { |
| return cache[i].value; |
| } |
| } |
| } |
| |
| function listCacheSet(cache, key, value) { |
| cache.push({ |
| key: key, |
| value: value |
| }); |
| return value; |
| } |
| |
| function makeAutoCategoryInterval(axis) { |
| var result = inner$5(axis).autoInterval; |
| return result != null ? result : inner$5(axis).autoInterval = axis.calculateCategoryInterval(); |
| } |
| /** |
| * Calculate interval for category axis ticks and labels. |
| * To get precise result, at least one of `getRotate` and `isHorizontal` |
| * should be implemented in axis. |
| */ |
| |
| |
| function calculateCategoryInterval(axis) { |
| var params = fetchAutoCategoryIntervalCalculationParams(axis); |
| var labelFormatter = makeLabelFormatter(axis); |
| var rotation = (params.axisRotate - params.labelRotate) / 180 * Math.PI; |
| var ordinalScale = axis.scale; |
| var ordinalExtent = ordinalScale.getExtent(); // Providing this method is for optimization: |
| // avoid generating a long array by `getTicks` |
| // in large category data case. |
| |
| var tickCount = ordinalScale.count(); |
| |
| if (ordinalExtent[1] - ordinalExtent[0] < 1) { |
| return 0; |
| } |
| |
| var step = 1; // Simple optimization. Empirical value: tick count should less than 40. |
| |
| if (tickCount > 40) { |
| step = Math.max(1, Math.floor(tickCount / 40)); |
| } |
| |
| var tickValue = ordinalExtent[0]; |
| var unitSpan = axis.dataToCoord(tickValue + 1) - axis.dataToCoord(tickValue); |
| var unitW = Math.abs(unitSpan * Math.cos(rotation)); |
| var unitH = Math.abs(unitSpan * Math.sin(rotation)); |
| var maxW = 0; |
| var maxH = 0; // Caution: Performance sensitive for large category data. |
| // Consider dataZoom, we should make appropriate step to avoid O(n) loop. |
| |
| for (; tickValue <= ordinalExtent[1]; tickValue += step) { |
| var width = 0; |
| var height = 0; // Not precise, do not consider align and vertical align |
| // and each distance from axis line yet. |
| |
| var rect = getBoundingRect(labelFormatter({ |
| value: tickValue |
| }), params.font, 'center', 'top'); // Magic number |
| |
| width = rect.width * 1.3; |
| height = rect.height * 1.3; // Min size, void long loop. |
| |
| maxW = Math.max(maxW, width, 7); |
| maxH = Math.max(maxH, height, 7); |
| } |
| |
| var dw = maxW / unitW; |
| var dh = maxH / unitH; // 0/0 is NaN, 1/0 is Infinity. |
| |
| isNaN(dw) && (dw = Infinity); |
| isNaN(dh) && (dh = Infinity); |
| var interval = Math.max(0, Math.floor(Math.min(dw, dh))); |
| var cache = inner$5(axis.model); |
| var axisExtent = axis.getExtent(); |
| var lastAutoInterval = cache.lastAutoInterval; |
| var lastTickCount = cache.lastTickCount; // Use cache to keep interval stable while moving zoom window, |
| // otherwise the calculated interval might jitter when the zoom |
| // window size is close to the interval-changing size. |
| // For example, if all of the axis labels are `a, b, c, d, e, f, g`. |
| // The jitter will cause that sometimes the displayed labels are |
| // `a, d, g` (interval: 2) sometimes `a, c, e`(interval: 1). |
| |
| if (lastAutoInterval != null && lastTickCount != null && Math.abs(lastAutoInterval - interval) <= 1 && Math.abs(lastTickCount - tickCount) <= 1 // Always choose the bigger one, otherwise the critical |
| // point is not the same when zooming in or zooming out. |
| && lastAutoInterval > interval // If the axis change is caused by chart resize, the cache should not |
| // be used. Otherwise some hidden labels might not be shown again. |
| && cache.axisExtent0 === axisExtent[0] && cache.axisExtent1 === axisExtent[1]) { |
| interval = lastAutoInterval; |
| } // Only update cache if cache not used, otherwise the |
| // changing of interval is too insensitive. |
| else { |
| cache.lastTickCount = tickCount; |
| cache.lastAutoInterval = interval; |
| cache.axisExtent0 = axisExtent[0]; |
| cache.axisExtent1 = axisExtent[1]; |
| } |
| |
| return interval; |
| } |
| |
| function fetchAutoCategoryIntervalCalculationParams(axis) { |
| var labelModel = axis.getLabelModel(); |
| return { |
| axisRotate: axis.getRotate ? axis.getRotate() : axis.isHorizontal && !axis.isHorizontal() ? 90 : 0, |
| labelRotate: labelModel.get('rotate') || 0, |
| font: labelModel.getFont() |
| }; |
| } |
| |
| function makeLabelsByNumericCategoryInterval(axis, categoryInterval, onlyTick) { |
| var labelFormatter = makeLabelFormatter(axis); |
| var ordinalScale = axis.scale; |
| var ordinalExtent = ordinalScale.getExtent(); |
| var labelModel = axis.getLabelModel(); |
| var result = []; // TODO: axisType: ordinalTime, pick the tick from each month/day/year/... |
| |
| var step = Math.max((categoryInterval || 0) + 1, 1); |
| var startTick = ordinalExtent[0]; |
| var tickCount = ordinalScale.count(); // Calculate start tick based on zero if possible to keep label consistent |
| // while zooming and moving while interval > 0. Otherwise the selection |
| // of displayable ticks and symbols probably keep changing. |
| // 3 is empirical value. |
| |
| if (startTick !== 0 && step > 1 && tickCount / step > 2) { |
| startTick = Math.round(Math.ceil(startTick / step) * step); |
| } // (1) Only add min max label here but leave overlap checking |
| // to render stage, which also ensure the returned list |
| // suitable for splitLine and splitArea rendering. |
| // (2) Scales except category always contain min max label so |
| // do not need to perform this process. |
| |
| |
| var showAllLabel = shouldShowAllLabels(axis); |
| var includeMinLabel = labelModel.get('showMinLabel') || showAllLabel; |
| var includeMaxLabel = labelModel.get('showMaxLabel') || showAllLabel; |
| |
| if (includeMinLabel && startTick !== ordinalExtent[0]) { |
| addItem(ordinalExtent[0]); |
| } // Optimize: avoid generating large array by `ordinalScale.getTicks()`. |
| |
| |
| var tickValue = startTick; |
| |
| for (; tickValue <= ordinalExtent[1]; tickValue += step) { |
| addItem(tickValue); |
| } |
| |
| if (includeMaxLabel && tickValue - step !== ordinalExtent[1]) { |
| addItem(ordinalExtent[1]); |
| } |
| |
| function addItem(tickValue) { |
| var tickObj = { |
| value: tickValue |
| }; |
| result.push(onlyTick ? tickValue : { |
| formattedLabel: labelFormatter(tickObj), |
| rawLabel: ordinalScale.getLabel(tickObj), |
| tickValue: tickValue |
| }); |
| } |
| |
| return result; |
| } |
| |
| function makeLabelsByCustomizedCategoryInterval(axis, categoryInterval, onlyTick) { |
| var ordinalScale = axis.scale; |
| var labelFormatter = makeLabelFormatter(axis); |
| var result = []; |
| each(ordinalScale.getTicks(), function (tick) { |
| var rawLabel = ordinalScale.getLabel(tick); |
| var tickValue = tick.value; |
| |
| if (categoryInterval(tick.value, rawLabel)) { |
| result.push(onlyTick ? tickValue : { |
| formattedLabel: labelFormatter(tick), |
| rawLabel: rawLabel, |
| tickValue: tickValue |
| }); |
| } |
| }); |
| return result; |
| } |
| |
| var NORMALIZED_EXTENT = [0, 1]; |
| /** |
| * Base class of Axis. |
| */ |
| |
| var Axis = |
| /** @class */ |
| function () { |
| function Axis(dim, scale, extent) { |
| this.onBand = false; |
| this.inverse = false; |
| this.dim = dim; |
| this.scale = scale; |
| this._extent = extent || [0, 0]; |
| } |
| /** |
| * If axis extent contain given coord |
| */ |
| |
| |
| Axis.prototype.contain = function (coord) { |
| var extent = this._extent; |
| var min = Math.min(extent[0], extent[1]); |
| var max = Math.max(extent[0], extent[1]); |
| return coord >= min && coord <= max; |
| }; |
| /** |
| * If axis extent contain given data |
| */ |
| |
| |
| Axis.prototype.containData = function (data) { |
| return this.scale.contain(data); |
| }; |
| /** |
| * Get coord extent. |
| */ |
| |
| |
| Axis.prototype.getExtent = function () { |
| return this._extent.slice(); |
| }; |
| /** |
| * Get precision used for formatting |
| */ |
| |
| |
| Axis.prototype.getPixelPrecision = function (dataExtent) { |
| return getPixelPrecision(dataExtent || this.scale.getExtent(), this._extent); |
| }; |
| /** |
| * Set coord extent |
| */ |
| |
| |
| Axis.prototype.setExtent = function (start, end) { |
| var extent = this._extent; |
| extent[0] = start; |
| extent[1] = end; |
| }; |
| /** |
| * Convert data to coord. Data is the rank if it has an ordinal scale |
| */ |
| |
| |
| Axis.prototype.dataToCoord = function (data, clamp) { |
| var extent = this._extent; |
| var scale = this.scale; |
| data = scale.normalize(data); |
| |
| if (this.onBand && scale.type === 'ordinal') { |
| extent = extent.slice(); |
| fixExtentWithBands(extent, scale.count()); |
| } |
| |
| return linearMap(data, NORMALIZED_EXTENT, extent, clamp); |
| }; |
| /** |
| * Convert coord to data. Data is the rank if it has an ordinal scale |
| */ |
| |
| |
| Axis.prototype.coordToData = function (coord, clamp) { |
| var extent = this._extent; |
| var scale = this.scale; |
| |
| if (this.onBand && scale.type === 'ordinal') { |
| extent = extent.slice(); |
| fixExtentWithBands(extent, scale.count()); |
| } |
| |
| var t = linearMap(coord, extent, NORMALIZED_EXTENT, clamp); |
| return this.scale.scale(t); |
| }; |
| /** |
| * Convert pixel point to data in axis |
| */ |
| |
| |
| Axis.prototype.pointToData = function (point, clamp) { |
| // Should be implemented in derived class if necessary. |
| return; |
| }; |
| /** |
| * Different from `zrUtil.map(axis.getTicks(), axis.dataToCoord, axis)`, |
| * `axis.getTicksCoords` considers `onBand`, which is used by |
| * `boundaryGap:true` of category axis and splitLine and splitArea. |
| * @param opt.tickModel default: axis.model.getModel('axisTick') |
| * @param opt.clamp If `true`, the first and the last |
| * tick must be at the axis end points. Otherwise, clip ticks |
| * that outside the axis extent. |
| */ |
| |
| |
| Axis.prototype.getTicksCoords = function (opt) { |
| opt = opt || {}; |
| var tickModel = opt.tickModel || this.getTickModel(); |
| var result = createAxisTicks(this, tickModel); |
| var ticks = result.ticks; |
| var ticksCoords = map(ticks, function (tickVal) { |
| return { |
| coord: this.dataToCoord(this.scale.type === 'ordinal' ? this.scale.getRawOrdinalNumber(tickVal) : tickVal), |
| tickValue: tickVal |
| }; |
| }, this); |
| var alignWithLabel = tickModel.get('alignWithLabel'); |
| fixOnBandTicksCoords(this, ticksCoords, alignWithLabel, opt.clamp); |
| return ticksCoords; |
| }; |
| |
| Axis.prototype.getMinorTicksCoords = function () { |
| if (this.scale.type === 'ordinal') { |
| // Category axis doesn't support minor ticks |
| return []; |
| } |
| |
| var minorTickModel = this.model.getModel('minorTick'); |
| var splitNumber = minorTickModel.get('splitNumber'); // Protection. |
| |
| if (!(splitNumber > 0 && splitNumber < 100)) { |
| splitNumber = 5; |
| } |
| |
| var minorTicks = this.scale.getMinorTicks(splitNumber); |
| var minorTicksCoords = map(minorTicks, function (minorTicksGroup) { |
| return map(minorTicksGroup, function (minorTick) { |
| return { |
| coord: this.dataToCoord(minorTick), |
| tickValue: minorTick |
| }; |
| }, this); |
| }, this); |
| return minorTicksCoords; |
| }; |
| |
| Axis.prototype.getViewLabels = function () { |
| return createAxisLabels(this).labels; |
| }; |
| |
| Axis.prototype.getLabelModel = function () { |
| return this.model.getModel('axisLabel'); |
| }; |
| /** |
| * Notice here we only get the default tick model. For splitLine |
| * or splitArea, we should pass the splitLineModel or splitAreaModel |
| * manually when calling `getTicksCoords`. |
| * In GL, this method may be overridden to: |
| * `axisModel.getModel('axisTick', grid3DModel.getModel('axisTick'));` |
| */ |
| |
| |
| Axis.prototype.getTickModel = function () { |
| return this.model.getModel('axisTick'); |
| }; |
| /** |
| * Get width of band |
| */ |
| |
| |
| Axis.prototype.getBandWidth = function () { |
| var axisExtent = this._extent; |
| var dataExtent = this.scale.getExtent(); |
| var len = dataExtent[1] - dataExtent[0] + (this.onBand ? 1 : 0); // Fix #2728, avoid NaN when only one data. |
| |
| len === 0 && (len = 1); |
| var size = Math.abs(axisExtent[1] - axisExtent[0]); |
| return Math.abs(size) / len; |
| }; |
| /** |
| * Only be called in category axis. |
| * Can be overridden, consider other axes like in 3D. |
| * @return Auto interval for cateogry axis tick and label |
| */ |
| |
| |
| Axis.prototype.calculateCategoryInterval = function () { |
| return calculateCategoryInterval(this); |
| }; |
| |
| return Axis; |
| }(); |
| |
| function fixExtentWithBands(extent, nTick) { |
| var size = extent[1] - extent[0]; |
| var len = nTick; |
| var margin = size / len / 2; |
| extent[0] += margin; |
| extent[1] -= margin; |
| } // If axis has labels [1, 2, 3, 4]. Bands on the axis are |
| // |---1---|---2---|---3---|---4---|. |
| // So the displayed ticks and splitLine/splitArea should between |
| // each data item, otherwise cause misleading (e.g., split tow bars |
| // of a single data item when there are two bar series). |
| // Also consider if tickCategoryInterval > 0 and onBand, ticks and |
| // splitLine/spliteArea should layout appropriately corresponding |
| // to displayed labels. (So we should not use `getBandWidth` in this |
| // case). |
| |
| |
| function fixOnBandTicksCoords(axis, ticksCoords, alignWithLabel, clamp) { |
| var ticksLen = ticksCoords.length; |
| |
| if (!axis.onBand || alignWithLabel || !ticksLen) { |
| return; |
| } |
| |
| var axisExtent = axis.getExtent(); |
| var last; |
| var diffSize; |
| |
| if (ticksLen === 1) { |
| ticksCoords[0].coord = axisExtent[0]; |
| last = ticksCoords[1] = { |
| coord: axisExtent[0] |
| }; |
| } else { |
| var crossLen = ticksCoords[ticksLen - 1].tickValue - ticksCoords[0].tickValue; |
| var shift_1 = (ticksCoords[ticksLen - 1].coord - ticksCoords[0].coord) / crossLen; |
| each(ticksCoords, function (ticksItem) { |
| ticksItem.coord -= shift_1 / 2; |
| }); |
| var dataExtent = axis.scale.getExtent(); |
| diffSize = 1 + dataExtent[1] - ticksCoords[ticksLen - 1].tickValue; |
| last = { |
| coord: ticksCoords[ticksLen - 1].coord + shift_1 * diffSize |
| }; |
| ticksCoords.push(last); |
| } |
| |
| var inverse = axisExtent[0] > axisExtent[1]; // Handling clamp. |
| |
| if (littleThan(ticksCoords[0].coord, axisExtent[0])) { |
| clamp ? ticksCoords[0].coord = axisExtent[0] : ticksCoords.shift(); |
| } |
| |
| if (clamp && littleThan(axisExtent[0], ticksCoords[0].coord)) { |
| ticksCoords.unshift({ |
| coord: axisExtent[0] |
| }); |
| } |
| |
| if (littleThan(axisExtent[1], last.coord)) { |
| clamp ? last.coord = axisExtent[1] : ticksCoords.pop(); |
| } |
| |
| if (clamp && littleThan(last.coord, axisExtent[1])) { |
| ticksCoords.push({ |
| coord: axisExtent[1] |
| }); |
| } |
| |
| function littleThan(a, b) { |
| // Avoid rounding error cause calculated tick coord different with extent. |
| // It may cause an extra unnecessary tick added. |
| a = round(a); |
| b = round(b); |
| return inverse ? a > b : a < b; |
| } |
| } |
| |
| // Should use `ComponentModel.extend` or `class XXXX extend ComponentModel` to create class. |
| // Then use `registerComponentModel` in `install` parameter when `use` this extension. For example: |
| // class Bar3DModel extends ComponentModel {} |
| // export function install(registers) { registers.registerComponentModel(Bar3DModel); } |
| // echarts.use(install); |
| |
| function extendComponentModel(proto) { |
| var Model = ComponentModel.extend(proto); |
| ComponentModel.registerClass(Model); |
| return Model; |
| } |
| function extendComponentView(proto) { |
| var View = ComponentView.extend(proto); |
| ComponentView.registerClass(View); |
| return View; |
| } |
| function extendSeriesModel(proto) { |
| var Model = SeriesModel.extend(proto); |
| SeriesModel.registerClass(Model); |
| return Model; |
| } |
| function extendChartView(proto) { |
| var View = ChartView.extend(proto); |
| ChartView.registerClass(View); |
| return View; |
| } |
| |
| function projectPointToLine(x1, y1, x2, y2, x, y, out, limitToEnds) { |
| var dx = x - x1; |
| var dy = y - y1; |
| var dx1 = x2 - x1; |
| var dy1 = y2 - y1; |
| var lineLen = Math.sqrt(dx1 * dx1 + dy1 * dy1); |
| dx1 /= lineLen; |
| dy1 /= lineLen; // dot product |
| |
| var projectedLen = dx * dx1 + dy * dy1; |
| var t = projectedLen / lineLen; |
| |
| if (limitToEnds) { |
| t = Math.min(Math.max(t, 0), 1); |
| } |
| |
| t *= lineLen; |
| var ox = out[0] = x1 + t * dx1; |
| var oy = out[1] = y1 + t * dy1; |
| return Math.sqrt((ox - x) * (ox - x) + (oy - y) * (oy - y)); |
| } |
| |
| |
| var pt0 = new Point(); |
| var pt1 = new Point(); |
| var pt2 = new Point(); |
| var dir = new Point(); |
| var dir2 = new Point(); |
| |
| var tmpArr = []; |
| var tmpProjPoint = new Point(); |
| /** |
| * Reduce the line segment attached to the label to limit the turn angle between two segments. |
| * @param linePoints |
| * @param minTurnAngle Radian of minimum turn angle. 0 - 180 |
| */ |
| |
| function limitTurnAngle(linePoints, minTurnAngle) { |
| if (!(minTurnAngle <= 180 && minTurnAngle > 0)) { |
| return; |
| } |
| |
| minTurnAngle = minTurnAngle / 180 * Math.PI; // The line points can be |
| // /pt1----pt2 (label) |
| // / |
| // pt0/ |
| |
| pt0.fromArray(linePoints[0]); |
| pt1.fromArray(linePoints[1]); |
| pt2.fromArray(linePoints[2]); |
| Point.sub(dir, pt0, pt1); |
| Point.sub(dir2, pt2, pt1); |
| var len1 = dir.len(); |
| var len2 = dir2.len(); |
| |
| if (len1 < 1e-3 || len2 < 1e-3) { |
| return; |
| } |
| |
| dir.scale(1 / len1); |
| dir2.scale(1 / len2); |
| var angleCos = dir.dot(dir2); |
| var minTurnAngleCos = Math.cos(minTurnAngle); |
| |
| if (minTurnAngleCos < angleCos) { |
| // Smaller than minTurnAngle |
| // Calculate project point of pt0 on pt1-pt2 |
| var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); |
| tmpProjPoint.fromArray(tmpArr); // Calculate new projected length with limited minTurnAngle and get the new connect point |
| |
| tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI - minTurnAngle)); // Limit the new calculated connect point between pt1 and pt2. |
| |
| var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); |
| |
| if (isNaN(t)) { |
| return; |
| } |
| |
| if (t < 0) { |
| Point.copy(tmpProjPoint, pt1); |
| } else if (t > 1) { |
| Point.copy(tmpProjPoint, pt2); |
| } |
| |
| tmpProjPoint.toArray(linePoints[1]); |
| } |
| } |
| /** |
| * Limit the angle of line and the surface |
| * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite |
| */ |
| |
| function limitSurfaceAngle(linePoints, surfaceNormal, maxSurfaceAngle) { |
| if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) { |
| return; |
| } |
| |
| maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI; |
| pt0.fromArray(linePoints[0]); |
| pt1.fromArray(linePoints[1]); |
| pt2.fromArray(linePoints[2]); |
| Point.sub(dir, pt1, pt0); |
| Point.sub(dir2, pt2, pt1); |
| var len1 = dir.len(); |
| var len2 = dir2.len(); |
| |
| if (len1 < 1e-3 || len2 < 1e-3) { |
| return; |
| } |
| |
| dir.scale(1 / len1); |
| dir2.scale(1 / len2); |
| var angleCos = dir.dot(surfaceNormal); |
| var maxSurfaceAngleCos = Math.cos(maxSurfaceAngle); |
| |
| if (angleCos < maxSurfaceAngleCos) { |
| // Calculate project point of pt0 on pt1-pt2 |
| var d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false); |
| tmpProjPoint.fromArray(tmpArr); |
| var HALF_PI = Math.PI / 2; |
| var angle2 = Math.acos(dir2.dot(surfaceNormal)); |
| var newAngle = HALF_PI + angle2 - maxSurfaceAngle; |
| |
| if (newAngle >= HALF_PI) { |
| // parallel |
| Point.copy(tmpProjPoint, pt2); |
| } else { |
| // Calculate new projected length with limited minTurnAngle and get the new connect point |
| tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle)); // Limit the new calculated connect point between pt1 and pt2. |
| |
| var t = pt2.x !== pt1.x ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x) : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y); |
| |
| if (isNaN(t)) { |
| return; |
| } |
| |
| if (t < 0) { |
| Point.copy(tmpProjPoint, pt1); |
| } else if (t > 1) { |
| Point.copy(tmpProjPoint, pt2); |
| } |
| } |
| |
| tmpProjPoint.toArray(linePoints[1]); |
| } |
| } |
| |
| function setLabelLineState(labelLine, ignore, stateName, stateModel) { |
| var isNormal = stateName === 'normal'; |
| var stateObj = isNormal ? labelLine : labelLine.ensureState(stateName); // Make sure display. |
| |
| stateObj.ignore = ignore; // Set smooth |
| |
| var smooth = stateModel.get('smooth'); |
| |
| if (smooth && smooth === true) { |
| smooth = 0.3; |
| } |
| |
| stateObj.shape = stateObj.shape || {}; |
| |
| if (smooth > 0) { |
| stateObj.shape.smooth = smooth; |
| } |
| |
| var styleObj = stateModel.getModel('lineStyle').getLineStyle(); |
| isNormal ? labelLine.useStyle(styleObj) : stateObj.style = styleObj; |
| } |
| |
| function buildLabelLinePath(path, shape) { |
| var smooth = shape.smooth; |
| var points = shape.points; |
| |
| if (!points) { |
| return; |
| } |
| |
| path.moveTo(points[0][0], points[0][1]); |
| |
| if (smooth > 0 && points.length >= 3) { |
| var len1 = dist(points[0], points[1]); |
| var len2 = dist(points[1], points[2]); |
| |
| if (!len1 || !len2) { |
| path.lineTo(points[1][0], points[1][1]); |
| path.lineTo(points[2][0], points[2][1]); |
| return; |
| } |
| |
| var moveLen = Math.min(len1, len2) * smooth; |
| var midPoint0 = lerp([], points[1], points[0], moveLen / len1); |
| var midPoint2 = lerp([], points[1], points[2], moveLen / len2); |
| var midPoint1 = lerp([], midPoint0, midPoint2, 0.5); |
| path.bezierCurveTo(midPoint0[0], midPoint0[1], midPoint0[0], midPoint0[1], midPoint1[0], midPoint1[1]); |
| path.bezierCurveTo(midPoint2[0], midPoint2[1], midPoint2[0], midPoint2[1], points[2][0], points[2][1]); |
| } else { |
| for (var i = 1; i < points.length; i++) { |
| path.lineTo(points[i][0], points[i][1]); |
| } |
| } |
| } |
| /** |
| * Create a label line if necessary and set it's style. |
| */ |
| |
| |
| function setLabelLineStyle(targetEl, statesModels, defaultStyle) { |
| var labelLine = targetEl.getTextGuideLine(); |
| var label = targetEl.getTextContent(); |
| |
| if (!label) { |
| // Not show label line if there is no label. |
| if (labelLine) { |
| targetEl.removeTextGuideLine(); |
| } |
| |
| return; |
| } |
| |
| var normalModel = statesModels.normal; |
| var showNormal = normalModel.get('show'); |
| var labelIgnoreNormal = label.ignore; |
| |
| for (var i = 0; i < DISPLAY_STATES.length; i++) { |
| var stateName = DISPLAY_STATES[i]; |
| var stateModel = statesModels[stateName]; |
| var isNormal = stateName === 'normal'; |
| |
| if (stateModel) { |
| var stateShow = stateModel.get('show'); |
| var isLabelIgnored = isNormal ? labelIgnoreNormal : retrieve2(label.states[stateName] && label.states[stateName].ignore, labelIgnoreNormal); |
| |
| if (isLabelIgnored // Not show when label is not shown in this state. |
| || !retrieve2(stateShow, showNormal) // Use normal state by default if not set. |
| ) { |
| var stateObj = isNormal ? labelLine : labelLine && labelLine.states[stateName]; |
| |
| if (stateObj) { |
| stateObj.ignore = true; |
| } |
| |
| continue; |
| } // Create labelLine if not exists |
| |
| |
| if (!labelLine) { |
| labelLine = new Polyline(); |
| targetEl.setTextGuideLine(labelLine); // Reset state of normal because it's new created. |
| // NOTE: NORMAL should always been the first! |
| |
| if (!isNormal && (labelIgnoreNormal || !showNormal)) { |
| setLabelLineState(labelLine, true, 'normal', statesModels.normal); |
| } // Use same state proxy. |
| |
| |
| if (targetEl.stateProxy) { |
| labelLine.stateProxy = targetEl.stateProxy; |
| } |
| } |
| |
| setLabelLineState(labelLine, false, stateName, stateModel); |
| } |
| } |
| |
| if (labelLine) { |
| defaults(labelLine.style, defaultStyle); // Not fill. |
| |
| labelLine.style.fill = null; |
| var showAbove = normalModel.get('showAbove'); |
| var labelLineConfig = targetEl.textGuideLineConfig = targetEl.textGuideLineConfig || {}; |
| labelLineConfig.showAbove = showAbove || false; // Custom the buildPath. |
| |
| labelLine.buildPath = buildLabelLinePath; |
| } |
| } |
| function getLabelLineStatesModels(itemModel, labelLineName) { |
| labelLineName = labelLineName || 'labelLine'; |
| var statesModels = { |
| normal: itemModel.getModel(labelLineName) |
| }; |
| |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| var stateName = SPECIAL_STATES[i]; |
| statesModels[stateName] = itemModel.getModel([stateName, labelLineName]); |
| } |
| |
| return statesModels; |
| } |
| |
| function prepareLayoutList(input) { |
| var list = []; |
| |
| for (var i = 0; i < input.length; i++) { |
| var rawItem = input[i]; |
| |
| if (rawItem.defaultAttr.ignore) { |
| continue; |
| } |
| |
| var label = rawItem.label; |
| var transform = label.getComputedTransform(); // NOTE: Get bounding rect after getComputedTransform, or label may not been updated by the host el. |
| |
| var localRect = label.getBoundingRect(); |
| var isAxisAligned = !transform || transform[1] < 1e-5 && transform[2] < 1e-5; |
| var minMargin = label.style.margin || 0; |
| var globalRect = localRect.clone(); |
| globalRect.applyTransform(transform); |
| globalRect.x -= minMargin / 2; |
| globalRect.y -= minMargin / 2; |
| globalRect.width += minMargin; |
| globalRect.height += minMargin; |
| var obb = isAxisAligned ? new OrientedBoundingRect(localRect, transform) : null; |
| list.push({ |
| label: label, |
| labelLine: rawItem.labelLine, |
| rect: globalRect, |
| localRect: localRect, |
| obb: obb, |
| priority: rawItem.priority, |
| defaultAttr: rawItem.defaultAttr, |
| layoutOption: rawItem.computedLayoutOption, |
| axisAligned: isAxisAligned, |
| transform: transform |
| }); |
| } |
| |
| return list; |
| } |
| |
| function shiftLayout(list, xyDim, sizeDim, minBound, maxBound, balanceShift) { |
| var len = list.length; |
| |
| if (len < 2) { |
| return; |
| } |
| |
| list.sort(function (a, b) { |
| return a.rect[xyDim] - b.rect[xyDim]; |
| }); |
| var lastPos = 0; |
| var delta; |
| var adjusted = false; |
| var totalShifts = 0; |
| |
| for (var i = 0; i < len; i++) { |
| var item = list[i]; |
| var rect = item.rect; |
| delta = rect[xyDim] - lastPos; |
| |
| if (delta < 0) { |
| // shiftForward(i, len, -delta); |
| rect[xyDim] -= delta; |
| item.label[xyDim] -= delta; |
| adjusted = true; |
| } |
| |
| var shift = Math.max(-delta, 0); |
| totalShifts += shift; |
| lastPos = rect[xyDim] + rect[sizeDim]; |
| } |
| |
| if (totalShifts > 0 && balanceShift) { |
| // Shift back to make the distribution more equally. |
| shiftList(-totalShifts / len, 0, len); |
| } // TODO bleedMargin? |
| |
| |
| var first = list[0]; |
| var last = list[len - 1]; |
| var minGap; |
| var maxGap; |
| updateMinMaxGap(); // If ends exceed two bounds, squeeze at most 80%, then take the gap of two bounds. |
| |
| minGap < 0 && squeezeGaps(-minGap, 0.8); |
| maxGap < 0 && squeezeGaps(maxGap, 0.8); |
| updateMinMaxGap(); |
| takeBoundsGap(minGap, maxGap, 1); |
| takeBoundsGap(maxGap, minGap, -1); // Handle bailout when there is not enough space. |
| |
| updateMinMaxGap(); |
| |
| if (minGap < 0) { |
| squeezeWhenBailout(-minGap); |
| } |
| |
| if (maxGap < 0) { |
| squeezeWhenBailout(maxGap); |
| } |
| |
| function updateMinMaxGap() { |
| minGap = first.rect[xyDim] - minBound; |
| maxGap = maxBound - last.rect[xyDim] - last.rect[sizeDim]; |
| } |
| |
| function takeBoundsGap(gapThisBound, gapOtherBound, moveDir) { |
| if (gapThisBound < 0) { |
| // Move from other gap if can. |
| var moveFromMaxGap = Math.min(gapOtherBound, -gapThisBound); |
| |
| if (moveFromMaxGap > 0) { |
| shiftList(moveFromMaxGap * moveDir, 0, len); |
| var remained = moveFromMaxGap + gapThisBound; |
| |
| if (remained < 0) { |
| squeezeGaps(-remained * moveDir, 1); |
| } |
| } else { |
| squeezeGaps(-gapThisBound * moveDir, 1); |
| } |
| } |
| } |
| |
| function shiftList(delta, start, end) { |
| if (delta !== 0) { |
| adjusted = true; |
| } |
| |
| for (var i = start; i < end; i++) { |
| var item = list[i]; |
| var rect = item.rect; |
| rect[xyDim] += delta; |
| item.label[xyDim] += delta; |
| } |
| } // Squeeze gaps if the labels exceed margin. |
| |
| |
| function squeezeGaps(delta, maxSqeezePercent) { |
| var gaps = []; |
| var totalGaps = 0; |
| |
| for (var i = 1; i < len; i++) { |
| var prevItemRect = list[i - 1].rect; |
| var gap = Math.max(list[i].rect[xyDim] - prevItemRect[xyDim] - prevItemRect[sizeDim], 0); |
| gaps.push(gap); |
| totalGaps += gap; |
| } |
| |
| if (!totalGaps) { |
| return; |
| } |
| |
| var squeezePercent = Math.min(Math.abs(delta) / totalGaps, maxSqeezePercent); |
| |
| if (delta > 0) { |
| for (var i = 0; i < len - 1; i++) { |
| // Distribute the shift delta to all gaps. |
| var movement = gaps[i] * squeezePercent; // Forward |
| |
| shiftList(movement, 0, i + 1); |
| } |
| } else { |
| // Backward |
| for (var i = len - 1; i > 0; i--) { |
| // Distribute the shift delta to all gaps. |
| var movement = gaps[i - 1] * squeezePercent; |
| shiftList(-movement, i, len); |
| } |
| } |
| } |
| /** |
| * Squeeze to allow overlap if there is no more space available. |
| * Let other overlapping strategy like hideOverlap do the job instead of keep exceeding the bounds. |
| */ |
| |
| |
| function squeezeWhenBailout(delta) { |
| var dir = delta < 0 ? -1 : 1; |
| delta = Math.abs(delta); |
| var moveForEachLabel = Math.ceil(delta / (len - 1)); |
| |
| for (var i = 0; i < len - 1; i++) { |
| if (dir > 0) { |
| // Forward |
| shiftList(moveForEachLabel, 0, i + 1); |
| } else { |
| // Backward |
| shiftList(-moveForEachLabel, len - i - 1, len); |
| } |
| |
| delta -= moveForEachLabel; |
| |
| if (delta <= 0) { |
| return; |
| } |
| } |
| } |
| |
| return adjusted; |
| } |
| /** |
| * Adjust labels on y direction to avoid overlap. |
| */ |
| |
| function shiftLayoutOnY(list, topBound, bottomBound, // If average the shifts on all labels and add them to 0 |
| balanceShift) { |
| return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift); |
| } |
| function hideOverlap(labelList) { |
| var displayedLabels = []; // TODO, render overflow visible first, put in the displayedLabels. |
| |
| labelList.sort(function (a, b) { |
| return b.priority - a.priority; |
| }); |
| var globalRect = new BoundingRect(0, 0, 0, 0); |
| |
| function hideEl(el) { |
| if (!el.ignore) { |
| // Show on emphasis. |
| var emphasisState = el.ensureState('emphasis'); |
| |
| if (emphasisState.ignore == null) { |
| emphasisState.ignore = false; |
| } |
| } |
| |
| el.ignore = true; |
| } |
| |
| for (var i = 0; i < labelList.length; i++) { |
| var labelItem = labelList[i]; |
| var isAxisAligned = labelItem.axisAligned; |
| var localRect = labelItem.localRect; |
| var transform = labelItem.transform; |
| var label = labelItem.label; |
| var labelLine = labelItem.labelLine; |
| globalRect.copy(labelItem.rect); // Add a threshold because layout may be aligned precisely. |
| |
| globalRect.width -= 0.1; |
| globalRect.height -= 0.1; |
| globalRect.x += 0.05; |
| globalRect.y += 0.05; |
| var obb = labelItem.obb; |
| var overlapped = false; |
| |
| for (var j = 0; j < displayedLabels.length; j++) { |
| var existsTextCfg = displayedLabels[j]; // Fast rejection. |
| |
| if (!globalRect.intersect(existsTextCfg.rect)) { |
| continue; |
| } |
| |
| if (isAxisAligned && existsTextCfg.axisAligned) { |
| // Is overlapped |
| overlapped = true; |
| break; |
| } |
| |
| if (!existsTextCfg.obb) { |
| // If self is not axis aligned. But other is. |
| existsTextCfg.obb = new OrientedBoundingRect(existsTextCfg.localRect, existsTextCfg.transform); |
| } |
| |
| if (!obb) { |
| // If self is axis aligned. But other is not. |
| obb = new OrientedBoundingRect(localRect, transform); |
| } |
| |
| if (obb.intersect(existsTextCfg.obb)) { |
| overlapped = true; |
| break; |
| } |
| } // TODO Callback to determine if this overlap should be handled? |
| |
| |
| if (overlapped) { |
| hideEl(label); |
| labelLine && hideEl(labelLine); |
| } else { |
| label.attr('ignore', labelItem.defaultAttr.ignore); |
| labelLine && labelLine.attr('ignore', labelItem.defaultAttr.labelGuideIgnore); |
| displayedLabels.push(labelItem); |
| } |
| } |
| } |
| |
| function createDom(id, painter, dpr) { |
| var newDom = platformApi.createCanvas(); |
| var width = painter.getWidth(); |
| var height = painter.getHeight(); |
| var newDomStyle = newDom.style; |
| if (newDomStyle) { |
| newDomStyle.position = 'absolute'; |
| newDomStyle.left = '0'; |
| newDomStyle.top = '0'; |
| newDomStyle.width = width + 'px'; |
| newDomStyle.height = height + 'px'; |
| newDom.setAttribute('data-zr-dom-id', id); |
| } |
| newDom.width = width * dpr; |
| newDom.height = height * dpr; |
| return newDom; |
| } |
| var Layer = (function (_super) { |
| __extends(Layer, _super); |
| function Layer(id, painter, dpr) { |
| var _this = _super.call(this) || this; |
| _this.motionBlur = false; |
| _this.lastFrameAlpha = 0.7; |
| _this.dpr = 1; |
| _this.virtual = false; |
| _this.config = {}; |
| _this.incremental = false; |
| _this.zlevel = 0; |
| _this.maxRepaintRectCount = 5; |
| _this.__dirty = true; |
| _this.__firstTimePaint = true; |
| _this.__used = false; |
| _this.__drawIndex = 0; |
| _this.__startIndex = 0; |
| _this.__endIndex = 0; |
| _this.__prevStartIndex = null; |
| _this.__prevEndIndex = null; |
| var dom; |
| dpr = dpr || devicePixelRatio; |
| if (typeof id === 'string') { |
| dom = createDom(id, painter, dpr); |
| } |
| else if (isObject(id)) { |
| dom = id; |
| id = dom.id; |
| } |
| _this.id = id; |
| _this.dom = dom; |
| var domStyle = dom.style; |
| if (domStyle) { |
| disableUserSelect(dom); |
| dom.onselectstart = function () { return false; }; |
| domStyle.padding = '0'; |
| domStyle.margin = '0'; |
| domStyle.borderWidth = '0'; |
| } |
| _this.painter = painter; |
| _this.dpr = dpr; |
| return _this; |
| } |
| Layer.prototype.getElementCount = function () { |
| return this.__endIndex - this.__startIndex; |
| }; |
| Layer.prototype.afterBrush = function () { |
| this.__prevStartIndex = this.__startIndex; |
| this.__prevEndIndex = this.__endIndex; |
| }; |
| Layer.prototype.initContext = function () { |
| this.ctx = this.dom.getContext('2d'); |
| this.ctx.dpr = this.dpr; |
| }; |
| Layer.prototype.setUnpainted = function () { |
| this.__firstTimePaint = true; |
| }; |
| Layer.prototype.createBackBuffer = function () { |
| var dpr = this.dpr; |
| this.domBack = createDom('back-' + this.id, this.painter, dpr); |
| this.ctxBack = this.domBack.getContext('2d'); |
| if (dpr !== 1) { |
| this.ctxBack.scale(dpr, dpr); |
| } |
| }; |
| Layer.prototype.createRepaintRects = function (displayList, prevList, viewWidth, viewHeight) { |
| if (this.__firstTimePaint) { |
| this.__firstTimePaint = false; |
| return null; |
| } |
| var mergedRepaintRects = []; |
| var maxRepaintRectCount = this.maxRepaintRectCount; |
| var full = false; |
| var pendingRect = new BoundingRect(0, 0, 0, 0); |
| function addRectToMergePool(rect) { |
| if (!rect.isFinite() || rect.isZero()) { |
| return; |
| } |
| if (mergedRepaintRects.length === 0) { |
| var boundingRect = new BoundingRect(0, 0, 0, 0); |
| boundingRect.copy(rect); |
| mergedRepaintRects.push(boundingRect); |
| } |
| else { |
| var isMerged = false; |
| var minDeltaArea = Infinity; |
| var bestRectToMergeIdx = 0; |
| for (var i = 0; i < mergedRepaintRects.length; ++i) { |
| var mergedRect = mergedRepaintRects[i]; |
| if (mergedRect.intersect(rect)) { |
| var pendingRect_1 = new BoundingRect(0, 0, 0, 0); |
| pendingRect_1.copy(mergedRect); |
| pendingRect_1.union(rect); |
| mergedRepaintRects[i] = pendingRect_1; |
| isMerged = true; |
| break; |
| } |
| else if (full) { |
| pendingRect.copy(rect); |
| pendingRect.union(mergedRect); |
| var aArea = rect.width * rect.height; |
| var bArea = mergedRect.width * mergedRect.height; |
| var pendingArea = pendingRect.width * pendingRect.height; |
| var deltaArea = pendingArea - aArea - bArea; |
| if (deltaArea < minDeltaArea) { |
| minDeltaArea = deltaArea; |
| bestRectToMergeIdx = i; |
| } |
| } |
| } |
| if (full) { |
| mergedRepaintRects[bestRectToMergeIdx].union(rect); |
| isMerged = true; |
| } |
| if (!isMerged) { |
| var boundingRect = new BoundingRect(0, 0, 0, 0); |
| boundingRect.copy(rect); |
| mergedRepaintRects.push(boundingRect); |
| } |
| if (!full) { |
| full = mergedRepaintRects.length >= maxRepaintRectCount; |
| } |
| } |
| } |
| for (var i = this.__startIndex; i < this.__endIndex; ++i) { |
| var el = displayList[i]; |
| if (el) { |
| var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true); |
| var prevRect = el.__isRendered && ((el.__dirty & REDRAW_BIT) || !shouldPaint) |
| ? el.getPrevPaintRect() |
| : null; |
| if (prevRect) { |
| addRectToMergePool(prevRect); |
| } |
| var curRect = shouldPaint && ((el.__dirty & REDRAW_BIT) || !el.__isRendered) |
| ? el.getPaintRect() |
| : null; |
| if (curRect) { |
| addRectToMergePool(curRect); |
| } |
| } |
| } |
| for (var i = this.__prevStartIndex; i < this.__prevEndIndex; ++i) { |
| var el = prevList[i]; |
| var shouldPaint = el.shouldBePainted(viewWidth, viewHeight, true, true); |
| if (el && (!shouldPaint || !el.__zr) && el.__isRendered) { |
| var prevRect = el.getPrevPaintRect(); |
| if (prevRect) { |
| addRectToMergePool(prevRect); |
| } |
| } |
| } |
| var hasIntersections; |
| do { |
| hasIntersections = false; |
| for (var i = 0; i < mergedRepaintRects.length;) { |
| if (mergedRepaintRects[i].isZero()) { |
| mergedRepaintRects.splice(i, 1); |
| continue; |
| } |
| for (var j = i + 1; j < mergedRepaintRects.length;) { |
| if (mergedRepaintRects[i].intersect(mergedRepaintRects[j])) { |
| hasIntersections = true; |
| mergedRepaintRects[i].union(mergedRepaintRects[j]); |
| mergedRepaintRects.splice(j, 1); |
| } |
| else { |
| j++; |
| } |
| } |
| i++; |
| } |
| } while (hasIntersections); |
| this._paintRects = mergedRepaintRects; |
| return mergedRepaintRects; |
| }; |
| Layer.prototype.debugGetPaintRects = function () { |
| return (this._paintRects || []).slice(); |
| }; |
| Layer.prototype.resize = function (width, height) { |
| var dpr = this.dpr; |
| var dom = this.dom; |
| var domStyle = dom.style; |
| var domBack = this.domBack; |
| if (domStyle) { |
| domStyle.width = width + 'px'; |
| domStyle.height = height + 'px'; |
| } |
| dom.width = width * dpr; |
| dom.height = height * dpr; |
| if (domBack) { |
| domBack.width = width * dpr; |
| domBack.height = height * dpr; |
| if (dpr !== 1) { |
| this.ctxBack.scale(dpr, dpr); |
| } |
| } |
| }; |
| Layer.prototype.clear = function (clearAll, clearColor, repaintRects) { |
| var dom = this.dom; |
| var ctx = this.ctx; |
| var width = dom.width; |
| var height = dom.height; |
| clearColor = clearColor || this.clearColor; |
| var haveMotionBLur = this.motionBlur && !clearAll; |
| var lastFrameAlpha = this.lastFrameAlpha; |
| var dpr = this.dpr; |
| var self = this; |
| if (haveMotionBLur) { |
| if (!this.domBack) { |
| this.createBackBuffer(); |
| } |
| this.ctxBack.globalCompositeOperation = 'copy'; |
| this.ctxBack.drawImage(dom, 0, 0, width / dpr, height / dpr); |
| } |
| var domBack = this.domBack; |
| function doClear(x, y, width, height) { |
| ctx.clearRect(x, y, width, height); |
| if (clearColor && clearColor !== 'transparent') { |
| var clearColorGradientOrPattern = void 0; |
| if (isGradientObject(clearColor)) { |
| var shouldCache = clearColor.global || (clearColor.__width === width |
| && clearColor.__height === height); |
| clearColorGradientOrPattern = shouldCache |
| && clearColor.__canvasGradient |
| || getCanvasGradient(ctx, clearColor, { |
| x: 0, |
| y: 0, |
| width: width, |
| height: height |
| }); |
| clearColor.__canvasGradient = clearColorGradientOrPattern; |
| clearColor.__width = width; |
| clearColor.__height = height; |
| } |
| else if (isImagePatternObject(clearColor)) { |
| clearColor.scaleX = clearColor.scaleX || dpr; |
| clearColor.scaleY = clearColor.scaleY || dpr; |
| clearColorGradientOrPattern = createCanvasPattern(ctx, clearColor, { |
| dirty: function () { |
| self.setUnpainted(); |
| self.__painter.refresh(); |
| } |
| }); |
| } |
| ctx.save(); |
| ctx.fillStyle = clearColorGradientOrPattern || clearColor; |
| ctx.fillRect(x, y, width, height); |
| ctx.restore(); |
| } |
| if (haveMotionBLur) { |
| ctx.save(); |
| ctx.globalAlpha = lastFrameAlpha; |
| ctx.drawImage(domBack, x, y, width, height); |
| ctx.restore(); |
| } |
| } |
| if (!repaintRects || haveMotionBLur) { |
| doClear(0, 0, width, height); |
| } |
| else if (repaintRects.length) { |
| each(repaintRects, function (rect) { |
| doClear(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr); |
| }); |
| } |
| }; |
| return Layer; |
| }(Eventful)); |
| |
| var HOVER_LAYER_ZLEVEL = 1e5; |
| var CANVAS_ZLEVEL = 314159; |
| var EL_AFTER_INCREMENTAL_INC = 0.01; |
| var INCREMENTAL_INC = 0.001; |
| 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; |
| } |
| function createRoot(width, height) { |
| var domRoot = document.createElement('div'); |
| domRoot.style.cssText = [ |
| 'position:relative', |
| 'width:' + width + 'px', |
| 'height:' + height + 'px', |
| 'padding:0', |
| 'margin:0', |
| 'border-width:0' |
| ].join(';') + ';'; |
| return domRoot; |
| } |
| var CanvasPainter = (function () { |
| function CanvasPainter(root, storage, opts, id) { |
| this.type = 'canvas'; |
| this._zlevelList = []; |
| this._prevDisplayList = []; |
| this._layers = {}; |
| this._layerConfig = {}; |
| this._needsManuallyCompositing = false; |
| this.type = 'canvas'; |
| var singleCanvas = !root.nodeName |
| || root.nodeName.toUpperCase() === 'CANVAS'; |
| this._opts = opts = extend({}, opts || {}); |
| this.dpr = opts.devicePixelRatio || devicePixelRatio; |
| this._singleCanvas = singleCanvas; |
| this.root = root; |
| var rootStyle = root.style; |
| if (rootStyle) { |
| disableUserSelect(root); |
| root.innerHTML = ''; |
| } |
| this.storage = storage; |
| var zlevelList = this._zlevelList; |
| this._prevDisplayList = []; |
| var layers = this._layers; |
| if (!singleCanvas) { |
| this._width = getSize(root, 0, opts); |
| this._height = getSize(root, 1, opts); |
| var domRoot = this._domRoot = createRoot(this._width, this._height); |
| root.appendChild(domRoot); |
| } |
| else { |
| var rootCanvas = root; |
| var width = rootCanvas.width; |
| var height = rootCanvas.height; |
| if (opts.width != null) { |
| width = opts.width; |
| } |
| if (opts.height != null) { |
| height = opts.height; |
| } |
| this.dpr = opts.devicePixelRatio || 1; |
| rootCanvas.width = width * this.dpr; |
| rootCanvas.height = height * this.dpr; |
| this._width = width; |
| this._height = height; |
| var mainLayer = new Layer(rootCanvas, this, this.dpr); |
| mainLayer.__builtin__ = true; |
| mainLayer.initContext(); |
| layers[CANVAS_ZLEVEL] = mainLayer; |
| mainLayer.zlevel = CANVAS_ZLEVEL; |
| zlevelList.push(CANVAS_ZLEVEL); |
| this._domRoot = root; |
| } |
| } |
| CanvasPainter.prototype.getType = function () { |
| return 'canvas'; |
| }; |
| CanvasPainter.prototype.isSingleCanvas = function () { |
| return this._singleCanvas; |
| }; |
| CanvasPainter.prototype.getViewportRoot = function () { |
| return this._domRoot; |
| }; |
| CanvasPainter.prototype.getViewportRootOffset = function () { |
| var viewportRoot = this.getViewportRoot(); |
| if (viewportRoot) { |
| return { |
| offsetLeft: viewportRoot.offsetLeft || 0, |
| offsetTop: viewportRoot.offsetTop || 0 |
| }; |
| } |
| }; |
| CanvasPainter.prototype.refresh = function (paintAll) { |
| var list = this.storage.getDisplayList(true); |
| var prevList = this._prevDisplayList; |
| var zlevelList = this._zlevelList; |
| this._redrawId = Math.random(); |
| this._paintList(list, prevList, paintAll, this._redrawId); |
| for (var i = 0; i < zlevelList.length; i++) { |
| var z = zlevelList[i]; |
| var layer = this._layers[z]; |
| if (!layer.__builtin__ && layer.refresh) { |
| var clearColor = i === 0 ? this._backgroundColor : null; |
| layer.refresh(clearColor); |
| } |
| } |
| if (this._opts.useDirtyRect) { |
| this._prevDisplayList = list.slice(); |
| } |
| return this; |
| }; |
| CanvasPainter.prototype.refreshHover = function () { |
| this._paintHoverList(this.storage.getDisplayList(false)); |
| }; |
| CanvasPainter.prototype._paintHoverList = function (list) { |
| var len = list.length; |
| var hoverLayer = this._hoverlayer; |
| hoverLayer && hoverLayer.clear(); |
| if (!len) { |
| return; |
| } |
| var scope = { |
| inHover: true, |
| viewWidth: this._width, |
| viewHeight: this._height |
| }; |
| var ctx; |
| for (var i = 0; i < len; i++) { |
| var el = list[i]; |
| if (el.__inHover) { |
| if (!hoverLayer) { |
| hoverLayer = this._hoverlayer = this.getLayer(HOVER_LAYER_ZLEVEL); |
| } |
| if (!ctx) { |
| ctx = hoverLayer.ctx; |
| ctx.save(); |
| } |
| brush(ctx, el, scope, i === len - 1); |
| } |
| } |
| if (ctx) { |
| ctx.restore(); |
| } |
| }; |
| CanvasPainter.prototype.getHoverLayer = function () { |
| return this.getLayer(HOVER_LAYER_ZLEVEL); |
| }; |
| CanvasPainter.prototype.paintOne = function (ctx, el) { |
| brushSingle(ctx, el); |
| }; |
| CanvasPainter.prototype._paintList = function (list, prevList, paintAll, redrawId) { |
| if (this._redrawId !== redrawId) { |
| return; |
| } |
| paintAll = paintAll || false; |
| this._updateLayerStatus(list); |
| var _a = this._doPaintList(list, prevList, paintAll), finished = _a.finished, needsRefreshHover = _a.needsRefreshHover; |
| if (this._needsManuallyCompositing) { |
| this._compositeManually(); |
| } |
| if (needsRefreshHover) { |
| this._paintHoverList(list); |
| } |
| if (!finished) { |
| var self_1 = this; |
| requestAnimationFrame$1(function () { |
| self_1._paintList(list, prevList, paintAll, redrawId); |
| }); |
| } |
| else { |
| this.eachLayer(function (layer) { |
| layer.afterBrush && layer.afterBrush(); |
| }); |
| } |
| }; |
| CanvasPainter.prototype._compositeManually = function () { |
| var ctx = this.getLayer(CANVAS_ZLEVEL).ctx; |
| var width = this._domRoot.width; |
| var height = this._domRoot.height; |
| ctx.clearRect(0, 0, width, height); |
| this.eachBuiltinLayer(function (layer) { |
| if (layer.virtual) { |
| ctx.drawImage(layer.dom, 0, 0, width, height); |
| } |
| }); |
| }; |
| CanvasPainter.prototype._doPaintList = function (list, prevList, paintAll) { |
| var _this = this; |
| var layerList = []; |
| var useDirtyRect = this._opts.useDirtyRect; |
| for (var zi = 0; zi < this._zlevelList.length; zi++) { |
| var zlevel = this._zlevelList[zi]; |
| var layer = this._layers[zlevel]; |
| if (layer.__builtin__ |
| && layer !== this._hoverlayer |
| && (layer.__dirty || paintAll)) { |
| layerList.push(layer); |
| } |
| } |
| var finished = true; |
| var needsRefreshHover = false; |
| var _loop_1 = function (k) { |
| var layer = layerList[k]; |
| var ctx = layer.ctx; |
| var repaintRects = useDirtyRect |
| && layer.createRepaintRects(list, prevList, this_1._width, this_1._height); |
| var start = paintAll ? layer.__startIndex : layer.__drawIndex; |
| var useTimer = !paintAll && layer.incremental && Date.now; |
| var startTime = useTimer && Date.now(); |
| var clearColor = layer.zlevel === this_1._zlevelList[0] |
| ? this_1._backgroundColor : null; |
| if (layer.__startIndex === layer.__endIndex) { |
| layer.clear(false, clearColor, repaintRects); |
| } |
| else if (start === layer.__startIndex) { |
| var firstEl = list[start]; |
| if (!firstEl.incremental || !firstEl.notClear || paintAll) { |
| layer.clear(false, clearColor, repaintRects); |
| } |
| } |
| if (start === -1) { |
| console.error('For some unknown reason. drawIndex is -1'); |
| start = layer.__startIndex; |
| } |
| var i; |
| var repaint = function (repaintRect) { |
| var scope = { |
| inHover: false, |
| allClipped: false, |
| prevEl: null, |
| viewWidth: _this._width, |
| viewHeight: _this._height |
| }; |
| for (i = start; i < layer.__endIndex; i++) { |
| var el = list[i]; |
| if (el.__inHover) { |
| needsRefreshHover = true; |
| } |
| _this._doPaintEl(el, layer, useDirtyRect, repaintRect, scope, i === layer.__endIndex - 1); |
| if (useTimer) { |
| var dTime = Date.now() - startTime; |
| if (dTime > 15) { |
| break; |
| } |
| } |
| } |
| if (scope.prevElClipPaths) { |
| ctx.restore(); |
| } |
| }; |
| if (repaintRects) { |
| if (repaintRects.length === 0) { |
| i = layer.__endIndex; |
| } |
| else { |
| var dpr = this_1.dpr; |
| for (var r = 0; r < repaintRects.length; ++r) { |
| var rect = repaintRects[r]; |
| ctx.save(); |
| ctx.beginPath(); |
| ctx.rect(rect.x * dpr, rect.y * dpr, rect.width * dpr, rect.height * dpr); |
| ctx.clip(); |
| repaint(rect); |
| ctx.restore(); |
| } |
| } |
| } |
| else { |
| ctx.save(); |
| repaint(); |
| ctx.restore(); |
| } |
| layer.__drawIndex = i; |
| if (layer.__drawIndex < layer.__endIndex) { |
| finished = false; |
| } |
| }; |
| var this_1 = this; |
| for (var k = 0; k < layerList.length; k++) { |
| _loop_1(k); |
| } |
| if (env.wxa) { |
| each(this._layers, function (layer) { |
| if (layer && layer.ctx && layer.ctx.draw) { |
| layer.ctx.draw(); |
| } |
| }); |
| } |
| return { |
| finished: finished, |
| needsRefreshHover: needsRefreshHover |
| }; |
| }; |
| CanvasPainter.prototype._doPaintEl = function (el, currentLayer, useDirtyRect, repaintRect, scope, isLast) { |
| var ctx = currentLayer.ctx; |
| if (useDirtyRect) { |
| var paintRect = el.getPaintRect(); |
| if (!repaintRect || paintRect && paintRect.intersect(repaintRect)) { |
| brush(ctx, el, scope, isLast); |
| el.setPrevPaintRect(paintRect); |
| } |
| } |
| else { |
| brush(ctx, el, scope, isLast); |
| } |
| }; |
| CanvasPainter.prototype.getLayer = function (zlevel, virtual) { |
| if (this._singleCanvas && !this._needsManuallyCompositing) { |
| zlevel = CANVAS_ZLEVEL; |
| } |
| var layer = this._layers[zlevel]; |
| if (!layer) { |
| layer = new Layer('zr_' + zlevel, this, this.dpr); |
| layer.zlevel = zlevel; |
| layer.__builtin__ = true; |
| if (this._layerConfig[zlevel]) { |
| merge(layer, this._layerConfig[zlevel], true); |
| } |
| else if (this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC]) { |
| merge(layer, this._layerConfig[zlevel - EL_AFTER_INCREMENTAL_INC], true); |
| } |
| if (virtual) { |
| layer.virtual = virtual; |
| } |
| this.insertLayer(zlevel, layer); |
| layer.initContext(); |
| } |
| return layer; |
| }; |
| CanvasPainter.prototype.insertLayer = function (zlevel, layer) { |
| var layersMap = this._layers; |
| var zlevelList = this._zlevelList; |
| var len = zlevelList.length; |
| var domRoot = this._domRoot; |
| var prevLayer = null; |
| var i = -1; |
| if (layersMap[zlevel]) { |
| if ("development" !== 'production') { |
| logError('ZLevel ' + zlevel + ' has been used already'); |
| } |
| return; |
| } |
| if (!isLayerValid(layer)) { |
| if ("development" !== 'production') { |
| logError('Layer of zlevel ' + zlevel + ' is not valid'); |
| } |
| return; |
| } |
| if (len > 0 && zlevel > zlevelList[0]) { |
| for (i = 0; i < len - 1; i++) { |
| if (zlevelList[i] < zlevel |
| && zlevelList[i + 1] > zlevel) { |
| break; |
| } |
| } |
| prevLayer = layersMap[zlevelList[i]]; |
| } |
| zlevelList.splice(i + 1, 0, zlevel); |
| layersMap[zlevel] = layer; |
| if (!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); |
| } |
| } |
| } |
| layer.__painter = this; |
| }; |
| CanvasPainter.prototype.eachLayer = function (cb, context) { |
| var zlevelList = this._zlevelList; |
| for (var i = 0; i < zlevelList.length; i++) { |
| var z = zlevelList[i]; |
| cb.call(context, this._layers[z], z); |
| } |
| }; |
| CanvasPainter.prototype.eachBuiltinLayer = function (cb, context) { |
| var zlevelList = this._zlevelList; |
| for (var i = 0; i < zlevelList.length; i++) { |
| var z = zlevelList[i]; |
| var layer = this._layers[z]; |
| if (layer.__builtin__) { |
| cb.call(context, layer, z); |
| } |
| } |
| }; |
| CanvasPainter.prototype.eachOtherLayer = function (cb, context) { |
| var zlevelList = this._zlevelList; |
| for (var i = 0; i < zlevelList.length; i++) { |
| var z = zlevelList[i]; |
| var layer = this._layers[z]; |
| if (!layer.__builtin__) { |
| cb.call(context, layer, z); |
| } |
| } |
| }; |
| CanvasPainter.prototype.getLayers = function () { |
| return this._layers; |
| }; |
| CanvasPainter.prototype._updateLayerStatus = function (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 = 1; i_1 < list.length; i_1++) { |
| var el = list[i_1]; |
| if (el.zlevel !== list[i_1 - 1].zlevel || el.incremental) { |
| this._needsManuallyCompositing = true; |
| break; |
| } |
| } |
| } |
| var prevLayer = null; |
| var incrementalLayerCount = 0; |
| var prevZlevel; |
| var i; |
| for (i = 0; i < list.length; i++) { |
| var el = list[i]; |
| var zlevel = el.zlevel; |
| var layer = void 0; |
| if (prevZlevel !== zlevel) { |
| prevZlevel = zlevel; |
| incrementalLayerCount = 0; |
| } |
| if (el.incremental) { |
| layer = this.getLayer(zlevel + INCREMENTAL_INC, this._needsManuallyCompositing); |
| layer.incremental = true; |
| incrementalLayerCount = 1; |
| } |
| else { |
| layer = this.getLayer(zlevel + (incrementalLayerCount > 0 ? EL_AFTER_INCREMENTAL_INC : 0), this._needsManuallyCompositing); |
| } |
| if (!layer.__builtin__) { |
| logError('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 { |
| layer.__drawIndex = -1; |
| } |
| updatePrevLayer(i); |
| prevLayer = layer; |
| } |
| if ((el.__dirty & REDRAW_BIT) && !el.__inHover) { |
| layer.__dirty = true; |
| if (layer.incremental && layer.__drawIndex < 0) { |
| layer.__drawIndex = i; |
| } |
| } |
| } |
| updatePrevLayer(i); |
| this.eachBuiltinLayer(function (layer, z) { |
| if (!layer.__used && layer.getElementCount() > 0) { |
| layer.__dirty = true; |
| layer.__startIndex = layer.__endIndex = layer.__drawIndex = 0; |
| } |
| if (layer.__dirty && layer.__drawIndex < 0) { |
| layer.__drawIndex = layer.__startIndex; |
| } |
| }); |
| }; |
| CanvasPainter.prototype.clear = function () { |
| this.eachBuiltinLayer(this._clearLayer); |
| return this; |
| }; |
| CanvasPainter.prototype._clearLayer = function (layer) { |
| layer.clear(); |
| }; |
| CanvasPainter.prototype.setBackgroundColor = function (backgroundColor) { |
| this._backgroundColor = backgroundColor; |
| each(this._layers, function (layer) { |
| layer.setUnpainted(); |
| }); |
| }; |
| CanvasPainter.prototype.configLayer = function (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 < this._zlevelList.length; i++) { |
| var _zlevel = this._zlevelList[i]; |
| if (_zlevel === zlevel || _zlevel === zlevel + EL_AFTER_INCREMENTAL_INC) { |
| var layer = this._layers[_zlevel]; |
| merge(layer, layerConfig[zlevel], true); |
| } |
| } |
| } |
| }; |
| CanvasPainter.prototype.delLayer = function (zlevel) { |
| var layers = this._layers; |
| var zlevelList = this._zlevelList; |
| var layer = layers[zlevel]; |
| if (!layer) { |
| return; |
| } |
| layer.dom.parentNode.removeChild(layer.dom); |
| delete layers[zlevel]; |
| zlevelList.splice(indexOf(zlevelList, zlevel), 1); |
| }; |
| CanvasPainter.prototype.resize = function (width, height) { |
| if (!this._domRoot.style) { |
| if (width == null || height == null) { |
| return; |
| } |
| this._width = width; |
| this._height = height; |
| this.getLayer(CANVAS_ZLEVEL).resize(width, height); |
| } |
| else { |
| var domRoot = this._domRoot; |
| domRoot.style.display = 'none'; |
| var opts = this._opts; |
| var root = this.root; |
| width != null && (opts.width = width); |
| height != null && (opts.height = height); |
| width = getSize(root, 0, opts); |
| height = getSize(root, 1, opts); |
| domRoot.style.display = ''; |
| if (this._width !== width || height !== this._height) { |
| domRoot.style.width = width + 'px'; |
| domRoot.style.height = height + 'px'; |
| for (var id in this._layers) { |
| if (this._layers.hasOwnProperty(id)) { |
| this._layers[id].resize(width, height); |
| } |
| } |
| this.refresh(true); |
| } |
| this._width = width; |
| this._height = height; |
| } |
| return this; |
| }; |
| CanvasPainter.prototype.clearLayer = function (zlevel) { |
| var layer = this._layers[zlevel]; |
| if (layer) { |
| layer.clear(); |
| } |
| }; |
| CanvasPainter.prototype.dispose = function () { |
| this.root.innerHTML = ''; |
| this.root = |
| this.storage = |
| this._domRoot = |
| this._layers = null; |
| }; |
| CanvasPainter.prototype.getRenderedCanvas = function (opts) { |
| opts = opts || {}; |
| if (this._singleCanvas && !this._compositeManually) { |
| return this._layers[CANVAS_ZLEVEL].dom; |
| } |
| var imageLayer = new Layer('image', this, opts.pixelRatio || this.dpr); |
| imageLayer.initContext(); |
| imageLayer.clear(false, opts.backgroundColor || this._backgroundColor); |
| var ctx = imageLayer.ctx; |
| if (opts.pixelRatio <= this.dpr) { |
| this.refresh(); |
| var width_1 = imageLayer.dom.width; |
| var height_1 = imageLayer.dom.height; |
| this.eachLayer(function (layer) { |
| if (layer.__builtin__) { |
| ctx.drawImage(layer.dom, 0, 0, width_1, height_1); |
| } |
| else if (layer.renderToCanvas) { |
| ctx.save(); |
| layer.renderToCanvas(ctx); |
| ctx.restore(); |
| } |
| }); |
| } |
| else { |
| var scope = { |
| inHover: false, |
| viewWidth: this._width, |
| viewHeight: this._height |
| }; |
| var displayList = this.storage.getDisplayList(true); |
| for (var i = 0, len = displayList.length; i < len; i++) { |
| var el = displayList[i]; |
| brush(ctx, el, scope, i === len - 1); |
| } |
| } |
| return imageLayer.dom; |
| }; |
| CanvasPainter.prototype.getWidth = function () { |
| return this._width; |
| }; |
| CanvasPainter.prototype.getHeight = function () { |
| return this._height; |
| }; |
| return CanvasPainter; |
| }()); |
| |
| function install(registers) { |
| registers.registerPainter('canvas', CanvasPainter); |
| } |
| |
| var LineSeriesModel = |
| /** @class */ |
| function (_super) { |
| __extends(LineSeriesModel, _super); |
| |
| function LineSeriesModel() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = LineSeriesModel.type; |
| _this.hasSymbolVisual = true; |
| return _this; |
| } |
| |
| LineSeriesModel.prototype.getInitialData = function (option) { |
| if ("development" !== 'production') { |
| var coordSys = option.coordinateSystem; |
| |
| if (coordSys !== 'polar' && coordSys !== 'cartesian2d') { |
| throw new Error('Line not support coordinateSystem besides cartesian and polar'); |
| } |
| } |
| |
| return createSeriesData(null, this, { |
| useEncodeDefaulter: true |
| }); |
| }; |
| |
| LineSeriesModel.prototype.getLegendIcon = function (opt) { |
| var group = new Group(); |
| var line = createSymbol('line', 0, opt.itemHeight / 2, opt.itemWidth, 0, opt.lineStyle.stroke, false); |
| group.add(line); |
| line.setStyle(opt.lineStyle); |
| var visualType = this.getData().getVisual('symbol'); |
| var visualRotate = this.getData().getVisual('symbolRotate'); |
| var symbolType = visualType === 'none' ? 'circle' : visualType; // Symbol size is 80% when there is a line |
| |
| var size = opt.itemHeight * 0.8; |
| var symbol = createSymbol(symbolType, (opt.itemWidth - size) / 2, (opt.itemHeight - size) / 2, size, size, opt.itemStyle.fill); |
| group.add(symbol); |
| symbol.setStyle(opt.itemStyle); |
| var symbolRotate = opt.iconRotate === 'inherit' ? visualRotate : opt.iconRotate || 0; |
| symbol.rotation = symbolRotate * Math.PI / 180; |
| symbol.setOrigin([opt.itemWidth / 2, opt.itemHeight / 2]); |
| |
| if (symbolType.indexOf('empty') > -1) { |
| symbol.style.stroke = symbol.style.fill; |
| symbol.style.fill = '#fff'; |
| symbol.style.lineWidth = 2; |
| } |
| |
| return group; |
| }; |
| |
| LineSeriesModel.type = 'series.line'; |
| LineSeriesModel.dependencies = ['grid', 'polar']; |
| LineSeriesModel.defaultOption = { |
| // zlevel: 0, |
| z: 3, |
| coordinateSystem: 'cartesian2d', |
| legendHoverLink: true, |
| clip: true, |
| label: { |
| position: 'top' |
| }, |
| // itemStyle: { |
| // }, |
| endLabel: { |
| show: false, |
| valueAnimation: true, |
| distance: 8 |
| }, |
| lineStyle: { |
| width: 2, |
| type: 'solid' |
| }, |
| emphasis: { |
| scale: true |
| }, |
| // areaStyle: { |
| // origin of areaStyle. Valid values: |
| // `'auto'/null/undefined`: from axisLine to data |
| // `'start'`: from min to data |
| // `'end'`: from data to max |
| // origin: 'auto' |
| // }, |
| // false, 'start', 'end', 'middle' |
| step: false, |
| // Disabled if step is true |
| smooth: false, |
| smoothMonotone: null, |
| symbol: 'emptyCircle', |
| symbolSize: 4, |
| symbolRotate: null, |
| showSymbol: true, |
| // `false`: follow the label interval strategy. |
| // `true`: show all symbols. |
| // `'auto'`: If possible, show all symbols, otherwise |
| // follow the label interval strategy. |
| showAllSymbol: 'auto', |
| // Whether to connect break point. |
| connectNulls: false, |
| // Sampling for large data. Can be: 'average', 'max', 'min', 'sum', 'lttb'. |
| sampling: 'none', |
| animationEasing: 'linear', |
| // Disable progressive |
| progressive: 0, |
| hoverLayerThreshold: Infinity, |
| universalTransition: { |
| divideShape: 'clone' |
| }, |
| triggerLineEvent: false |
| }; |
| return LineSeriesModel; |
| }(SeriesModel); |
| |
| /** |
| * @return label string. Not null/undefined |
| */ |
| |
| function getDefaultLabel(data, dataIndex) { |
| var labelDims = data.mapDimensionsAll('defaultedLabel'); |
| var len = labelDims.length; // Simple optimization (in lots of cases, label dims length is 1) |
| |
| if (len === 1) { |
| var rawVal = retrieveRawValue(data, dataIndex, labelDims[0]); |
| return rawVal != null ? rawVal + '' : null; |
| } else if (len) { |
| var vals = []; |
| |
| for (var i = 0; i < labelDims.length; i++) { |
| vals.push(retrieveRawValue(data, dataIndex, labelDims[i])); |
| } |
| |
| return vals.join(' '); |
| } |
| } |
| function getDefaultInterpolatedLabel(data, interpolatedValue) { |
| var labelDims = data.mapDimensionsAll('defaultedLabel'); |
| |
| if (!isArray(interpolatedValue)) { |
| return interpolatedValue + ''; |
| } |
| |
| var vals = []; |
| |
| for (var i = 0; i < labelDims.length; i++) { |
| var dimIndex = data.getDimensionIndex(labelDims[i]); |
| |
| if (dimIndex >= 0) { |
| vals.push(interpolatedValue[dimIndex]); |
| } |
| } |
| |
| return vals.join(' '); |
| } |
| |
| var Symbol = |
| /** @class */ |
| function (_super) { |
| __extends(Symbol, _super); |
| |
| function Symbol(data, idx, seriesScope, opts) { |
| var _this = _super.call(this) || this; |
| |
| _this.updateData(data, idx, seriesScope, opts); |
| |
| return _this; |
| } |
| |
| Symbol.prototype._createSymbol = function (symbolType, data, idx, symbolSize, keepAspect) { |
| // Remove paths created before |
| this.removeAll(); // let symbolPath = createSymbol( |
| // symbolType, -0.5, -0.5, 1, 1, color |
| // ); |
| // If width/height are set too small (e.g., set to 1) on ios10 |
| // and macOS Sierra, a circle stroke become a rect, no matter what |
| // the scale is set. So we set width/height as 2. See #4150. |
| |
| var symbolPath = createSymbol(symbolType, -1, -1, 2, 2, null, keepAspect); |
| symbolPath.attr({ |
| z2: 100, |
| culling: true, |
| scaleX: symbolSize[0] / 2, |
| scaleY: symbolSize[1] / 2 |
| }); // Rewrite drift method |
| |
| symbolPath.drift = driftSymbol; |
| this._symbolType = symbolType; |
| this.add(symbolPath); |
| }; |
| /** |
| * Stop animation |
| * @param {boolean} toLastFrame |
| */ |
| |
| |
| Symbol.prototype.stopSymbolAnimation = function (toLastFrame) { |
| this.childAt(0).stopAnimation(null, toLastFrame); |
| }; |
| |
| Symbol.prototype.getSymbolType = function () { |
| return this._symbolType; |
| }; |
| /** |
| * FIXME: |
| * Caution: This method breaks the encapsulation of this module, |
| * but it indeed brings convenience. So do not use the method |
| * unless you detailedly know all the implements of `Symbol`, |
| * especially animation. |
| * |
| * Get symbol path element. |
| */ |
| |
| |
| Symbol.prototype.getSymbolPath = function () { |
| return this.childAt(0); |
| }; |
| /** |
| * Highlight symbol |
| */ |
| |
| |
| Symbol.prototype.highlight = function () { |
| enterEmphasis(this.childAt(0)); |
| }; |
| /** |
| * Downplay symbol |
| */ |
| |
| |
| Symbol.prototype.downplay = function () { |
| leaveEmphasis(this.childAt(0)); |
| }; |
| /** |
| * @param {number} zlevel |
| * @param {number} z |
| */ |
| |
| |
| Symbol.prototype.setZ = function (zlevel, z) { |
| var symbolPath = this.childAt(0); |
| symbolPath.zlevel = zlevel; |
| symbolPath.z = z; |
| }; |
| |
| Symbol.prototype.setDraggable = function (draggable, hasCursorOption) { |
| var symbolPath = this.childAt(0); |
| symbolPath.draggable = draggable; |
| symbolPath.cursor = !hasCursorOption && draggable ? 'move' : symbolPath.cursor; |
| }; |
| /** |
| * Update symbol properties |
| */ |
| |
| |
| Symbol.prototype.updateData = function (data, idx, seriesScope, opts) { |
| this.silent = false; |
| var symbolType = data.getItemVisual(idx, 'symbol') || 'circle'; |
| var seriesModel = data.hostModel; |
| var symbolSize = Symbol.getSymbolSize(data, idx); |
| var isInit = symbolType !== this._symbolType; |
| var disableAnimation = opts && opts.disableAnimation; |
| |
| if (isInit) { |
| var keepAspect = data.getItemVisual(idx, 'symbolKeepAspect'); |
| |
| this._createSymbol(symbolType, data, idx, symbolSize, keepAspect); |
| } else { |
| var symbolPath = this.childAt(0); |
| symbolPath.silent = false; |
| var target = { |
| scaleX: symbolSize[0] / 2, |
| scaleY: symbolSize[1] / 2 |
| }; |
| disableAnimation ? symbolPath.attr(target) : updateProps(symbolPath, target, seriesModel, idx); |
| saveOldStyle(symbolPath); |
| } |
| |
| this._updateCommon(data, idx, symbolSize, seriesScope, opts); |
| |
| if (isInit) { |
| var symbolPath = this.childAt(0); |
| |
| if (!disableAnimation) { |
| var target = { |
| scaleX: this._sizeX, |
| scaleY: this._sizeY, |
| style: { |
| // Always fadeIn. Because it has fadeOut animation when symbol is removed.. |
| opacity: symbolPath.style.opacity |
| } |
| }; |
| symbolPath.scaleX = symbolPath.scaleY = 0; |
| symbolPath.style.opacity = 0; |
| initProps(symbolPath, target, seriesModel, idx); |
| } |
| } |
| |
| if (disableAnimation) { |
| // Must stop leave transition manually if don't call initProps or updateProps. |
| this.childAt(0).stopAnimation('leave'); |
| } |
| }; |
| |
| Symbol.prototype._updateCommon = function (data, idx, symbolSize, seriesScope, opts) { |
| var symbolPath = this.childAt(0); |
| var seriesModel = data.hostModel; |
| var emphasisItemStyle; |
| var blurItemStyle; |
| var selectItemStyle; |
| var focus; |
| var blurScope; |
| var emphasisDisabled; |
| var labelStatesModels; |
| var hoverScale; |
| var cursorStyle; |
| |
| if (seriesScope) { |
| emphasisItemStyle = seriesScope.emphasisItemStyle; |
| blurItemStyle = seriesScope.blurItemStyle; |
| selectItemStyle = seriesScope.selectItemStyle; |
| focus = seriesScope.focus; |
| blurScope = seriesScope.blurScope; |
| labelStatesModels = seriesScope.labelStatesModels; |
| hoverScale = seriesScope.hoverScale; |
| cursorStyle = seriesScope.cursorStyle; |
| emphasisDisabled = seriesScope.emphasisDisabled; |
| } |
| |
| if (!seriesScope || data.hasItemOption) { |
| var itemModel = seriesScope && seriesScope.itemModel ? seriesScope.itemModel : data.getItemModel(idx); |
| var emphasisModel = itemModel.getModel('emphasis'); |
| emphasisItemStyle = emphasisModel.getModel('itemStyle').getItemStyle(); |
| selectItemStyle = itemModel.getModel(['select', 'itemStyle']).getItemStyle(); |
| blurItemStyle = itemModel.getModel(['blur', 'itemStyle']).getItemStyle(); |
| focus = emphasisModel.get('focus'); |
| blurScope = emphasisModel.get('blurScope'); |
| emphasisDisabled = emphasisModel.get('disabled'); |
| labelStatesModels = getLabelStatesModels(itemModel); |
| hoverScale = emphasisModel.getShallow('scale'); |
| cursorStyle = itemModel.getShallow('cursor'); |
| } |
| |
| var symbolRotate = data.getItemVisual(idx, 'symbolRotate'); |
| symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0); |
| var symbolOffset = normalizeSymbolOffset(data.getItemVisual(idx, 'symbolOffset'), symbolSize); |
| |
| if (symbolOffset) { |
| symbolPath.x = symbolOffset[0]; |
| symbolPath.y = symbolOffset[1]; |
| } |
| |
| cursorStyle && symbolPath.attr('cursor', cursorStyle); |
| var symbolStyle = data.getItemVisual(idx, 'style'); |
| var visualColor = symbolStyle.fill; |
| |
| if (symbolPath instanceof ZRImage) { |
| var pathStyle = symbolPath.style; |
| symbolPath.useStyle(extend({ |
| // TODO other properties like x, y ? |
| image: pathStyle.image, |
| x: pathStyle.x, |
| y: pathStyle.y, |
| width: pathStyle.width, |
| height: pathStyle.height |
| }, symbolStyle)); |
| } else { |
| if (symbolPath.__isEmptyBrush) { |
| // fill and stroke will be swapped if it's empty. |
| // So we cloned a new style to avoid it affecting the original style in visual storage. |
| // TODO Better implementation. No empty logic! |
| symbolPath.useStyle(extend({}, symbolStyle)); |
| } else { |
| symbolPath.useStyle(symbolStyle); |
| } // Disable decal because symbol scale will been applied on the decal. |
| |
| |
| symbolPath.style.decal = null; |
| symbolPath.setColor(visualColor, opts && opts.symbolInnerColor); |
| symbolPath.style.strokeNoScale = true; |
| } |
| |
| var liftZ = data.getItemVisual(idx, 'liftZ'); |
| var z2Origin = this._z2; |
| |
| if (liftZ != null) { |
| if (z2Origin == null) { |
| this._z2 = symbolPath.z2; |
| symbolPath.z2 += liftZ; |
| } |
| } else if (z2Origin != null) { |
| symbolPath.z2 = z2Origin; |
| this._z2 = null; |
| } |
| |
| var useNameLabel = opts && opts.useNameLabel; |
| setLabelStyle(symbolPath, labelStatesModels, { |
| labelFetcher: seriesModel, |
| labelDataIndex: idx, |
| defaultText: getLabelDefaultText, |
| inheritColor: visualColor, |
| defaultOpacity: symbolStyle.opacity |
| }); // Do not execute util needed. |
| |
| function getLabelDefaultText(idx) { |
| return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx); |
| } |
| |
| this._sizeX = symbolSize[0] / 2; |
| this._sizeY = symbolSize[1] / 2; |
| var emphasisState = symbolPath.ensureState('emphasis'); |
| emphasisState.style = emphasisItemStyle; |
| symbolPath.ensureState('select').style = selectItemStyle; |
| symbolPath.ensureState('blur').style = blurItemStyle; // null / undefined / true means to use default strategy. |
| // 0 / false / negative number / NaN / Infinity means no scale. |
| |
| var scaleRatio = hoverScale == null || hoverScale === true ? Math.max(1.1, 3 / this._sizeY) // PENDING: restrict hoverScale > 1? It seems unreasonable to scale down |
| : isFinite(hoverScale) && hoverScale > 0 ? +hoverScale : 1; // always set scale to allow resetting |
| |
| emphasisState.scaleX = this._sizeX * scaleRatio; |
| emphasisState.scaleY = this._sizeY * scaleRatio; |
| this.setSymbolScale(1); |
| toggleHoverEmphasis(this, focus, blurScope, emphasisDisabled); |
| }; |
| |
| Symbol.prototype.setSymbolScale = function (scale) { |
| this.scaleX = this.scaleY = scale; |
| }; |
| |
| Symbol.prototype.fadeOut = function (cb, seriesModel, opt) { |
| var symbolPath = this.childAt(0); |
| var dataIndex = getECData(this).dataIndex; |
| var animationOpt = opt && opt.animation; // Avoid mistaken hover when fading out |
| |
| this.silent = symbolPath.silent = true; // Not show text when animating |
| |
| if (opt && opt.fadeLabel) { |
| var textContent = symbolPath.getTextContent(); |
| |
| if (textContent) { |
| removeElement(textContent, { |
| style: { |
| opacity: 0 |
| } |
| }, seriesModel, { |
| dataIndex: dataIndex, |
| removeOpt: animationOpt, |
| cb: function () { |
| symbolPath.removeTextContent(); |
| } |
| }); |
| } |
| } else { |
| symbolPath.removeTextContent(); |
| } |
| |
| removeElement(symbolPath, { |
| style: { |
| opacity: 0 |
| }, |
| scaleX: 0, |
| scaleY: 0 |
| }, seriesModel, { |
| dataIndex: dataIndex, |
| cb: cb, |
| removeOpt: animationOpt |
| }); |
| }; |
| |
| Symbol.getSymbolSize = function (data, idx) { |
| return normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize')); |
| }; |
| |
| return Symbol; |
| }(Group); |
| |
| function driftSymbol(dx, dy) { |
| this.parent.drift(dx, dy); |
| } |
| |
| function symbolNeedsDraw(data, point, idx, opt) { |
| return point && !isNaN(point[0]) && !isNaN(point[1]) && !(opt.isIgnore && opt.isIgnore(idx)) // We do not set clipShape on group, because it will cut part of |
| // the symbol element shape. We use the same clip shape here as |
| // the line clip. |
| && !(opt.clipShape && !opt.clipShape.contain(point[0], point[1])) && data.getItemVisual(idx, 'symbol') !== 'none'; |
| } |
| |
| function normalizeUpdateOpt(opt) { |
| if (opt != null && !isObject(opt)) { |
| opt = { |
| isIgnore: opt |
| }; |
| } |
| |
| return opt || {}; |
| } |
| |
| function makeSeriesScope(data) { |
| var seriesModel = data.hostModel; |
| var emphasisModel = seriesModel.getModel('emphasis'); |
| return { |
| emphasisItemStyle: emphasisModel.getModel('itemStyle').getItemStyle(), |
| blurItemStyle: seriesModel.getModel(['blur', 'itemStyle']).getItemStyle(), |
| selectItemStyle: seriesModel.getModel(['select', 'itemStyle']).getItemStyle(), |
| focus: emphasisModel.get('focus'), |
| blurScope: emphasisModel.get('blurScope'), |
| emphasisDisabled: emphasisModel.get('disabled'), |
| hoverScale: emphasisModel.get('scale'), |
| labelStatesModels: getLabelStatesModels(seriesModel), |
| cursorStyle: seriesModel.get('cursor') |
| }; |
| } |
| |
| var SymbolDraw = |
| /** @class */ |
| function () { |
| function SymbolDraw(SymbolCtor) { |
| this.group = new Group(); |
| this._SymbolCtor = SymbolCtor || Symbol; |
| } |
| /** |
| * Update symbols draw by new data |
| */ |
| |
| |
| SymbolDraw.prototype.updateData = function (data, opt) { |
| // Remove progressive els. |
| this._progressiveEls = null; |
| opt = normalizeUpdateOpt(opt); |
| var group = this.group; |
| var seriesModel = data.hostModel; |
| var oldData = this._data; |
| var SymbolCtor = this._SymbolCtor; |
| var disableAnimation = opt.disableAnimation; |
| var seriesScope = makeSeriesScope(data); |
| var symbolUpdateOpt = { |
| disableAnimation: disableAnimation |
| }; |
| |
| var getSymbolPoint = opt.getSymbolPoint || function (idx) { |
| return data.getItemLayout(idx); |
| }; // There is no oldLineData only when first rendering or switching from |
| // stream mode to normal mode, where previous elements should be removed. |
| |
| |
| if (!oldData) { |
| group.removeAll(); |
| } |
| |
| data.diff(oldData).add(function (newIdx) { |
| var point = getSymbolPoint(newIdx); |
| |
| if (symbolNeedsDraw(data, point, newIdx, opt)) { |
| var symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt); |
| symbolEl.setPosition(point); |
| data.setItemGraphicEl(newIdx, symbolEl); |
| group.add(symbolEl); |
| } |
| }).update(function (newIdx, oldIdx) { |
| var symbolEl = oldData.getItemGraphicEl(oldIdx); |
| var point = getSymbolPoint(newIdx); |
| |
| if (!symbolNeedsDraw(data, point, newIdx, opt)) { |
| group.remove(symbolEl); |
| return; |
| } |
| |
| var newSymbolType = data.getItemVisual(newIdx, 'symbol') || 'circle'; |
| var oldSymbolType = symbolEl && symbolEl.getSymbolType && symbolEl.getSymbolType(); |
| |
| if (!symbolEl // Create a new if symbol type changed. |
| || oldSymbolType && oldSymbolType !== newSymbolType) { |
| group.remove(symbolEl); |
| symbolEl = new SymbolCtor(data, newIdx, seriesScope, symbolUpdateOpt); |
| symbolEl.setPosition(point); |
| } else { |
| symbolEl.updateData(data, newIdx, seriesScope, symbolUpdateOpt); |
| var target = { |
| x: point[0], |
| y: point[1] |
| }; |
| disableAnimation ? symbolEl.attr(target) : updateProps(symbolEl, target, seriesModel); |
| } // Add back |
| |
| |
| group.add(symbolEl); |
| data.setItemGraphicEl(newIdx, symbolEl); |
| }).remove(function (oldIdx) { |
| var el = oldData.getItemGraphicEl(oldIdx); |
| el && el.fadeOut(function () { |
| group.remove(el); |
| }, seriesModel); |
| }).execute(); |
| this._getSymbolPoint = getSymbolPoint; |
| this._data = data; |
| }; |
| |
| SymbolDraw.prototype.updateLayout = function () { |
| var _this = this; |
| |
| var data = this._data; |
| |
| if (data) { |
| // Not use animation |
| data.eachItemGraphicEl(function (el, idx) { |
| var point = _this._getSymbolPoint(idx); |
| |
| el.setPosition(point); |
| el.markRedraw(); |
| }); |
| } |
| }; |
| |
| SymbolDraw.prototype.incrementalPrepareUpdate = function (data) { |
| this._seriesScope = makeSeriesScope(data); |
| this._data = null; |
| this.group.removeAll(); |
| }; |
| /** |
| * Update symbols draw by new data |
| */ |
| |
| SymbolDraw.prototype.incrementalUpdate = function (taskParams, data, opt) { |
| // Clear |
| this._progressiveEls = []; |
| opt = normalizeUpdateOpt(opt); |
| |
| function updateIncrementalAndHover(el) { |
| if (!el.isGroup) { |
| el.incremental = true; |
| el.ensureState('emphasis').hoverLayer = true; |
| } |
| } |
| |
| for (var idx = taskParams.start; idx < taskParams.end; idx++) { |
| var point = data.getItemLayout(idx); |
| |
| if (symbolNeedsDraw(data, point, idx, opt)) { |
| var el = new this._SymbolCtor(data, idx, this._seriesScope); |
| el.traverse(updateIncrementalAndHover); |
| el.setPosition(point); |
| this.group.add(el); |
| data.setItemGraphicEl(idx, el); |
| |
| this._progressiveEls.push(el); |
| } |
| } |
| }; |
| |
| SymbolDraw.prototype.eachRendered = function (cb) { |
| traverseElements(this._progressiveEls || this.group, cb); |
| }; |
| |
| SymbolDraw.prototype.remove = function (enableAnimation) { |
| var group = this.group; |
| var data = this._data; // Incremental model do not have this._data. |
| |
| if (data && enableAnimation) { |
| data.eachItemGraphicEl(function (el) { |
| el.fadeOut(function () { |
| group.remove(el); |
| }, data.hostModel); |
| }); |
| } else { |
| group.removeAll(); |
| } |
| }; |
| return SymbolDraw; |
| }(); |
| |
| function prepareDataCoordInfo(coordSys, data, valueOrigin) { |
| var baseAxis = coordSys.getBaseAxis(); |
| var valueAxis = coordSys.getOtherAxis(baseAxis); |
| var valueStart = getValueStart(valueAxis, valueOrigin); |
| var baseAxisDim = baseAxis.dim; |
| var valueAxisDim = valueAxis.dim; |
| var valueDim = data.mapDimension(valueAxisDim); |
| var baseDim = data.mapDimension(baseAxisDim); |
| var baseDataOffset = valueAxisDim === 'x' || valueAxisDim === 'radius' ? 1 : 0; |
| var dims = map(coordSys.dimensions, function (coordDim) { |
| return data.mapDimension(coordDim); |
| }); |
| var stacked = false; |
| var stackResultDim = data.getCalculationInfo('stackResultDimension'); |
| |
| if (isDimensionStacked(data, dims[0] |
| /* , dims[1] */ |
| )) { |
| // jshint ignore:line |
| stacked = true; |
| dims[0] = stackResultDim; |
| } |
| |
| if (isDimensionStacked(data, dims[1] |
| /* , dims[0] */ |
| )) { |
| // jshint ignore:line |
| stacked = true; |
| dims[1] = stackResultDim; |
| } |
| |
| return { |
| dataDimsForPoint: dims, |
| valueStart: valueStart, |
| valueAxisDim: valueAxisDim, |
| baseAxisDim: baseAxisDim, |
| stacked: !!stacked, |
| valueDim: valueDim, |
| baseDim: baseDim, |
| baseDataOffset: baseDataOffset, |
| stackedOverDimension: data.getCalculationInfo('stackedOverDimension') |
| }; |
| } |
| |
| function getValueStart(valueAxis, valueOrigin) { |
| var valueStart = 0; |
| var extent = valueAxis.scale.getExtent(); |
| |
| if (valueOrigin === 'start') { |
| valueStart = extent[0]; |
| } else if (valueOrigin === 'end') { |
| valueStart = extent[1]; |
| } // If origin is specified as a number, use it as |
| // valueStart directly |
| else if (isNumber(valueOrigin) && !isNaN(valueOrigin)) { |
| valueStart = valueOrigin; |
| } // auto |
| else { |
| // Both positive |
| if (extent[0] > 0) { |
| valueStart = extent[0]; |
| } // Both negative |
| else if (extent[1] < 0) { |
| valueStart = extent[1]; |
| } // If is one positive, and one negative, onZero shall be true |
| |
| } |
| |
| return valueStart; |
| } |
| |
| function getStackedOnPoint(dataCoordInfo, coordSys, data, idx) { |
| var value = NaN; |
| |
| if (dataCoordInfo.stacked) { |
| value = data.get(data.getCalculationInfo('stackedOverDimension'), idx); |
| } |
| |
| if (isNaN(value)) { |
| value = dataCoordInfo.valueStart; |
| } |
| |
| var baseDataOffset = dataCoordInfo.baseDataOffset; |
| var stackedData = []; |
| stackedData[baseDataOffset] = data.get(dataCoordInfo.baseDim, idx); |
| stackedData[1 - baseDataOffset] = value; |
| return coordSys.dataToPoint(stackedData); |
| } |
| |
| function diffData(oldData, newData) { |
| var diffResult = []; |
| newData.diff(oldData).add(function (idx) { |
| diffResult.push({ |
| cmd: '+', |
| idx: idx |
| }); |
| }).update(function (newIdx, oldIdx) { |
| diffResult.push({ |
| cmd: '=', |
| idx: oldIdx, |
| idx1: newIdx |
| }); |
| }).remove(function (idx) { |
| diffResult.push({ |
| cmd: '-', |
| idx: idx |
| }); |
| }).execute(); |
| return diffResult; |
| } |
| |
| function lineAnimationDiff(oldData, newData, oldStackedOnPoints, newStackedOnPoints, oldCoordSys, newCoordSys, oldValueOrigin, newValueOrigin) { |
| var diff = diffData(oldData, newData); // let newIdList = newData.mapArray(newData.getId); |
| // let oldIdList = oldData.mapArray(oldData.getId); |
| // convertToIntId(newIdList, oldIdList); |
| // // FIXME One data ? |
| // diff = arrayDiff(oldIdList, newIdList); |
| |
| var currPoints = []; |
| var nextPoints = []; // Points for stacking base line |
| |
| var currStackedPoints = []; |
| var nextStackedPoints = []; |
| var status = []; |
| var sortedIndices = []; |
| var rawIndices = []; |
| var newDataOldCoordInfo = prepareDataCoordInfo(oldCoordSys, newData, oldValueOrigin); // const oldDataNewCoordInfo = prepareDataCoordInfo(newCoordSys, oldData, newValueOrigin); |
| |
| var oldPoints = oldData.getLayout('points') || []; |
| var newPoints = newData.getLayout('points') || []; |
| |
| for (var i = 0; i < diff.length; i++) { |
| var diffItem = diff[i]; |
| var pointAdded = true; |
| var oldIdx2 = void 0; |
| var newIdx2 = void 0; // FIXME, animation is not so perfect when dataZoom window moves fast |
| // Which is in case remvoing or add more than one data in the tail or head |
| |
| switch (diffItem.cmd) { |
| case '=': |
| oldIdx2 = diffItem.idx * 2; |
| newIdx2 = diffItem.idx1 * 2; |
| var currentX = oldPoints[oldIdx2]; |
| var currentY = oldPoints[oldIdx2 + 1]; |
| var nextX = newPoints[newIdx2]; |
| var nextY = newPoints[newIdx2 + 1]; // If previous data is NaN, use next point directly |
| |
| if (isNaN(currentX) || isNaN(currentY)) { |
| currentX = nextX; |
| currentY = nextY; |
| } |
| |
| currPoints.push(currentX, currentY); |
| nextPoints.push(nextX, nextY); |
| currStackedPoints.push(oldStackedOnPoints[oldIdx2], oldStackedOnPoints[oldIdx2 + 1]); |
| nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]); |
| rawIndices.push(newData.getRawIndex(diffItem.idx1)); |
| break; |
| |
| case '+': |
| var newIdx = diffItem.idx; |
| var newDataDimsForPoint = newDataOldCoordInfo.dataDimsForPoint; |
| var oldPt = oldCoordSys.dataToPoint([newData.get(newDataDimsForPoint[0], newIdx), newData.get(newDataDimsForPoint[1], newIdx)]); |
| newIdx2 = newIdx * 2; |
| currPoints.push(oldPt[0], oldPt[1]); |
| nextPoints.push(newPoints[newIdx2], newPoints[newIdx2 + 1]); |
| var stackedOnPoint = getStackedOnPoint(newDataOldCoordInfo, oldCoordSys, newData, newIdx); |
| currStackedPoints.push(stackedOnPoint[0], stackedOnPoint[1]); |
| nextStackedPoints.push(newStackedOnPoints[newIdx2], newStackedOnPoints[newIdx2 + 1]); |
| rawIndices.push(newData.getRawIndex(newIdx)); |
| break; |
| |
| case '-': |
| pointAdded = false; |
| } // Original indices |
| |
| |
| if (pointAdded) { |
| status.push(diffItem); |
| sortedIndices.push(sortedIndices.length); |
| } |
| } // Diff result may be crossed if all items are changed |
| // Sort by data index |
| |
| |
| sortedIndices.sort(function (a, b) { |
| return rawIndices[a] - rawIndices[b]; |
| }); |
| var len = currPoints.length; |
| var sortedCurrPoints = createFloat32Array(len); |
| var sortedNextPoints = createFloat32Array(len); |
| var sortedCurrStackedPoints = createFloat32Array(len); |
| var sortedNextStackedPoints = createFloat32Array(len); |
| var sortedStatus = []; |
| |
| for (var i = 0; i < sortedIndices.length; i++) { |
| var idx = sortedIndices[i]; |
| var i2 = i * 2; |
| var idx2 = idx * 2; |
| sortedCurrPoints[i2] = currPoints[idx2]; |
| sortedCurrPoints[i2 + 1] = currPoints[idx2 + 1]; |
| sortedNextPoints[i2] = nextPoints[idx2]; |
| sortedNextPoints[i2 + 1] = nextPoints[idx2 + 1]; |
| sortedCurrStackedPoints[i2] = currStackedPoints[idx2]; |
| sortedCurrStackedPoints[i2 + 1] = currStackedPoints[idx2 + 1]; |
| sortedNextStackedPoints[i2] = nextStackedPoints[idx2]; |
| sortedNextStackedPoints[i2 + 1] = nextStackedPoints[idx2 + 1]; |
| sortedStatus[i] = status[idx]; |
| } |
| |
| return { |
| current: sortedCurrPoints, |
| next: sortedNextPoints, |
| stackedOnCurrent: sortedCurrStackedPoints, |
| stackedOnNext: sortedNextStackedPoints, |
| status: sortedStatus |
| }; |
| } |
| |
| var mathMin$5 = Math.min; |
| var mathMax$5 = Math.max; |
| |
| function isPointNull(x, y) { |
| return isNaN(x) || isNaN(y); |
| } |
| /** |
| * Draw smoothed line in non-monotone, in may cause undesired curve in extreme |
| * situations. This should be used when points are non-monotone neither in x or |
| * y dimension. |
| */ |
| |
| |
| function drawSegment(ctx, points, start, segLen, allLen, dir, smooth, smoothMonotone, connectNulls) { |
| var prevX; |
| var prevY; |
| var cpx0; |
| var cpy0; |
| var cpx1; |
| var cpy1; |
| var idx = start; |
| var k = 0; |
| |
| for (; k < segLen; k++) { |
| var x = points[idx * 2]; |
| var y = points[idx * 2 + 1]; |
| |
| if (idx >= allLen || idx < 0) { |
| break; |
| } |
| |
| if (isPointNull(x, y)) { |
| if (connectNulls) { |
| idx += dir; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (idx === start) { |
| ctx[dir > 0 ? 'moveTo' : 'lineTo'](x, y); |
| cpx0 = x; |
| cpy0 = y; |
| } else { |
| var dx = x - prevX; |
| var dy = y - prevY; // Ignore tiny segment. |
| |
| if (dx * dx + dy * dy < 0.5) { |
| idx += dir; |
| continue; |
| } |
| |
| if (smooth > 0) { |
| var nextIdx = idx + dir; |
| var nextX = points[nextIdx * 2]; |
| var nextY = points[nextIdx * 2 + 1]; // Ignore duplicate point |
| |
| while (nextX === x && nextY === y && k < segLen) { |
| k++; |
| nextIdx += dir; |
| idx += dir; |
| nextX = points[nextIdx * 2]; |
| nextY = points[nextIdx * 2 + 1]; |
| x = points[idx * 2]; |
| y = points[idx * 2 + 1]; |
| dx = x - prevX; |
| dy = y - prevY; |
| } |
| |
| var tmpK = k + 1; |
| |
| if (connectNulls) { |
| // Find next point not null |
| while (isPointNull(nextX, nextY) && tmpK < segLen) { |
| tmpK++; |
| nextIdx += dir; |
| nextX = points[nextIdx * 2]; |
| nextY = points[nextIdx * 2 + 1]; |
| } |
| } |
| |
| var ratioNextSeg = 0.5; |
| var vx = 0; |
| var vy = 0; |
| var nextCpx0 = void 0; |
| var nextCpy0 = void 0; // Is last point |
| |
| if (tmpK >= segLen || isPointNull(nextX, nextY)) { |
| cpx1 = x; |
| cpy1 = y; |
| } else { |
| vx = nextX - prevX; |
| vy = nextY - prevY; |
| var dx0 = x - prevX; |
| var dx1 = nextX - x; |
| var dy0 = y - prevY; |
| var dy1 = nextY - y; |
| var lenPrevSeg = void 0; |
| var lenNextSeg = void 0; |
| |
| if (smoothMonotone === 'x') { |
| lenPrevSeg = Math.abs(dx0); |
| lenNextSeg = Math.abs(dx1); |
| var dir_1 = vx > 0 ? 1 : -1; |
| cpx1 = x - dir_1 * lenPrevSeg * smooth; |
| cpy1 = y; |
| nextCpx0 = x + dir_1 * lenNextSeg * smooth; |
| nextCpy0 = y; |
| } else if (smoothMonotone === 'y') { |
| lenPrevSeg = Math.abs(dy0); |
| lenNextSeg = Math.abs(dy1); |
| var dir_2 = vy > 0 ? 1 : -1; |
| cpx1 = x; |
| cpy1 = y - dir_2 * lenPrevSeg * smooth; |
| nextCpx0 = x; |
| nextCpy0 = y + dir_2 * lenNextSeg * smooth; |
| } else { |
| lenPrevSeg = Math.sqrt(dx0 * dx0 + dy0 * dy0); |
| lenNextSeg = Math.sqrt(dx1 * dx1 + dy1 * dy1); // Use ratio of seg length |
| |
| ratioNextSeg = lenNextSeg / (lenNextSeg + lenPrevSeg); |
| cpx1 = x - vx * smooth * (1 - ratioNextSeg); |
| cpy1 = y - vy * smooth * (1 - ratioNextSeg); // cp0 of next segment |
| |
| nextCpx0 = x + vx * smooth * ratioNextSeg; |
| nextCpy0 = y + vy * smooth * ratioNextSeg; // Smooth constraint between point and next point. |
| // Avoid exceeding extreme after smoothing. |
| |
| nextCpx0 = mathMin$5(nextCpx0, mathMax$5(nextX, x)); |
| nextCpy0 = mathMin$5(nextCpy0, mathMax$5(nextY, y)); |
| nextCpx0 = mathMax$5(nextCpx0, mathMin$5(nextX, x)); |
| nextCpy0 = mathMax$5(nextCpy0, mathMin$5(nextY, y)); // Reclaculate cp1 based on the adjusted cp0 of next seg. |
| |
| vx = nextCpx0 - x; |
| vy = nextCpy0 - y; |
| cpx1 = x - vx * lenPrevSeg / lenNextSeg; |
| cpy1 = y - vy * lenPrevSeg / lenNextSeg; // Smooth constraint between point and prev point. |
| // Avoid exceeding extreme after smoothing. |
| |
| cpx1 = mathMin$5(cpx1, mathMax$5(prevX, x)); |
| cpy1 = mathMin$5(cpy1, mathMax$5(prevY, y)); |
| cpx1 = mathMax$5(cpx1, mathMin$5(prevX, x)); |
| cpy1 = mathMax$5(cpy1, mathMin$5(prevY, y)); // Adjust next cp0 again. |
| |
| vx = x - cpx1; |
| vy = y - cpy1; |
| nextCpx0 = x + vx * lenNextSeg / lenPrevSeg; |
| nextCpy0 = y + vy * lenNextSeg / lenPrevSeg; |
| } |
| } |
| |
| ctx.bezierCurveTo(cpx0, cpy0, cpx1, cpy1, x, y); |
| cpx0 = nextCpx0; |
| cpy0 = nextCpy0; |
| } else { |
| ctx.lineTo(x, y); |
| } |
| } |
| |
| prevX = x; |
| prevY = y; |
| idx += dir; |
| } |
| |
| return k; |
| } |
| |
| var ECPolylineShape = |
| /** @class */ |
| function () { |
| function ECPolylineShape() { |
| this.smooth = 0; |
| this.smoothConstraint = true; |
| } |
| |
| return ECPolylineShape; |
| }(); |
| |
| var ECPolyline = |
| /** @class */ |
| function (_super) { |
| __extends(ECPolyline, _super); |
| |
| function ECPolyline(opts) { |
| var _this = _super.call(this, opts) || this; |
| |
| _this.type = 'ec-polyline'; |
| return _this; |
| } |
| |
| ECPolyline.prototype.getDefaultStyle = function () { |
| return { |
| stroke: '#000', |
| fill: null |
| }; |
| }; |
| |
| ECPolyline.prototype.getDefaultShape = function () { |
| return new ECPolylineShape(); |
| }; |
| |
| ECPolyline.prototype.buildPath = function (ctx, shape) { |
| var points = shape.points; |
| var i = 0; |
| var len = points.length / 2; // const result = getBoundingBox(points, shape.smoothConstraint); |
| |
| if (shape.connectNulls) { |
| // Must remove first and last null values avoid draw error in polygon |
| for (; len > 0; len--) { |
| if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) { |
| break; |
| } |
| } |
| |
| for (; i < len; i++) { |
| if (!isPointNull(points[i * 2], points[i * 2 + 1])) { |
| break; |
| } |
| } |
| } |
| |
| while (i < len) { |
| i += drawSegment(ctx, points, i, len, len, 1, shape.smooth, shape.smoothMonotone, shape.connectNulls) + 1; |
| } |
| }; |
| |
| ECPolyline.prototype.getPointOn = function (xOrY, dim) { |
| if (!this.path) { |
| this.createPathProxy(); |
| this.buildPath(this.path, this.shape); |
| } |
| |
| var path = this.path; |
| var data = path.data; |
| var CMD = PathProxy.CMD; |
| var x0; |
| var y0; |
| var isDimX = dim === 'x'; |
| var roots = []; |
| |
| for (var i = 0; i < data.length;) { |
| var cmd = data[i++]; |
| var x = void 0; |
| var y = void 0; |
| var x2 = void 0; |
| var y2 = void 0; |
| var x3 = void 0; |
| var y3 = void 0; |
| var t = void 0; |
| |
| switch (cmd) { |
| case CMD.M: |
| x0 = data[i++]; |
| y0 = data[i++]; |
| break; |
| |
| case CMD.L: |
| x = data[i++]; |
| y = data[i++]; |
| t = isDimX ? (xOrY - x0) / (x - x0) : (xOrY - y0) / (y - y0); |
| |
| if (t <= 1 && t >= 0) { |
| var val = isDimX ? (y - y0) * t + y0 : (x - x0) * t + x0; |
| return isDimX ? [xOrY, val] : [val, xOrY]; |
| } |
| |
| x0 = x; |
| y0 = y; |
| break; |
| |
| case CMD.C: |
| x = data[i++]; |
| y = data[i++]; |
| x2 = data[i++]; |
| y2 = data[i++]; |
| x3 = data[i++]; |
| y3 = data[i++]; |
| var nRoot = isDimX ? cubicRootAt(x0, x, x2, x3, xOrY, roots) : cubicRootAt(y0, y, y2, y3, xOrY, roots); |
| |
| if (nRoot > 0) { |
| for (var i_1 = 0; i_1 < nRoot; i_1++) { |
| var t_1 = roots[i_1]; |
| |
| if (t_1 <= 1 && t_1 >= 0) { |
| var val = isDimX ? cubicAt(y0, y, y2, y3, t_1) : cubicAt(x0, x, x2, x3, t_1); |
| return isDimX ? [xOrY, val] : [val, xOrY]; |
| } |
| } |
| } |
| |
| x0 = x3; |
| y0 = y3; |
| break; |
| } |
| } |
| }; |
| |
| return ECPolyline; |
| }(Path); |
| |
| var ECPolygonShape = |
| /** @class */ |
| function (_super) { |
| __extends(ECPolygonShape, _super); |
| |
| function ECPolygonShape() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| return ECPolygonShape; |
| }(ECPolylineShape); |
| |
| var ECPolygon = |
| /** @class */ |
| function (_super) { |
| __extends(ECPolygon, _super); |
| |
| function ECPolygon(opts) { |
| var _this = _super.call(this, opts) || this; |
| |
| _this.type = 'ec-polygon'; |
| return _this; |
| } |
| |
| ECPolygon.prototype.getDefaultShape = function () { |
| return new ECPolygonShape(); |
| }; |
| |
| ECPolygon.prototype.buildPath = function (ctx, shape) { |
| var points = shape.points; |
| var stackedOnPoints = shape.stackedOnPoints; |
| var i = 0; |
| var len = points.length / 2; |
| var smoothMonotone = shape.smoothMonotone; |
| |
| if (shape.connectNulls) { |
| // Must remove first and last null values avoid draw error in polygon |
| for (; len > 0; len--) { |
| if (!isPointNull(points[len * 2 - 2], points[len * 2 - 1])) { |
| break; |
| } |
| } |
| |
| for (; i < len; i++) { |
| if (!isPointNull(points[i * 2], points[i * 2 + 1])) { |
| break; |
| } |
| } |
| } |
| |
| while (i < len) { |
| var k = drawSegment(ctx, points, i, len, len, 1, shape.smooth, smoothMonotone, shape.connectNulls); |
| drawSegment(ctx, stackedOnPoints, i + k - 1, k, len, -1, shape.stackedOnSmooth, smoothMonotone, shape.connectNulls); |
| i += k + 1; |
| ctx.closePath(); |
| } |
| }; |
| |
| return ECPolygon; |
| }(Path); |
| |
| function createGridClipPath(cartesian, hasAnimation, seriesModel, done, during) { |
| var rect = cartesian.getArea(); |
| var x = rect.x; |
| var y = rect.y; |
| var width = rect.width; |
| var height = rect.height; |
| var lineWidth = seriesModel.get(['lineStyle', 'width']) || 2; // Expand the clip path a bit to avoid the border is clipped and looks thinner |
| |
| x -= lineWidth / 2; |
| y -= lineWidth / 2; |
| width += lineWidth; |
| height += lineWidth; // fix: https://github.com/apache/incubator-echarts/issues/11369 |
| |
| x = Math.floor(x); |
| width = Math.round(width); |
| var clipPath = new Rect({ |
| shape: { |
| x: x, |
| y: y, |
| width: width, |
| height: height |
| } |
| }); |
| |
| if (hasAnimation) { |
| var baseAxis = cartesian.getBaseAxis(); |
| var isHorizontal = baseAxis.isHorizontal(); |
| var isAxisInversed = baseAxis.inverse; |
| |
| if (isHorizontal) { |
| if (isAxisInversed) { |
| clipPath.shape.x += width; |
| } |
| |
| clipPath.shape.width = 0; |
| } else { |
| if (!isAxisInversed) { |
| clipPath.shape.y += height; |
| } |
| |
| clipPath.shape.height = 0; |
| } |
| |
| var duringCb = isFunction(during) ? function (percent) { |
| during(percent, clipPath); |
| } : null; |
| initProps(clipPath, { |
| shape: { |
| width: width, |
| height: height, |
| x: x, |
| y: y |
| } |
| }, seriesModel, null, done, duringCb); |
| } |
| |
| return clipPath; |
| } |
| |
| function createPolarClipPath(polar, hasAnimation, seriesModel) { |
| var sectorArea = polar.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent. |
| |
| var r0 = round(sectorArea.r0, 1); |
| var r = round(sectorArea.r, 1); |
| var clipPath = new Sector({ |
| shape: { |
| cx: round(polar.cx, 1), |
| cy: round(polar.cy, 1), |
| r0: r0, |
| r: r, |
| startAngle: sectorArea.startAngle, |
| endAngle: sectorArea.endAngle, |
| clockwise: sectorArea.clockwise |
| } |
| }); |
| |
| if (hasAnimation) { |
| var isRadial = polar.getBaseAxis().dim === 'angle'; |
| |
| if (isRadial) { |
| clipPath.shape.endAngle = sectorArea.startAngle; |
| } else { |
| clipPath.shape.r = r0; |
| } |
| |
| initProps(clipPath, { |
| shape: { |
| endAngle: sectorArea.endAngle, |
| r: r |
| } |
| }, seriesModel); |
| } |
| |
| return clipPath; |
| } |
| |
| function createClipPath(coordSys, hasAnimation, seriesModel, done, during) { |
| if (!coordSys) { |
| return null; |
| } else if (coordSys.type === 'polar') { |
| return createPolarClipPath(coordSys, hasAnimation, seriesModel); |
| } else if (coordSys.type === 'cartesian2d') { |
| return createGridClipPath(coordSys, hasAnimation, seriesModel, done, during); |
| } |
| |
| return null; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| function isCoordinateSystemType(coordSys, type) { |
| return coordSys.type === type; |
| } |
| |
| function isPointsSame(points1, points2) { |
| if (points1.length !== points2.length) { |
| return; |
| } |
| |
| for (var i = 0; i < points1.length; i++) { |
| if (points1[i] !== points2[i]) { |
| return; |
| } |
| } |
| |
| return true; |
| } |
| |
| function bboxFromPoints(points) { |
| var minX = Infinity; |
| var minY = Infinity; |
| var maxX = -Infinity; |
| var maxY = -Infinity; |
| |
| for (var i = 0; i < points.length;) { |
| var x = points[i++]; |
| var y = points[i++]; |
| |
| if (!isNaN(x)) { |
| minX = Math.min(x, minX); |
| maxX = Math.max(x, maxX); |
| } |
| |
| if (!isNaN(y)) { |
| minY = Math.min(y, minY); |
| maxY = Math.max(y, maxY); |
| } |
| } |
| |
| return [[minX, minY], [maxX, maxY]]; |
| } |
| |
| function getBoundingDiff(points1, points2) { |
| var _a = bboxFromPoints(points1), |
| min1 = _a[0], |
| max1 = _a[1]; |
| |
| var _b = bboxFromPoints(points2), |
| min2 = _b[0], |
| max2 = _b[1]; // Get a max value from each corner of two boundings. |
| |
| |
| return Math.max(Math.abs(min1[0] - min2[0]), Math.abs(min1[1] - min2[1]), Math.abs(max1[0] - max2[0]), Math.abs(max1[1] - max2[1])); |
| } |
| |
| function getSmooth(smooth) { |
| return isNumber(smooth) ? smooth : smooth ? 0.5 : 0; |
| } |
| |
| function getStackedOnPoints(coordSys, data, dataCoordInfo) { |
| if (!dataCoordInfo.valueDim) { |
| return []; |
| } |
| |
| var len = data.count(); |
| var points = createFloat32Array(len * 2); |
| |
| for (var idx = 0; idx < len; idx++) { |
| var pt = getStackedOnPoint(dataCoordInfo, coordSys, data, idx); |
| points[idx * 2] = pt[0]; |
| points[idx * 2 + 1] = pt[1]; |
| } |
| |
| return points; |
| } |
| |
| function turnPointsIntoStep(points, coordSys, stepTurnAt, connectNulls) { |
| var baseAxis = coordSys.getBaseAxis(); |
| var baseIndex = baseAxis.dim === 'x' || baseAxis.dim === 'radius' ? 0 : 1; |
| var stepPoints = []; |
| var i = 0; |
| var stepPt = []; |
| var pt = []; |
| var nextPt = []; |
| var filteredPoints = []; |
| |
| if (connectNulls) { |
| for (i = 0; i < points.length; i += 2) { |
| if (!isNaN(points[i]) && !isNaN(points[i + 1])) { |
| filteredPoints.push(points[i], points[i + 1]); |
| } |
| } |
| |
| points = filteredPoints; |
| } |
| |
| for (i = 0; i < points.length - 2; i += 2) { |
| nextPt[0] = points[i + 2]; |
| nextPt[1] = points[i + 3]; |
| pt[0] = points[i]; |
| pt[1] = points[i + 1]; |
| stepPoints.push(pt[0], pt[1]); |
| |
| switch (stepTurnAt) { |
| case 'end': |
| stepPt[baseIndex] = nextPt[baseIndex]; |
| stepPt[1 - baseIndex] = pt[1 - baseIndex]; |
| stepPoints.push(stepPt[0], stepPt[1]); |
| break; |
| |
| case 'middle': |
| var middle = (pt[baseIndex] + nextPt[baseIndex]) / 2; |
| var stepPt2 = []; |
| stepPt[baseIndex] = stepPt2[baseIndex] = middle; |
| stepPt[1 - baseIndex] = pt[1 - baseIndex]; |
| stepPt2[1 - baseIndex] = nextPt[1 - baseIndex]; |
| stepPoints.push(stepPt[0], stepPt[1]); |
| stepPoints.push(stepPt2[0], stepPt2[1]); |
| break; |
| |
| default: |
| // default is start |
| stepPt[baseIndex] = pt[baseIndex]; |
| stepPt[1 - baseIndex] = nextPt[1 - baseIndex]; |
| stepPoints.push(stepPt[0], stepPt[1]); |
| } |
| } // Last points |
| |
| |
| stepPoints.push(points[i++], points[i++]); |
| return stepPoints; |
| } |
| /** |
| * Clip color stops to edge. Avoid creating too large gradients. |
| * Which may lead to blurry when GPU acceleration is enabled. See #15680 |
| * |
| * The stops has been sorted from small to large. |
| */ |
| |
| |
| function clipColorStops(colorStops, maxSize) { |
| var newColorStops = []; |
| var len = colorStops.length; // coord will always < 0 in prevOutOfRangeColorStop. |
| |
| var prevOutOfRangeColorStop; |
| var prevInRangeColorStop; |
| |
| function lerpStop(stop0, stop1, clippedCoord) { |
| var coord0 = stop0.coord; |
| var p = (clippedCoord - coord0) / (stop1.coord - coord0); |
| var color = lerp$1(p, [stop0.color, stop1.color]); |
| return { |
| coord: clippedCoord, |
| color: color |
| }; |
| } |
| |
| for (var i = 0; i < len; i++) { |
| var stop_1 = colorStops[i]; |
| var coord = stop_1.coord; |
| |
| if (coord < 0) { |
| prevOutOfRangeColorStop = stop_1; |
| } else if (coord > maxSize) { |
| if (prevInRangeColorStop) { |
| newColorStops.push(lerpStop(prevInRangeColorStop, stop_1, maxSize)); |
| } else if (prevOutOfRangeColorStop) { |
| // If there are two stops and coord range is between these two stops |
| newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0), lerpStop(prevOutOfRangeColorStop, stop_1, maxSize)); |
| } // All following stop will be out of range. So just ignore them. |
| |
| |
| break; |
| } else { |
| if (prevOutOfRangeColorStop) { |
| newColorStops.push(lerpStop(prevOutOfRangeColorStop, stop_1, 0)); // Reset |
| |
| prevOutOfRangeColorStop = null; |
| } |
| |
| newColorStops.push(stop_1); |
| prevInRangeColorStop = stop_1; |
| } |
| } |
| |
| return newColorStops; |
| } |
| |
| function getVisualGradient(data, coordSys, api) { |
| var visualMetaList = data.getVisual('visualMeta'); |
| |
| if (!visualMetaList || !visualMetaList.length || !data.count()) { |
| // When data.count() is 0, gradient range can not be calculated. |
| return; |
| } |
| |
| if (coordSys.type !== 'cartesian2d') { |
| if ("development" !== 'production') { |
| console.warn('Visual map on line style is only supported on cartesian2d.'); |
| } |
| |
| return; |
| } |
| |
| var coordDim; |
| var visualMeta; |
| |
| for (var i = visualMetaList.length - 1; i >= 0; i--) { |
| var dimInfo = data.getDimensionInfo(visualMetaList[i].dimension); |
| coordDim = dimInfo && dimInfo.coordDim; // Can only be x or y |
| |
| if (coordDim === 'x' || coordDim === 'y') { |
| visualMeta = visualMetaList[i]; |
| break; |
| } |
| } |
| |
| if (!visualMeta) { |
| if ("development" !== 'production') { |
| console.warn('Visual map on line style only support x or y dimension.'); |
| } |
| |
| return; |
| } // If the area to be rendered is bigger than area defined by LinearGradient, |
| // the canvas spec prescribes that the color of the first stop and the last |
| // stop should be used. But if two stops are added at offset 0, in effect |
| // browsers use the color of the second stop to render area outside |
| // LinearGradient. So we can only infinitesimally extend area defined in |
| // LinearGradient to render `outerColors`. |
| |
| |
| var axis = coordSys.getAxis(coordDim); // dataToCoord mapping may not be linear, but must be monotonic. |
| |
| var colorStops = map(visualMeta.stops, function (stop) { |
| // offset will be calculated later. |
| return { |
| coord: axis.toGlobalCoord(axis.dataToCoord(stop.value)), |
| color: stop.color |
| }; |
| }); |
| var stopLen = colorStops.length; |
| var outerColors = visualMeta.outerColors.slice(); |
| |
| if (stopLen && colorStops[0].coord > colorStops[stopLen - 1].coord) { |
| colorStops.reverse(); |
| outerColors.reverse(); |
| } |
| |
| var colorStopsInRange = clipColorStops(colorStops, coordDim === 'x' ? api.getWidth() : api.getHeight()); |
| var inRangeStopLen = colorStopsInRange.length; |
| |
| if (!inRangeStopLen && stopLen) { |
| // All stops are out of range. All will be the same color. |
| return colorStops[0].coord < 0 ? outerColors[1] ? outerColors[1] : colorStops[stopLen - 1].color : outerColors[0] ? outerColors[0] : colorStops[0].color; |
| } |
| |
| var tinyExtent = 10; // Arbitrary value: 10px |
| |
| var minCoord = colorStopsInRange[0].coord - tinyExtent; |
| var maxCoord = colorStopsInRange[inRangeStopLen - 1].coord + tinyExtent; |
| var coordSpan = maxCoord - minCoord; |
| |
| if (coordSpan < 1e-3) { |
| return 'transparent'; |
| } |
| |
| each(colorStopsInRange, function (stop) { |
| stop.offset = (stop.coord - minCoord) / coordSpan; |
| }); |
| colorStopsInRange.push({ |
| // NOTE: inRangeStopLen may still be 0 if stoplen is zero. |
| offset: inRangeStopLen ? colorStopsInRange[inRangeStopLen - 1].offset : 0.5, |
| color: outerColors[1] || 'transparent' |
| }); |
| colorStopsInRange.unshift({ |
| offset: inRangeStopLen ? colorStopsInRange[0].offset : 0.5, |
| color: outerColors[0] || 'transparent' |
| }); |
| var gradient = new LinearGradient(0, 0, 0, 0, colorStopsInRange, true); |
| gradient[coordDim] = minCoord; |
| gradient[coordDim + '2'] = maxCoord; |
| return gradient; |
| } |
| |
| function getIsIgnoreFunc(seriesModel, data, coordSys) { |
| var showAllSymbol = seriesModel.get('showAllSymbol'); |
| var isAuto = showAllSymbol === 'auto'; |
| |
| if (showAllSymbol && !isAuto) { |
| return; |
| } |
| |
| var categoryAxis = coordSys.getAxesByScale('ordinal')[0]; |
| |
| if (!categoryAxis) { |
| return; |
| } // Note that category label interval strategy might bring some weird effect |
| // in some scenario: users may wonder why some of the symbols are not |
| // displayed. So we show all symbols as possible as we can. |
| |
| |
| if (isAuto // Simplify the logic, do not determine label overlap here. |
| && canShowAllSymbolForCategory(categoryAxis, data)) { |
| return; |
| } // Otherwise follow the label interval strategy on category axis. |
| |
| |
| var categoryDataDim = data.mapDimension(categoryAxis.dim); |
| var labelMap = {}; |
| each(categoryAxis.getViewLabels(), function (labelItem) { |
| var ordinalNumber = categoryAxis.scale.getRawOrdinalNumber(labelItem.tickValue); |
| labelMap[ordinalNumber] = 1; |
| }); |
| return function (dataIndex) { |
| return !labelMap.hasOwnProperty(data.get(categoryDataDim, dataIndex)); |
| }; |
| } |
| |
| function canShowAllSymbolForCategory(categoryAxis, data) { |
| // In most cases, line is monotonous on category axis, and the label size |
| // is close with each other. So we check the symbol size and some of the |
| // label size alone with the category axis to estimate whether all symbol |
| // can be shown without overlap. |
| var axisExtent = categoryAxis.getExtent(); |
| var availSize = Math.abs(axisExtent[1] - axisExtent[0]) / categoryAxis.scale.count(); |
| isNaN(availSize) && (availSize = 0); // 0/0 is NaN. |
| // Sampling some points, max 5. |
| |
| var dataLen = data.count(); |
| var step = Math.max(1, Math.round(dataLen / 5)); |
| |
| for (var dataIndex = 0; dataIndex < dataLen; dataIndex += step) { |
| if (Symbol.getSymbolSize(data, dataIndex // Only for cartesian, where `isHorizontal` exists. |
| )[categoryAxis.isHorizontal() ? 1 : 0] // Empirical number |
| * 1.5 > availSize) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| function isPointNull$1(x, y) { |
| return isNaN(x) || isNaN(y); |
| } |
| |
| function getLastIndexNotNull(points) { |
| var len = points.length / 2; |
| |
| for (; len > 0; len--) { |
| if (!isPointNull$1(points[len * 2 - 2], points[len * 2 - 1])) { |
| break; |
| } |
| } |
| |
| return len - 1; |
| } |
| |
| function getPointAtIndex(points, idx) { |
| return [points[idx * 2], points[idx * 2 + 1]]; |
| } |
| |
| function getIndexRange(points, xOrY, dim) { |
| var len = points.length / 2; |
| var dimIdx = dim === 'x' ? 0 : 1; |
| var a; |
| var b; |
| var prevIndex = 0; |
| var nextIndex = -1; |
| |
| for (var i = 0; i < len; i++) { |
| b = points[i * 2 + dimIdx]; |
| |
| if (isNaN(b) || isNaN(points[i * 2 + 1 - dimIdx])) { |
| continue; |
| } |
| |
| if (i === 0) { |
| a = b; |
| continue; |
| } |
| |
| if (a <= xOrY && b >= xOrY || a >= xOrY && b <= xOrY) { |
| nextIndex = i; |
| break; |
| } |
| |
| prevIndex = i; |
| a = b; |
| } |
| |
| return { |
| range: [prevIndex, nextIndex], |
| t: (xOrY - a) / (b - a) |
| }; |
| } |
| |
| function anyStateShowEndLabel(seriesModel) { |
| if (seriesModel.get(['endLabel', 'show'])) { |
| return true; |
| } |
| |
| for (var i = 0; i < SPECIAL_STATES.length; i++) { |
| if (seriesModel.get([SPECIAL_STATES[i], 'endLabel', 'show'])) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| function createLineClipPath(lineView, coordSys, hasAnimation, seriesModel) { |
| if (isCoordinateSystemType(coordSys, 'cartesian2d')) { |
| var endLabelModel_1 = seriesModel.getModel('endLabel'); |
| var valueAnimation_1 = endLabelModel_1.get('valueAnimation'); |
| var data_1 = seriesModel.getData(); |
| var labelAnimationRecord_1 = { |
| lastFrameIndex: 0 |
| }; |
| var during = anyStateShowEndLabel(seriesModel) ? function (percent, clipRect) { |
| lineView._endLabelOnDuring(percent, clipRect, data_1, labelAnimationRecord_1, valueAnimation_1, endLabelModel_1, coordSys); |
| } : null; |
| var isHorizontal = coordSys.getBaseAxis().isHorizontal(); |
| var clipPath = createGridClipPath(coordSys, hasAnimation, seriesModel, function () { |
| var endLabel = lineView._endLabel; |
| |
| if (endLabel && hasAnimation) { |
| if (labelAnimationRecord_1.originalX != null) { |
| endLabel.attr({ |
| x: labelAnimationRecord_1.originalX, |
| y: labelAnimationRecord_1.originalY |
| }); |
| } |
| } |
| }, during); // Expand clip shape to avoid clipping when line value exceeds axis |
| |
| if (!seriesModel.get('clip', true)) { |
| var rectShape = clipPath.shape; |
| var expandSize = Math.max(rectShape.width, rectShape.height); |
| |
| if (isHorizontal) { |
| rectShape.y -= expandSize; |
| rectShape.height += expandSize * 2; |
| } else { |
| rectShape.x -= expandSize; |
| rectShape.width += expandSize * 2; |
| } |
| } // Set to the final frame. To make sure label layout is right. |
| |
| |
| if (during) { |
| during(1, clipPath); |
| } |
| |
| return clipPath; |
| } else { |
| if ("development" !== 'production') { |
| if (seriesModel.get(['endLabel', 'show'])) { |
| console.warn('endLabel is not supported for lines in polar systems.'); |
| } |
| } |
| |
| return createPolarClipPath(coordSys, hasAnimation, seriesModel); |
| } |
| } |
| |
| function getEndLabelStateSpecified(endLabelModel, coordSys) { |
| var baseAxis = coordSys.getBaseAxis(); |
| var isHorizontal = baseAxis.isHorizontal(); |
| var isBaseInversed = baseAxis.inverse; |
| var align = isHorizontal ? isBaseInversed ? 'right' : 'left' : 'center'; |
| var verticalAlign = isHorizontal ? 'middle' : isBaseInversed ? 'top' : 'bottom'; |
| return { |
| normal: { |
| align: endLabelModel.get('align') || align, |
| verticalAlign: endLabelModel.get('verticalAlign') || verticalAlign |
| } |
| }; |
| } |
| |
| var LineView = |
| /** @class */ |
| function (_super) { |
| __extends(LineView, _super); |
| |
| function LineView() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| LineView.prototype.init = function () { |
| var lineGroup = new Group(); |
| var symbolDraw = new SymbolDraw(); |
| this.group.add(symbolDraw.group); |
| this._symbolDraw = symbolDraw; |
| this._lineGroup = lineGroup; |
| }; |
| |
| LineView.prototype.render = function (seriesModel, ecModel, api) { |
| var _this = this; |
| |
| var coordSys = seriesModel.coordinateSystem; |
| var group = this.group; |
| var data = seriesModel.getData(); |
| var lineStyleModel = seriesModel.getModel('lineStyle'); |
| var areaStyleModel = seriesModel.getModel('areaStyle'); |
| var points = data.getLayout('points') || []; |
| var isCoordSysPolar = coordSys.type === 'polar'; |
| var prevCoordSys = this._coordSys; |
| var symbolDraw = this._symbolDraw; |
| var polyline = this._polyline; |
| var polygon = this._polygon; |
| var lineGroup = this._lineGroup; |
| var hasAnimation = seriesModel.get('animation'); |
| var isAreaChart = !areaStyleModel.isEmpty(); |
| var valueOrigin = areaStyleModel.get('origin'); |
| var dataCoordInfo = prepareDataCoordInfo(coordSys, data, valueOrigin); |
| var stackedOnPoints = isAreaChart && getStackedOnPoints(coordSys, data, dataCoordInfo); |
| var showSymbol = seriesModel.get('showSymbol'); |
| var connectNulls = seriesModel.get('connectNulls'); |
| var isIgnoreFunc = showSymbol && !isCoordSysPolar && getIsIgnoreFunc(seriesModel, data, coordSys); // Remove temporary symbols |
| |
| var oldData = this._data; |
| oldData && oldData.eachItemGraphicEl(function (el, idx) { |
| if (el.__temp) { |
| group.remove(el); |
| oldData.setItemGraphicEl(idx, null); |
| } |
| }); // Remove previous created symbols if showSymbol changed to false |
| |
| if (!showSymbol) { |
| symbolDraw.remove(); |
| } |
| |
| group.add(lineGroup); // FIXME step not support polar |
| |
| var step = !isCoordSysPolar ? seriesModel.get('step') : false; |
| var clipShapeForSymbol; |
| |
| if (coordSys && coordSys.getArea && seriesModel.get('clip', true)) { |
| clipShapeForSymbol = coordSys.getArea(); // Avoid float number rounding error for symbol on the edge of axis extent. |
| // See #7913 and `test/dataZoom-clip.html`. |
| |
| if (clipShapeForSymbol.width != null) { |
| clipShapeForSymbol.x -= 0.1; |
| clipShapeForSymbol.y -= 0.1; |
| clipShapeForSymbol.width += 0.2; |
| clipShapeForSymbol.height += 0.2; |
| } else if (clipShapeForSymbol.r0) { |
| clipShapeForSymbol.r0 -= 0.5; |
| clipShapeForSymbol.r += 0.5; |
| } |
| } |
| |
| this._clipShapeForSymbol = clipShapeForSymbol; |
| var visualColor = getVisualGradient(data, coordSys, api) || data.getVisual('style')[data.getVisual('drawType')]; // Initialization animation or coordinate system changed |
| |
| if (!(polyline && prevCoordSys.type === coordSys.type && step === this._step)) { |
| showSymbol && symbolDraw.updateData(data, { |
| isIgnore: isIgnoreFunc, |
| clipShape: clipShapeForSymbol, |
| disableAnimation: true, |
| getSymbolPoint: function (idx) { |
| return [points[idx * 2], points[idx * 2 + 1]]; |
| } |
| }); |
| hasAnimation && this._initSymbolLabelAnimation(data, coordSys, clipShapeForSymbol); |
| |
| if (step) { |
| // TODO If stacked series is not step |
| points = turnPointsIntoStep(points, coordSys, step, connectNulls); |
| |
| if (stackedOnPoints) { |
| stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls); |
| } |
| } |
| |
| polyline = this._newPolyline(points); |
| |
| if (isAreaChart) { |
| polygon = this._newPolygon(points, stackedOnPoints); |
| } // If areaStyle is removed |
| else if (polygon) { |
| lineGroup.remove(polygon); |
| polygon = this._polygon = null; |
| } // NOTE: Must update _endLabel before setClipPath. |
| |
| |
| if (!isCoordSysPolar) { |
| this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor)); |
| } |
| |
| lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel)); |
| } else { |
| if (isAreaChart && !polygon) { |
| // If areaStyle is added |
| polygon = this._newPolygon(points, stackedOnPoints); |
| } else if (polygon && !isAreaChart) { |
| // If areaStyle is removed |
| lineGroup.remove(polygon); |
| polygon = this._polygon = null; |
| } // NOTE: Must update _endLabel before setClipPath. |
| |
| |
| if (!isCoordSysPolar) { |
| this._initOrUpdateEndLabel(seriesModel, coordSys, convertToColorString(visualColor)); |
| } // Update clipPath |
| |
| |
| var oldClipPath = lineGroup.getClipPath(); |
| |
| if (oldClipPath) { |
| var newClipPath = createLineClipPath(this, coordSys, false, seriesModel); |
| initProps(oldClipPath, { |
| shape: newClipPath.shape |
| }, seriesModel); |
| } else { |
| lineGroup.setClipPath(createLineClipPath(this, coordSys, true, seriesModel)); |
| } // Always update, or it is wrong in the case turning on legend |
| // because points are not changed. |
| |
| |
| showSymbol && symbolDraw.updateData(data, { |
| isIgnore: isIgnoreFunc, |
| clipShape: clipShapeForSymbol, |
| disableAnimation: true, |
| getSymbolPoint: function (idx) { |
| return [points[idx * 2], points[idx * 2 + 1]]; |
| } |
| }); // In the case data zoom triggered refreshing frequently |
| // Data may not change if line has a category axis. So it should animate nothing. |
| |
| if (!isPointsSame(this._stackedOnPoints, stackedOnPoints) || !isPointsSame(this._points, points)) { |
| if (hasAnimation) { |
| this._doUpdateAnimation(data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls); |
| } else { |
| // Not do it in update with animation |
| if (step) { |
| // TODO If stacked series is not step |
| points = turnPointsIntoStep(points, coordSys, step, connectNulls); |
| |
| if (stackedOnPoints) { |
| stackedOnPoints = turnPointsIntoStep(stackedOnPoints, coordSys, step, connectNulls); |
| } |
| } |
| |
| polyline.setShape({ |
| points: points |
| }); |
| polygon && polygon.setShape({ |
| points: points, |
| stackedOnPoints: stackedOnPoints |
| }); |
| } |
| } |
| } |
| |
| var emphasisModel = seriesModel.getModel('emphasis'); |
| var focus = emphasisModel.get('focus'); |
| var blurScope = emphasisModel.get('blurScope'); |
| var emphasisDisabled = emphasisModel.get('disabled'); |
| polyline.useStyle(defaults( // Use color in lineStyle first |
| lineStyleModel.getLineStyle(), { |
| fill: 'none', |
| stroke: visualColor, |
| lineJoin: 'bevel' |
| })); |
| setStatesStylesFromModel(polyline, seriesModel, 'lineStyle'); |
| |
| if (polyline.style.lineWidth > 0 && seriesModel.get(['emphasis', 'lineStyle', 'width']) === 'bolder') { |
| var emphasisLineStyle = polyline.getState('emphasis').style; |
| emphasisLineStyle.lineWidth = +polyline.style.lineWidth + 1; |
| } // Needs seriesIndex for focus |
| |
| |
| getECData(polyline).seriesIndex = seriesModel.seriesIndex; |
| toggleHoverEmphasis(polyline, focus, blurScope, emphasisDisabled); |
| var smooth = getSmooth(seriesModel.get('smooth')); |
| var smoothMonotone = seriesModel.get('smoothMonotone'); |
| polyline.setShape({ |
| smooth: smooth, |
| smoothMonotone: smoothMonotone, |
| connectNulls: connectNulls |
| }); |
| |
| if (polygon) { |
| var stackedOnSeries = data.getCalculationInfo('stackedOnSeries'); |
| var stackedOnSmooth = 0; |
| polygon.useStyle(defaults(areaStyleModel.getAreaStyle(), { |
| fill: visualColor, |
| opacity: 0.7, |
| lineJoin: 'bevel', |
| decal: data.getVisual('style').decal |
| })); |
| |
| if (stackedOnSeries) { |
| stackedOnSmooth = getSmooth(stackedOnSeries.get('smooth')); |
| } |
| |
| polygon.setShape({ |
| smooth: smooth, |
| stackedOnSmooth: stackedOnSmooth, |
| smoothMonotone: smoothMonotone, |
| connectNulls: connectNulls |
| }); |
| setStatesStylesFromModel(polygon, seriesModel, 'areaStyle'); // Needs seriesIndex for focus |
| |
| getECData(polygon).seriesIndex = seriesModel.seriesIndex; |
| toggleHoverEmphasis(polygon, focus, blurScope, emphasisDisabled); |
| } |
| |
| var changePolyState = function (toState) { |
| _this._changePolyState(toState); |
| }; |
| |
| data.eachItemGraphicEl(function (el) { |
| // Switch polyline / polygon state if element changed its state. |
| el && (el.onHoverStateChange = changePolyState); |
| }); |
| this._polyline.onHoverStateChange = changePolyState; |
| this._data = data; // Save the coordinate system for transition animation when data changed |
| |
| this._coordSys = coordSys; |
| this._stackedOnPoints = stackedOnPoints; |
| this._points = points; |
| this._step = step; |
| this._valueOrigin = valueOrigin; |
| |
| if (seriesModel.get('triggerLineEvent')) { |
| this.packEventData(seriesModel, polyline); |
| polygon && this.packEventData(seriesModel, polygon); |
| } |
| }; |
| |
| LineView.prototype.packEventData = function (seriesModel, el) { |
| getECData(el).eventData = { |
| componentType: 'series', |
| componentSubType: 'line', |
| componentIndex: seriesModel.componentIndex, |
| seriesIndex: seriesModel.seriesIndex, |
| seriesName: seriesModel.name, |
| seriesType: 'line' |
| }; |
| }; |
| |
| LineView.prototype.highlight = function (seriesModel, ecModel, api, payload) { |
| var data = seriesModel.getData(); |
| var dataIndex = queryDataIndex(data, payload); |
| |
| this._changePolyState('emphasis'); |
| |
| if (!(dataIndex instanceof Array) && dataIndex != null && dataIndex >= 0) { |
| var points = data.getLayout('points'); |
| var symbol = data.getItemGraphicEl(dataIndex); |
| |
| if (!symbol) { |
| // Create a temporary symbol if it is not exists |
| var x = points[dataIndex * 2]; |
| var y = points[dataIndex * 2 + 1]; |
| |
| if (isNaN(x) || isNaN(y)) { |
| // Null data |
| return; |
| } // fix #11360: shouldn't draw symbol outside clipShapeForSymbol |
| |
| |
| if (this._clipShapeForSymbol && !this._clipShapeForSymbol.contain(x, y)) { |
| return; |
| } |
| |
| var zlevel = seriesModel.get('zlevel') || 0; |
| var z = seriesModel.get('z') || 0; |
| symbol = new Symbol(data, dataIndex); |
| symbol.x = x; |
| symbol.y = y; |
| symbol.setZ(zlevel, z); // ensure label text of the temporary symbol is in front of line and area polygon |
| |
| var symbolLabel = symbol.getSymbolPath().getTextContent(); |
| |
| if (symbolLabel) { |
| symbolLabel.zlevel = zlevel; |
| symbolLabel.z = z; |
| symbolLabel.z2 = this._polyline.z2 + 1; |
| } |
| |
| symbol.__temp = true; |
| data.setItemGraphicEl(dataIndex, symbol); // Stop scale animation |
| |
| symbol.stopSymbolAnimation(true); |
| this.group.add(symbol); |
| } |
| |
| symbol.highlight(); |
| } else { |
| // Highlight whole series |
| ChartView.prototype.highlight.call(this, seriesModel, ecModel, api, payload); |
| } |
| }; |
| |
| LineView.prototype.downplay = function (seriesModel, ecModel, api, payload) { |
| var data = seriesModel.getData(); |
| var dataIndex = queryDataIndex(data, payload); |
| |
| this._changePolyState('normal'); |
| |
| if (dataIndex != null && dataIndex >= 0) { |
| var symbol = data.getItemGraphicEl(dataIndex); |
| |
| if (symbol) { |
| if (symbol.__temp) { |
| data.setItemGraphicEl(dataIndex, null); |
| this.group.remove(symbol); |
| } else { |
| symbol.downplay(); |
| } |
| } |
| } else { |
| // FIXME |
| // can not downplay completely. |
| // Downplay whole series |
| ChartView.prototype.downplay.call(this, seriesModel, ecModel, api, payload); |
| } |
| }; |
| |
| LineView.prototype._changePolyState = function (toState) { |
| var polygon = this._polygon; |
| setStatesFlag(this._polyline, toState); |
| polygon && setStatesFlag(polygon, toState); |
| }; |
| |
| LineView.prototype._newPolyline = function (points) { |
| var polyline = this._polyline; // Remove previous created polyline |
| |
| if (polyline) { |
| this._lineGroup.remove(polyline); |
| } |
| |
| polyline = new ECPolyline({ |
| shape: { |
| points: points |
| }, |
| segmentIgnoreThreshold: 2, |
| z2: 10 |
| }); |
| |
| this._lineGroup.add(polyline); |
| |
| this._polyline = polyline; |
| return polyline; |
| }; |
| |
| LineView.prototype._newPolygon = function (points, stackedOnPoints) { |
| var polygon = this._polygon; // Remove previous created polygon |
| |
| if (polygon) { |
| this._lineGroup.remove(polygon); |
| } |
| |
| polygon = new ECPolygon({ |
| shape: { |
| points: points, |
| stackedOnPoints: stackedOnPoints |
| }, |
| segmentIgnoreThreshold: 2 |
| }); |
| |
| this._lineGroup.add(polygon); |
| |
| this._polygon = polygon; |
| return polygon; |
| }; |
| |
| LineView.prototype._initSymbolLabelAnimation = function (data, coordSys, clipShape) { |
| var isHorizontalOrRadial; |
| var isCoordSysPolar; |
| var baseAxis = coordSys.getBaseAxis(); |
| var isAxisInverse = baseAxis.inverse; |
| |
| if (coordSys.type === 'cartesian2d') { |
| isHorizontalOrRadial = baseAxis.isHorizontal(); |
| isCoordSysPolar = false; |
| } else if (coordSys.type === 'polar') { |
| isHorizontalOrRadial = baseAxis.dim === 'angle'; |
| isCoordSysPolar = true; |
| } |
| |
| var seriesModel = data.hostModel; |
| var seriesDuration = seriesModel.get('animationDuration'); |
| |
| if (isFunction(seriesDuration)) { |
| seriesDuration = seriesDuration(null); |
| } |
| |
| var seriesDalay = seriesModel.get('animationDelay') || 0; |
| var seriesDalayValue = isFunction(seriesDalay) ? seriesDalay(null) : seriesDalay; |
| data.eachItemGraphicEl(function (symbol, idx) { |
| var el = symbol; |
| |
| if (el) { |
| var point = [symbol.x, symbol.y]; |
| var start = void 0; |
| var end = void 0; |
| var current = void 0; |
| |
| if (clipShape) { |
| if (isCoordSysPolar) { |
| var polarClip = clipShape; |
| var coord = coordSys.pointToCoord(point); |
| |
| if (isHorizontalOrRadial) { |
| start = polarClip.startAngle; |
| end = polarClip.endAngle; |
| current = -coord[1] / 180 * Math.PI; |
| } else { |
| start = polarClip.r0; |
| end = polarClip.r; |
| current = coord[0]; |
| } |
| } else { |
| var gridClip = clipShape; |
| |
| if (isHorizontalOrRadial) { |
| start = gridClip.x; |
| end = gridClip.x + gridClip.width; |
| current = symbol.x; |
| } else { |
| start = gridClip.y + gridClip.height; |
| end = gridClip.y; |
| current = symbol.y; |
| } |
| } |
| } |
| |
| var ratio = end === start ? 0 : (current - start) / (end - start); |
| |
| if (isAxisInverse) { |
| ratio = 1 - ratio; |
| } |
| |
| var delay = isFunction(seriesDalay) ? seriesDalay(idx) : seriesDuration * ratio + seriesDalayValue; |
| var symbolPath = el.getSymbolPath(); |
| var text = symbolPath.getTextContent(); |
| el.attr({ |
| scaleX: 0, |
| scaleY: 0 |
| }); |
| el.animateTo({ |
| scaleX: 1, |
| scaleY: 1 |
| }, { |
| duration: 200, |
| setToFinal: true, |
| delay: delay |
| }); |
| |
| if (text) { |
| text.animateFrom({ |
| style: { |
| opacity: 0 |
| } |
| }, { |
| duration: 300, |
| delay: delay |
| }); |
| } |
| |
| symbolPath.disableLabelAnimation = true; |
| } |
| }); |
| }; |
| |
| LineView.prototype._initOrUpdateEndLabel = function (seriesModel, coordSys, inheritColor) { |
| var endLabelModel = seriesModel.getModel('endLabel'); |
| |
| if (anyStateShowEndLabel(seriesModel)) { |
| var data_2 = seriesModel.getData(); |
| var polyline = this._polyline; // series may be filtered. |
| |
| var points = data_2.getLayout('points'); |
| |
| if (!points) { |
| polyline.removeTextContent(); |
| this._endLabel = null; |
| return; |
| } |
| |
| var endLabel = this._endLabel; |
| |
| if (!endLabel) { |
| endLabel = this._endLabel = new ZRText({ |
| z2: 200 // should be higher than item symbol |
| |
| }); |
| endLabel.ignoreClip = true; |
| polyline.setTextContent(this._endLabel); |
| polyline.disableLabelAnimation = true; |
| } // Find last non-NaN data to display data |
| |
| |
| var dataIndex = getLastIndexNotNull(points); |
| |
| if (dataIndex >= 0) { |
| setLabelStyle(polyline, getLabelStatesModels(seriesModel, 'endLabel'), { |
| inheritColor: inheritColor, |
| labelFetcher: seriesModel, |
| labelDataIndex: dataIndex, |
| defaultText: function (dataIndex, opt, interpolatedValue) { |
| return interpolatedValue != null ? getDefaultInterpolatedLabel(data_2, interpolatedValue) : getDefaultLabel(data_2, dataIndex); |
| }, |
| enableTextSetter: true |
| }, getEndLabelStateSpecified(endLabelModel, coordSys)); |
| polyline.textConfig.position = null; |
| } |
| } else if (this._endLabel) { |
| this._polyline.removeTextContent(); |
| |
| this._endLabel = null; |
| } |
| }; |
| |
| LineView.prototype._endLabelOnDuring = function (percent, clipRect, data, animationRecord, valueAnimation, endLabelModel, coordSys) { |
| var endLabel = this._endLabel; |
| var polyline = this._polyline; |
| |
| if (endLabel) { |
| // NOTE: Don't remove percent < 1. percent === 1 means the first frame during render. |
| // The label is not prepared at this time. |
| if (percent < 1 && animationRecord.originalX == null) { |
| animationRecord.originalX = endLabel.x; |
| animationRecord.originalY = endLabel.y; |
| } |
| |
| var points = data.getLayout('points'); |
| var seriesModel = data.hostModel; |
| var connectNulls = seriesModel.get('connectNulls'); |
| var precision = endLabelModel.get('precision'); |
| var distance = endLabelModel.get('distance') || 0; |
| var baseAxis = coordSys.getBaseAxis(); |
| var isHorizontal = baseAxis.isHorizontal(); |
| var isBaseInversed = baseAxis.inverse; |
| var clipShape = clipRect.shape; |
| var xOrY = isBaseInversed ? isHorizontal ? clipShape.x : clipShape.y + clipShape.height : isHorizontal ? clipShape.x + clipShape.width : clipShape.y; |
| var distanceX = (isHorizontal ? distance : 0) * (isBaseInversed ? -1 : 1); |
| var distanceY = (isHorizontal ? 0 : -distance) * (isBaseInversed ? -1 : 1); |
| var dim = isHorizontal ? 'x' : 'y'; |
| var dataIndexRange = getIndexRange(points, xOrY, dim); |
| var indices = dataIndexRange.range; |
| var diff = indices[1] - indices[0]; |
| var value = void 0; |
| |
| if (diff >= 1) { |
| // diff > 1 && connectNulls, which is on the null data. |
| if (diff > 1 && !connectNulls) { |
| var pt = getPointAtIndex(points, indices[0]); |
| endLabel.attr({ |
| x: pt[0] + distanceX, |
| y: pt[1] + distanceY |
| }); |
| valueAnimation && (value = seriesModel.getRawValue(indices[0])); |
| } else { |
| var pt = polyline.getPointOn(xOrY, dim); |
| pt && endLabel.attr({ |
| x: pt[0] + distanceX, |
| y: pt[1] + distanceY |
| }); |
| var startValue = seriesModel.getRawValue(indices[0]); |
| var endValue = seriesModel.getRawValue(indices[1]); |
| valueAnimation && (value = interpolateRawValues(data, precision, startValue, endValue, dataIndexRange.t)); |
| } |
| |
| animationRecord.lastFrameIndex = indices[0]; |
| } else { |
| // If diff <= 0, which is the range is not found(Include NaN) |
| // Choose the first point or last point. |
| var idx = percent === 1 || animationRecord.lastFrameIndex > 0 ? indices[0] : 0; |
| var pt = getPointAtIndex(points, idx); |
| valueAnimation && (value = seriesModel.getRawValue(idx)); |
| endLabel.attr({ |
| x: pt[0] + distanceX, |
| y: pt[1] + distanceY |
| }); |
| } |
| |
| if (valueAnimation) { |
| labelInner(endLabel).setLabelText(value); |
| } |
| } |
| }; |
| /** |
| * @private |
| */ |
| // FIXME Two value axis |
| |
| |
| LineView.prototype._doUpdateAnimation = function (data, stackedOnPoints, coordSys, api, step, valueOrigin, connectNulls) { |
| var polyline = this._polyline; |
| var polygon = this._polygon; |
| var seriesModel = data.hostModel; |
| var diff = lineAnimationDiff(this._data, data, this._stackedOnPoints, stackedOnPoints, this._coordSys, coordSys, this._valueOrigin); |
| var current = diff.current; |
| var stackedOnCurrent = diff.stackedOnCurrent; |
| var next = diff.next; |
| var stackedOnNext = diff.stackedOnNext; |
| |
| if (step) { |
| // TODO If stacked series is not step |
| current = turnPointsIntoStep(diff.current, coordSys, step, connectNulls); |
| stackedOnCurrent = turnPointsIntoStep(diff.stackedOnCurrent, coordSys, step, connectNulls); |
| next = turnPointsIntoStep(diff.next, coordSys, step, connectNulls); |
| stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step, connectNulls); |
| } // Don't apply animation if diff is large. |
| // For better result and avoid memory explosion problems like |
| // https://github.com/apache/incubator-echarts/issues/12229 |
| |
| |
| if (getBoundingDiff(current, next) > 3000 || polygon && getBoundingDiff(stackedOnCurrent, stackedOnNext) > 3000) { |
| polyline.stopAnimation(); |
| polyline.setShape({ |
| points: next |
| }); |
| |
| if (polygon) { |
| polygon.stopAnimation(); |
| polygon.setShape({ |
| points: next, |
| stackedOnPoints: stackedOnNext |
| }); |
| } |
| |
| return; |
| } |
| |
| polyline.shape.__points = diff.current; |
| polyline.shape.points = current; |
| var target = { |
| shape: { |
| points: next |
| } |
| }; // Also animate the original points. |
| // If points reference is changed when turning into step line. |
| |
| if (diff.current !== current) { |
| target.shape.__points = diff.next; |
| } // Stop previous animation. |
| |
| |
| polyline.stopAnimation(); |
| updateProps(polyline, target, seriesModel); |
| |
| if (polygon) { |
| polygon.setShape({ |
| // Reuse the points with polyline. |
| points: current, |
| stackedOnPoints: stackedOnCurrent |
| }); |
| polygon.stopAnimation(); |
| updateProps(polygon, { |
| shape: { |
| stackedOnPoints: stackedOnNext |
| } |
| }, seriesModel); // If use attr directly in updateProps. |
| |
| if (polyline.shape.points !== polygon.shape.points) { |
| polygon.shape.points = polyline.shape.points; |
| } |
| } |
| |
| var updatedDataInfo = []; |
| var diffStatus = diff.status; |
| |
| for (var i = 0; i < diffStatus.length; i++) { |
| var cmd = diffStatus[i].cmd; |
| |
| if (cmd === '=') { |
| var el = data.getItemGraphicEl(diffStatus[i].idx1); |
| |
| if (el) { |
| updatedDataInfo.push({ |
| el: el, |
| ptIdx: i // Index of points |
| |
| }); |
| } |
| } |
| } |
| |
| if (polyline.animators && polyline.animators.length) { |
| polyline.animators[0].during(function () { |
| polygon && polygon.dirtyShape(); |
| var points = polyline.shape.__points; |
| |
| for (var i = 0; i < updatedDataInfo.length; i++) { |
| var el = updatedDataInfo[i].el; |
| var offset = updatedDataInfo[i].ptIdx * 2; |
| el.x = points[offset]; |
| el.y = points[offset + 1]; |
| el.markRedraw(); |
| } |
| }); |
| } |
| }; |
| |
| LineView.prototype.remove = function (ecModel) { |
| var group = this.group; |
| var oldData = this._data; |
| |
| this._lineGroup.removeAll(); |
| |
| this._symbolDraw.remove(true); // Remove temporary created elements when highlighting |
| |
| |
| oldData && oldData.eachItemGraphicEl(function (el, idx) { |
| if (el.__temp) { |
| group.remove(el); |
| oldData.setItemGraphicEl(idx, null); |
| } |
| }); |
| this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._endLabel = this._data = null; |
| }; |
| |
| LineView.type = 'line'; |
| return LineView; |
| }(ChartView); |
| |
| function pointsLayout(seriesType, forceStoreInTypedArray) { |
| return { |
| seriesType: seriesType, |
| plan: createRenderPlanner(), |
| reset: function (seriesModel) { |
| var data = seriesModel.getData(); |
| var coordSys = seriesModel.coordinateSystem; |
| var pipelineContext = seriesModel.pipelineContext; |
| var useTypedArray = forceStoreInTypedArray || pipelineContext.large; |
| |
| if (!coordSys) { |
| return; |
| } |
| |
| var dims = map(coordSys.dimensions, function (dim) { |
| return data.mapDimension(dim); |
| }).slice(0, 2); |
| var dimLen = dims.length; |
| var stackResultDim = data.getCalculationInfo('stackResultDimension'); |
| |
| if (isDimensionStacked(data, dims[0])) { |
| dims[0] = stackResultDim; |
| } |
| |
| if (isDimensionStacked(data, dims[1])) { |
| dims[1] = stackResultDim; |
| } |
| |
| var store = data.getStore(); |
| var dimIdx0 = data.getDimensionIndex(dims[0]); |
| var dimIdx1 = data.getDimensionIndex(dims[1]); |
| return dimLen && { |
| progress: function (params, data) { |
| var segCount = params.end - params.start; |
| var points = useTypedArray && createFloat32Array(segCount * dimLen); |
| var tmpIn = []; |
| var tmpOut = []; |
| |
| for (var i = params.start, offset = 0; i < params.end; i++) { |
| var point = void 0; |
| |
| if (dimLen === 1) { |
| var x = store.get(dimIdx0, i); // NOTE: Make sure the second parameter is null to use default strategy. |
| |
| point = coordSys.dataToPoint(x, null, tmpOut); |
| } else { |
| tmpIn[0] = store.get(dimIdx0, i); |
| tmpIn[1] = store.get(dimIdx1, i); // Let coordinate system to handle the NaN data. |
| |
| point = coordSys.dataToPoint(tmpIn, null, tmpOut); |
| } |
| |
| if (useTypedArray) { |
| points[offset++] = point[0]; |
| points[offset++] = point[1]; |
| } else { |
| data.setItemLayout(i, point.slice()); |
| } |
| } |
| |
| useTypedArray && data.setLayout('points', points); |
| } |
| }; |
| } |
| }; |
| } |
| |
| var samplers = { |
| average: function (frame) { |
| var sum = 0; |
| var count = 0; |
| |
| for (var i = 0; i < frame.length; i++) { |
| if (!isNaN(frame[i])) { |
| sum += frame[i]; |
| count++; |
| } |
| } // Return NaN if count is 0 |
| |
| |
| return count === 0 ? NaN : sum / count; |
| }, |
| sum: function (frame) { |
| var sum = 0; |
| |
| for (var i = 0; i < frame.length; i++) { |
| // Ignore NaN |
| sum += frame[i] || 0; |
| } |
| |
| return sum; |
| }, |
| max: function (frame) { |
| var max = -Infinity; |
| |
| for (var i = 0; i < frame.length; i++) { |
| frame[i] > max && (max = frame[i]); |
| } // NaN will cause illegal axis extent. |
| |
| |
| return isFinite(max) ? max : NaN; |
| }, |
| min: function (frame) { |
| var min = Infinity; |
| |
| for (var i = 0; i < frame.length; i++) { |
| frame[i] < min && (min = frame[i]); |
| } // NaN will cause illegal axis extent. |
| |
| |
| return isFinite(min) ? min : NaN; |
| }, |
| // TODO |
| // Median |
| nearest: function (frame) { |
| return frame[0]; |
| } |
| }; |
| |
| var indexSampler = function (frame) { |
| return Math.round(frame.length / 2); |
| }; |
| |
| function dataSample(seriesType) { |
| return { |
| seriesType: seriesType, |
| // FIXME:TS never used, so comment it |
| // modifyOutputEnd: true, |
| reset: function (seriesModel, ecModel, api) { |
| var data = seriesModel.getData(); |
| var sampling = seriesModel.get('sampling'); |
| var coordSys = seriesModel.coordinateSystem; |
| var count = data.count(); // Only cartesian2d support down sampling. Disable it when there is few data. |
| |
| if (count > 10 && coordSys.type === 'cartesian2d' && sampling) { |
| var baseAxis = coordSys.getBaseAxis(); |
| var valueAxis = coordSys.getOtherAxis(baseAxis); |
| var extent = baseAxis.getExtent(); |
| var dpr = api.getDevicePixelRatio(); // Coordinste system has been resized |
| |
| var size = Math.abs(extent[1] - extent[0]) * (dpr || 1); |
| var rate = Math.round(count / size); |
| |
| if (isFinite(rate) && rate > 1) { |
| if (sampling === 'lttb') { |
| seriesModel.setData(data.lttbDownSample(data.mapDimension(valueAxis.dim), 1 / rate)); |
| } |
| |
| var sampler = void 0; |
| |
| if (isString(sampling)) { |
| sampler = samplers[sampling]; |
| } else if (isFunction(sampling)) { |
| sampler = sampling; |
| } |
| |
| if (sampler) { |
| // Only support sample the first dim mapped from value axis. |
| seriesModel.setData(data.downSample(data.mapDimension(valueAxis.dim), 1 / rate, sampler, indexSampler)); |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| function install$1(registers) { |
| registers.registerChartView(LineView); |
| registers.registerSeriesModel(LineSeriesModel); |
| registers.registerLayout(pointsLayout('line', true)); |
| registers.registerVisual({ |
| seriesType: 'line', |
| reset: function (seriesModel) { |
| var data = seriesModel.getData(); // Visual coding for legend |
| |
| var lineStyle = seriesModel.getModel('lineStyle').getLineStyle(); |
| |
| if (lineStyle && !lineStyle.stroke) { |
| // Fill in visual should be palette color if |
| // has color callback |
| lineStyle.stroke = data.getVisual('style').fill; |
| } |
| |
| data.setVisual('legendLineStyle', lineStyle); |
| } |
| }); // Down sample after filter |
| |
| registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('line')); |
| } |
| |
| var BaseBarSeriesModel = |
| /** @class */ |
| function (_super) { |
| __extends(BaseBarSeriesModel, _super); |
| |
| function BaseBarSeriesModel() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = BaseBarSeriesModel.type; |
| return _this; |
| } |
| |
| BaseBarSeriesModel.prototype.getInitialData = function (option, ecModel) { |
| return createSeriesData(null, this, { |
| useEncodeDefaulter: true |
| }); |
| }; |
| |
| BaseBarSeriesModel.prototype.getMarkerPosition = function (value, dims, startingAtTick) { |
| var coordSys = this.coordinateSystem; |
| |
| if (coordSys && coordSys.clampData) { |
| // PENDING if clamp ? |
| var pt_1 = coordSys.dataToPoint(coordSys.clampData(value)); |
| |
| if (startingAtTick) { |
| each(coordSys.getAxes(), function (axis, idx) { |
| // If axis type is category, use tick coords instead |
| if (axis.type === 'category') { |
| var tickCoords = axis.getTicksCoords(); |
| var tickIdx = coordSys.clampData(value)[idx]; // The index of rightmost tick of markArea is 1 larger than x1/y1 index |
| |
| if (dims && (dims[idx] === 'x1' || dims[idx] === 'y1')) { |
| tickIdx += 1; |
| } |
| |
| tickIdx > tickCoords.length - 1 && (tickIdx = tickCoords.length - 1); |
| tickIdx < 0 && (tickIdx = 0); |
| tickCoords[tickIdx] && (pt_1[idx] = axis.toGlobalCoord(tickCoords[tickIdx].coord)); |
| } |
| }); |
| } else { |
| var data = this.getData(); |
| var offset = data.getLayout('offset'); |
| var size = data.getLayout('size'); |
| var offsetIndex = coordSys.getBaseAxis().isHorizontal() ? 0 : 1; |
| pt_1[offsetIndex] += offset + size / 2; |
| } |
| |
| return pt_1; |
| } |
| |
| return [NaN, NaN]; |
| }; |
| |
| BaseBarSeriesModel.type = 'series.__base_bar__'; |
| BaseBarSeriesModel.defaultOption = { |
| // zlevel: 0, |
| z: 2, |
| coordinateSystem: 'cartesian2d', |
| legendHoverLink: true, |
| // stack: null |
| // Cartesian coordinate system |
| // xAxisIndex: 0, |
| // yAxisIndex: 0, |
| barMinHeight: 0, |
| barMinAngle: 0, |
| // cursor: null, |
| large: false, |
| largeThreshold: 400, |
| progressive: 3e3, |
| progressiveChunkMode: 'mod' |
| }; |
| return BaseBarSeriesModel; |
| }(SeriesModel); |
| |
| SeriesModel.registerClass(BaseBarSeriesModel); |
| |
| var BarSeriesModel = |
| /** @class */ |
| function (_super) { |
| __extends(BarSeriesModel, _super); |
| |
| function BarSeriesModel() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = BarSeriesModel.type; |
| return _this; |
| } |
| |
| BarSeriesModel.prototype.getInitialData = function () { |
| return createSeriesData(null, this, { |
| useEncodeDefaulter: true, |
| createInvertedIndices: !!this.get('realtimeSort', true) || null |
| }); |
| }; |
| /** |
| * @override |
| */ |
| |
| |
| BarSeriesModel.prototype.getProgressive = function () { |
| // Do not support progressive in normal mode. |
| return this.get('large') ? this.get('progressive') : false; |
| }; |
| /** |
| * @override |
| */ |
| |
| |
| BarSeriesModel.prototype.getProgressiveThreshold = function () { |
| // Do not support progressive in normal mode. |
| var progressiveThreshold = this.get('progressiveThreshold'); |
| var largeThreshold = this.get('largeThreshold'); |
| |
| if (largeThreshold > progressiveThreshold) { |
| progressiveThreshold = largeThreshold; |
| } |
| |
| return progressiveThreshold; |
| }; |
| |
| BarSeriesModel.prototype.brushSelector = function (dataIndex, data, selectors) { |
| return selectors.rect(data.getItemLayout(dataIndex)); |
| }; |
| |
| BarSeriesModel.type = 'series.bar'; |
| BarSeriesModel.dependencies = ['grid', 'polar']; |
| BarSeriesModel.defaultOption = inheritDefaultOption(BaseBarSeriesModel.defaultOption, { |
| // If clipped |
| // Only available on cartesian2d |
| clip: true, |
| roundCap: false, |
| showBackground: false, |
| backgroundStyle: { |
| color: 'rgba(180, 180, 180, 0.2)', |
| borderColor: null, |
| borderWidth: 0, |
| borderType: 'solid', |
| borderRadius: 0, |
| shadowBlur: 0, |
| shadowColor: null, |
| shadowOffsetX: 0, |
| shadowOffsetY: 0, |
| opacity: 1 |
| }, |
| select: { |
| itemStyle: { |
| borderColor: '#212121' |
| } |
| }, |
| realtimeSort: false |
| }); |
| return BarSeriesModel; |
| }(BaseBarSeriesModel); |
| |
| /** |
| * Sausage: similar to sector, but have half circle on both sides |
| */ |
| |
| var SausageShape = |
| /** @class */ |
| function () { |
| function SausageShape() { |
| this.cx = 0; |
| this.cy = 0; |
| this.r0 = 0; |
| this.r = 0; |
| this.startAngle = 0; |
| this.endAngle = Math.PI * 2; |
| this.clockwise = true; |
| } |
| |
| return SausageShape; |
| }(); |
| |
| var SausagePath = |
| /** @class */ |
| function (_super) { |
| __extends(SausagePath, _super); |
| |
| function SausagePath(opts) { |
| var _this = _super.call(this, opts) || this; |
| |
| _this.type = 'sausage'; |
| return _this; |
| } |
| |
| SausagePath.prototype.getDefaultShape = function () { |
| return new SausageShape(); |
| }; |
| |
| SausagePath.prototype.buildPath = function (ctx, shape) { |
| var cx = shape.cx; |
| var cy = shape.cy; |
| var r0 = Math.max(shape.r0 || 0, 0); |
| var r = Math.max(shape.r, 0); |
| var dr = (r - r0) * 0.5; |
| var rCenter = r0 + dr; |
| var startAngle = shape.startAngle; |
| var endAngle = shape.endAngle; |
| var clockwise = shape.clockwise; |
| var PI2 = Math.PI * 2; |
| var lessThanCircle = clockwise ? endAngle - startAngle < PI2 : startAngle - endAngle < PI2; |
| |
| if (!lessThanCircle) { |
| // Normalize angles |
| startAngle = endAngle - (clockwise ? PI2 : -PI2); |
| } |
| |
| var unitStartX = Math.cos(startAngle); |
| var unitStartY = Math.sin(startAngle); |
| var unitEndX = Math.cos(endAngle); |
| var unitEndY = Math.sin(endAngle); |
| |
| if (lessThanCircle) { |
| ctx.moveTo(unitStartX * r0 + cx, unitStartY * r0 + cy); |
| ctx.arc(unitStartX * rCenter + cx, unitStartY * rCenter + cy, dr, -Math.PI + startAngle, startAngle, !clockwise); |
| } else { |
| ctx.moveTo(unitStartX * r + cx, unitStartY * r + cy); |
| } |
| |
| ctx.arc(cx, cy, r, startAngle, endAngle, !clockwise); |
| ctx.arc(unitEndX * rCenter + cx, unitEndY * rCenter + cy, dr, endAngle - Math.PI * 2, endAngle - Math.PI, !clockwise); |
| |
| if (r0 !== 0) { |
| ctx.arc(cx, cy, r0, endAngle, startAngle, clockwise); |
| } // ctx.closePath(); |
| |
| }; |
| |
| return SausagePath; |
| }(Path); |
| |
| function createSectorCalculateTextPosition(positionMapping, opts) { |
| opts = opts || {}; |
| var isRoundCap = opts.isRoundCap; |
| return function (out, opts, boundingRect) { |
| var textPosition = opts.position; |
| |
| if (!textPosition || textPosition instanceof Array) { |
| return calculateTextPosition(out, opts, boundingRect); |
| } |
| |
| var mappedSectorPosition = positionMapping(textPosition); |
| var distance = opts.distance != null ? opts.distance : 5; |
| var sector = this.shape; |
| var cx = sector.cx; |
| var cy = sector.cy; |
| var r = sector.r; |
| var r0 = sector.r0; |
| var middleR = (r + r0) / 2; |
| var startAngle = sector.startAngle; |
| var endAngle = sector.endAngle; |
| var middleAngle = (startAngle + endAngle) / 2; |
| var extraDist = isRoundCap ? Math.abs(r - r0) / 2 : 0; |
| var mathCos = Math.cos; |
| var mathSin = Math.sin; // base position: top-left |
| |
| var x = cx + r * mathCos(startAngle); |
| var y = cy + r * mathSin(startAngle); |
| var textAlign = 'left'; |
| var textVerticalAlign = 'top'; |
| |
| switch (mappedSectorPosition) { |
| case 'startArc': |
| x = cx + (r0 - distance) * mathCos(middleAngle); |
| y = cy + (r0 - distance) * mathSin(middleAngle); |
| textAlign = 'center'; |
| textVerticalAlign = 'top'; |
| break; |
| |
| case 'insideStartArc': |
| x = cx + (r0 + distance) * mathCos(middleAngle); |
| y = cy + (r0 + distance) * mathSin(middleAngle); |
| textAlign = 'center'; |
| textVerticalAlign = 'bottom'; |
| break; |
| |
| case 'startAngle': |
| x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, distance + extraDist, false); |
| y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, distance + extraDist, false); |
| textAlign = 'right'; |
| textVerticalAlign = 'middle'; |
| break; |
| |
| case 'insideStartAngle': |
| x = cx + middleR * mathCos(startAngle) + adjustAngleDistanceX(startAngle, -distance + extraDist, false); |
| y = cy + middleR * mathSin(startAngle) + adjustAngleDistanceY(startAngle, -distance + extraDist, false); |
| textAlign = 'left'; |
| textVerticalAlign = 'middle'; |
| break; |
| |
| case 'middle': |
| x = cx + middleR * mathCos(middleAngle); |
| y = cy + middleR * mathSin(middleAngle); |
| textAlign = 'center'; |
| textVerticalAlign = 'middle'; |
| break; |
| |
| case 'endArc': |
| x = cx + (r + distance) * mathCos(middleAngle); |
| y = cy + (r + distance) * mathSin(middleAngle); |
| textAlign = 'center'; |
| textVerticalAlign = 'bottom'; |
| break; |
| |
| case 'insideEndArc': |
| x = cx + (r - distance) * mathCos(middleAngle); |
| y = cy + (r - distance) * mathSin(middleAngle); |
| textAlign = 'center'; |
| textVerticalAlign = 'top'; |
| break; |
| |
| case 'endAngle': |
| x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, distance + extraDist, true); |
| y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, distance + extraDist, true); |
| textAlign = 'left'; |
| textVerticalAlign = 'middle'; |
| break; |
| |
| case 'insideEndAngle': |
| x = cx + middleR * mathCos(endAngle) + adjustAngleDistanceX(endAngle, -distance + extraDist, true); |
| y = cy + middleR * mathSin(endAngle) + adjustAngleDistanceY(endAngle, -distance + extraDist, true); |
| textAlign = 'right'; |
| textVerticalAlign = 'middle'; |
| break; |
| |
| default: |
| return calculateTextPosition(out, opts, boundingRect); |
| } |
| |
| out = out || {}; |
| out.x = x; |
| out.y = y; |
| out.align = textAlign; |
| out.verticalAlign = textVerticalAlign; |
| return out; |
| }; |
| } |
| function setSectorTextRotation(sector, textPosition, positionMapping, rotateType) { |
| if (isNumber(rotateType)) { |
| // user-set rotation |
| sector.setTextConfig({ |
| rotation: rotateType |
| }); |
| return; |
| } else if (isArray(textPosition)) { |
| // user-set position, use 0 as auto rotation |
| sector.setTextConfig({ |
| rotation: 0 |
| }); |
| return; |
| } |
| |
| var shape = sector.shape; |
| var startAngle = shape.clockwise ? shape.startAngle : shape.endAngle; |
| var endAngle = shape.clockwise ? shape.endAngle : shape.startAngle; |
| var middleAngle = (startAngle + endAngle) / 2; |
| var anchorAngle; |
| var mappedSectorPosition = positionMapping(textPosition); |
| |
| switch (mappedSectorPosition) { |
| case 'startArc': |
| case 'insideStartArc': |
| case 'middle': |
| case 'insideEndArc': |
| case 'endArc': |
| anchorAngle = middleAngle; |
| break; |
| |
| case 'startAngle': |
| case 'insideStartAngle': |
| anchorAngle = startAngle; |
| break; |
| |
| case 'endAngle': |
| case 'insideEndAngle': |
| anchorAngle = endAngle; |
| break; |
| |
| default: |
| sector.setTextConfig({ |
| rotation: 0 |
| }); |
| return; |
| } |
| |
| var rotate = Math.PI * 1.5 - anchorAngle; |
| /** |
| * TODO: labels with rotate > Math.PI / 2 should be rotate another |
| * half round flipped to increase readability. However, only middle |
| * position supports this for now, because in other positions, the |
| * anchor point is not at the center of the text, so the positions |
| * after rotating is not as expected. |
| */ |
| |
| if (mappedSectorPosition === 'middle' && rotate > Math.PI / 2 && rotate < Math.PI * 1.5) { |
| rotate -= Math.PI; |
| } |
| |
| sector.setTextConfig({ |
| rotation: rotate |
| }); |
| } |
| |
| function adjustAngleDistanceX(angle, distance, isEnd) { |
| return distance * Math.sin(angle) * (isEnd ? -1 : 1); |
| } |
| |
| function adjustAngleDistanceY(angle, distance, isEnd) { |
| return distance * Math.cos(angle) * (isEnd ? 1 : -1); |
| } |
| |
| var mathMax$6 = Math.max; |
| var mathMin$6 = Math.min; |
| |
| function getClipArea(coord, data) { |
| var coordSysClipArea = coord.getArea && coord.getArea(); |
| |
| if (isCoordinateSystemType(coord, 'cartesian2d')) { |
| var baseAxis = coord.getBaseAxis(); // When boundaryGap is false or using time axis. bar may exceed the grid. |
| // We should not clip this part. |
| // See test/bar2.html |
| |
| if (baseAxis.type !== 'category' || !baseAxis.onBand) { |
| var expandWidth = data.getLayout('bandWidth'); |
| |
| if (baseAxis.isHorizontal()) { |
| coordSysClipArea.x -= expandWidth; |
| coordSysClipArea.width += expandWidth * 2; |
| } else { |
| coordSysClipArea.y -= expandWidth; |
| coordSysClipArea.height += expandWidth * 2; |
| } |
| } |
| } |
| |
| return coordSysClipArea; |
| } |
| |
| var BarView = |
| /** @class */ |
| function (_super) { |
| __extends(BarView, _super); |
| |
| function BarView() { |
| var _this = _super.call(this) || this; |
| |
| _this.type = BarView.type; |
| _this._isFirstFrame = true; |
| return _this; |
| } |
| |
| BarView.prototype.render = function (seriesModel, ecModel, api, payload) { |
| this._model = seriesModel; |
| |
| this._removeOnRenderedListener(api); |
| |
| this._updateDrawMode(seriesModel); |
| |
| var coordinateSystemType = seriesModel.get('coordinateSystem'); |
| |
| if (coordinateSystemType === 'cartesian2d' || coordinateSystemType === 'polar') { |
| // Clear previously rendered progressive elements. |
| this._progressiveEls = null; |
| this._isLargeDraw ? this._renderLarge(seriesModel, ecModel, api) : this._renderNormal(seriesModel, ecModel, api, payload); |
| } else if ("development" !== 'production') { |
| warn('Only cartesian2d and polar supported for bar.'); |
| } |
| }; |
| |
| BarView.prototype.incrementalPrepareRender = function (seriesModel) { |
| this._clear(); |
| |
| this._updateDrawMode(seriesModel); // incremental also need to clip, otherwise might be overlow. |
| // But must not set clip in each frame, otherwise all of the children will be marked redraw. |
| |
| |
| this._updateLargeClip(seriesModel); |
| }; |
| |
| BarView.prototype.incrementalRender = function (params, seriesModel) { |
| // Reset |
| this._progressiveEls = []; // Do not support progressive in normal mode. |
| |
| this._incrementalRenderLarge(params, seriesModel); |
| }; |
| |
| BarView.prototype.eachRendered = function (cb) { |
| traverseElements(this._progressiveEls || this.group, cb); |
| }; |
| |
| BarView.prototype._updateDrawMode = function (seriesModel) { |
| var isLargeDraw = seriesModel.pipelineContext.large; |
| |
| if (this._isLargeDraw == null || isLargeDraw !== this._isLargeDraw) { |
| this._isLargeDraw = isLargeDraw; |
| |
| this._clear(); |
| } |
| }; |
| |
| BarView.prototype._renderNormal = function (seriesModel, ecModel, api, payload) { |
| var group = this.group; |
| var data = seriesModel.getData(); |
| var oldData = this._data; |
| var coord = seriesModel.coordinateSystem; |
| var baseAxis = coord.getBaseAxis(); |
| var isHorizontalOrRadial; |
| |
| if (coord.type === 'cartesian2d') { |
| isHorizontalOrRadial = baseAxis.isHorizontal(); |
| } else if (coord.type === 'polar') { |
| isHorizontalOrRadial = baseAxis.dim === 'angle'; |
| } |
| |
| var animationModel = seriesModel.isAnimationEnabled() ? seriesModel : null; |
| var realtimeSortCfg = shouldRealtimeSort(seriesModel, coord); |
| |
| if (realtimeSortCfg) { |
| this._enableRealtimeSort(realtimeSortCfg, data, api); |
| } |
| |
| var needsClip = seriesModel.get('clip', true) || realtimeSortCfg; |
| var coordSysClipArea = getClipArea(coord, data); // If there is clipPath created in large mode. Remove it. |
| |
| group.removeClipPath(); // We don't use clipPath in normal mode because we needs a perfect animation |
| // And don't want the label are clipped. |
| |
| var roundCap = seriesModel.get('roundCap', true); |
| var drawBackground = seriesModel.get('showBackground', true); |
| var backgroundModel = seriesModel.getModel('backgroundStyle'); |
| var barBorderRadius = backgroundModel.get('borderRadius') || 0; |
| var bgEls = []; |
| var oldBgEls = this._backgroundEls; |
| var isInitSort = payload && payload.isInitSort; |
| var isChangeOrder = payload && payload.type === 'changeAxisOrder'; |
| |
| function createBackground(dataIndex) { |
| var bgLayout = getLayout[coord.type](data, dataIndex); |
| var bgEl = createBackgroundEl(coord, isHorizontalOrRadial, bgLayout); |
| bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius. |
| |
| if (coord.type === 'cartesian2d') { |
| bgEl.setShape('r', barBorderRadius); |
| } |
| |
| bgEls[dataIndex] = bgEl; |
| return bgEl; |
| } |
| data.diff(oldData).add(function (dataIndex) { |
| var itemModel = data.getItemModel(dataIndex); |
| var layout = getLayout[coord.type](data, dataIndex, itemModel); |
| |
| if (drawBackground) { |
| createBackground(dataIndex); |
| } // If dataZoom in filteMode: 'empty', the baseValue can be set as NaN in "axisProxy". |
| |
| |
| if (!data.hasValue(dataIndex) || !isValidLayout[coord.type](layout)) { |
| return; |
| } |
| |
| var isClipped = false; |
| |
| if (needsClip) { |
| // Clip will modify the layout params. |
| // And return a boolean to determine if the shape are fully clipped. |
| isClipped = clip[coord.type](coordSysClipArea, layout); |
| } |
| |
| var el = elementCreator[coord.type](seriesModel, data, dataIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, false, roundCap); |
| |
| if (realtimeSortCfg) { |
| /** |
| * Force label animation because even if the element is |
| * ignored because it's clipped, it may not be clipped after |
| * changing order. Then, if not using forceLabelAnimation, |
| * the label animation was never started, in which case, |
| * the label will be the final value and doesn't have label |
| * animation. |
| */ |
| el.forceLabelAnimation = true; |
| } |
| |
| updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar'); |
| |
| if (isInitSort) { |
| el.attr({ |
| shape: layout |
| }); |
| } else if (realtimeSortCfg) { |
| updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, dataIndex, isHorizontalOrRadial, false, false); |
| } else { |
| initProps(el, { |
| shape: layout |
| }, seriesModel, dataIndex); |
| } |
| |
| data.setItemGraphicEl(dataIndex, el); |
| group.add(el); |
| el.ignore = isClipped; |
| }).update(function (newIndex, oldIndex) { |
| var itemModel = data.getItemModel(newIndex); |
| var layout = getLayout[coord.type](data, newIndex, itemModel); |
| |
| if (drawBackground) { |
| var bgEl = void 0; |
| |
| if (oldBgEls.length === 0) { |
| bgEl = createBackground(oldIndex); |
| } else { |
| bgEl = oldBgEls[oldIndex]; |
| bgEl.useStyle(backgroundModel.getItemStyle()); // Only cartesian2d support borderRadius. |
| |
| if (coord.type === 'cartesian2d') { |
| bgEl.setShape('r', barBorderRadius); |
| } |
| |
| bgEls[newIndex] = bgEl; |
| } |
| |
| var bgLayout = getLayout[coord.type](data, newIndex); |
| var shape = createBackgroundShape(isHorizontalOrRadial, bgLayout, coord); |
| updateProps(bgEl, { |
| shape: shape |
| }, animationModel, newIndex); |
| } |
| |
| var el = oldData.getItemGraphicEl(oldIndex); |
| |
| if (!data.hasValue(newIndex) || !isValidLayout[coord.type](layout)) { |
| group.remove(el); |
| return; |
| } |
| |
| var isClipped = false; |
| |
| if (needsClip) { |
| isClipped = clip[coord.type](coordSysClipArea, layout); |
| |
| if (isClipped) { |
| group.remove(el); |
| } |
| } |
| |
| if (!el) { |
| el = elementCreator[coord.type](seriesModel, data, newIndex, layout, isHorizontalOrRadial, animationModel, baseAxis.model, !!el, roundCap); |
| } else { |
| saveOldStyle(el); |
| } |
| |
| if (realtimeSortCfg) { |
| el.forceLabelAnimation = true; |
| } |
| |
| if (isChangeOrder) { |
| var textEl = el.getTextContent(); |
| |
| if (textEl) { |
| var labelInnerStore = labelInner(textEl); |
| |
| if (labelInnerStore.prevValue != null) { |
| /** |
| * Set preValue to be value so that no new label |
| * should be started, otherwise, it will take a full |
| * `animationDurationUpdate` time to finish the |
| * animation, which is not expected. |
| */ |
| labelInnerStore.prevValue = labelInnerStore.value; |
| } |
| } |
| } // Not change anything if only order changed. |
| // Especially not change label. |
| else { |
| updateStyle(el, data, newIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, coord.type === 'polar'); |
| } |
| |
| if (isInitSort) { |
| el.attr({ |
| shape: layout |
| }); |
| } else if (realtimeSortCfg) { |
| updateRealtimeAnimation(realtimeSortCfg, animationModel, el, layout, newIndex, isHorizontalOrRadial, true, isChangeOrder); |
| } else { |
| updateProps(el, { |
| shape: layout |
| }, seriesModel, newIndex, null); |
| } |
| |
| data.setItemGraphicEl(newIndex, el); |
| el.ignore = isClipped; |
| group.add(el); |
| }).remove(function (dataIndex) { |
| var el = oldData.getItemGraphicEl(dataIndex); |
| el && removeElementWithFadeOut(el, seriesModel, dataIndex); |
| }).execute(); |
| var bgGroup = this._backgroundGroup || (this._backgroundGroup = new Group()); |
| bgGroup.removeAll(); |
| |
| for (var i = 0; i < bgEls.length; ++i) { |
| bgGroup.add(bgEls[i]); |
| } |
| |
| group.add(bgGroup); |
| this._backgroundEls = bgEls; |
| this._data = data; |
| }; |
| |
| BarView.prototype._renderLarge = function (seriesModel, ecModel, api) { |
| this._clear(); |
| |
| createLarge(seriesModel, this.group); |
| |
| this._updateLargeClip(seriesModel); |
| }; |
| |
| BarView.prototype._incrementalRenderLarge = function (params, seriesModel) { |
| this._removeBackground(); |
| |
| createLarge(seriesModel, this.group, this._progressiveEls, true); |
| }; |
| |
| BarView.prototype._updateLargeClip = function (seriesModel) { |
| // Use clipPath in large mode. |
| var clipPath = seriesModel.get('clip', true) && createClipPath(seriesModel.coordinateSystem, false, seriesModel); |
| var group = this.group; |
| |
| if (clipPath) { |
| group.setClipPath(clipPath); |
| } else { |
| group.removeClipPath(); |
| } |
| }; |
| |
| BarView.prototype._enableRealtimeSort = function (realtimeSortCfg, data, api) { |
| var _this = this; // If no data in the first frame, wait for data to initSort |
| |
| |
| if (!data.count()) { |
| return; |
| } |
| |
| var baseAxis = realtimeSortCfg.baseAxis; |
| |
| if (this._isFirstFrame) { |
| this._dispatchInitSort(data, realtimeSortCfg, api); |
| |
| this._isFirstFrame = false; |
| } else { |
| var orderMapping_1 = function (idx) { |
| var el = data.getItemGraphicEl(idx); |
| var shape = el && el.shape; |
| return shape && // The result should be consistent with the initial sort by data value. |
| // Do not support the case that both positive and negative exist. |
| Math.abs(baseAxis.isHorizontal() ? shape.height : shape.width) // If data is NaN, shape.xxx may be NaN, so use || 0 here in case |
| || 0; |
| }; |
| |
| this._onRendered = function () { |
| _this._updateSortWithinSameData(data, orderMapping_1, baseAxis, api); |
| }; |
| |
| api.getZr().on('rendered', this._onRendered); |
| } |
| }; |
| |
| BarView.prototype._dataSort = function (data, baseAxis, orderMapping) { |
| var info = []; |
| data.each(data.mapDimension(baseAxis.dim), function (ordinalNumber, dataIdx) { |
| var mappedValue = orderMapping(dataIdx); |
| mappedValue = mappedValue == null ? NaN : mappedValue; |
| info.push({ |
| dataIndex: dataIdx, |
| mappedValue: mappedValue, |
| ordinalNumber: ordinalNumber |
| }); |
| }); |
| info.sort(function (a, b) { |
| // If NaN, it will be treated as min val. |
| return b.mappedValue - a.mappedValue; |
| }); |
| return { |
| ordinalNumbers: map(info, function (item) { |
| return item.ordinalNumber; |
| }) |
| }; |
| }; |
| |
| BarView.prototype._isOrderChangedWithinSameData = function (data, orderMapping, baseAxis) { |
| var scale = baseAxis.scale; |
| var ordinalDataDim = data.mapDimension(baseAxis.dim); |
| var lastValue = Number.MAX_VALUE; |
| |
| for (var tickNum = 0, len = scale.getOrdinalMeta().categories.length; tickNum < len; ++tickNum) { |
| var rawIdx = data.rawIndexOf(ordinalDataDim, scale.getRawOrdinalNumber(tickNum)); |
| var value = rawIdx < 0 // If some tick have no bar, the tick will be treated as min. |
| ? Number.MIN_VALUE // PENDING: if dataZoom on baseAxis exits, is it a performance issue? |
| : orderMapping(data.indexOfRawIndex(rawIdx)); |
| |
| if (value > lastValue) { |
| return true; |
| } |
| |
| lastValue = value; |
| } |
| |
| return false; |
| }; |
| /* |
| * Consider the case when A and B changed order, whose representing |
| * bars are both out of sight, we don't wish to trigger reorder action |
| * as long as the order in the view doesn't change. |
| */ |
| |
| |
| BarView.prototype._isOrderDifferentInView = function (orderInfo, baseAxis) { |
| var scale = baseAxis.scale; |
| var extent = scale.getExtent(); |
| var tickNum = Math.max(0, extent[0]); |
| var tickMax = Math.min(extent[1], scale.getOrdinalMeta().categories.length - 1); |
| |
| for (; tickNum <= tickMax; ++tickNum) { |
| if (orderInfo.ordinalNumbers[tickNum] !== scale.getRawOrdinalNumber(tickNum)) { |
| return true; |
| } |
| } |
| }; |
| |
| BarView.prototype._updateSortWithinSameData = function (data, orderMapping, baseAxis, api) { |
| if (!this._isOrderChangedWithinSameData(data, orderMapping, baseAxis)) { |
| return; |
| } |
| |
| var sortInfo = this._dataSort(data, baseAxis, orderMapping); |
| |
| if (this._isOrderDifferentInView(sortInfo, baseAxis)) { |
| this._removeOnRenderedListener(api); |
| |
| api.dispatchAction({ |
| type: 'changeAxisOrder', |
| componentType: baseAxis.dim + 'Axis', |
| axisId: baseAxis.index, |
| sortInfo: sortInfo |
| }); |
| } |
| }; |
| |
| BarView.prototype._dispatchInitSort = function (data, realtimeSortCfg, api) { |
| var baseAxis = realtimeSortCfg.baseAxis; |
| |
| var sortResult = this._dataSort(data, baseAxis, function (dataIdx) { |
| return data.get(data.mapDimension(realtimeSortCfg.otherAxis.dim), dataIdx); |
| }); |
| |
| api.dispatchAction({ |
| type: 'changeAxisOrder', |
| componentType: baseAxis.dim + 'Axis', |
| isInitSort: true, |
| axisId: baseAxis.index, |
| sortInfo: sortResult |
| }); |
| }; |
| |
| BarView.prototype.remove = function (ecModel, api) { |
| this._clear(this._model); |
| |
| this._removeOnRenderedListener(api); |
| }; |
| |
| BarView.prototype.dispose = function (ecModel, api) { |
| this._removeOnRenderedListener(api); |
| }; |
| |
| BarView.prototype._removeOnRenderedListener = function (api) { |
| if (this._onRendered) { |
| api.getZr().off('rendered', this._onRendered); |
| this._onRendered = null; |
| } |
| }; |
| |
| BarView.prototype._clear = function (model) { |
| var group = this.group; |
| var data = this._data; |
| |
| if (model && model.isAnimationEnabled() && data && !this._isLargeDraw) { |
| this._removeBackground(); |
| |
| this._backgroundEls = []; |
| data.eachItemGraphicEl(function (el) { |
| removeElementWithFadeOut(el, model, getECData(el).dataIndex); |
| }); |
| } else { |
| group.removeAll(); |
| } |
| |
| this._data = null; |
| this._isFirstFrame = true; |
| }; |
| |
| BarView.prototype._removeBackground = function () { |
| this.group.remove(this._backgroundGroup); |
| this._backgroundGroup = null; |
| }; |
| |
| BarView.type = 'bar'; |
| return BarView; |
| }(ChartView); |
| |
| var clip = { |
| cartesian2d: function (coordSysBoundingRect, layout) { |
| var signWidth = layout.width < 0 ? -1 : 1; |
| var signHeight = layout.height < 0 ? -1 : 1; // Needs positive width and height |
| |
| if (signWidth < 0) { |
| layout.x += layout.width; |
| layout.width = -layout.width; |
| } |
| |
| if (signHeight < 0) { |
| layout.y += layout.height; |
| layout.height = -layout.height; |
| } |
| |
| var coordSysX2 = coordSysBoundingRect.x + coordSysBoundingRect.width; |
| var coordSysY2 = coordSysBoundingRect.y + coordSysBoundingRect.height; |
| var x = mathMax$6(layout.x, coordSysBoundingRect.x); |
| var x2 = mathMin$6(layout.x + layout.width, coordSysX2); |
| var y = mathMax$6(layout.y, coordSysBoundingRect.y); |
| var y2 = mathMin$6(layout.y + layout.height, coordSysY2); |
| var xClipped = x2 < x; |
| var yClipped = y2 < y; // When xClipped or yClipped, the element will be marked as `ignore`. |
| // But we should also place the element at the edge of the coord sys bounding rect. |
| // Because if data changed and the bar shows again, its transition animation |
| // will begin at this place. |
| |
| layout.x = xClipped && x > coordSysX2 ? x2 : x; |
| layout.y = yClipped && y > coordSysY2 ? y2 : y; |
| layout.width = xClipped ? 0 : x2 - x; |
| layout.height = yClipped ? 0 : y2 - y; // Reverse back |
| |
| if (signWidth < 0) { |
| layout.x += layout.width; |
| layout.width = -layout.width; |
| } |
| |
| if (signHeight < 0) { |
| layout.y += layout.height; |
| layout.height = -layout.height; |
| } |
| |
| return xClipped || yClipped; |
| }, |
| polar: function (coordSysClipArea, layout) { |
| var signR = layout.r0 <= layout.r ? 1 : -1; // Make sure r is larger than r0 |
| |
| if (signR < 0) { |
| var tmp = layout.r; |
| layout.r = layout.r0; |
| layout.r0 = tmp; |
| } |
| |
| var r = mathMin$6(layout.r, coordSysClipArea.r); |
| var r0 = mathMax$6(layout.r0, coordSysClipArea.r0); |
| layout.r = r; |
| layout.r0 = r0; |
| var clipped = r - r0 < 0; // Reverse back |
| |
| if (signR < 0) { |
| var tmp = layout.r; |
| layout.r = layout.r0; |
| layout.r0 = tmp; |
| } |
| |
| return clipped; |
| } |
| }; |
| var elementCreator = { |
| cartesian2d: function (seriesModel, data, newIndex, layout, isHorizontal, animationModel, axisModel, isUpdate, roundCap) { |
| var rect = new Rect({ |
| shape: extend({}, layout), |
| z2: 1 |
| }); |
| rect.__dataIndex = newIndex; |
| rect.name = 'item'; |
| |
| if (animationModel) { |
| var rectShape = rect.shape; |
| var animateProperty = isHorizontal ? 'height' : 'width'; |
| rectShape[animateProperty] = 0; |
| } |
| |
| return rect; |
| }, |
| polar: function (seriesModel, data, newIndex, layout, isRadial, animationModel, axisModel, isUpdate, roundCap) { |
| var ShapeClass = !isRadial && roundCap ? SausagePath : Sector; |
| var sector = new ShapeClass({ |
| shape: layout, |
| z2: 1 |
| }); |
| sector.name = 'item'; |
| var positionMap = createPolarPositionMapping(isRadial); |
| sector.calculateTextPosition = createSectorCalculateTextPosition(positionMap, { |
| isRoundCap: ShapeClass === SausagePath |
| }); // Animation |
| |
| if (animationModel) { |
| var sectorShape = sector.shape; |
| var animateProperty = isRadial ? 'r' : 'endAngle'; |
| var animateTarget = {}; |
| sectorShape[animateProperty] = isRadial ? 0 : layout.startAngle; |
| animateTarget[animateProperty] = layout[animateProperty]; |
| (isUpdate ? updateProps : initProps)(sector, { |
| shape: animateTarget // __value: typeof dataValue === 'string' ? parseInt(dataValue, 10) : dataValue |
| |
| }, animationModel); |
| } |
| |
| return sector; |
| } |
| }; |
| |
| function shouldRealtimeSort(seriesModel, coordSys) { |
| var realtimeSortOption = seriesModel.get('realtimeSort', true); |
| var baseAxis = coordSys.getBaseAxis(); |
| |
| if ("development" !== 'production') { |
| if (realtimeSortOption) { |
| if (baseAxis.type !== 'category') { |
| warn('`realtimeSort` will not work because this bar series is not based on a category axis.'); |
| } |
| |
| if (coordSys.type !== 'cartesian2d') { |
| warn('`realtimeSort` will not work because this bar series is not on cartesian2d.'); |
| } |
| } |
| } |
| |
| if (realtimeSortOption && baseAxis.type === 'category' && coordSys.type === 'cartesian2d') { |
| return { |
| baseAxis: baseAxis, |
| otherAxis: coordSys.getOtherAxis(baseAxis) |
| }; |
| } |
| } |
| |
| function updateRealtimeAnimation(realtimeSortCfg, seriesAnimationModel, el, layout, newIndex, isHorizontal, isUpdate, isChangeOrder) { |
| var seriesTarget; |
| var axisTarget; |
| |
| if (isHorizontal) { |
| axisTarget = { |
| x: layout.x, |
| width: layout.width |
| }; |
| seriesTarget = { |
| y: layout.y, |
| height: layout.height |
| }; |
| } else { |
| axisTarget = { |
| y: layout.y, |
| height: layout.height |
| }; |
| seriesTarget = { |
| x: layout.x, |
| width: layout.width |
| }; |
| } |
| |
| if (!isChangeOrder) { |
| // Keep the original growth animation if only axis order changed. |
| // Not start a new animation. |
| (isUpdate ? updateProps : initProps)(el, { |
| shape: seriesTarget |
| }, seriesAnimationModel, newIndex, null); |
| } |
| |
| var axisAnimationModel = seriesAnimationModel ? realtimeSortCfg.baseAxis.model : null; |
| (isUpdate ? updateProps : initProps)(el, { |
| shape: axisTarget |
| }, axisAnimationModel, newIndex); |
| } |
| |
| function checkPropertiesNotValid(obj, props) { |
| for (var i = 0; i < props.length; i++) { |
| if (!isFinite(obj[props[i]])) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| var rectPropties = ['x', 'y', 'width', 'height']; |
| var polarPropties = ['cx', 'cy', 'r', 'startAngle', 'endAngle']; |
| var isValidLayout = { |
| cartesian2d: function (layout) { |
| return !checkPropertiesNotValid(layout, rectPropties); |
| }, |
| polar: function (layout) { |
| return !checkPropertiesNotValid(layout, polarPropties); |
| } |
| }; |
| var getLayout = { |
| // itemModel is only used to get borderWidth, which is not needed |
| // when calculating bar background layout. |
| cartesian2d: function (data, dataIndex, itemModel) { |
| var layout = data.getItemLayout(dataIndex); |
| var fixedLineWidth = itemModel ? getLineWidth(itemModel, layout) : 0; // fix layout with lineWidth |
| |
| var signX = layout.width > 0 ? 1 : -1; |
| var signY = layout.height > 0 ? 1 : -1; |
| return { |
| x: layout.x + signX * fixedLineWidth / 2, |
| y: layout.y + signY * fixedLineWidth / 2, |
| width: layout.width - signX * fixedLineWidth, |
| height: layout.height - signY * fixedLineWidth |
| }; |
| }, |
| polar: function (data, dataIndex, itemModel) { |
| var layout = data.getItemLayout(dataIndex); |
| return { |
| cx: layout.cx, |
| cy: layout.cy, |
| r0: layout.r0, |
| r: layout.r, |
| startAngle: layout.startAngle, |
| endAngle: layout.endAngle, |
| clockwise: layout.clockwise |
| }; |
| } |
| }; |
| |
| function isZeroOnPolar(layout) { |
| return layout.startAngle != null && layout.endAngle != null && layout.startAngle === layout.endAngle; |
| } |
| |
| function createPolarPositionMapping(isRadial) { |
| return function (isRadial) { |
| var arcOrAngle = isRadial ? 'Arc' : 'Angle'; |
| return function (position) { |
| switch (position) { |
| case 'start': |
| case 'insideStart': |
| case 'end': |
| case 'insideEnd': |
| return position + arcOrAngle; |
| |
| default: |
| return position; |
| } |
| }; |
| }(isRadial); |
| } |
| |
| function updateStyle(el, data, dataIndex, itemModel, layout, seriesModel, isHorizontalOrRadial, isPolar) { |
| var style = data.getItemVisual(dataIndex, 'style'); |
| |
| if (!isPolar) { |
| el.setShape('r', itemModel.get(['itemStyle', 'borderRadius']) || 0); |
| } |
| |
| el.useStyle(style); |
| var cursorStyle = itemModel.getShallow('cursor'); |
| cursorStyle && el.attr('cursor', cursorStyle); |
| var labelPositionOutside = isPolar ? isHorizontalOrRadial ? layout.r >= layout.r0 ? 'endArc' : 'startArc' : layout.endAngle >= layout.startAngle ? 'endAngle' : 'startAngle' : isHorizontalOrRadial ? layout.height >= 0 ? 'bottom' : 'top' : layout.width >= 0 ? 'right' : 'left'; |
| var labelStatesModels = getLabelStatesModels(itemModel); |
| setLabelStyle(el, labelStatesModels, { |
| labelFetcher: seriesModel, |
| labelDataIndex: dataIndex, |
| defaultText: getDefaultLabel(seriesModel.getData(), dataIndex), |
| inheritColor: style.fill, |
| defaultOpacity: style.opacity, |
| defaultOutsidePosition: labelPositionOutside |
| }); |
| var label = el.getTextContent(); |
| |
| if (isPolar && label) { |
| var position = itemModel.get(['label', 'position']); |
| el.textConfig.inside = position === 'middle' ? true : null; |
| setSectorTextRotation(el, position === 'outside' ? labelPositionOutside : position, createPolarPositionMapping(isHorizontalOrRadial), itemModel.get(['label', 'rotate'])); |
| } |
| |
| setLabelValueAnimation(label, labelStatesModels, seriesModel.getRawValue(dataIndex), function (value) { |
| return getDefaultInterpolatedLabel(data, value); |
| }); |
| var emphasisModel = itemModel.getModel(['emphasis']); |
| toggleHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); |
| setStatesStylesFromModel(el, itemModel); |
| |
| if (isZeroOnPolar(layout)) { |
| el.style.fill = 'none'; |
| el.style.stroke = 'none'; |
| each(el.states, function (state) { |
| if (state.style) { |
| state.style.fill = state.style.stroke = 'none'; |
| } |
| }); |
| } |
| } // In case width or height are too small. |
| |
| |
| function getLineWidth(itemModel, rawLayout) { |
| // Has no border. |
| var borderColor = itemModel.get(['itemStyle', 'borderColor']); |
| |
| if (!borderColor || borderColor === 'none') { |
| return 0; |
| } |
| |
| var lineWidth = itemModel.get(['itemStyle', 'borderWidth']) || 0; // width or height may be NaN for empty data |
| |
| var width = isNaN(rawLayout.width) ? Number.MAX_VALUE : Math.abs(rawLayout.width); |
| var height = isNaN(rawLayout.height) ? Number.MAX_VALUE : Math.abs(rawLayout.height); |
| return Math.min(lineWidth, width, height); |
| } |
| |
| var LagePathShape = |
| /** @class */ |
| function () { |
| function LagePathShape() {} |
| |
| return LagePathShape; |
| }(); |
| |
| var LargePath = |
| /** @class */ |
| function (_super) { |
| __extends(LargePath, _super); |
| |
| function LargePath(opts) { |
| var _this = _super.call(this, opts) || this; |
| |
| _this.type = 'largeBar'; |
| return _this; |
| } |
| |
| LargePath.prototype.getDefaultShape = function () { |
| return new LagePathShape(); |
| }; |
| |
| LargePath.prototype.buildPath = function (ctx, shape) { |
| // Drawing lines is more efficient than drawing |
| // a whole line or drawing rects. |
| var points = shape.points; |
| var baseDimIdx = this.baseDimIdx; |
| var valueDimIdx = 1 - this.baseDimIdx; |
| var startPoint = []; |
| var size = []; |
| var barWidth = this.barWidth; |
| |
| for (var i = 0; i < points.length; i += 3) { |
| size[baseDimIdx] = barWidth; |
| size[valueDimIdx] = points[i + 2]; |
| startPoint[baseDimIdx] = points[i + baseDimIdx]; |
| startPoint[valueDimIdx] = points[i + valueDimIdx]; |
| ctx.rect(startPoint[0], startPoint[1], size[0], size[1]); |
| } |
| }; |
| |
| return LargePath; |
| }(Path); |
| |
| function createLarge(seriesModel, group, progressiveEls, incremental) { |
| // TODO support polar |
| var data = seriesModel.getData(); |
| var baseDimIdx = data.getLayout('valueAxisHorizontal') ? 1 : 0; |
| var largeDataIndices = data.getLayout('largeDataIndices'); |
| var barWidth = data.getLayout('size'); |
| var backgroundModel = seriesModel.getModel('backgroundStyle'); |
| var bgPoints = data.getLayout('largeBackgroundPoints'); |
| |
| if (bgPoints) { |
| var bgEl = new LargePath({ |
| shape: { |
| points: bgPoints |
| }, |
| incremental: !!incremental, |
| silent: true, |
| z2: 0 |
| }); |
| bgEl.baseDimIdx = baseDimIdx; |
| bgEl.largeDataIndices = largeDataIndices; |
| bgEl.barWidth = barWidth; |
| bgEl.useStyle(backgroundModel.getItemStyle()); |
| group.add(bgEl); |
| progressiveEls && progressiveEls.push(bgEl); |
| } |
| |
| var el = new LargePath({ |
| shape: { |
| points: data.getLayout('largePoints') |
| }, |
| incremental: !!incremental, |
| ignoreCoarsePointer: true, |
| z2: 1 |
| }); |
| el.baseDimIdx = baseDimIdx; |
| el.largeDataIndices = largeDataIndices; |
| el.barWidth = barWidth; |
| group.add(el); |
| el.useStyle(data.getVisual('style')); // Enable tooltip and user mouse/touch event handlers. |
| |
| getECData(el).seriesIndex = seriesModel.seriesIndex; |
| |
| if (!seriesModel.get('silent')) { |
| el.on('mousedown', largePathUpdateDataIndex); |
| el.on('mousemove', largePathUpdateDataIndex); |
| } |
| |
| progressiveEls && progressiveEls.push(el); |
| } // Use throttle to avoid frequently traverse to find dataIndex. |
| |
| |
| var largePathUpdateDataIndex = throttle(function (event) { |
| var largePath = this; |
| var dataIndex = largePathFindDataIndex(largePath, event.offsetX, event.offsetY); |
| getECData(largePath).dataIndex = dataIndex >= 0 ? dataIndex : null; |
| }, 30, false); |
| |
| function largePathFindDataIndex(largePath, x, y) { |
| var baseDimIdx = largePath.baseDimIdx; |
| var valueDimIdx = 1 - baseDimIdx; |
| var points = largePath.shape.points; |
| var largeDataIndices = largePath.largeDataIndices; |
| var startPoint = []; |
| var size = []; |
| var barWidth = largePath.barWidth; |
| |
| for (var i = 0, len = points.length / 3; i < len; i++) { |
| var ii = i * 3; |
| size[baseDimIdx] = barWidth; |
| size[valueDimIdx] = points[ii + 2]; |
| startPoint[baseDimIdx] = points[ii + baseDimIdx]; |
| startPoint[valueDimIdx] = points[ii + valueDimIdx]; |
| |
| if (size[valueDimIdx] < 0) { |
| startPoint[valueDimIdx] += size[valueDimIdx]; |
| size[valueDimIdx] = -size[valueDimIdx]; |
| } |
| |
| if (x >= startPoint[0] && x <= startPoint[0] + size[0] && y >= startPoint[1] && y <= startPoint[1] + size[1]) { |
| return largeDataIndices[i]; |
| } |
| } |
| |
| return -1; |
| } |
| |
| function createBackgroundShape(isHorizontalOrRadial, layout, coord) { |
| if (isCoordinateSystemType(coord, 'cartesian2d')) { |
| var rectShape = layout; |
| var coordLayout = coord.getArea(); |
| return { |
| x: isHorizontalOrRadial ? rectShape.x : coordLayout.x, |
| y: isHorizontalOrRadial ? coordLayout.y : rectShape.y, |
| width: isHorizontalOrRadial ? rectShape.width : coordLayout.width, |
| height: isHorizontalOrRadial ? coordLayout.height : rectShape.height |
| }; |
| } else { |
| var coordLayout = coord.getArea(); |
| var sectorShape = layout; |
| return { |
| cx: coordLayout.cx, |
| cy: coordLayout.cy, |
| r0: isHorizontalOrRadial ? coordLayout.r0 : sectorShape.r0, |
| r: isHorizontalOrRadial ? coordLayout.r : sectorShape.r, |
| startAngle: isHorizontalOrRadial ? sectorShape.startAngle : 0, |
| endAngle: isHorizontalOrRadial ? sectorShape.endAngle : Math.PI * 2 |
| }; |
| } |
| } |
| |
| function createBackgroundEl(coord, isHorizontalOrRadial, layout) { |
| var ElementClz = coord.type === 'polar' ? Sector : Rect; |
| return new ElementClz({ |
| shape: createBackgroundShape(isHorizontalOrRadial, layout, coord), |
| silent: true, |
| z2: 0 |
| }); |
| } |
| |
| function install$2(registers) { |
| registers.registerChartView(BarView); |
| registers.registerSeriesModel(BarSeriesModel); |
| registers.registerLayout(registers.PRIORITY.VISUAL.LAYOUT, curry(layout, 'bar')); // Do layout after other overall layout, which can prepare some information. |
| |
| registers.registerLayout(registers.PRIORITY.VISUAL.PROGRESSIVE_LAYOUT, createProgressiveLayout('bar')); // Down sample after filter |
| |
| registers.registerProcessor(registers.PRIORITY.PROCESSOR.STATISTIC, dataSample('bar')); |
| /** |
| * @payload |
| * @property {string} [componentType=series] |
| * @property {number} [dx] |
| * @property {number} [dy] |
| * @property {number} [zoom] |
| * @property {number} [originX] |
| * @property {number} [originY] |
| */ |
| |
| registers.registerAction({ |
| type: 'changeAxisOrder', |
| event: 'changeAxisOrder', |
| update: 'update' |
| }, function (payload, ecModel) { |
| var componentType = payload.componentType || 'series'; |
| ecModel.eachComponent({ |
| mainType: componentType, |
| query: payload |
| }, function (componentModel) { |
| if (payload.sortInfo) { |
| componentModel.axis.setCategorySortInfo(payload.sortInfo); |
| } |
| }); |
| }); |
| } |
| |
| var PI2$6 = Math.PI * 2; |
| var RADIAN = Math.PI / 180; |
| |
| function getViewRect(seriesModel, api) { |
| return getLayoutRect(seriesModel.getBoxLayoutParams(), { |
| width: api.getWidth(), |
| height: api.getHeight() |
| }); |
| } |
| |
| function getBasicPieLayout(seriesModel, api) { |
| var viewRect = getViewRect(seriesModel, api); // center can be string or number when coordinateSystem is specified |
| |
| var center = seriesModel.get('center'); |
| var radius = seriesModel.get('radius'); |
| |
| if (!isArray(radius)) { |
| radius = [0, radius]; |
| } |
| |
| var width = parsePercent$1(viewRect.width, api.getWidth()); |
| var height = parsePercent$1(viewRect.height, api.getHeight()); |
| var size = Math.min(width, height); |
| var r0 = parsePercent$1(radius[0], size / 2); |
| var r = parsePercent$1(radius[1], size / 2); |
| var cx; |
| var cy; |
| var coordSys = seriesModel.coordinateSystem; |
| |
| if (coordSys) { |
| // percentage is not allowed when coordinate system is specified |
| var point = coordSys.dataToPoint(center); |
| cx = point[0] || 0; |
| cy = point[1] || 0; |
| } else { |
| if (!isArray(center)) { |
| center = [center, center]; |
| } |
| |
| cx = parsePercent$1(center[0], width) + viewRect.x; |
| cy = parsePercent$1(center[1], height) + viewRect.y; |
| } |
| |
| return { |
| cx: cx, |
| cy: cy, |
| r0: r0, |
| r: r |
| }; |
| } |
| function pieLayout(seriesType, ecModel, api) { |
| ecModel.eachSeriesByType(seriesType, function (seriesModel) { |
| var data = seriesModel.getData(); |
| var valueDim = data.mapDimension('value'); |
| var viewRect = getViewRect(seriesModel, api); |
| |
| var _a = getBasicPieLayout(seriesModel, api), |
| cx = _a.cx, |
| cy = _a.cy, |
| r = _a.r, |
| r0 = _a.r0; |
| |
| var startAngle = -seriesModel.get('startAngle') * RADIAN; |
| var minAngle = seriesModel.get('minAngle') * RADIAN; |
| var validDataCount = 0; |
| data.each(valueDim, function (value) { |
| !isNaN(value) && validDataCount++; |
| }); |
| var sum = data.getSum(valueDim); // Sum may be 0 |
| |
| var unitRadian = Math.PI / (sum || validDataCount) * 2; |
| var clockwise = seriesModel.get('clockwise'); |
| var roseType = seriesModel.get('roseType'); |
| var stillShowZeroSum = seriesModel.get('stillShowZeroSum'); // [0...max] |
| |
| var extent = data.getDataExtent(valueDim); |
| extent[0] = 0; // In the case some sector angle is smaller than minAngle |
| |
| var restAngle = PI2$6; |
| var valueSumLargerThanMinAngle = 0; |
| var currentAngle = startAngle; |
| var dir = clockwise ? 1 : -1; |
| data.setLayout({ |
| viewRect: viewRect, |
| r: r |
| }); |
| data.each(valueDim, function (value, idx) { |
| var angle; |
| |
| if (isNaN(value)) { |
| data.setItemLayout(idx, { |
| angle: NaN, |
| startAngle: NaN, |
| endAngle: NaN, |
| clockwise: clockwise, |
| cx: cx, |
| cy: cy, |
| r0: r0, |
| r: roseType ? NaN : r |
| }); |
| return; |
| } // FIXME 兼容 2.0 但是 roseType 是 area 的时候才是这样? |
| |
| |
| if (roseType !== 'area') { |
| angle = sum === 0 && stillShowZeroSum ? unitRadian : value * unitRadian; |
| } else { |
| angle = PI2$6 / validDataCount; |
| } |
| |
| if (angle < minAngle) { |
| angle = minAngle; |
| restAngle -= minAngle; |
| } else { |
| valueSumLargerThanMinAngle += value; |
| } |
| |
| var endAngle = currentAngle + dir * angle; |
| data.setItemLayout(idx, { |
| angle: angle, |
| startAngle: currentAngle, |
| endAngle: endAngle, |
| clockwise: clockwise, |
| cx: cx, |
| cy: cy, |
| r0: r0, |
| r: roseType ? linearMap(value, extent, [r0, r]) : r |
| }); |
| currentAngle = endAngle; |
| }); // Some sector is constrained by minAngle |
| // Rest sectors needs recalculate angle |
| |
| if (restAngle < PI2$6 && validDataCount) { |
| // Average the angle if rest angle is not enough after all angles is |
| // Constrained by minAngle |
| if (restAngle <= 1e-3) { |
| var angle_1 = PI2$6 / validDataCount; |
| data.each(valueDim, function (value, idx) { |
| if (!isNaN(value)) { |
| var layout_1 = data.getItemLayout(idx); |
| layout_1.angle = angle_1; |
| layout_1.startAngle = startAngle + dir * idx * angle_1; |
| layout_1.endAngle = startAngle + dir * (idx + 1) * angle_1; |
| } |
| }); |
| } else { |
| unitRadian = restAngle / valueSumLargerThanMinAngle; |
| currentAngle = startAngle; |
| data.each(valueDim, function (value, idx) { |
| if (!isNaN(value)) { |
| var layout_2 = data.getItemLayout(idx); |
| var angle = layout_2.angle === minAngle ? minAngle : value * unitRadian; |
| layout_2.startAngle = currentAngle; |
| layout_2.endAngle = currentAngle + dir * angle; |
| currentAngle += dir * angle; |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| function dataFilter(seriesType) { |
| return { |
| seriesType: seriesType, |
| reset: function (seriesModel, ecModel) { |
| var legendModels = ecModel.findComponents({ |
| mainType: 'legend' |
| }); |
| |
| if (!legendModels || !legendModels.length) { |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| data.filterSelf(function (idx) { |
| var name = data.getName(idx); // If in any legend component the status is not selected. |
| |
| for (var i = 0; i < legendModels.length; i++) { |
| // @ts-ignore FIXME: LegendModel |
| if (!legendModels[i].isSelected(name)) { |
| return false; |
| } |
| } |
| |
| return true; |
| }); |
| } |
| }; |
| } |
| |
| var RADIAN$1 = Math.PI / 180; |
| |
| function adjustSingleSide(list, cx, cy, r, dir, viewWidth, viewHeight, viewLeft, viewTop, farthestX) { |
| if (list.length < 2) { |
| return; |
| } |
| |
| function recalculateXOnSemiToAlignOnEllipseCurve(semi) { |
| var rB = semi.rB; |
| var rB2 = rB * rB; |
| |
| for (var i = 0; i < semi.list.length; i++) { |
| var item = semi.list[i]; |
| var dy = Math.abs(item.label.y - cy); // horizontal r is always same with original r because x is not changed. |
| |
| var rA = r + item.len; |
| var rA2 = rA * rA; // Use ellipse implicit function to calculate x |
| |
| var dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2); |
| var newX = cx + (dx + item.len2) * dir; |
| var deltaX = newX - item.label.x; |
| var newTargetWidth = item.targetTextWidth - deltaX * dir; // text x is changed, so need to recalculate width. |
| |
| constrainTextWidth(item, newTargetWidth, true); |
| item.label.x = newX; |
| } |
| } // Adjust X based on the shifted y. Make tight labels aligned on an ellipse curve. |
| |
| |
| function recalculateX(items) { |
| // Extremes of |
| var topSemi = { |
| list: [], |
| maxY: 0 |
| }; |
| var bottomSemi = { |
| list: [], |
| maxY: 0 |
| }; |
| |
| for (var i = 0; i < items.length; i++) { |
| if (items[i].labelAlignTo !== 'none') { |
| continue; |
| } |
| |
| var item = items[i]; |
| var semi = item.label.y > cy ? bottomSemi : topSemi; |
| var dy = Math.abs(item.label.y - cy); |
| |
| if (dy >= semi.maxY) { |
| var dx = item.label.x - cx - item.len2 * dir; // horizontal r is always same with original r because x is not changed. |
| |
| var rA = r + item.len; // Canculate rB based on the topest / bottemest label. |
| |
| var rB = Math.abs(dx) < rA ? Math.sqrt(dy * dy / (1 - dx * dx / rA / rA)) : rA; |
| semi.rB = rB; |
| semi.maxY = dy; |
| } |
| |
| semi.list.push(item); |
| } |
| |
| recalculateXOnSemiToAlignOnEllipseCurve(topSemi); |
| recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi); |
| } |
| |
| var len = list.length; |
| |
| for (var i = 0; i < len; i++) { |
| if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') { |
| var dx = list[i].label.x - farthestX; |
| list[i].linePoints[1][0] += dx; |
| list[i].label.x = farthestX; |
| } |
| } |
| |
| if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) { |
| recalculateX(list); |
| } |
| } |
| |
| function avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop) { |
| var leftList = []; |
| var rightList = []; |
| var leftmostX = Number.MAX_VALUE; |
| var rightmostX = -Number.MAX_VALUE; |
| |
| for (var i = 0; i < labelLayoutList.length; i++) { |
| var label = labelLayoutList[i].label; |
| |
| if (isPositionCenter(labelLayoutList[i])) { |
| continue; |
| } |
| |
| if (label.x < cx) { |
| leftmostX = Math.min(leftmostX, label.x); |
| leftList.push(labelLayoutList[i]); |
| } else { |
| rightmostX = Math.max(rightmostX, label.x); |
| rightList.push(labelLayoutList[i]); |
| } |
| } |
| |
| for (var i = 0; i < labelLayoutList.length; i++) { |
| var layout = labelLayoutList[i]; |
| |
| if (!isPositionCenter(layout) && layout.linePoints) { |
| if (layout.labelStyleWidth != null) { |
| continue; |
| } |
| |
| var label = layout.label; |
| var linePoints = layout.linePoints; |
| var targetTextWidth = void 0; |
| |
| if (layout.labelAlignTo === 'edge') { |
| if (label.x < cx) { |
| targetTextWidth = linePoints[2][0] - layout.labelDistance - viewLeft - layout.edgeDistance; |
| } else { |
| targetTextWidth = viewLeft + viewWidth - layout.edgeDistance - linePoints[2][0] - layout.labelDistance; |
| } |
| } else if (layout.labelAlignTo === 'labelLine') { |
| if (label.x < cx) { |
| targetTextWidth = leftmostX - viewLeft - layout.bleedMargin; |
| } else { |
| targetTextWidth = viewLeft + viewWidth - rightmostX - layout.bleedMargin; |
| } |
| } else { |
| if (label.x < cx) { |
| targetTextWidth = label.x - viewLeft - layout.bleedMargin; |
| } else { |
| targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin; |
| } |
| } |
| |
| layout.targetTextWidth = targetTextWidth; |
| constrainTextWidth(layout, targetTextWidth); |
| } |
| } |
| |
| adjustSingleSide(rightList, cx, cy, r, 1, viewWidth, viewHeight, viewLeft, viewTop, rightmostX); |
| adjustSingleSide(leftList, cx, cy, r, -1, viewWidth, viewHeight, viewLeft, viewTop, leftmostX); |
| |
| for (var i = 0; i < labelLayoutList.length; i++) { |
| var layout = labelLayoutList[i]; |
| |
| if (!isPositionCenter(layout) && layout.linePoints) { |
| var label = layout.label; |
| var linePoints = layout.linePoints; |
| var isAlignToEdge = layout.labelAlignTo === 'edge'; |
| var padding = label.style.padding; |
| var paddingH = padding ? padding[1] + padding[3] : 0; // textRect.width already contains paddingH if bgColor is set |
| |
| var extraPaddingH = label.style.backgroundColor ? 0 : paddingH; |
| var realTextWidth = layout.rect.width + extraPaddingH; |
| var dist = linePoints[1][0] - linePoints[2][0]; |
| |
| if (isAlignToEdge) { |
| if (label.x < cx) { |
| linePoints[2][0] = viewLeft + layout.edgeDistance + realTextWidth + layout.labelDistance; |
| } else { |
| linePoints[2][0] = viewLeft + viewWidth - layout.edgeDistance - realTextWidth - layout.labelDistance; |
| } |
| } else { |
| if (label.x < cx) { |
| linePoints[2][0] = label.x + layout.labelDistance; |
| } else { |
| linePoints[2][0] = label.x - layout.labelDistance; |
| } |
| |
| linePoints[1][0] = linePoints[2][0] + dist; |
| } |
| |
| linePoints[1][1] = linePoints[2][1] = label.y; |
| } |
| } |
| } |
| /** |
| * Set max width of each label, and then wrap each label to the max width. |
| * |
| * @param layout label layout |
| * @param availableWidth max width for the label to display |
| * @param forceRecalculate recaculate the text layout even if the current width |
| * is smaller than `availableWidth`. This is useful when the text was previously |
| * wrapped by calling `constrainTextWidth` but now `availableWidth` changed, in |
| * which case, previous wrapping should be redo. |
| */ |
| |
| |
| function constrainTextWidth(layout, availableWidth, forceRecalculate) { |
| if (forceRecalculate === void 0) { |
| forceRecalculate = false; |
| } |
| |
| if (layout.labelStyleWidth != null) { |
| // User-defined style.width has the highest priority. |
| return; |
| } |
| |
| var label = layout.label; |
| var style = label.style; |
| var textRect = layout.rect; |
| var bgColor = style.backgroundColor; |
| var padding = style.padding; |
| var paddingH = padding ? padding[1] + padding[3] : 0; |
| var overflow = style.overflow; // textRect.width already contains paddingH if bgColor is set |
| |
| var oldOuterWidth = textRect.width + (bgColor ? 0 : paddingH); |
| |
| if (availableWidth < oldOuterWidth || forceRecalculate) { |
| var oldHeight = textRect.height; |
| |
| if (overflow && overflow.match('break')) { |
| // Temporarily set background to be null to calculate |
| // the bounding box without background. |
| label.setStyle('backgroundColor', null); // Set constraining width |
| |
| label.setStyle('width', availableWidth - paddingH); // This is the real bounding box of the text without padding. |
| |
| var innerRect = label.getBoundingRect(); |
| label.setStyle('width', Math.ceil(innerRect.width)); |
| label.setStyle('backgroundColor', bgColor); |
| } else { |
| var availableInnerWidth = availableWidth - paddingH; |
| var newWidth = availableWidth < oldOuterWidth // Current text is too wide, use `availableWidth` as max width. |
| ? availableInnerWidth : // Current available width is enough, but the text may have |
| // already been wrapped with a smaller available width. |
| forceRecalculate ? availableInnerWidth > layout.unconstrainedWidth // Current available is larger than text width, |
| // so don't constrain width (otherwise it may have |
| // empty space in the background). |
| ? null // Current available is smaller than text width, so |
| // use the current available width as constraining |
| // width. |
| : availableInnerWidth : // Current available width is enough, so no need to |
| // constrain. |
| null; |
| label.setStyle('width', newWidth); |
| } |
| |
| var newRect = label.getBoundingRect(); |
| textRect.width = newRect.width; |
| var margin = (label.style.margin || 0) + 2.1; |
| textRect.height = newRect.height + margin; |
| textRect.y -= (textRect.height - oldHeight) / 2; |
| } |
| } |
| |
| function isPositionCenter(sectorShape) { |
| // Not change x for center label |
| return sectorShape.position === 'center'; |
| } |
| |
| function pieLabelLayout(seriesModel) { |
| var data = seriesModel.getData(); |
| var labelLayoutList = []; |
| var cx; |
| var cy; |
| var hasLabelRotate = false; |
| var minShowLabelRadian = (seriesModel.get('minShowLabelAngle') || 0) * RADIAN$1; |
| var viewRect = data.getLayout('viewRect'); |
| var r = data.getLayout('r'); |
| var viewWidth = viewRect.width; |
| var viewLeft = viewRect.x; |
| var viewTop = viewRect.y; |
| var viewHeight = viewRect.height; |
| |
| function setNotShow(el) { |
| el.ignore = true; |
| } |
| |
| function isLabelShown(label) { |
| if (!label.ignore) { |
| return true; |
| } |
| |
| for (var key in label.states) { |
| if (label.states[key].ignore === false) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| data.each(function (idx) { |
| var sector = data.getItemGraphicEl(idx); |
| var sectorShape = sector.shape; |
| var label = sector.getTextContent(); |
| var labelLine = sector.getTextGuideLine(); |
| var itemModel = data.getItemModel(idx); |
| var labelModel = itemModel.getModel('label'); // Use position in normal or emphasis |
| |
| var labelPosition = labelModel.get('position') || itemModel.get(['emphasis', 'label', 'position']); |
| var labelDistance = labelModel.get('distanceToLabelLine'); |
| var labelAlignTo = labelModel.get('alignTo'); |
| var edgeDistance = parsePercent$1(labelModel.get('edgeDistance'), viewWidth); |
| var bleedMargin = labelModel.get('bleedMargin'); |
| var labelLineModel = itemModel.getModel('labelLine'); |
| var labelLineLen = labelLineModel.get('length'); |
| labelLineLen = parsePercent$1(labelLineLen, viewWidth); |
| var labelLineLen2 = labelLineModel.get('length2'); |
| labelLineLen2 = parsePercent$1(labelLineLen2, viewWidth); |
| |
| if (Math.abs(sectorShape.endAngle - sectorShape.startAngle) < minShowLabelRadian) { |
| each(label.states, setNotShow); |
| label.ignore = true; |
| |
| if (labelLine) { |
| each(labelLine.states, setNotShow); |
| labelLine.ignore = true; |
| } |
| |
| return; |
| } |
| |
| if (!isLabelShown(label)) { |
| return; |
| } |
| |
| var midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2; |
| var nx = Math.cos(midAngle); |
| var ny = Math.sin(midAngle); |
| var textX; |
| var textY; |
| var linePoints; |
| var textAlign; |
| cx = sectorShape.cx; |
| cy = sectorShape.cy; |
| var isLabelInside = labelPosition === 'inside' || labelPosition === 'inner'; |
| |
| if (labelPosition === 'center') { |
| textX = sectorShape.cx; |
| textY = sectorShape.cy; |
| textAlign = 'center'; |
| } else { |
| var x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx; |
| var y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy; |
| textX = x1 + nx * 3; |
| textY = y1 + ny * 3; |
| |
| if (!isLabelInside) { |
| // For roseType |
| var x2 = x1 + nx * (labelLineLen + r - sectorShape.r); |
| var y2 = y1 + ny * (labelLineLen + r - sectorShape.r); |
| var x3 = x2 + (nx < 0 ? -1 : 1) * labelLineLen2; |
| var y3 = y2; |
| |
| if (labelAlignTo === 'edge') { |
| // Adjust textX because text align of edge is opposite |
| textX = nx < 0 ? viewLeft + edgeDistance : viewLeft + viewWidth - edgeDistance; |
| } else { |
| textX = x3 + (nx < 0 ? -labelDistance : labelDistance); |
| } |
| |
| textY = y3; |
| linePoints = [[x1, y1], [x2, y2], [x3, y3]]; |
| } |
| |
| textAlign = isLabelInside ? 'center' : labelAlignTo === 'edge' ? nx > 0 ? 'right' : 'left' : nx > 0 ? 'left' : 'right'; |
| } |
| |
| var PI = Math.PI; |
| var labelRotate = 0; |
| var rotate = labelModel.get('rotate'); |
| |
| if (isNumber(rotate)) { |
| labelRotate = rotate * (PI / 180); |
| } else if (labelPosition === 'center') { |
| labelRotate = 0; |
| } else if (rotate === 'radial' || rotate === true) { |
| var radialAngle = nx < 0 ? -midAngle + PI : -midAngle; |
| labelRotate = radialAngle; |
| } else if (rotate === 'tangential' && labelPosition !== 'outside' && labelPosition !== 'outer') { |
| var rad = Math.atan2(nx, ny); |
| |
| if (rad < 0) { |
| rad = PI * 2 + rad; |
| } |
| |
| var isDown = ny > 0; |
| |
| if (isDown) { |
| rad = PI + rad; |
| } |
| |
| labelRotate = rad - PI; |
| } |
| |
| hasLabelRotate = !!labelRotate; |
| label.x = textX; |
| label.y = textY; |
| label.rotation = labelRotate; |
| label.setStyle({ |
| verticalAlign: 'middle' |
| }); // Not sectorShape the inside label |
| |
| if (!isLabelInside) { |
| var textRect = label.getBoundingRect().clone(); |
| textRect.applyTransform(label.getComputedTransform()); // Text has a default 1px stroke. Exclude this. |
| |
| var margin = (label.style.margin || 0) + 2.1; |
| textRect.y -= margin / 2; |
| textRect.height += margin; |
| labelLayoutList.push({ |
| label: label, |
| labelLine: labelLine, |
| position: labelPosition, |
| len: labelLineLen, |
| len2: labelLineLen2, |
| minTurnAngle: labelLineModel.get('minTurnAngle'), |
| maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'), |
| surfaceNormal: new Point(nx, ny), |
| linePoints: linePoints, |
| textAlign: textAlign, |
| labelDistance: labelDistance, |
| labelAlignTo: labelAlignTo, |
| edgeDistance: edgeDistance, |
| bleedMargin: bleedMargin, |
| rect: textRect, |
| unconstrainedWidth: textRect.width, |
| labelStyleWidth: label.style.width |
| }); |
| } else { |
| label.setStyle({ |
| align: textAlign |
| }); |
| var selectState = label.states.select; |
| |
| if (selectState) { |
| selectState.x += label.x; |
| selectState.y += label.y; |
| } |
| } |
| |
| sector.setTextConfig({ |
| inside: isLabelInside |
| }); |
| }); |
| |
| if (!hasLabelRotate && seriesModel.get('avoidLabelOverlap')) { |
| avoidOverlap(labelLayoutList, cx, cy, r, viewWidth, viewHeight, viewLeft, viewTop); |
| } |
| |
| for (var i = 0; i < labelLayoutList.length; i++) { |
| var layout = labelLayoutList[i]; |
| var label = layout.label; |
| var labelLine = layout.labelLine; |
| var notShowLabel = isNaN(label.x) || isNaN(label.y); |
| |
| if (label) { |
| label.setStyle({ |
| align: layout.textAlign |
| }); |
| |
| if (notShowLabel) { |
| each(label.states, setNotShow); |
| label.ignore = true; |
| } |
| |
| var selectState = label.states.select; |
| |
| if (selectState) { |
| selectState.x += label.x; |
| selectState.y += label.y; |
| } |
| } |
| |
| if (labelLine) { |
| var linePoints = layout.linePoints; |
| |
| if (notShowLabel || !linePoints) { |
| each(labelLine.states, setNotShow); |
| labelLine.ignore = true; |
| } else { |
| limitTurnAngle(linePoints, layout.minTurnAngle); |
| limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle); |
| labelLine.setShape({ |
| points: linePoints |
| }); // Set the anchor to the midpoint of sector |
| |
| label.__hostTarget.textGuideLineConfig = { |
| anchor: new Point(linePoints[0][0], linePoints[0][1]) |
| }; |
| } |
| } |
| } |
| } |
| |
| function getSectorCornerRadius(model, shape, zeroIfNull) { |
| var cornerRadius = model.get('borderRadius'); |
| |
| if (cornerRadius == null) { |
| return zeroIfNull ? { |
| cornerRadius: 0 |
| } : null; |
| } |
| |
| if (!isArray(cornerRadius)) { |
| cornerRadius = [cornerRadius, cornerRadius, cornerRadius, cornerRadius]; |
| } |
| |
| var dr = Math.abs(shape.r || 0 - shape.r0 || 0); |
| return { |
| cornerRadius: map(cornerRadius, function (cr) { |
| return parsePercent(cr, dr); |
| }) |
| }; |
| } |
| |
| /** |
| * Piece of pie including Sector, Label, LabelLine |
| */ |
| |
| var PiePiece = |
| /** @class */ |
| function (_super) { |
| __extends(PiePiece, _super); |
| |
| function PiePiece(data, idx, startAngle) { |
| var _this = _super.call(this) || this; |
| |
| _this.z2 = 2; |
| var text = new ZRText(); |
| |
| _this.setTextContent(text); |
| |
| _this.updateData(data, idx, startAngle, true); |
| |
| return _this; |
| } |
| |
| PiePiece.prototype.updateData = function (data, idx, startAngle, firstCreate) { |
| var sector = this; |
| var seriesModel = data.hostModel; |
| var itemModel = data.getItemModel(idx); |
| var emphasisModel = itemModel.getModel('emphasis'); |
| var layout = data.getItemLayout(idx); // cornerRadius & innerCornerRadius doesn't exist in the item layout. Use `0` if null value is specified. |
| // see `setItemLayout` in `pieLayout.ts`. |
| |
| var sectorShape = extend(getSectorCornerRadius(itemModel.getModel('itemStyle'), layout, true), layout); // Ignore NaN data. |
| |
| if (isNaN(sectorShape.startAngle)) { |
| // Use NaN shape to avoid drawing shape. |
| sector.setShape(sectorShape); |
| return; |
| } |
| |
| if (firstCreate) { |
| sector.setShape(sectorShape); |
| var animationType = seriesModel.getShallow('animationType'); |
| |
| if (seriesModel.ecModel.ssr) { |
| // Use scale animation in SSR mode(opacity?) |
| // Because CSS SVG animation doesn't support very customized shape animation. |
| initProps(sector, { |
| scaleX: 0, |
| scaleY: 0 |
| }, seriesModel, { |
| dataIndex: idx, |
| isFrom: true |
| }); |
| sector.originX = sectorShape.cx; |
| sector.originY = sectorShape.cy; |
| } else if (animationType === 'scale') { |
| sector.shape.r = layout.r0; |
| initProps(sector, { |
| shape: { |
| r: layout.r |
| } |
| }, seriesModel, idx); |
| } // Expansion |
| else { |
| if (startAngle != null) { |
| sector.setShape({ |
| startAngle: startAngle, |
| endAngle: startAngle |
| }); |
| initProps(sector, { |
| shape: { |
| startAngle: layout.startAngle, |
| endAngle: layout.endAngle |
| } |
| }, seriesModel, idx); |
| } else { |
| sector.shape.endAngle = layout.startAngle; |
| updateProps(sector, { |
| shape: { |
| endAngle: layout.endAngle |
| } |
| }, seriesModel, idx); |
| } |
| } |
| } else { |
| saveOldStyle(sector); // Transition animation from the old shape |
| |
| updateProps(sector, { |
| shape: sectorShape |
| }, seriesModel, idx); |
| } |
| |
| sector.useStyle(data.getItemVisual(idx, 'style')); |
| setStatesStylesFromModel(sector, itemModel); |
| var midAngle = (layout.startAngle + layout.endAngle) / 2; |
| var offset = seriesModel.get('selectedOffset'); |
| var dx = Math.cos(midAngle) * offset; |
| var dy = Math.sin(midAngle) * offset; |
| var cursorStyle = itemModel.getShallow('cursor'); |
| cursorStyle && sector.attr('cursor', cursorStyle); |
| |
| this._updateLabel(seriesModel, data, idx); |
| |
| sector.ensureState('emphasis').shape = extend({ |
| r: layout.r + (emphasisModel.get('scale') ? emphasisModel.get('scaleSize') || 0 : 0) |
| }, getSectorCornerRadius(emphasisModel.getModel('itemStyle'), layout)); |
| extend(sector.ensureState('select'), { |
| x: dx, |
| y: dy, |
| shape: getSectorCornerRadius(itemModel.getModel(['select', 'itemStyle']), layout) |
| }); |
| extend(sector.ensureState('blur'), { |
| shape: getSectorCornerRadius(itemModel.getModel(['blur', 'itemStyle']), layout) |
| }); |
| var labelLine = sector.getTextGuideLine(); |
| var labelText = sector.getTextContent(); |
| labelLine && extend(labelLine.ensureState('select'), { |
| x: dx, |
| y: dy |
| }); // TODO: needs dx, dy in zrender? |
| |
| extend(labelText.ensureState('select'), { |
| x: dx, |
| y: dy |
| }); |
| toggleHoverEmphasis(this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled')); |
| }; |
| |
| PiePiece.prototype._updateLabel = function (seriesModel, data, idx) { |
| var sector = this; |
| var itemModel = data.getItemModel(idx); |
| var labelLineModel = itemModel.getModel('labelLine'); |
| var style = data.getItemVisual(idx, 'style'); |
| var visualColor = style && style.fill; |
| var visualOpacity = style && style.opacity; |
| setLabelStyle(sector, getLabelStatesModels(itemModel), { |
| labelFetcher: data.hostModel, |
| labelDataIndex: idx, |
| inheritColor: visualColor, |
| defaultOpacity: visualOpacity, |
| defaultText: seriesModel.getFormattedLabel(idx, 'normal') || data.getName(idx) |
| }); |
| var labelText = sector.getTextContent(); // Set textConfig on sector. |
| |
| sector.setTextConfig({ |
| // reset position, rotation |
| position: null, |
| rotation: null |
| }); // Make sure update style on labelText after setLabelStyle. |
| // Because setLabelStyle will replace a new style on it. |
| |
| labelText.attr({ |
| z2: 10 |
| }); |
| var labelPosition = seriesModel.get(['label', 'position']); |
| |
| if (labelPosition !== 'outside' && labelPosition !== 'outer') { |
| sector.removeTextGuideLine(); |
| } else { |
| var polyline = this.getTextGuideLine(); |
| |
| if (!polyline) { |
| polyline = new Polyline(); |
| this.setTextGuideLine(polyline); |
| } // Default use item visual color |
| |
| |
| setLabelLineStyle(this, getLabelLineStatesModels(itemModel), { |
| stroke: visualColor, |
| opacity: retrieve3(labelLineModel.get(['lineStyle', 'opacity']), visualOpacity, 1) |
| }); |
| } |
| }; |
| |
| return PiePiece; |
| }(Sector); // Pie view |
| |
| |
| var PieView = |
| /** @class */ |
| function (_super) { |
| __extends(PieView, _super); |
| |
| function PieView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.ignoreLabelLineUpdate = true; |
| return _this; |
| } |
| |
| PieView.prototype.render = function (seriesModel, ecModel, api, payload) { |
| var data = seriesModel.getData(); |
| var oldData = this._data; |
| var group = this.group; |
| var startAngle; // First render |
| |
| if (!oldData && data.count() > 0) { |
| var shape = data.getItemLayout(0); |
| |
| for (var s = 1; isNaN(shape && shape.startAngle) && s < data.count(); ++s) { |
| shape = data.getItemLayout(s); |
| } |
| |
| if (shape) { |
| startAngle = shape.startAngle; |
| } |
| } // remove empty-circle if it exists |
| |
| |
| if (this._emptyCircleSector) { |
| group.remove(this._emptyCircleSector); |
| } // when all data are filtered, show lightgray empty circle |
| |
| |
| if (data.count() === 0 && seriesModel.get('showEmptyCircle')) { |
| var sector = new Sector({ |
| shape: getBasicPieLayout(seriesModel, api) |
| }); |
| sector.useStyle(seriesModel.getModel('emptyCircleStyle').getItemStyle()); |
| this._emptyCircleSector = sector; |
| group.add(sector); |
| } |
| |
| data.diff(oldData).add(function (idx) { |
| var piePiece = new PiePiece(data, idx, startAngle); |
| data.setItemGraphicEl(idx, piePiece); |
| group.add(piePiece); |
| }).update(function (newIdx, oldIdx) { |
| var piePiece = oldData.getItemGraphicEl(oldIdx); |
| piePiece.updateData(data, newIdx, startAngle); |
| piePiece.off('click'); |
| group.add(piePiece); |
| data.setItemGraphicEl(newIdx, piePiece); |
| }).remove(function (idx) { |
| var piePiece = oldData.getItemGraphicEl(idx); |
| removeElementWithFadeOut(piePiece, seriesModel, idx); |
| }).execute(); |
| pieLabelLayout(seriesModel); // Always use initial animation. |
| |
| if (seriesModel.get('animationTypeUpdate') !== 'expansion') { |
| this._data = data; |
| } |
| }; |
| |
| PieView.prototype.dispose = function () {}; |
| |
| PieView.prototype.containPoint = function (point, seriesModel) { |
| var data = seriesModel.getData(); |
| var itemLayout = data.getItemLayout(0); |
| |
| if (itemLayout) { |
| var dx = point[0] - itemLayout.cx; |
| var dy = point[1] - itemLayout.cy; |
| var radius = Math.sqrt(dx * dx + dy * dy); |
| return radius <= itemLayout.r && radius >= itemLayout.r0; |
| } |
| }; |
| |
| PieView.type = 'pie'; |
| return PieView; |
| }(ChartView); |
| |
| /** |
| * [Usage]: |
| * (1) |
| * createListSimply(seriesModel, ['value']); |
| * (2) |
| * createListSimply(seriesModel, { |
| * coordDimensions: ['value'], |
| * dimensionsCount: 5 |
| * }); |
| */ |
| |
| function createSeriesDataSimply(seriesModel, opt, nameList) { |
| opt = isArray(opt) && { |
| coordDimensions: opt |
| } || extend({ |
| encodeDefine: seriesModel.getEncode() |
| }, opt); |
| var source = seriesModel.getSource(); |
| var dimensions = prepareSeriesDataSchema(source, opt).dimensions; |
| var list = new SeriesData(dimensions, seriesModel); |
| list.initData(source, nameList); |
| return list; |
| } |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| |
| /** |
| * LegendVisualProvider is an bridge that pick encoded color from data and |
| * provide to the legend component. |
| */ |
| var LegendVisualProvider = |
| /** @class */ |
| function () { |
| function LegendVisualProvider( // Function to get data after filtered. It stores all the encoding info |
| getDataWithEncodedVisual, // Function to get raw data before filtered. |
| getRawData) { |
| this._getDataWithEncodedVisual = getDataWithEncodedVisual; |
| this._getRawData = getRawData; |
| } |
| |
| LegendVisualProvider.prototype.getAllNames = function () { |
| var rawData = this._getRawData(); // We find the name from the raw data. In case it's filtered by the legend component. |
| // Normally, the name can be found in rawData, but can't be found in filtered data will display as gray. |
| |
| |
| return rawData.mapArray(rawData.getName); |
| }; |
| |
| LegendVisualProvider.prototype.containName = function (name) { |
| var rawData = this._getRawData(); |
| |
| return rawData.indexOfName(name) >= 0; |
| }; |
| |
| LegendVisualProvider.prototype.indexOfName = function (name) { |
| // Only get data when necessary. |
| // Because LegendVisualProvider constructor may be new in the stage that data is not prepared yet. |
| // Invoking Series#getData immediately will throw an error. |
| var dataWithEncodedVisual = this._getDataWithEncodedVisual(); |
| |
| return dataWithEncodedVisual.indexOfName(name); |
| }; |
| |
| LegendVisualProvider.prototype.getItemVisual = function (dataIndex, key) { |
| // Get encoded visual properties from final filtered data. |
| var dataWithEncodedVisual = this._getDataWithEncodedVisual(); |
| |
| return dataWithEncodedVisual.getItemVisual(dataIndex, key); |
| }; |
| |
| return LegendVisualProvider; |
| }(); |
| |
| var innerData = makeInner(); |
| |
| var PieSeriesModel = |
| /** @class */ |
| function (_super) { |
| __extends(PieSeriesModel, _super); |
| |
| function PieSeriesModel() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| /** |
| * @overwrite |
| */ |
| |
| |
| PieSeriesModel.prototype.init = function (option) { |
| _super.prototype.init.apply(this, arguments); // Enable legend selection for each data item |
| // Use a function instead of direct access because data reference may changed |
| |
| |
| this.legendVisualProvider = new LegendVisualProvider(bind(this.getData, this), bind(this.getRawData, this)); |
| |
| this._defaultLabelLine(option); |
| }; |
| /** |
| * @overwrite |
| */ |
| |
| |
| PieSeriesModel.prototype.mergeOption = function () { |
| _super.prototype.mergeOption.apply(this, arguments); |
| }; |
| /** |
| * @overwrite |
| */ |
| |
| |
| PieSeriesModel.prototype.getInitialData = function () { |
| return createSeriesDataSimply(this, { |
| coordDimensions: ['value'], |
| encodeDefaulter: curry(makeSeriesEncodeForNameBased, this) |
| }); |
| }; |
| /** |
| * @overwrite |
| */ |
| |
| |
| PieSeriesModel.prototype.getDataParams = function (dataIndex) { |
| var data = this.getData(); // update seats when data is changed |
| |
| var dataInner = innerData(data); |
| var seats = dataInner.seats; |
| |
| if (!seats) { |
| var valueList_1 = []; |
| data.each(data.mapDimension('value'), function (value) { |
| valueList_1.push(value); |
| }); |
| seats = dataInner.seats = getPercentSeats(valueList_1, data.hostModel.get('percentPrecision')); |
| } |
| |
| var params = _super.prototype.getDataParams.call(this, dataIndex); // seats may be empty when sum is 0 |
| |
| |
| params.percent = seats[dataIndex] || 0; |
| params.$vars.push('percent'); |
| return params; |
| }; |
| |
| PieSeriesModel.prototype._defaultLabelLine = function (option) { |
| // Extend labelLine emphasis |
| defaultEmphasis(option, 'labelLine', ['show']); |
| var labelLineNormalOpt = option.labelLine; |
| var labelLineEmphasisOpt = option.emphasis.labelLine; // Not show label line if `label.normal.show = false` |
| |
| labelLineNormalOpt.show = labelLineNormalOpt.show && option.label.show; |
| labelLineEmphasisOpt.show = labelLineEmphasisOpt.show && option.emphasis.label.show; |
| }; |
| |
| PieSeriesModel.type = 'series.pie'; |
| PieSeriesModel.defaultOption = { |
| // zlevel: 0, |
| z: 2, |
| legendHoverLink: true, |
| colorBy: 'data', |
| // 默认全局居中 |
| center: ['50%', '50%'], |
| radius: [0, '75%'], |
| // 默认顺时针 |
| clockwise: true, |
| startAngle: 90, |
| // 最小角度改为0 |
| minAngle: 0, |
| // If the angle of a sector less than `minShowLabelAngle`, |
| // the label will not be displayed. |
| minShowLabelAngle: 0, |
| // 选中时扇区偏移量 |
| selectedOffset: 10, |
| // 选择模式,默认关闭,可选single,multiple |
| // selectedMode: false, |
| // 南丁格尔玫瑰图模式,'radius'(半径) | 'area'(面积) |
| // roseType: null, |
| percentPrecision: 2, |
| // If still show when all data zero. |
| stillShowZeroSum: true, |
| // cursor: null, |
| left: 0, |
| top: 0, |
| right: 0, |
| bottom: 0, |
| width: null, |
| height: null, |
| label: { |
| // color: 'inherit', |
| // If rotate around circle |
| rotate: 0, |
| show: true, |
| overflow: 'truncate', |
| // 'outer', 'inside', 'center' |
| position: 'outer', |
| // 'none', 'labelLine', 'edge'. Works only when position is 'outer' |
| alignTo: 'none', |
| // Closest distance between label and chart edge. |
| // Works only position is 'outer' and alignTo is 'edge'. |
| edgeDistance: '25%', |
| // Works only position is 'outer' and alignTo is not 'edge'. |
| bleedMargin: 10, |
| // Distance between text and label line. |
| distanceToLabelLine: 5 // formatter: 标签文本格式器,同 tooltip.formatter,不支持异步回调 |
| // 默认使用全局文本样式,详见 textStyle |
| // distance: 当position为inner时有效,为label位置到圆心的距离与圆半径(环状图为内外半径和)的比例系数 |
| |
| }, |
| // Enabled when label.normal.position is 'outer' |
| labelLine: { |
| show: true, |
| // 引导线两段中的第一段长度 |
| length: 15, |
| // 引导线两段中的第二段长度 |
| length2: 15, |
| smooth: false, |
| minTurnAngle: 90, |
| maxSurfaceAngle: 90, |
| lineStyle: { |
| // color: 各异, |
| width: 1, |
| type: 'solid' |
| } |
| }, |
| itemStyle: { |
| borderWidth: 1, |
| borderJoin: 'round' |
| }, |
| showEmptyCircle: true, |
| emptyCircleStyle: { |
| color: 'lightgray', |
| opacity: 1 |
| }, |
| labelLayout: { |
| // Hide the overlapped label. |
| hideOverlap: true |
| }, |
| emphasis: { |
| scale: true, |
| scaleSize: 5 |
| }, |
| // If use strategy to avoid label overlapping |
| avoidLabelOverlap: true, |
| // Animation type. Valid values: expansion, scale |
| animationType: 'expansion', |
| animationDuration: 1000, |
| // Animation type when update. Valid values: transition, expansion |
| animationTypeUpdate: 'transition', |
| animationEasingUpdate: 'cubicInOut', |
| animationDurationUpdate: 500, |
| animationEasing: 'cubicInOut' |
| }; |
| return PieSeriesModel; |
| }(SeriesModel); |
| |
| function negativeDataFilter(seriesType) { |
| return { |
| seriesType: seriesType, |
| reset: function (seriesModel, ecModel) { |
| var data = seriesModel.getData(); |
| data.filterSelf(function (idx) { |
| // handle negative value condition |
| var valueDim = data.mapDimension('value'); |
| var curValue = data.get(valueDim, idx); |
| |
| if (isNumber(curValue) && !isNaN(curValue) && curValue < 0) { |
| return false; |
| } |
| |
| return true; |
| }); |
| } |
| }; |
| } |
| |
| function install$3(registers) { |
| registers.registerChartView(PieView); |
| registers.registerSeriesModel(PieSeriesModel); |
| createLegacyDataSelectAction('pie', registers.registerAction); |
| registers.registerLayout(curry(pieLayout, 'pie')); |
| registers.registerProcessor(dataFilter('pie')); |
| registers.registerProcessor(negativeDataFilter('pie')); |
| } |
| |
| var GridModel = |
| /** @class */ |
| function (_super) { |
| __extends(GridModel, _super); |
| |
| function GridModel() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| GridModel.type = 'grid'; |
| GridModel.dependencies = ['xAxis', 'yAxis']; |
| GridModel.layoutMode = 'box'; |
| GridModel.defaultOption = { |
| show: false, |
| // zlevel: 0, |
| z: 0, |
| left: '10%', |
| top: 60, |
| right: '10%', |
| bottom: 70, |
| // If grid size contain label |
| containLabel: false, |
| // width: {totalWidth} - left - right, |
| // height: {totalHeight} - top - bottom, |
| backgroundColor: 'rgba(0,0,0,0)', |
| borderWidth: 1, |
| borderColor: '#ccc' |
| }; |
| return GridModel; |
| }(ComponentModel); |
| |
| var CartesianAxisModel = |
| /** @class */ |
| function (_super) { |
| __extends(CartesianAxisModel, _super); |
| |
| function CartesianAxisModel() { |
| return _super !== null && _super.apply(this, arguments) || this; |
| } |
| |
| CartesianAxisModel.prototype.getCoordSysModel = function () { |
| return this.getReferringComponents('grid', SINGLE_REFERRING).models[0]; |
| }; |
| |
| CartesianAxisModel.type = 'cartesian2dAxis'; |
| return CartesianAxisModel; |
| }(ComponentModel); |
| mixin(CartesianAxisModel, AxisModelCommonMixin); |
| |
| var defaultOption = { |
| show: true, |
| // zlevel: 0, |
| z: 0, |
| // Inverse the axis. |
| inverse: false, |
| // Axis name displayed. |
| name: '', |
| // 'start' | 'middle' | 'end' |
| nameLocation: 'end', |
| // By degree. By default auto rotate by nameLocation. |
| nameRotate: null, |
| nameTruncate: { |
| maxWidth: null, |
| ellipsis: '...', |
| placeholder: '.' |
| }, |
| // Use global text style by default. |
| nameTextStyle: {}, |
| // The gap between axisName and axisLine. |
| nameGap: 15, |
| // Default `false` to support tooltip. |
| silent: false, |
| // Default `false` to avoid legacy user event listener fail. |
| triggerEvent: false, |
| tooltip: { |
| show: false |
| }, |
| axisPointer: {}, |
| axisLine: { |
| show: true, |
| onZero: true, |
| onZeroAxisIndex: null, |
| lineStyle: { |
| color: '#6E7079', |
| width: 1, |
| type: 'solid' |
| }, |
| // The arrow at both ends the the axis. |
| symbol: ['none', 'none'], |
| symbolSize: [10, 15] |
| }, |
| axisTick: { |
| show: true, |
| // Whether axisTick is inside the grid or outside the grid. |
| inside: false, |
| // The length of axisTick. |
| length: 5, |
| lineStyle: { |
| width: 1 |
| } |
| }, |
| axisLabel: { |
| show: true, |
| // Whether axisLabel is inside the grid or outside the grid. |
| inside: false, |
| rotate: 0, |
| // true | false | null/undefined (auto) |
| showMinLabel: null, |
| // true | false | null/undefined (auto) |
| showMaxLabel: null, |
| margin: 8, |
| // formatter: null, |
| fontSize: 12 |
| }, |
| splitLine: { |
| show: true, |
| lineStyle: { |
| color: ['#E0E6F1'], |
| width: 1, |
| type: 'solid' |
| } |
| }, |
| splitArea: { |
| show: false, |
| areaStyle: { |
| color: ['rgba(250,250,250,0.2)', 'rgba(210,219,238,0.2)'] |
| } |
| } |
| }; |
| var categoryAxis = merge({ |
| // The gap at both ends of the axis. For categoryAxis, boolean. |
| boundaryGap: true, |
| // Set false to faster category collection. |
| deduplication: null, |
| // splitArea: { |
| // show: false |
| // }, |
| splitLine: { |
| show: false |
| }, |
| axisTick: { |
| // If tick is align with label when boundaryGap is true |
| alignWithLabel: false, |
| interval: 'auto' |
| }, |
| axisLabel: { |
| interval: 'auto' |
| } |
| }, defaultOption); |
| var valueAxis = merge({ |
| boundaryGap: [0, 0], |
| axisLine: { |
| // Not shown when other axis is categoryAxis in cartesian |
| show: 'auto' |
| }, |
| axisTick: { |
| // Not shown when other axis is categoryAxis in cartesian |
| show: 'auto' |
| }, |
| // TODO |
| // min/max: [30, datamin, 60] or [20, datamin] or [datamin, 60] |
| splitNumber: 5, |
| minorTick: { |
| // Minor tick, not available for cateogry axis. |
| show: false, |
| // Split number of minor ticks. The value should be in range of (0, 100) |
| splitNumber: 5, |
| // Length of minor tick |
| length: 3, |
| // Line style |
| lineStyle: {// Default to be same with axisTick |
| } |
| }, |
| minorSplitLine: { |
| show: false, |
| lineStyle: { |
| color: '#F4F7FD', |
| width: 1 |
| } |
| } |
| }, defaultOption); |
| var timeAxis = merge({ |
| splitNumber: 6, |
| axisLabel: { |
| // To eliminate labels that are not nice |
| showMinLabel: false, |
| showMaxLabel: false, |
| rich: { |
| primary: { |
| fontWeight: 'bold' |
| } |
| } |
| }, |
| splitLine: { |
| show: false |
| } |
| }, valueAxis); |
| var logAxis = defaults({ |
| logBase: 10 |
| }, valueAxis); |
| var axisDefault = { |
| category: categoryAxis, |
| value: valueAxis, |
| time: timeAxis, |
| log: logAxis |
| }; |
| |
| /* |
| * 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. |
| */ |
| |
| |
| /** |
| * AUTO-GENERATED FILE. DO NOT MODIFY. |
| */ |
| |
| /* |
| * 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. |
| */ |
| var AXIS_TYPES = { |
| value: 1, |
| category: 1, |
| time: 1, |
| log: 1 |
| }; |
| |
| /** |
| * Generate sub axis model class |
| * @param axisName 'x' 'y' 'radius' 'angle' 'parallel' ... |
| */ |
| |
| function axisModelCreator(registers, axisName, BaseAxisModelClass, extraDefaultOption) { |
| each(AXIS_TYPES, function (v, axisType) { |
| var defaultOption = merge(merge({}, axisDefault[axisType], true), extraDefaultOption, true); |
| |
| var AxisModel = |
| /** @class */ |
| function (_super) { |
| __extends(AxisModel, _super); |
| |
| function AxisModel() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = axisName + 'Axis.' + axisType; |
| return _this; |
| } |
| |
| AxisModel.prototype.mergeDefaultAndTheme = function (option, ecModel) { |
| var layoutMode = fetchLayoutMode(this); |
| var inputPositionParams = layoutMode ? getLayoutParams(option) : {}; |
| var themeModel = ecModel.getTheme(); |
| merge(option, themeModel.get(axisType + 'Axis')); |
| merge(option, this.getDefaultOption()); |
| option.type = getAxisType(option); |
| |
| if (layoutMode) { |
| mergeLayoutParam(option, inputPositionParams, layoutMode); |
| } |
| }; |
| |
| AxisModel.prototype.optionUpdated = function () { |
| var thisOption = this.option; |
| |
| if (thisOption.type === 'category') { |
| this.__ordinalMeta = OrdinalMeta.createByAxisModel(this); |
| } |
| }; |
| /** |
| * Should not be called before all of 'getInitailData' finished. |
| * Because categories are collected during initializing data. |
| */ |
| |
| |
| AxisModel.prototype.getCategories = function (rawData) { |
| var option = this.option; // FIXME |
| // warning if called before all of 'getInitailData' finished. |
| |
| if (option.type === 'category') { |
| if (rawData) { |
| return option.data; |
| } |
| |
| return this.__ordinalMeta.categories; |
| } |
| }; |
| |
| AxisModel.prototype.getOrdinalMeta = function () { |
| return this.__ordinalMeta; |
| }; |
| |
| AxisModel.type = axisName + 'Axis.' + axisType; |
| AxisModel.defaultOption = defaultOption; |
| return AxisModel; |
| }(BaseAxisModelClass); |
| |
| registers.registerComponentModel(AxisModel); |
| }); |
| registers.registerSubTypeDefaulter(axisName + 'Axis', getAxisType); |
| } |
| |
| function getAxisType(option) { |
| // Default axis with data is category axis |
| return option.type || (option.data ? 'category' : 'value'); |
| } |
| |
| var Cartesian = |
| /** @class */ |
| function () { |
| function Cartesian(name) { |
| this.type = 'cartesian'; |
| this._dimList = []; |
| this._axes = {}; |
| this.name = name || ''; |
| } |
| |
| Cartesian.prototype.getAxis = function (dim) { |
| return this._axes[dim]; |
| }; |
| |
| Cartesian.prototype.getAxes = function () { |
| return map(this._dimList, function (dim) { |
| return this._axes[dim]; |
| }, this); |
| }; |
| |
| Cartesian.prototype.getAxesByScale = function (scaleType) { |
| scaleType = scaleType.toLowerCase(); |
| return filter(this.getAxes(), function (axis) { |
| return axis.scale.type === scaleType; |
| }); |
| }; |
| |
| Cartesian.prototype.addAxis = function (axis) { |
| var dim = axis.dim; |
| this._axes[dim] = axis; |
| |
| this._dimList.push(dim); |
| }; |
| |
| return Cartesian; |
| }(); |
| |
| var cartesian2DDimensions = ['x', 'y']; |
| |
| function canCalculateAffineTransform(scale) { |
| return scale.type === 'interval' || scale.type === 'time'; |
| } |
| |
| var Cartesian2D = |
| /** @class */ |
| function (_super) { |
| __extends(Cartesian2D, _super); |
| |
| function Cartesian2D() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'cartesian2d'; |
| _this.dimensions = cartesian2DDimensions; |
| return _this; |
| } |
| /** |
| * Calculate an affine transform matrix if two axes are time or value. |
| * It's mainly for accelartion on the large time series data. |
| */ |
| |
| |
| Cartesian2D.prototype.calcAffineTransform = function () { |
| this._transform = this._invTransform = null; |
| var xAxisScale = this.getAxis('x').scale; |
| var yAxisScale = this.getAxis('y').scale; |
| |
| if (!canCalculateAffineTransform(xAxisScale) || !canCalculateAffineTransform(yAxisScale)) { |
| return; |
| } |
| |
| var xScaleExtent = xAxisScale.getExtent(); |
| var yScaleExtent = yAxisScale.getExtent(); |
| var start = this.dataToPoint([xScaleExtent[0], yScaleExtent[0]]); |
| var end = this.dataToPoint([xScaleExtent[1], yScaleExtent[1]]); |
| var xScaleSpan = xScaleExtent[1] - xScaleExtent[0]; |
| var yScaleSpan = yScaleExtent[1] - yScaleExtent[0]; |
| |
| if (!xScaleSpan || !yScaleSpan) { |
| return; |
| } // Accelerate data to point calculation on the special large time series data. |
| |
| |
| var scaleX = (end[0] - start[0]) / xScaleSpan; |
| var scaleY = (end[1] - start[1]) / yScaleSpan; |
| var translateX = start[0] - xScaleExtent[0] * scaleX; |
| var translateY = start[1] - yScaleExtent[0] * scaleY; |
| var m = this._transform = [scaleX, 0, 0, scaleY, translateX, translateY]; |
| this._invTransform = invert([], m); |
| }; |
| /** |
| * Base axis will be used on stacking. |
| */ |
| |
| |
| Cartesian2D.prototype.getBaseAxis = function () { |
| return this.getAxesByScale('ordinal')[0] || this.getAxesByScale('time')[0] || this.getAxis('x'); |
| }; |
| |
| Cartesian2D.prototype.containPoint = function (point) { |
| var axisX = this.getAxis('x'); |
| var axisY = this.getAxis('y'); |
| return axisX.contain(axisX.toLocalCoord(point[0])) && axisY.contain(axisY.toLocalCoord(point[1])); |
| }; |
| |
| Cartesian2D.prototype.containData = function (data) { |
| return this.getAxis('x').containData(data[0]) && this.getAxis('y').containData(data[1]); |
| }; |
| |
| Cartesian2D.prototype.containZone = function (data1, data2) { |
| var zoneDiag1 = this.dataToPoint(data1); |
| var zoneDiag2 = this.dataToPoint(data2); |
| var area = this.getArea(); |
| var zone = new BoundingRect(zoneDiag1[0], zoneDiag1[1], zoneDiag2[0] - zoneDiag1[0], zoneDiag2[1] - zoneDiag1[1]); |
| return area.intersect(zone); |
| }; |
| |
| Cartesian2D.prototype.dataToPoint = function (data, clamp, out) { |
| out = out || []; |
| var xVal = data[0]; |
| var yVal = data[1]; // Fast path |
| |
| if (this._transform // It's supported that if data is like `[Inifity, 123]`, where only Y pixel calculated. |
| && xVal != null && isFinite(xVal) && yVal != null && isFinite(yVal)) { |
| return applyTransform(out, data, this._transform); |
| } |
| |
| var xAxis = this.getAxis('x'); |
| var yAxis = this.getAxis('y'); |
| out[0] = xAxis.toGlobalCoord(xAxis.dataToCoord(xVal, clamp)); |
| out[1] = yAxis.toGlobalCoord(yAxis.dataToCoord(yVal, clamp)); |
| return out; |
| }; |
| |
| Cartesian2D.prototype.clampData = function (data, out) { |
| var xScale = this.getAxis('x').scale; |
| var yScale = this.getAxis('y').scale; |
| var xAxisExtent = xScale.getExtent(); |
| var yAxisExtent = yScale.getExtent(); |
| var x = xScale.parse(data[0]); |
| var y = yScale.parse(data[1]); |
| out = out || []; |
| out[0] = Math.min(Math.max(Math.min(xAxisExtent[0], xAxisExtent[1]), x), Math.max(xAxisExtent[0], xAxisExtent[1])); |
| out[1] = Math.min(Math.max(Math.min(yAxisExtent[0], yAxisExtent[1]), y), Math.max(yAxisExtent[0], yAxisExtent[1])); |
| return out; |
| }; |
| |
| Cartesian2D.prototype.pointToData = function (point, clamp) { |
| var out = []; |
| |
| if (this._invTransform) { |
| return applyTransform(out, point, this._invTransform); |
| } |
| |
| var xAxis = this.getAxis('x'); |
| var yAxis = this.getAxis('y'); |
| out[0] = xAxis.coordToData(xAxis.toLocalCoord(point[0]), clamp); |
| out[1] = yAxis.coordToData(yAxis.toLocalCoord(point[1]), clamp); |
| return out; |
| }; |
| |
| Cartesian2D.prototype.getOtherAxis = function (axis) { |
| return this.getAxis(axis.dim === 'x' ? 'y' : 'x'); |
| }; |
| /** |
| * Get rect area of cartesian. |
| * Area will have a contain function to determine if a point is in the coordinate system. |
| */ |
| |
| |
| Cartesian2D.prototype.getArea = function () { |
| var xExtent = this.getAxis('x').getGlobalExtent(); |
| var yExtent = this.getAxis('y').getGlobalExtent(); |
| var x = Math.min(xExtent[0], xExtent[1]); |
| var y = Math.min(yExtent[0], yExtent[1]); |
| var width = Math.max(xExtent[0], xExtent[1]) - x; |
| var height = Math.max(yExtent[0], yExtent[1]) - y; |
| return new BoundingRect(x, y, width, height); |
| }; |
| |
| return Cartesian2D; |
| }(Cartesian); |
| |
| var Axis2D = |
| /** @class */ |
| function (_super) { |
| __extends(Axis2D, _super); |
| |
| function Axis2D(dim, scale, coordExtent, axisType, position) { |
| var _this = _super.call(this, dim, scale, coordExtent) || this; |
| /** |
| * Index of axis, can be used as key |
| * Injected outside. |
| */ |
| |
| |
| _this.index = 0; |
| _this.type = axisType || 'value'; |
| _this.position = position || 'bottom'; |
| return _this; |
| } |
| |
| Axis2D.prototype.isHorizontal = function () { |
| var position = this.position; |
| return position === 'top' || position === 'bottom'; |
| }; |
| /** |
| * Each item cooresponds to this.getExtent(), which |
| * means globalExtent[0] may greater than globalExtent[1], |
| * unless `asc` is input. |
| * |
| * @param {boolean} [asc] |
| * @return {Array.<number>} |
| */ |
| |
| |
| Axis2D.prototype.getGlobalExtent = function (asc) { |
| var ret = this.getExtent(); |
| ret[0] = this.toGlobalCoord(ret[0]); |
| ret[1] = this.toGlobalCoord(ret[1]); |
| asc && ret[0] > ret[1] && ret.reverse(); |
| return ret; |
| }; |
| |
| Axis2D.prototype.pointToData = function (point, clamp) { |
| return this.coordToData(this.toLocalCoord(point[this.dim === 'x' ? 0 : 1]), clamp); |
| }; |
| /** |
| * Set ordinalSortInfo |
| * @param info new OrdinalSortInfo |
| */ |
| |
| |
| Axis2D.prototype.setCategorySortInfo = function (info) { |
| if (this.type !== 'category') { |
| return false; |
| } |
| |
| this.model.option.categorySortInfo = info; |
| this.scale.setSortInfo(info); |
| }; |
| |
| return Axis2D; |
| }(Axis); |
| |
| /** |
| * Can only be called after coordinate system creation stage. |
| * (Can be called before coordinate system update stage). |
| */ |
| |
| function layout$1(gridModel, axisModel, opt) { |
| opt = opt || {}; |
| var grid = gridModel.coordinateSystem; |
| var axis = axisModel.axis; |
| var layout = {}; |
| var otherAxisOnZeroOf = axis.getAxesOnZeroOf()[0]; |
| var rawAxisPosition = axis.position; |
| var axisPosition = otherAxisOnZeroOf ? 'onZero' : rawAxisPosition; |
| var axisDim = axis.dim; |
| var rect = grid.getRect(); |
| var rectBound = [rect.x, rect.x + rect.width, rect.y, rect.y + rect.height]; |
| var idx = { |
| left: 0, |
| right: 1, |
| top: 0, |
| bottom: 1, |
| onZero: 2 |
| }; |
| var axisOffset = axisModel.get('offset') || 0; |
| var posBound = axisDim === 'x' ? [rectBound[2] - axisOffset, rectBound[3] + axisOffset] : [rectBound[0] - axisOffset, rectBound[1] + axisOffset]; |
| |
| if (otherAxisOnZeroOf) { |
| var onZeroCoord = otherAxisOnZeroOf.toGlobalCoord(otherAxisOnZeroOf.dataToCoord(0)); |
| posBound[idx.onZero] = Math.max(Math.min(onZeroCoord, posBound[1]), posBound[0]); |
| } // Axis position |
| |
| |
| layout.position = [axisDim === 'y' ? posBound[idx[axisPosition]] : rectBound[0], axisDim === 'x' ? posBound[idx[axisPosition]] : rectBound[3]]; // Axis rotation |
| |
| layout.rotation = Math.PI / 2 * (axisDim === 'x' ? 0 : 1); // Tick and label direction, x y is axisDim |
| |
| var dirMap = { |
| top: -1, |
| bottom: 1, |
| left: -1, |
| right: 1 |
| }; |
| layout.labelDirection = layout.tickDirection = layout.nameDirection = dirMap[rawAxisPosition]; |
| layout.labelOffset = otherAxisOnZeroOf ? posBound[idx[rawAxisPosition]] - posBound[idx.onZero] : 0; |
| |
| if (axisModel.get(['axisTick', 'inside'])) { |
| layout.tickDirection = -layout.tickDirection; |
| } |
| |
| if (retrieve(opt.labelInside, axisModel.get(['axisLabel', 'inside']))) { |
| layout.labelDirection = -layout.labelDirection; |
| } // Special label rotation |
| |
| |
| var labelRotate = axisModel.get(['axisLabel', 'rotate']); |
| layout.labelRotate = axisPosition === 'top' ? -labelRotate : labelRotate; // Over splitLine and splitArea |
| |
| layout.z2 = 1; |
| return layout; |
| } |
| function isCartesian2DSeries(seriesModel) { |
| return seriesModel.get('coordinateSystem') === 'cartesian2d'; |
| } |
| function findAxisModels(seriesModel) { |
| var axisModelMap = { |
| xAxisModel: null, |
| yAxisModel: null |
| }; |
| each(axisModelMap, function (v, key) { |
| var axisType = key.replace(/Model$/, ''); |
| var axisModel = seriesModel.getReferringComponents(axisType, SINGLE_REFERRING).models[0]; |
| |
| if ("development" !== 'production') { |
| if (!axisModel) { |
| throw new Error(axisType + ' "' + retrieve3(seriesModel.get(axisType + 'Index'), seriesModel.get(axisType + 'Id'), 0) + '" not found'); |
| } |
| } |
| |
| axisModelMap[key] = axisModel; |
| }); |
| return axisModelMap; |
| } |
| |
| var mathLog$1 = Math.log; |
| function alignScaleTicks(scale, axisModel, alignToScale) { |
| var intervalScaleProto = IntervalScale.prototype; // NOTE: There is a precondition for log scale here: |
| // In log scale we store _interval and _extent of exponent value. |
| // So if we use the method of InternalScale to set/get these data. |
| // It process the exponent value, which is linear and what we want here. |
| |
| var alignToTicks = intervalScaleProto.getTicks.call(alignToScale); |
| var alignToNicedTicks = intervalScaleProto.getTicks.call(alignToScale, true); |
| var alignToSplitNumber = alignToTicks.length - 1; |
| var alignToInterval = intervalScaleProto.getInterval.call(alignToScale); |
| var scaleExtent = getScaleExtent(scale, axisModel); |
| var rawExtent = scaleExtent.extent; |
| var isMinFixed = scaleExtent.fixMin; |
| var isMaxFixed = scaleExtent.fixMax; |
| |
| if (scale.type === 'log') { |
| var logBase = mathLog$1(scale.base); |
| rawExtent = [mathLog$1(rawExtent[0]) / logBase, mathLog$1(rawExtent[1]) / logBase]; |
| } |
| |
| scale.setExtent(rawExtent[0], rawExtent[1]); |
| scale.calcNiceExtent({ |
| splitNumber: alignToSplitNumber, |
| fixMin: isMinFixed, |
| fixMax: isMaxFixed |
| }); |
| var extent = intervalScaleProto.getExtent.call(scale); // Need to update the rawExtent. |
| // Because value in rawExtent may be not parsed. e.g. 'dataMin', 'dataMax' |
| |
| if (isMinFixed) { |
| rawExtent[0] = extent[0]; |
| } |
| |
| if (isMaxFixed) { |
| rawExtent[1] = extent[1]; |
| } |
| |
| var interval = intervalScaleProto.getInterval.call(scale); |
| var min = rawExtent[0]; |
| var max = rawExtent[1]; |
| |
| if (isMinFixed && isMaxFixed) { |
| // User set min, max, divide to get new interval |
| interval = (max - min) / alignToSplitNumber; |
| } else if (isMinFixed) { |
| max = rawExtent[0] + interval * alignToSplitNumber; // User set min, expand extent on the other side |
| |
| while (max < rawExtent[1] && isFinite(max) && isFinite(rawExtent[1])) { |
| interval = increaseInterval(interval); |
| max = rawExtent[0] + interval * alignToSplitNumber; |
| } |
| } else if (isMaxFixed) { |
| // User set max, expand extent on the other side |
| min = rawExtent[1] - interval * alignToSplitNumber; |
| |
| while (min > rawExtent[0] && isFinite(min) && isFinite(rawExtent[0])) { |
| interval = increaseInterval(interval); |
| min = rawExtent[1] - interval * alignToSplitNumber; |
| } |
| } else { |
| var nicedSplitNumber = scale.getTicks().length - 1; |
| |
| if (nicedSplitNumber > alignToSplitNumber) { |
| interval = increaseInterval(interval); |
| } |
| |
| var range = interval * alignToSplitNumber; |
| max = Math.ceil(rawExtent[1] / interval) * interval; |
| min = round(max - range); // Not change the result that crossing zero. |
| |
| if (min < 0 && rawExtent[0] >= 0) { |
| min = 0; |
| max = round(range); |
| } else if (max > 0 && rawExtent[1] <= 0) { |
| max = 0; |
| min = -round(range); |
| } |
| } // Adjust min, max based on the extent of alignTo. When min or max is set in alignTo scale |
| |
| |
| var t0 = (alignToTicks[0].value - alignToNicedTicks[0].value) / alignToInterval; |
| var t1 = (alignToTicks[alignToSplitNumber].value - alignToNicedTicks[alignToSplitNumber].value) / alignToInterval; // NOTE: Must in setExtent -> setInterval -> setNiceExtent order. |
| |
| intervalScaleProto.setExtent.call(scale, min + interval * t0, max + interval * t1); |
| intervalScaleProto.setInterval.call(scale, interval); |
| |
| if (t0 || t1) { |
| intervalScaleProto.setNiceExtent.call(scale, min + interval, max - interval); |
| } |
| |
| if ("development" !== 'production') { |
| var ticks = intervalScaleProto.getTicks.call(scale); |
| |
| if (ticks[1] && (!isValueNice(interval) || getPrecisionSafe(ticks[1].value) > getPrecisionSafe(interval))) { |
| warn( // eslint-disable-next-line |
| "The ticks may be not readable when set min: " + axisModel.get('min') + ", max: " + axisModel.get('max') + " and alignTicks: true"); |
| } |
| } |
| } |
| |
| var Grid = |
| /** @class */ |
| function () { |
| function Grid(gridModel, ecModel, api) { |
| // FIXME:TS where used (different from registered type 'cartesian2d')? |
| this.type = 'grid'; |
| this._coordsMap = {}; |
| this._coordsList = []; |
| this._axesMap = {}; |
| this._axesList = []; |
| this.axisPointerEnabled = true; |
| this.dimensions = cartesian2DDimensions; |
| |
| this._initCartesian(gridModel, ecModel, api); |
| |
| this.model = gridModel; |
| } |
| |
| Grid.prototype.getRect = function () { |
| return this._rect; |
| }; |
| |
| Grid.prototype.update = function (ecModel, api) { |
| var axesMap = this._axesMap; |
| |
| this._updateScale(ecModel, this.model); |
| |
| function updateAxisTicks(axes) { |
| var alignTo; // Axis is added in order of axisIndex. |
| |
| var axesIndices = keys(axes); |
| var len = axesIndices.length; |
| |
| if (!len) { |
| return; |
| } |
| |
| var axisNeedsAlign = []; // Process once and calculate the ticks for those don't use alignTicks. |
| |
| for (var i = len - 1; i >= 0; i--) { |
| var idx = +axesIndices[i]; // Convert to number. |
| |
| var axis = axes[idx]; |
| var model = axis.model; |
| var scale = axis.scale; |
| |
| if ( // Only value and log axis without interval support alignTicks. |
| isIntervalOrLogScale(scale) && model.get('alignTicks') && model.get('interval') == null) { |
| axisNeedsAlign.push(axis); |
| } else { |
| niceScaleExtent(scale, model); |
| |
| if (isIntervalOrLogScale(scale)) { |
| // Can only align to interval or log axis. |
| alignTo = axis; |
| } |
| } |
| } |
| // PENDING. Should we find the axis that both set interval, min, max and align to this one? |
| |
| if (axisNeedsAlign.length) { |
| if (!alignTo) { |
| alignTo = axisNeedsAlign.pop(); |
| niceScaleExtent(alignTo.scale, alignTo.model); |
| } |
| |
| each(axisNeedsAlign, function (axis) { |
| alignScaleTicks(axis.scale, axis.model, alignTo.scale); |
| }); |
| } |
| } |
| |
| updateAxisTicks(axesMap.x); |
| updateAxisTicks(axesMap.y); // Key: axisDim_axisIndex, value: boolean, whether onZero target. |
| |
| var onZeroRecords = {}; |
| each(axesMap.x, function (xAxis) { |
| fixAxisOnZero(axesMap, 'y', xAxis, onZeroRecords); |
| }); |
| each(axesMap.y, function (yAxis) { |
| fixAxisOnZero(axesMap, 'x', yAxis, onZeroRecords); |
| }); // Resize again if containLabel is enabled |
| // FIXME It may cause getting wrong grid size in data processing stage |
| |
| this.resize(this.model, api); |
| }; |
| /** |
| * Resize the grid |
| */ |
| |
| |
| Grid.prototype.resize = function (gridModel, api, ignoreContainLabel) { |
| var boxLayoutParams = gridModel.getBoxLayoutParams(); |
| var isContainLabel = !ignoreContainLabel && gridModel.get('containLabel'); |
| var gridRect = getLayoutRect(boxLayoutParams, { |
| width: api.getWidth(), |
| height: api.getHeight() |
| }); |
| this._rect = gridRect; |
| var axesList = this._axesList; |
| adjustAxes(); // Minus label size |
| |
| if (isContainLabel) { |
| each(axesList, function (axis) { |
| if (!axis.model.get(['axisLabel', 'inside'])) { |
| var labelUnionRect = estimateLabelUnionRect(axis); |
| |
| if (labelUnionRect) { |
| var dim = axis.isHorizontal() ? 'height' : 'width'; |
| var margin = axis.model.get(['axisLabel', 'margin']); |
| gridRect[dim] -= labelUnionRect[dim] + margin; |
| |
| if (axis.position === 'top') { |
| gridRect.y += labelUnionRect.height + margin; |
| } else if (axis.position === 'left') { |
| gridRect.x += labelUnionRect.width + margin; |
| } |
| } |
| } |
| }); |
| adjustAxes(); |
| } |
| |
| each(this._coordsList, function (coord) { |
| // Calculate affine matrix to accelerate the data to point transform. |
| // If all the axes scales are time or value. |
| coord.calcAffineTransform(); |
| }); |
| |
| function adjustAxes() { |
| each(axesList, function (axis) { |
| var isHorizontal = axis.isHorizontal(); |
| var extent = isHorizontal ? [0, gridRect.width] : [0, gridRect.height]; |
| var idx = axis.inverse ? 1 : 0; |
| axis.setExtent(extent[idx], extent[1 - idx]); |
| updateAxisTransform(axis, isHorizontal ? gridRect.x : gridRect.y); |
| }); |
| } |
| }; |
| |
| Grid.prototype.getAxis = function (dim, axisIndex) { |
| var axesMapOnDim = this._axesMap[dim]; |
| |
| if (axesMapOnDim != null) { |
| return axesMapOnDim[axisIndex || 0]; |
| } |
| }; |
| |
| Grid.prototype.getAxes = function () { |
| return this._axesList.slice(); |
| }; |
| |
| Grid.prototype.getCartesian = function (xAxisIndex, yAxisIndex) { |
| if (xAxisIndex != null && yAxisIndex != null) { |
| var key = 'x' + xAxisIndex + 'y' + yAxisIndex; |
| return this._coordsMap[key]; |
| } |
| |
| if (isObject(xAxisIndex)) { |
| yAxisIndex = xAxisIndex.yAxisIndex; |
| xAxisIndex = xAxisIndex.xAxisIndex; |
| } |
| |
| for (var i = 0, coordList = this._coordsList; i < coordList.length; i++) { |
| if (coordList[i].getAxis('x').index === xAxisIndex || coordList[i].getAxis('y').index === yAxisIndex) { |
| return coordList[i]; |
| } |
| } |
| }; |
| |
| Grid.prototype.getCartesians = function () { |
| return this._coordsList.slice(); |
| }; |
| /** |
| * @implements |
| */ |
| |
| |
| Grid.prototype.convertToPixel = function (ecModel, finder, value) { |
| var target = this._findConvertTarget(finder); |
| |
| return target.cartesian ? target.cartesian.dataToPoint(value) : target.axis ? target.axis.toGlobalCoord(target.axis.dataToCoord(value)) : null; |
| }; |
| /** |
| * @implements |
| */ |
| |
| |
| Grid.prototype.convertFromPixel = function (ecModel, finder, value) { |
| var target = this._findConvertTarget(finder); |
| |
| return target.cartesian ? target.cartesian.pointToData(value) : target.axis ? target.axis.coordToData(target.axis.toLocalCoord(value)) : null; |
| }; |
| |
| Grid.prototype._findConvertTarget = function (finder) { |
| var seriesModel = finder.seriesModel; |
| var xAxisModel = finder.xAxisModel || seriesModel && seriesModel.getReferringComponents('xAxis', SINGLE_REFERRING).models[0]; |
| var yAxisModel = finder.yAxisModel || seriesModel && seriesModel.getReferringComponents('yAxis', SINGLE_REFERRING).models[0]; |
| var gridModel = finder.gridModel; |
| var coordsList = this._coordsList; |
| var cartesian; |
| var axis; |
| |
| if (seriesModel) { |
| cartesian = seriesModel.coordinateSystem; |
| indexOf(coordsList, cartesian) < 0 && (cartesian = null); |
| } else if (xAxisModel && yAxisModel) { |
| cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); |
| } else if (xAxisModel) { |
| axis = this.getAxis('x', xAxisModel.componentIndex); |
| } else if (yAxisModel) { |
| axis = this.getAxis('y', yAxisModel.componentIndex); |
| } // Lowest priority. |
| else if (gridModel) { |
| var grid = gridModel.coordinateSystem; |
| |
| if (grid === this) { |
| cartesian = this._coordsList[0]; |
| } |
| } |
| |
| return { |
| cartesian: cartesian, |
| axis: axis |
| }; |
| }; |
| /** |
| * @implements |
| */ |
| |
| |
| Grid.prototype.containPoint = function (point) { |
| var coord = this._coordsList[0]; |
| |
| if (coord) { |
| return coord.containPoint(point); |
| } |
| }; |
| /** |
| * Initialize cartesian coordinate systems |
| */ |
| |
| |
| Grid.prototype._initCartesian = function (gridModel, ecModel, api) { |
| var _this = this; |
| |
| var grid = this; |
| var axisPositionUsed = { |
| left: false, |
| right: false, |
| top: false, |
| bottom: false |
| }; |
| var axesMap = { |
| x: {}, |
| y: {} |
| }; |
| var axesCount = { |
| x: 0, |
| y: 0 |
| }; // Create axis |
| |
| ecModel.eachComponent('xAxis', createAxisCreator('x'), this); |
| ecModel.eachComponent('yAxis', createAxisCreator('y'), this); |
| |
| if (!axesCount.x || !axesCount.y) { |
| // Roll back when there no either x or y axis |
| this._axesMap = {}; |
| this._axesList = []; |
| return; |
| } |
| |
| this._axesMap = axesMap; // Create cartesian2d |
| |
| each(axesMap.x, function (xAxis, xAxisIndex) { |
| each(axesMap.y, function (yAxis, yAxisIndex) { |
| var key = 'x' + xAxisIndex + 'y' + yAxisIndex; |
| var cartesian = new Cartesian2D(key); |
| cartesian.master = _this; |
| cartesian.model = gridModel; |
| _this._coordsMap[key] = cartesian; |
| |
| _this._coordsList.push(cartesian); |
| |
| cartesian.addAxis(xAxis); |
| cartesian.addAxis(yAxis); |
| }); |
| }); |
| |
| function createAxisCreator(dimName) { |
| return function (axisModel, idx) { |
| if (!isAxisUsedInTheGrid(axisModel, gridModel)) { |
| return; |
| } |
| |
| var axisPosition = axisModel.get('position'); |
| |
| if (dimName === 'x') { |
| // Fix position |
| if (axisPosition !== 'top' && axisPosition !== 'bottom') { |
| // Default bottom of X |
| axisPosition = axisPositionUsed.bottom ? 'top' : 'bottom'; |
| } |
| } else { |
| // Fix position |
| if (axisPosition !== 'left' && axisPosition !== 'right') { |
| // Default left of Y |
| axisPosition = axisPositionUsed.left ? 'right' : 'left'; |
| } |
| } |
| |
| axisPositionUsed[axisPosition] = true; |
| var axis = new Axis2D(dimName, createScaleByModel(axisModel), [0, 0], axisModel.get('type'), axisPosition); |
| var isCategory = axis.type === 'category'; |
| axis.onBand = isCategory && axisModel.get('boundaryGap'); |
| axis.inverse = axisModel.get('inverse'); // Inject axis into axisModel |
| |
| axisModel.axis = axis; // Inject axisModel into axis |
| |
| axis.model = axisModel; // Inject grid info axis |
| |
| axis.grid = grid; // Index of axis, can be used as key |
| |
| axis.index = idx; |
| |
| grid._axesList.push(axis); |
| |
| axesMap[dimName][idx] = axis; |
| axesCount[dimName]++; |
| }; |
| } |
| }; |
| /** |
| * Update cartesian properties from series. |
| */ |
| |
| |
| Grid.prototype._updateScale = function (ecModel, gridModel) { |
| // Reset scale |
| each(this._axesList, function (axis) { |
| axis.scale.setExtent(Infinity, -Infinity); |
| |
| if (axis.type === 'category') { |
| var categorySortInfo = axis.model.get('categorySortInfo'); |
| axis.scale.setSortInfo(categorySortInfo); |
| } |
| }); |
| ecModel.eachSeries(function (seriesModel) { |
| if (isCartesian2DSeries(seriesModel)) { |
| var axesModelMap = findAxisModels(seriesModel); |
| var xAxisModel = axesModelMap.xAxisModel; |
| var yAxisModel = axesModelMap.yAxisModel; |
| |
| if (!isAxisUsedInTheGrid(xAxisModel, gridModel) || !isAxisUsedInTheGrid(yAxisModel, gridModel)) { |
| return; |
| } |
| |
| var cartesian = this.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); |
| var data = seriesModel.getData(); |
| var xAxis = cartesian.getAxis('x'); |
| var yAxis = cartesian.getAxis('y'); |
| unionExtent(data, xAxis); |
| unionExtent(data, yAxis); |
| } |
| }, this); |
| |
| function unionExtent(data, axis) { |
| each(getDataDimensionsOnAxis(data, axis.dim), function (dim) { |
| axis.scale.unionExtentFromData(data, dim); |
| }); |
| } |
| }; |
| /** |
| * @param dim 'x' or 'y' or 'auto' or null/undefined |
| */ |
| |
| |
| Grid.prototype.getTooltipAxes = function (dim) { |
| var baseAxes = []; |
| var otherAxes = []; |
| each(this.getCartesians(), function (cartesian) { |
| var baseAxis = dim != null && dim !== 'auto' ? cartesian.getAxis(dim) : cartesian.getBaseAxis(); |
| var otherAxis = cartesian.getOtherAxis(baseAxis); |
| indexOf(baseAxes, baseAxis) < 0 && baseAxes.push(baseAxis); |
| indexOf(otherAxes, otherAxis) < 0 && otherAxes.push(otherAxis); |
| }); |
| return { |
| baseAxes: baseAxes, |
| otherAxes: otherAxes |
| }; |
| }; |
| |
| Grid.create = function (ecModel, api) { |
| var grids = []; |
| ecModel.eachComponent('grid', function (gridModel, idx) { |
| var grid = new Grid(gridModel, ecModel, api); |
| grid.name = 'grid_' + idx; // dataSampling requires axis extent, so resize |
| // should be performed in create stage. |
| |
| grid.resize(gridModel, api, true); |
| gridModel.coordinateSystem = grid; |
| grids.push(grid); |
| }); // Inject the coordinateSystems into seriesModel |
| |
| ecModel.eachSeries(function (seriesModel) { |
| if (!isCartesian2DSeries(seriesModel)) { |
| return; |
| } |
| |
| var axesModelMap = findAxisModels(seriesModel); |
| var xAxisModel = axesModelMap.xAxisModel; |
| var yAxisModel = axesModelMap.yAxisModel; |
| var gridModel = xAxisModel.getCoordSysModel(); |
| |
| if ("development" !== 'production') { |
| if (!gridModel) { |
| throw new Error('Grid "' + retrieve3(xAxisModel.get('gridIndex'), xAxisModel.get('gridId'), 0) + '" not found'); |
| } |
| |
| if (xAxisModel.getCoordSysModel() !== yAxisModel.getCoordSysModel()) { |
| throw new Error('xAxis and yAxis must use the same grid'); |
| } |
| } |
| |
| var grid = gridModel.coordinateSystem; |
| seriesModel.coordinateSystem = grid.getCartesian(xAxisModel.componentIndex, yAxisModel.componentIndex); |
| }); |
| return grids; |
| }; // For deciding which dimensions to use when creating list data |
| |
| |
| Grid.dimensions = cartesian2DDimensions; |
| return Grid; |
| }(); |
| /** |
| * Check if the axis is used in the specified grid. |
| */ |
| |
| |
| function isAxisUsedInTheGrid(axisModel, gridModel) { |
| return axisModel.getCoordSysModel() === gridModel; |
| } |
| |
| function fixAxisOnZero(axesMap, otherAxisDim, axis, // Key: see `getOnZeroRecordKey` |
| onZeroRecords) { |
| axis.getAxesOnZeroOf = function () { |
| // TODO: onZero of multiple axes. |
| return otherAxisOnZeroOf ? [otherAxisOnZeroOf] : []; |
| }; // onZero can not be enabled in these two situations: |
| // 1. When any other axis is a category axis. |
| // 2. When no axis is cross 0 point. |
| |
| |
| var otherAxes = axesMap[otherAxisDim]; |
| var otherAxisOnZeroOf; |
| var axisModel = axis.model; |
| var onZero = axisModel.get(['axisLine', 'onZero']); |
| var onZeroAxisIndex = axisModel.get(['axisLine', 'onZeroAxisIndex']); |
| |
| if (!onZero) { |
| return; |
| } // If target axis is specified. |
| |
| |
| if (onZeroAxisIndex != null) { |
| if (canOnZeroToAxis(otherAxes[onZeroAxisIndex])) { |
| otherAxisOnZeroOf = otherAxes[onZeroAxisIndex]; |
| } |
| } else { |
| // Find the first available other axis. |
| for (var idx in otherAxes) { |
| if (otherAxes.hasOwnProperty(idx) && canOnZeroToAxis(otherAxes[idx]) // Consider that two Y axes on one value axis, |
| // if both onZero, the two Y axes overlap. |
| && !onZeroRecords[getOnZeroRecordKey(otherAxes[idx])]) { |
| otherAxisOnZeroOf = otherAxes[idx]; |
| break; |
| } |
| } |
| } |
| |
| if (otherAxisOnZeroOf) { |
| onZeroRecords[getOnZeroRecordKey(otherAxisOnZeroOf)] = true; |
| } |
| |
| function getOnZeroRecordKey(axis) { |
| return axis.dim + '_' + axis.index; |
| } |
| } |
| |
| function canOnZeroToAxis(axis) { |
| return axis && axis.type !== 'category' && axis.type !== 'time' && ifAxisCrossZero(axis); |
| } |
| |
| function updateAxisTransform(axis, coordBase) { |
| var axisExtent = axis.getExtent(); |
| var axisExtentSum = axisExtent[0] + axisExtent[1]; // Fast transform |
| |
| axis.toGlobalCoord = axis.dim === 'x' ? function (coord) { |
| return coord + coordBase; |
| } : function (coord) { |
| return axisExtentSum - coord + coordBase; |
| }; |
| axis.toLocalCoord = axis.dim === 'x' ? function (coord) { |
| return coord - coordBase; |
| } : function (coord) { |
| return axisExtentSum - coord + coordBase; |
| }; |
| } |
| |
| var PI$4 = Math.PI; |
| /** |
| * A final axis is translated and rotated from a "standard axis". |
| * So opt.position and opt.rotation is required. |
| * |
| * A standard axis is and axis from [0, 0] to [0, axisExtent[1]], |
| * for example: (0, 0) ------------> (0, 50) |
| * |
| * nameDirection or tickDirection or labelDirection is 1 means tick |
| * or label is below the standard axis, whereas is -1 means above |
| * the standard axis. labelOffset means offset between label and axis, |
| * which is useful when 'onZero', where axisLabel is in the grid and |
| * label in outside grid. |
| * |
| * Tips: like always, |
| * positive rotation represents anticlockwise, and negative rotation |
| * represents clockwise. |
| * The direction of position coordinate is the same as the direction |
| * of screen coordinate. |
| * |
| * Do not need to consider axis 'inverse', which is auto processed by |
| * axis extent. |
| */ |
| |
| var AxisBuilder = |
| /** @class */ |
| function () { |
| function AxisBuilder(axisModel, opt) { |
| this.group = new Group(); |
| this.opt = opt; |
| this.axisModel = axisModel; // Default value |
| |
| defaults(opt, { |
| labelOffset: 0, |
| nameDirection: 1, |
| tickDirection: 1, |
| labelDirection: 1, |
| silent: true, |
| handleAutoShown: function () { |
| return true; |
| } |
| }); // FIXME Not use a seperate text group? |
| |
| var transformGroup = new Group({ |
| x: opt.position[0], |
| y: opt.position[1], |
| rotation: opt.rotation |
| }); // this.group.add(transformGroup); |
| // this._transformGroup = transformGroup; |
| |
| transformGroup.updateTransform(); |
| this._transformGroup = transformGroup; |
| } |
| |
| AxisBuilder.prototype.hasBuilder = function (name) { |
| return !!builders[name]; |
| }; |
| |
| AxisBuilder.prototype.add = function (name) { |
| builders[name](this.opt, this.axisModel, this.group, this._transformGroup); |
| }; |
| |
| AxisBuilder.prototype.getGroup = function () { |
| return this.group; |
| }; |
| |
| AxisBuilder.innerTextLayout = function (axisRotation, textRotation, direction) { |
| var rotationDiff = remRadian(textRotation - axisRotation); |
| var textAlign; |
| var textVerticalAlign; |
| |
| if (isRadianAroundZero(rotationDiff)) { |
| // Label is parallel with axis line. |
| textVerticalAlign = direction > 0 ? 'top' : 'bottom'; |
| textAlign = 'center'; |
| } else if (isRadianAroundZero(rotationDiff - PI$4)) { |
| // Label is inverse parallel with axis line. |
| textVerticalAlign = direction > 0 ? 'bottom' : 'top'; |
| textAlign = 'center'; |
| } else { |
| textVerticalAlign = 'middle'; |
| |
| if (rotationDiff > 0 && rotationDiff < PI$4) { |
| textAlign = direction > 0 ? 'right' : 'left'; |
| } else { |
| textAlign = direction > 0 ? 'left' : 'right'; |
| } |
| } |
| |
| return { |
| rotation: rotationDiff, |
| textAlign: textAlign, |
| textVerticalAlign: textVerticalAlign |
| }; |
| }; |
| |
| AxisBuilder.makeAxisEventDataBase = function (axisModel) { |
| var eventData = { |
| componentType: axisModel.mainType, |
| componentIndex: axisModel.componentIndex |
| }; |
| eventData[axisModel.mainType + 'Index'] = axisModel.componentIndex; |
| return eventData; |
| }; |
| |
| AxisBuilder.isLabelSilent = function (axisModel) { |
| var tooltipOpt = axisModel.get('tooltip'); |
| return axisModel.get('silent') // Consider mouse cursor, add these restrictions. |
| || !(axisModel.get('triggerEvent') || tooltipOpt && tooltipOpt.show); |
| }; |
| |
| return AxisBuilder; |
| }(); |
| var builders = { |
| axisLine: function (opt, axisModel, group, transformGroup) { |
| var shown = axisModel.get(['axisLine', 'show']); |
| |
| if (shown === 'auto' && opt.handleAutoShown) { |
| shown = opt.handleAutoShown('axisLine'); |
| } |
| |
| if (!shown) { |
| return; |
| } |
| |
| var extent = axisModel.axis.getExtent(); |
| var matrix = transformGroup.transform; |
| var pt1 = [extent[0], 0]; |
| var pt2 = [extent[1], 0]; |
| var inverse = pt1[0] > pt2[0]; |
| |
| if (matrix) { |
| applyTransform(pt1, pt1, matrix); |
| applyTransform(pt2, pt2, matrix); |
| } |
| |
| var lineStyle = extend({ |
| lineCap: 'round' |
| }, axisModel.getModel(['axisLine', 'lineStyle']).getLineStyle()); |
| var line = new Line({ |
| shape: { |
| x1: pt1[0], |
| y1: pt1[1], |
| x2: pt2[0], |
| y2: pt2[1] |
| }, |
| style: lineStyle, |
| strokeContainThreshold: opt.strokeContainThreshold || 5, |
| silent: true, |
| z2: 1 |
| }); |
| subPixelOptimizeLine$1(line.shape, line.style.lineWidth); |
| line.anid = 'line'; |
| group.add(line); |
| var arrows = axisModel.get(['axisLine', 'symbol']); |
| |
| if (arrows != null) { |
| var arrowSize = axisModel.get(['axisLine', 'symbolSize']); |
| |
| if (isString(arrows)) { |
| // Use the same arrow for start and end point |
| arrows = [arrows, arrows]; |
| } |
| |
| if (isString(arrowSize) || isNumber(arrowSize)) { |
| // Use the same size for width and height |
| arrowSize = [arrowSize, arrowSize]; |
| } |
| |
| var arrowOffset = normalizeSymbolOffset(axisModel.get(['axisLine', 'symbolOffset']) || 0, arrowSize); |
| var symbolWidth_1 = arrowSize[0]; |
| var symbolHeight_1 = arrowSize[1]; |
| each([{ |
| rotate: opt.rotation + Math.PI / 2, |
| offset: arrowOffset[0], |
| r: 0 |
| }, { |
| rotate: opt.rotation - Math.PI / 2, |
| offset: arrowOffset[1], |
| r: Math.sqrt((pt1[0] - pt2[0]) * (pt1[0] - pt2[0]) + (pt1[1] - pt2[1]) * (pt1[1] - pt2[1])) |
| }], function (point, index) { |
| if (arrows[index] !== 'none' && arrows[index] != null) { |
| var symbol = createSymbol(arrows[index], -symbolWidth_1 / 2, -symbolHeight_1 / 2, symbolWidth_1, symbolHeight_1, lineStyle.stroke, true); // Calculate arrow position with offset |
| |
| var r = point.r + point.offset; |
| var pt = inverse ? pt2 : pt1; |
| symbol.attr({ |
| rotation: point.rotate, |
| x: pt[0] + r * Math.cos(opt.rotation), |
| y: pt[1] - r * Math.sin(opt.rotation), |
| silent: true, |
| z2: 11 |
| }); |
| group.add(symbol); |
| } |
| }); |
| } |
| }, |
| axisTickLabel: function (opt, axisModel, group, transformGroup) { |
| var ticksEls = buildAxisMajorTicks(group, transformGroup, axisModel, opt); |
| var labelEls = buildAxisLabel(group, transformGroup, axisModel, opt); |
| fixMinMaxLabelShow(axisModel, labelEls, ticksEls); |
| buildAxisMinorTicks(group, transformGroup, axisModel, opt.tickDirection); // This bit fixes the label overlap issue for the time chart. |
| // See https://github.com/apache/echarts/issues/14266 for more. |
| |
| if (axisModel.get(['axisLabel', 'hideOverlap'])) { |
| var labelList = prepareLayoutList(map(labelEls, function (label) { |
| return { |
| label: label, |
| priority: label.z2, |
| defaultAttr: { |
| ignore: label.ignore |
| } |
| }; |
| })); |
| hideOverlap(labelList); |
| } |
| }, |
| axisName: function (opt, axisModel, group, transformGroup) { |
| var name = retrieve(opt.axisName, axisModel.get('name')); |
| |
| if (!name) { |
| return; |
| } |
| |
| var nameLocation = axisModel.get('nameLocation'); |
| var nameDirection = opt.nameDirection; |
| var textStyleModel = axisModel.getModel('nameTextStyle'); |
| var gap = axisModel.get('nameGap') || 0; |
| var extent = axisModel.axis.getExtent(); |
| var gapSignal = extent[0] > extent[1] ? -1 : 1; |
| var pos = [nameLocation === 'start' ? extent[0] - gapSignal * gap : nameLocation === 'end' ? extent[1] + gapSignal * gap : (extent[0] + extent[1]) / 2, // Reuse labelOffset. |
| isNameLocationCenter(nameLocation) ? opt.labelOffset + nameDirection * gap : 0]; |
| var labelLayout; |
| var nameRotation = axisModel.get('nameRotate'); |
| |
| if (nameRotation != null) { |
| nameRotation = nameRotation * PI$4 / 180; // To radian. |
| } |
| |
| var axisNameAvailableWidth; |
| |
| if (isNameLocationCenter(nameLocation)) { |
| labelLayout = AxisBuilder.innerTextLayout(opt.rotation, nameRotation != null ? nameRotation : opt.rotation, // Adapt to axis. |
| nameDirection); |
| } else { |
| labelLayout = endTextLayout(opt.rotation, nameLocation, nameRotation || 0, extent); |
| axisNameAvailableWidth = opt.axisNameAvailableWidth; |
| |
| if (axisNameAvailableWidth != null) { |
| axisNameAvailableWidth = Math.abs(axisNameAvailableWidth / Math.sin(labelLayout.rotation)); |
| !isFinite(axisNameAvailableWidth) && (axisNameAvailableWidth = null); |
| } |
| } |
| |
| var textFont = textStyleModel.getFont(); |
| var truncateOpt = axisModel.get('nameTruncate', true) || {}; |
| var ellipsis = truncateOpt.ellipsis; |
| var maxWidth = retrieve(opt.nameTruncateMaxWidth, truncateOpt.maxWidth, axisNameAvailableWidth); |
| var textEl = new ZRText({ |
| x: pos[0], |
| y: pos[1], |
| rotation: labelLayout.rotation, |
| silent: AxisBuilder.isLabelSilent(axisModel), |
| style: createTextStyle(textStyleModel, { |
| text: name, |
| font: textFont, |
| overflow: 'truncate', |
| width: maxWidth, |
| ellipsis: ellipsis, |
| fill: textStyleModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']), |
| align: textStyleModel.get('align') || labelLayout.textAlign, |
| verticalAlign: textStyleModel.get('verticalAlign') || labelLayout.textVerticalAlign |
| }), |
| z2: 1 |
| }); |
| setTooltipConfig({ |
| el: textEl, |
| componentModel: axisModel, |
| itemName: name |
| }); |
| textEl.__fullText = name; // Id for animation |
| |
| textEl.anid = 'name'; |
| |
| if (axisModel.get('triggerEvent')) { |
| var eventData = AxisBuilder.makeAxisEventDataBase(axisModel); |
| eventData.targetType = 'axisName'; |
| eventData.name = name; |
| getECData(textEl).eventData = eventData; |
| } // FIXME |
| |
| |
| transformGroup.add(textEl); |
| textEl.updateTransform(); |
| group.add(textEl); |
| textEl.decomposeTransform(); |
| } |
| }; |
| |
| function endTextLayout(rotation, textPosition, textRotate, extent) { |
| var rotationDiff = remRadian(textRotate - rotation); |
| var textAlign; |
| var textVerticalAlign; |
| var inverse = extent[0] > extent[1]; |
| var onLeft = textPosition === 'start' && !inverse || textPosition !== 'start' && inverse; |
| |
| if (isRadianAroundZero(rotationDiff - PI$4 / 2)) { |
| textVerticalAlign = onLeft ? 'bottom' : 'top'; |
| textAlign = 'center'; |
| } else if (isRadianAroundZero(rotationDiff - PI$4 * 1.5)) { |
| textVerticalAlign = onLeft ? 'top' : 'bottom'; |
| textAlign = 'center'; |
| } else { |
| textVerticalAlign = 'middle'; |
| |
| if (rotationDiff < PI$4 * 1.5 && rotationDiff > PI$4 / 2) { |
| textAlign = onLeft ? 'left' : 'right'; |
| } else { |
| textAlign = onLeft ? 'right' : 'left'; |
| } |
| } |
| |
| return { |
| rotation: rotationDiff, |
| textAlign: textAlign, |
| textVerticalAlign: textVerticalAlign |
| }; |
| } |
| |
| function fixMinMaxLabelShow(axisModel, labelEls, tickEls) { |
| if (shouldShowAllLabels(axisModel.axis)) { |
| return; |
| } // If min or max are user set, we need to check |
| // If the tick on min(max) are overlap on their neighbour tick |
| // If they are overlapped, we need to hide the min(max) tick label |
| |
| |
| var showMinLabel = axisModel.get(['axisLabel', 'showMinLabel']); |
| var showMaxLabel = axisModel.get(['axisLabel', 'showMaxLabel']); // FIXME |
| // Have not consider onBand yet, where tick els is more than label els. |
| |
| labelEls = labelEls || []; |
| tickEls = tickEls || []; |
| var firstLabel = labelEls[0]; |
| var nextLabel = labelEls[1]; |
| var lastLabel = labelEls[labelEls.length - 1]; |
| var prevLabel = labelEls[labelEls.length - 2]; |
| var firstTick = tickEls[0]; |
| var nextTick = tickEls[1]; |
| var lastTick = tickEls[tickEls.length - 1]; |
| var prevTick = tickEls[tickEls.length - 2]; |
| |
| if (showMinLabel === false) { |
| ignoreEl(firstLabel); |
| ignoreEl(firstTick); |
| } else if (isTwoLabelOverlapped(firstLabel, nextLabel)) { |
| if (showMinLabel) { |
| ignoreEl(nextLabel); |
| ignoreEl(nextTick); |
| } else { |
| ignoreEl(firstLabel); |
| ignoreEl(firstTick); |
| } |
| } |
| |
| if (showMaxLabel === false) { |
| ignoreEl(lastLabel); |
| ignoreEl(lastTick); |
| } else if (isTwoLabelOverlapped(prevLabel, lastLabel)) { |
| if (showMaxLabel) { |
| ignoreEl(prevLabel); |
| ignoreEl(prevTick); |
| } else { |
| ignoreEl(lastLabel); |
| ignoreEl(lastTick); |
| } |
| } |
| } |
| |
| function ignoreEl(el) { |
| el && (el.ignore = true); |
| } |
| |
| function isTwoLabelOverlapped(current, next) { |
| // current and next has the same rotation. |
| var firstRect = current && current.getBoundingRect().clone(); |
| var nextRect = next && next.getBoundingRect().clone(); |
| |
| if (!firstRect || !nextRect) { |
| return; |
| } // When checking intersect of two rotated labels, we use mRotationBack |
| // to avoid that boundingRect is enlarge when using `boundingRect.applyTransform`. |
| |
| |
| var mRotationBack = identity([]); |
| rotate(mRotationBack, mRotationBack, -current.rotation); |
| firstRect.applyTransform(mul$1([], mRotationBack, current.getLocalTransform())); |
| nextRect.applyTransform(mul$1([], mRotationBack, next.getLocalTransform())); |
| return firstRect.intersect(nextRect); |
| } |
| |
| function isNameLocationCenter(nameLocation) { |
| return nameLocation === 'middle' || nameLocation === 'center'; |
| } |
| |
| function createTicks(ticksCoords, tickTransform, tickEndCoord, tickLineStyle, anidPrefix) { |
| var tickEls = []; |
| var pt1 = []; |
| var pt2 = []; |
| |
| for (var i = 0; i < ticksCoords.length; i++) { |
| var tickCoord = ticksCoords[i].coord; |
| pt1[0] = tickCoord; |
| pt1[1] = 0; |
| pt2[0] = tickCoord; |
| pt2[1] = tickEndCoord; |
| |
| if (tickTransform) { |
| applyTransform(pt1, pt1, tickTransform); |
| applyTransform(pt2, pt2, tickTransform); |
| } // Tick line, Not use group transform to have better line draw |
| |
| |
| var tickEl = new Line({ |
| shape: { |
| x1: pt1[0], |
| y1: pt1[1], |
| x2: pt2[0], |
| y2: pt2[1] |
| }, |
| style: tickLineStyle, |
| z2: 2, |
| autoBatch: true, |
| silent: true |
| }); |
| subPixelOptimizeLine$1(tickEl.shape, tickEl.style.lineWidth); |
| tickEl.anid = anidPrefix + '_' + ticksCoords[i].tickValue; |
| tickEls.push(tickEl); |
| } |
| |
| return tickEls; |
| } |
| |
| function buildAxisMajorTicks(group, transformGroup, axisModel, opt) { |
| var axis = axisModel.axis; |
| var tickModel = axisModel.getModel('axisTick'); |
| var shown = tickModel.get('show'); |
| |
| if (shown === 'auto' && opt.handleAutoShown) { |
| shown = opt.handleAutoShown('axisTick'); |
| } |
| |
| if (!shown || axis.scale.isBlank()) { |
| return; |
| } |
| |
| var lineStyleModel = tickModel.getModel('lineStyle'); |
| var tickEndCoord = opt.tickDirection * tickModel.get('length'); |
| var ticksCoords = axis.getTicksCoords(); |
| var ticksEls = createTicks(ticksCoords, transformGroup.transform, tickEndCoord, defaults(lineStyleModel.getLineStyle(), { |
| stroke: axisModel.get(['axisLine', 'lineStyle', 'color']) |
| }), 'ticks'); |
| |
| for (var i = 0; i < ticksEls.length; i++) { |
| group.add(ticksEls[i]); |
| } |
| |
| return ticksEls; |
| } |
| |
| function buildAxisMinorTicks(group, transformGroup, axisModel, tickDirection) { |
| var axis = axisModel.axis; |
| var minorTickModel = axisModel.getModel('minorTick'); |
| |
| if (!minorTickModel.get('show') || axis.scale.isBlank()) { |
| return; |
| } |
| |
| var minorTicksCoords = axis.getMinorTicksCoords(); |
| |
| if (!minorTicksCoords.length) { |
| return; |
| } |
| |
| var lineStyleModel = minorTickModel.getModel('lineStyle'); |
| var tickEndCoord = tickDirection * minorTickModel.get('length'); |
| var minorTickLineStyle = defaults(lineStyleModel.getLineStyle(), defaults(axisModel.getModel('axisTick').getLineStyle(), { |
| stroke: axisModel.get(['axisLine', 'lineStyle', 'color']) |
| })); |
| |
| for (var i = 0; i < minorTicksCoords.length; i++) { |
| var minorTicksEls = createTicks(minorTicksCoords[i], transformGroup.transform, tickEndCoord, minorTickLineStyle, 'minorticks_' + i); |
| |
| for (var k = 0; k < minorTicksEls.length; k++) { |
| group.add(minorTicksEls[k]); |
| } |
| } |
| } |
| |
| function buildAxisLabel(group, transformGroup, axisModel, opt) { |
| var axis = axisModel.axis; |
| var show = retrieve(opt.axisLabelShow, axisModel.get(['axisLabel', 'show'])); |
| |
| if (!show || axis.scale.isBlank()) { |
| return; |
| } |
| |
| var labelModel = axisModel.getModel('axisLabel'); |
| var labelMargin = labelModel.get('margin'); |
| var labels = axis.getViewLabels(); // Special label rotate. |
| |
| var labelRotation = (retrieve(opt.labelRotate, labelModel.get('rotate')) || 0) * PI$4 / 180; |
| var labelLayout = AxisBuilder.innerTextLayout(opt.rotation, labelRotation, opt.labelDirection); |
| var rawCategoryData = axisModel.getCategories && axisModel.getCategories(true); |
| var labelEls = []; |
| var silent = AxisBuilder.isLabelSilent(axisModel); |
| var triggerEvent = axisModel.get('triggerEvent'); |
| each(labels, function (labelItem, index) { |
| var tickValue = axis.scale.type === 'ordinal' ? axis.scale.getRawOrdinalNumber(labelItem.tickValue) : labelItem.tickValue; |
| var formattedLabel = labelItem.formattedLabel; |
| var rawLabel = labelItem.rawLabel; |
| var itemLabelModel = labelModel; |
| |
| if (rawCategoryData && rawCategoryData[tickValue]) { |
| var rawCategoryItem = rawCategoryData[tickValue]; |
| |
| if (isObject(rawCategoryItem) && rawCategoryItem.textStyle) { |
| itemLabelModel = new Model(rawCategoryItem.textStyle, labelModel, axisModel.ecModel); |
| } |
| } |
| |
| var textColor = itemLabelModel.getTextColor() || axisModel.get(['axisLine', 'lineStyle', 'color']); |
| var tickCoord = axis.dataToCoord(tickValue); |
| var textEl = new ZRText({ |
| x: tickCoord, |
| y: opt.labelOffset + opt.labelDirection * labelMargin, |
| rotation: labelLayout.rotation, |
| silent: silent, |
| z2: 10 + (labelItem.level || 0), |
| style: createTextStyle(itemLabelModel, { |
| text: formattedLabel, |
| align: itemLabelModel.getShallow('align', true) || labelLayout.textAlign, |
| verticalAlign: itemLabelModel.getShallow('verticalAlign', true) || itemLabelModel.getShallow('baseline', true) || labelLayout.textVerticalAlign, |
| fill: isFunction(textColor) ? textColor( // (1) In category axis with data zoom, tick is not the original |
| // index of axis.data. So tick should not be exposed to user |
| // in category axis. |
| // (2) Compatible with previous version, which always use formatted label as |
| // input. But in interval scale the formatted label is like '223,445', which |
| // maked user repalce ','. So we modify it to return original val but remain |
| // it as 'string' to avoid error in replacing. |
| axis.type === 'category' ? rawLabel : axis.type === 'value' ? tickValue + '' : tickValue, index) : textColor |
| }) |
| }); |
| textEl.anid = 'label_' + tickValue; // Pack data for mouse event |
| |
| if (triggerEvent) { |
| var eventData = AxisBuilder.makeAxisEventDataBase(axisModel); |
| eventData.targetType = 'axisLabel'; |
| eventData.value = rawLabel; |
| eventData.tickIndex = index; |
| |
| if (axis.type === 'category') { |
| eventData.dataIndex = tickValue; |
| } |
| |
| getECData(textEl).eventData = eventData; |
| } // FIXME |
| |
| |
| transformGroup.add(textEl); |
| textEl.updateTransform(); |
| labelEls.push(textEl); |
| group.add(textEl); |
| textEl.decomposeTransform(); |
| }); |
| return labelEls; |
| } |
| |
| function fixValue(axisModel) { |
| var axisInfo = getAxisInfo(axisModel); |
| |
| if (!axisInfo) { |
| return; |
| } |
| |
| var axisPointerModel = axisInfo.axisPointerModel; |
| var scale = axisInfo.axis.scale; |
| var option = axisPointerModel.option; |
| var status = axisPointerModel.get('status'); |
| var value = axisPointerModel.get('value'); // Parse init value for category and time axis. |
| |
| if (value != null) { |
| value = scale.parse(value); |
| } |
| |
| var useHandle = isHandleTrigger(axisPointerModel); // If `handle` used, `axisPointer` will always be displayed, so value |
| // and status should be initialized. |
| |
| if (status == null) { |
| option.status = useHandle ? 'show' : 'hide'; |
| } |
| |
| var extent = scale.getExtent().slice(); |
| extent[0] > extent[1] && extent.reverse(); |
| |
| if ( // Pick a value on axis when initializing. |
| value == null // If both `handle` and `dataZoom` are used, value may be out of axis extent, |
| // where we should re-pick a value to keep `handle` displaying normally. |
| || value > extent[1]) { |
| // Make handle displayed on the end of the axis when init, which looks better. |
| value = extent[1]; |
| } |
| |
| if (value < extent[0]) { |
| value = extent[0]; |
| } |
| |
| option.value = value; |
| |
| if (useHandle) { |
| option.status = axisInfo.axis.scale.isBlank() ? 'hide' : 'show'; |
| } |
| } |
| function getAxisInfo(axisModel) { |
| var coordSysAxesInfo = (axisModel.ecModel.getComponent('axisPointer') || {}).coordSysAxesInfo; |
| return coordSysAxesInfo && coordSysAxesInfo.axesInfo[makeKey(axisModel)]; |
| } |
| function getAxisPointerModel(axisModel) { |
| var axisInfo = getAxisInfo(axisModel); |
| return axisInfo && axisInfo.axisPointerModel; |
| } |
| |
| function isHandleTrigger(axisPointerModel) { |
| return !!axisPointerModel.get(['handle', 'show']); |
| } |
| /** |
| * @param {module:echarts/model/Model} model |
| * @return {string} unique key |
| */ |
| |
| |
| function makeKey(model) { |
| return model.type + '||' + model.id; |
| } |
| |
| var axisPointerClazz = {}; |
| /** |
| * Base class of AxisView. |
| */ |
| |
| var AxisView = |
| /** @class */ |
| function (_super) { |
| __extends(AxisView, _super); |
| |
| function AxisView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = AxisView.type; |
| return _this; |
| } |
| /** |
| * @override |
| */ |
| |
| |
| AxisView.prototype.render = function (axisModel, ecModel, api, payload) { |
| // FIXME |
| // This process should proformed after coordinate systems updated |
| // (axis scale updated), and should be performed each time update. |
| // So put it here temporarily, although it is not appropriate to |
| // put a model-writing procedure in `view`. |
| this.axisPointerClass && fixValue(axisModel); |
| |
| _super.prototype.render.apply(this, arguments); |
| |
| this._doUpdateAxisPointerClass(axisModel, api, true); |
| }; |
| /** |
| * Action handler. |
| */ |
| |
| |
| AxisView.prototype.updateAxisPointer = function (axisModel, ecModel, api, payload) { |
| this._doUpdateAxisPointerClass(axisModel, api, false); |
| }; |
| /** |
| * @override |
| */ |
| |
| |
| AxisView.prototype.remove = function (ecModel, api) { |
| var axisPointer = this._axisPointer; |
| axisPointer && axisPointer.remove(api); |
| }; |
| /** |
| * @override |
| */ |
| |
| |
| AxisView.prototype.dispose = function (ecModel, api) { |
| this._disposeAxisPointer(api); |
| |
| _super.prototype.dispose.apply(this, arguments); |
| }; |
| |
| AxisView.prototype._doUpdateAxisPointerClass = function (axisModel, api, forceRender) { |
| var Clazz = AxisView.getAxisPointerClass(this.axisPointerClass); |
| |
| if (!Clazz) { |
| return; |
| } |
| |
| var axisPointerModel = getAxisPointerModel(axisModel); |
| axisPointerModel ? (this._axisPointer || (this._axisPointer = new Clazz())).render(axisModel, axisPointerModel, api, forceRender) : this._disposeAxisPointer(api); |
| }; |
| |
| AxisView.prototype._disposeAxisPointer = function (api) { |
| this._axisPointer && this._axisPointer.dispose(api); |
| this._axisPointer = null; |
| }; |
| |
| AxisView.registerAxisPointerClass = function (type, clazz) { |
| if ("development" !== 'production') { |
| if (axisPointerClazz[type]) { |
| throw new Error('axisPointer ' + type + ' exists'); |
| } |
| } |
| |
| axisPointerClazz[type] = clazz; |
| }; |
| |
| AxisView.getAxisPointerClass = function (type) { |
| return type && axisPointerClazz[type]; |
| }; |
| AxisView.type = 'axis'; |
| return AxisView; |
| }(ComponentView); |
| |
| var inner$6 = makeInner(); |
| function rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel) { |
| var axis = axisModel.axis; |
| |
| if (axis.scale.isBlank()) { |
| return; |
| } // TODO: TYPE |
| |
| |
| var splitAreaModel = axisModel.getModel('splitArea'); |
| var areaStyleModel = splitAreaModel.getModel('areaStyle'); |
| var areaColors = areaStyleModel.get('color'); |
| var gridRect = gridModel.coordinateSystem.getRect(); |
| var ticksCoords = axis.getTicksCoords({ |
| tickModel: splitAreaModel, |
| clamp: true |
| }); |
| |
| if (!ticksCoords.length) { |
| return; |
| } // For Making appropriate splitArea animation, the color and anid |
| // should be corresponding to previous one if possible. |
| |
| |
| var areaColorsLen = areaColors.length; |
| var lastSplitAreaColors = inner$6(axisView).splitAreaColors; |
| var newSplitAreaColors = createHashMap(); |
| var colorIndex = 0; |
| |
| if (lastSplitAreaColors) { |
| for (var i = 0; i < ticksCoords.length; i++) { |
| var cIndex = lastSplitAreaColors.get(ticksCoords[i].tickValue); |
| |
| if (cIndex != null) { |
| colorIndex = (cIndex + (areaColorsLen - 1) * i) % areaColorsLen; |
| break; |
| } |
| } |
| } |
| |
| var prev = axis.toGlobalCoord(ticksCoords[0].coord); |
| var areaStyle = areaStyleModel.getAreaStyle(); |
| areaColors = isArray(areaColors) ? areaColors : [areaColors]; |
| |
| for (var i = 1; i < ticksCoords.length; i++) { |
| var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); |
| var x = void 0; |
| var y = void 0; |
| var width = void 0; |
| var height = void 0; |
| |
| if (axis.isHorizontal()) { |
| x = prev; |
| y = gridRect.y; |
| width = tickCoord - x; |
| height = gridRect.height; |
| prev = x + width; |
| } else { |
| x = gridRect.x; |
| y = prev; |
| width = gridRect.width; |
| height = tickCoord - y; |
| prev = y + height; |
| } |
| |
| var tickValue = ticksCoords[i - 1].tickValue; |
| tickValue != null && newSplitAreaColors.set(tickValue, colorIndex); |
| axisGroup.add(new Rect({ |
| anid: tickValue != null ? 'area_' + tickValue : null, |
| shape: { |
| x: x, |
| y: y, |
| width: width, |
| height: height |
| }, |
| style: defaults({ |
| fill: areaColors[colorIndex] |
| }, areaStyle), |
| autoBatch: true, |
| silent: true |
| })); |
| colorIndex = (colorIndex + 1) % areaColorsLen; |
| } |
| |
| inner$6(axisView).splitAreaColors = newSplitAreaColors; |
| } |
| function rectCoordAxisHandleRemove(axisView) { |
| inner$6(axisView).splitAreaColors = null; |
| } |
| |
| var axisBuilderAttrs = ['axisLine', 'axisTickLabel', 'axisName']; |
| var selfBuilderAttrs = ['splitArea', 'splitLine', 'minorSplitLine']; |
| |
| var CartesianAxisView = |
| /** @class */ |
| function (_super) { |
| __extends(CartesianAxisView, _super); |
| |
| function CartesianAxisView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = CartesianAxisView.type; |
| _this.axisPointerClass = 'CartesianAxisPointer'; |
| return _this; |
| } |
| /** |
| * @override |
| */ |
| |
| |
| CartesianAxisView.prototype.render = function (axisModel, ecModel, api, payload) { |
| this.group.removeAll(); |
| var oldAxisGroup = this._axisGroup; |
| this._axisGroup = new Group(); |
| this.group.add(this._axisGroup); |
| |
| if (!axisModel.get('show')) { |
| return; |
| } |
| |
| var gridModel = axisModel.getCoordSysModel(); |
| var layout = layout$1(gridModel, axisModel); |
| var axisBuilder = new AxisBuilder(axisModel, extend({ |
| handleAutoShown: function (elementType) { |
| var cartesians = gridModel.coordinateSystem.getCartesians(); |
| |
| for (var i = 0; i < cartesians.length; i++) { |
| if (isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) { |
| // Still show axis tick or axisLine if other axis is value / log |
| return true; |
| } |
| } // Not show axisTick or axisLine if other axis is category / time |
| |
| |
| return false; |
| } |
| }, layout)); |
| each(axisBuilderAttrs, axisBuilder.add, axisBuilder); |
| |
| this._axisGroup.add(axisBuilder.getGroup()); |
| |
| each(selfBuilderAttrs, function (name) { |
| if (axisModel.get([name, 'show'])) { |
| axisElementBuilders[name](this, this._axisGroup, axisModel, gridModel); |
| } |
| }, this); // THIS is a special case for bar racing chart. |
| // Update the axis label from the natural initial layout to |
| // sorted layout should has no animation. |
| |
| var isInitialSortFromBarRacing = payload && payload.type === 'changeAxisOrder' && payload.isInitSort; |
| |
| if (!isInitialSortFromBarRacing) { |
| groupTransition(oldAxisGroup, this._axisGroup, axisModel); |
| } |
| |
| _super.prototype.render.call(this, axisModel, ecModel, api, payload); |
| }; |
| |
| CartesianAxisView.prototype.remove = function () { |
| rectCoordAxisHandleRemove(this); |
| }; |
| |
| CartesianAxisView.type = 'cartesianAxis'; |
| return CartesianAxisView; |
| }(AxisView); |
| |
| var axisElementBuilders = { |
| splitLine: function (axisView, axisGroup, axisModel, gridModel) { |
| var axis = axisModel.axis; |
| |
| if (axis.scale.isBlank()) { |
| return; |
| } |
| |
| var splitLineModel = axisModel.getModel('splitLine'); |
| var lineStyleModel = splitLineModel.getModel('lineStyle'); |
| var lineColors = lineStyleModel.get('color'); |
| lineColors = isArray(lineColors) ? lineColors : [lineColors]; |
| var gridRect = gridModel.coordinateSystem.getRect(); |
| var isHorizontal = axis.isHorizontal(); |
| var lineCount = 0; |
| var ticksCoords = axis.getTicksCoords({ |
| tickModel: splitLineModel |
| }); |
| var p1 = []; |
| var p2 = []; |
| var lineStyle = lineStyleModel.getLineStyle(); |
| |
| for (var i = 0; i < ticksCoords.length; i++) { |
| var tickCoord = axis.toGlobalCoord(ticksCoords[i].coord); |
| |
| if (isHorizontal) { |
| p1[0] = tickCoord; |
| p1[1] = gridRect.y; |
| p2[0] = tickCoord; |
| p2[1] = gridRect.y + gridRect.height; |
| } else { |
| p1[0] = gridRect.x; |
| p1[1] = tickCoord; |
| p2[0] = gridRect.x + gridRect.width; |
| p2[1] = tickCoord; |
| } |
| |
| var colorIndex = lineCount++ % lineColors.length; |
| var tickValue = ticksCoords[i].tickValue; |
| var line = new Line({ |
| anid: tickValue != null ? 'line_' + ticksCoords[i].tickValue : null, |
| autoBatch: true, |
| shape: { |
| x1: p1[0], |
| y1: p1[1], |
| x2: p2[0], |
| y2: p2[1] |
| }, |
| style: defaults({ |
| stroke: lineColors[colorIndex] |
| }, lineStyle), |
| silent: true |
| }); |
| subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth); |
| axisGroup.add(line); |
| } |
| }, |
| minorSplitLine: function (axisView, axisGroup, axisModel, gridModel) { |
| var axis = axisModel.axis; |
| var minorSplitLineModel = axisModel.getModel('minorSplitLine'); |
| var lineStyleModel = minorSplitLineModel.getModel('lineStyle'); |
| var gridRect = gridModel.coordinateSystem.getRect(); |
| var isHorizontal = axis.isHorizontal(); |
| var minorTicksCoords = axis.getMinorTicksCoords(); |
| |
| if (!minorTicksCoords.length) { |
| return; |
| } |
| |
| var p1 = []; |
| var p2 = []; |
| var lineStyle = lineStyleModel.getLineStyle(); |
| |
| for (var i = 0; i < minorTicksCoords.length; i++) { |
| for (var k = 0; k < minorTicksCoords[i].length; k++) { |
| var tickCoord = axis.toGlobalCoord(minorTicksCoords[i][k].coord); |
| |
| if (isHorizontal) { |
| p1[0] = tickCoord; |
| p1[1] = gridRect.y; |
| p2[0] = tickCoord; |
| p2[1] = gridRect.y + gridRect.height; |
| } else { |
| p1[0] = gridRect.x; |
| p1[1] = tickCoord; |
| p2[0] = gridRect.x + gridRect.width; |
| p2[1] = tickCoord; |
| } |
| |
| var line = new Line({ |
| anid: 'minor_line_' + minorTicksCoords[i][k].tickValue, |
| autoBatch: true, |
| shape: { |
| x1: p1[0], |
| y1: p1[1], |
| x2: p2[0], |
| y2: p2[1] |
| }, |
| style: lineStyle, |
| silent: true |
| }); |
| subPixelOptimizeLine$1(line.shape, lineStyle.lineWidth); |
| axisGroup.add(line); |
| } |
| } |
| }, |
| splitArea: function (axisView, axisGroup, axisModel, gridModel) { |
| rectCoordAxisBuildSplitArea(axisView, axisGroup, axisModel, gridModel); |
| } |
| }; |
| |
| var CartesianXAxisView = |
| /** @class */ |
| function (_super) { |
| __extends(CartesianXAxisView, _super); |
| |
| function CartesianXAxisView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = CartesianXAxisView.type; |
| return _this; |
| } |
| |
| CartesianXAxisView.type = 'xAxis'; |
| return CartesianXAxisView; |
| }(CartesianAxisView); |
| |
| var CartesianYAxisView = |
| /** @class */ |
| function (_super) { |
| __extends(CartesianYAxisView, _super); |
| |
| function CartesianYAxisView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = CartesianXAxisView.type; |
| return _this; |
| } |
| |
| CartesianYAxisView.type = 'yAxis'; |
| return CartesianYAxisView; |
| }(CartesianAxisView); |
| |
| var GridView = |
| /** @class */ |
| function (_super) { |
| __extends(GridView, _super); |
| |
| function GridView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'grid'; |
| return _this; |
| } |
| |
| GridView.prototype.render = function (gridModel, ecModel) { |
| this.group.removeAll(); |
| |
| if (gridModel.get('show')) { |
| this.group.add(new Rect({ |
| shape: gridModel.coordinateSystem.getRect(), |
| style: defaults({ |
| fill: gridModel.get('backgroundColor') |
| }, gridModel.getItemStyle()), |
| silent: true, |
| z2: -1 |
| })); |
| } |
| }; |
| |
| GridView.type = 'grid'; |
| return GridView; |
| }(ComponentView); |
| |
| var extraOption = { |
| // gridIndex: 0, |
| // gridId: '', |
| offset: 0 |
| }; |
| function install$4(registers) { |
| registers.registerComponentView(GridView); |
| registers.registerComponentModel(GridModel); |
| registers.registerCoordinateSystem('cartesian2d', Grid); |
| axisModelCreator(registers, 'x', CartesianAxisModel, extraOption); |
| axisModelCreator(registers, 'y', CartesianAxisModel, extraOption); |
| registers.registerComponentView(CartesianXAxisView); |
| registers.registerComponentView(CartesianYAxisView); |
| registers.registerPreprocessor(function (option) { |
| // Only create grid when need |
| if (option.xAxis && option.yAxis && !option.grid) { |
| option.grid = {}; |
| } |
| }); |
| } |
| |
| var DEFAULT_OPTION = { |
| label: { |
| enabled: true |
| }, |
| decal: { |
| show: false |
| } |
| }; |
| var inner$7 = makeInner(); |
| var decalPaletteScope = {}; |
| function ariaVisual(ecModel, api) { |
| var ariaModel = ecModel.getModel('aria'); // See "area enabled" detection code in `GlobalModel.ts`. |
| |
| if (!ariaModel.get('enabled')) { |
| return; |
| } |
| |
| var defaultOption = clone(DEFAULT_OPTION); |
| merge(defaultOption.label, ecModel.getLocaleModel().get('aria'), false); |
| merge(ariaModel.option, defaultOption, false); |
| setDecal(); |
| setLabel(); |
| |
| function setDecal() { |
| var decalModel = ariaModel.getModel('decal'); |
| var useDecal = decalModel.get('show'); |
| |
| if (useDecal) { |
| // Each type of series use one scope. |
| // Pie and funnel are using different scopes. |
| var paletteScopeGroupByType_1 = createHashMap(); |
| ecModel.eachSeries(function (seriesModel) { |
| if (seriesModel.isColorBySeries()) { |
| return; |
| } |
| |
| var decalScope = paletteScopeGroupByType_1.get(seriesModel.type); |
| |
| if (!decalScope) { |
| decalScope = {}; |
| paletteScopeGroupByType_1.set(seriesModel.type, decalScope); |
| } |
| |
| inner$7(seriesModel).scope = decalScope; |
| }); |
| ecModel.eachRawSeries(function (seriesModel) { |
| if (ecModel.isSeriesFiltered(seriesModel)) { |
| return; |
| } |
| |
| if (isFunction(seriesModel.enableAriaDecal)) { |
| // Let series define how to use decal palette on data |
| seriesModel.enableAriaDecal(); |
| return; |
| } |
| |
| var data = seriesModel.getData(); |
| |
| if (!seriesModel.isColorBySeries()) { |
| var dataAll_1 = seriesModel.getRawData(); |
| var idxMap_1 = {}; |
| var decalScope_1 = inner$7(seriesModel).scope; |
| data.each(function (idx) { |
| var rawIdx = data.getRawIndex(idx); |
| idxMap_1[rawIdx] = idx; |
| }); |
| var dataCount_1 = dataAll_1.count(); |
| dataAll_1.each(function (rawIdx) { |
| var idx = idxMap_1[rawIdx]; |
| var name = dataAll_1.getName(rawIdx) || rawIdx + ''; |
| var paletteDecal = getDecalFromPalette(seriesModel.ecModel, name, decalScope_1, dataCount_1); |
| var specifiedDecal = data.getItemVisual(idx, 'decal'); |
| data.setItemVisual(idx, 'decal', mergeDecal(specifiedDecal, paletteDecal)); |
| }); |
| } else { |
| var paletteDecal = getDecalFromPalette(seriesModel.ecModel, seriesModel.name, decalPaletteScope, ecModel.getSeriesCount()); |
| var specifiedDecal = data.getVisual('decal'); |
| data.setVisual('decal', mergeDecal(specifiedDecal, paletteDecal)); |
| } |
| |
| function mergeDecal(specifiedDecal, paletteDecal) { |
| // Merge decal from palette to decal from itemStyle. |
| // User do not need to specify all of the decal props. |
| var resultDecal = specifiedDecal ? extend(extend({}, paletteDecal), specifiedDecal) : paletteDecal; |
| resultDecal.dirty = true; |
| return resultDecal; |
| } |
| }); |
| } |
| } |
| |
| function setLabel() { |
| var labelLocale = ecModel.getLocaleModel().get('aria'); |
| var labelModel = ariaModel.getModel('label'); |
| labelModel.option = defaults(labelModel.option, labelLocale); |
| |
| if (!labelModel.get('enabled')) { |
| return; |
| } |
| |
| var dom = api.getZr().dom; |
| |
| if (labelModel.get('description')) { |
| dom.setAttribute('aria-label', labelModel.get('description')); |
| return; |
| } |
| |
| var seriesCnt = ecModel.getSeriesCount(); |
| var maxDataCnt = labelModel.get(['data', 'maxCount']) || 10; |
| var maxSeriesCnt = labelModel.get(['series', 'maxCount']) || 10; |
| var displaySeriesCnt = Math.min(seriesCnt, maxSeriesCnt); |
| var ariaLabel; |
| |
| if (seriesCnt < 1) { |
| // No series, no aria label |
| return; |
| } else { |
| var title = getTitle(); |
| |
| if (title) { |
| var withTitle = labelModel.get(['general', 'withTitle']); |
| ariaLabel = replace(withTitle, { |
| title: title |
| }); |
| } else { |
| ariaLabel = labelModel.get(['general', 'withoutTitle']); |
| } |
| |
| var seriesLabels_1 = []; |
| var prefix = seriesCnt > 1 ? labelModel.get(['series', 'multiple', 'prefix']) : labelModel.get(['series', 'single', 'prefix']); |
| ariaLabel += replace(prefix, { |
| seriesCount: seriesCnt |
| }); |
| ecModel.eachSeries(function (seriesModel, idx) { |
| if (idx < displaySeriesCnt) { |
| var seriesLabel = void 0; |
| var seriesName = seriesModel.get('name'); |
| var withName = seriesName ? 'withName' : 'withoutName'; |
| seriesLabel = seriesCnt > 1 ? labelModel.get(['series', 'multiple', withName]) : labelModel.get(['series', 'single', withName]); |
| seriesLabel = replace(seriesLabel, { |
| seriesId: seriesModel.seriesIndex, |
| seriesName: seriesModel.get('name'), |
| seriesType: getSeriesTypeName(seriesModel.subType) |
| }); |
| var data = seriesModel.getData(); |
| |
| if (data.count() > maxDataCnt) { |
| // Show part of data |
| var partialLabel = labelModel.get(['data', 'partialData']); |
| seriesLabel += replace(partialLabel, { |
| displayCnt: maxDataCnt |
| }); |
| } else { |
| seriesLabel += labelModel.get(['data', 'allData']); |
| } |
| |
| var middleSeparator_1 = labelModel.get(['data', 'separator', 'middle']); |
| var endSeparator_1 = labelModel.get(['data', 'separator', 'end']); |
| var dataLabels = []; |
| |
| for (var i = 0; i < data.count(); i++) { |
| if (i < maxDataCnt) { |
| var name_1 = data.getName(i); |
| var value = data.getValues(i); |
| var dataLabel = labelModel.get(['data', name_1 ? 'withName' : 'withoutName']); |
| dataLabels.push(replace(dataLabel, { |
| name: name_1, |
| value: value.join(middleSeparator_1) |
| })); |
| } |
| } |
| |
| seriesLabel += dataLabels.join(middleSeparator_1) + endSeparator_1; |
| seriesLabels_1.push(seriesLabel); |
| } |
| }); |
| var separatorModel = labelModel.getModel(['series', 'multiple', 'separator']); |
| var middleSeparator = separatorModel.get('middle'); |
| var endSeparator = separatorModel.get('end'); |
| ariaLabel += seriesLabels_1.join(middleSeparator) + endSeparator; |
| dom.setAttribute('aria-label', ariaLabel); |
| } |
| } |
| |
| function replace(str, keyValues) { |
| if (!isString(str)) { |
| return str; |
| } |
| |
| var result = str; |
| each(keyValues, function (value, key) { |
| result = result.replace(new RegExp('\\{\\s*' + key + '\\s*\\}', 'g'), value); |
| }); |
| return result; |
| } |
| |
| function getTitle() { |
| var title = ecModel.get('title'); |
| |
| if (title && title.length) { |
| title = title[0]; |
| } |
| |
| return title && title.text; |
| } |
| |
| function getSeriesTypeName(type) { |
| return ecModel.getLocaleModel().get(['series', 'typeNames'])[type] || '自定义图'; |
| } |
| } |
| |
| function ariaPreprocessor(option) { |
| if (!option || !option.aria) { |
| return; |
| } |
| |
| var aria = option.aria; // aria.show is deprecated and should use aria.enabled instead |
| |
| if (aria.show != null) { |
| aria.enabled = aria.show; |
| } |
| |
| aria.label = aria.label || {}; // move description, general, series, data to be under aria.label |
| |
| each(['description', 'general', 'series', 'data'], function (name) { |
| if (aria[name] != null) { |
| aria.label[name] = aria[name]; |
| } |
| }); |
| } |
| |
| function install$5(registers) { |
| registers.registerPreprocessor(ariaPreprocessor); |
| registers.registerVisual(registers.PRIORITY.VISUAL.ARIA, ariaVisual); |
| } |
| |
| var DatasetModel = |
| /** @class */ |
| function (_super) { |
| __extends(DatasetModel, _super); |
| |
| function DatasetModel() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'dataset'; |
| return _this; |
| } |
| |
| DatasetModel.prototype.init = function (option, parentModel, ecModel) { |
| _super.prototype.init.call(this, option, parentModel, ecModel); |
| |
| this._sourceManager = new SourceManager(this); |
| disableTransformOptionMerge(this); |
| }; |
| |
| DatasetModel.prototype.mergeOption = function (newOption, ecModel) { |
| _super.prototype.mergeOption.call(this, newOption, ecModel); |
| |
| disableTransformOptionMerge(this); |
| }; |
| |
| DatasetModel.prototype.optionUpdated = function () { |
| this._sourceManager.dirty(); |
| }; |
| |
| DatasetModel.prototype.getSourceManager = function () { |
| return this._sourceManager; |
| }; |
| |
| DatasetModel.type = 'dataset'; |
| DatasetModel.defaultOption = { |
| seriesLayoutBy: SERIES_LAYOUT_BY_COLUMN |
| }; |
| return DatasetModel; |
| }(ComponentModel); |
| |
| var DatasetView = |
| /** @class */ |
| function (_super) { |
| __extends(DatasetView, _super); |
| |
| function DatasetView() { |
| var _this = _super !== null && _super.apply(this, arguments) || this; |
| |
| _this.type = 'dataset'; |
| return _this; |
| } |
| |
| DatasetView.type = 'dataset'; |
| return DatasetView; |
| }(ComponentView); |
| |
| function install$6(registers) { |
| registers.registerComponentModel(DatasetModel); |
| registers.registerComponentView(DatasetView); |
| } |
| |
| use([install]); |
| use([install$1, install$2, install$3]); |
| use([install$4, install$5, install$6]); |
| |
| exports.Axis = Axis; |
| exports.ChartView = ChartView; |
| exports.ComponentModel = ComponentModel; |
| exports.ComponentView = ComponentView; |
| exports.List = SeriesData; |
| exports.Model = Model; |
| exports.PRIORITY = PRIORITY; |
| exports.SeriesModel = SeriesModel; |
| exports.color = color; |
| exports.connect = connect; |
| exports.dataTool = dataTool; |
| exports.dependencies = dependencies; |
| exports.disConnect = disConnect; |
| exports.disconnect = disconnect; |
| exports.dispose = dispose$1; |
| exports.env = env; |
| exports.extendChartView = extendChartView; |
| exports.extendComponentModel = extendComponentModel; |
| exports.extendComponentView = extendComponentView; |
| exports.extendSeriesModel = extendSeriesModel; |
| exports.format = format$1; |
| exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions; |
| exports.getInstanceByDom = getInstanceByDom; |
| exports.getInstanceById = getInstanceById; |
| exports.getMap = getMap; |
| exports.graphic = graphic; |
| exports.helper = helper; |
| exports.init = init$1; |
| exports.innerDrawElementOnCanvas = brushSingle; |
| exports.matrix = matrix; |
| exports.number = number; |
| exports.parseGeoJSON = parseGeoJSON; |
| exports.parseGeoJson = parseGeoJSON; |
| exports.registerAction = registerAction; |
| exports.registerCoordinateSystem = registerCoordinateSystem; |
| exports.registerLayout = registerLayout; |
| exports.registerLoading = registerLoading; |
| exports.registerLocale = registerLocale; |
| exports.registerMap = registerMap; |
| exports.registerPostInit = registerPostInit; |
| exports.registerPostUpdate = registerPostUpdate; |
| exports.registerPreprocessor = registerPreprocessor; |
| exports.registerProcessor = registerProcessor; |
| exports.registerTheme = registerTheme; |
| exports.registerTransform = registerTransform; |
| exports.registerUpdateLifecycle = registerUpdateLifecycle; |
| exports.registerVisual = registerVisual; |
| exports.setCanvasCreator = setCanvasCreator; |
| exports.setPlatformAPI = setPlatformAPI; |
| exports.throttle = throttle; |
| exports.time = time; |
| exports.use = use; |
| exports.util = util$1; |
| exports.vector = vector; |
| exports.version = version$1; |
| exports.zrUtil = util; |
| exports.zrender = zrender; |
| |
| Object.defineProperty(exports, '__esModule', { value: true }); |
| |
| }))); |
| //# sourceMappingURL=echarts.simple.js.map |