echarts.js 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import {__DEV__} from './config';
  20. import * as zrender from 'zrender/src/zrender';
  21. import * as zrUtil from 'zrender/src/core/util';
  22. import * as colorTool from 'zrender/src/tool/color';
  23. import env from 'zrender/src/core/env';
  24. import timsort from 'zrender/src/core/timsort';
  25. import Eventful from 'zrender/src/mixin/Eventful';
  26. import GlobalModel from './model/Global';
  27. import ExtensionAPI from './ExtensionAPI';
  28. import CoordinateSystemManager from './CoordinateSystem';
  29. import OptionManager from './model/OptionManager';
  30. import backwardCompat from './preprocessor/backwardCompat';
  31. import dataStack from './processor/dataStack';
  32. import ComponentModel from './model/Component';
  33. import SeriesModel from './model/Series';
  34. import ComponentView from './view/Component';
  35. import ChartView from './view/Chart';
  36. import * as graphic from './util/graphic';
  37. import * as modelUtil from './util/model';
  38. import {throttle} from './util/throttle';
  39. import seriesColor from './visual/seriesColor';
  40. import aria from './visual/aria';
  41. import loadingDefault from './loading/default';
  42. import Scheduler from './stream/Scheduler';
  43. import lightTheme from './theme/light';
  44. import darkTheme from './theme/dark';
  45. import './component/dataset';
  46. import mapDataStorage from './coord/geo/mapDataStorage';
  47. var assert = zrUtil.assert;
  48. var each = zrUtil.each;
  49. var isFunction = zrUtil.isFunction;
  50. var isObject = zrUtil.isObject;
  51. var parseClassType = ComponentModel.parseClassType;
  52. export var version = '4.9.0';
  53. export var dependencies = {
  54. zrender: '4.3.2'
  55. };
  56. var TEST_FRAME_REMAIN_TIME = 1;
  57. var PRIORITY_PROCESSOR_FILTER = 1000;
  58. var PRIORITY_PROCESSOR_SERIES_FILTER = 800;
  59. var PRIORITY_PROCESSOR_DATASTACK = 900;
  60. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  61. var PRIORITY_VISUAL_LAYOUT = 1000;
  62. var PRIORITY_VISUAL_PROGRESSIVE_LAYOUT = 1100;
  63. var PRIORITY_VISUAL_GLOBAL = 2000;
  64. var PRIORITY_VISUAL_CHART = 3000;
  65. var PRIORITY_VISUAL_POST_CHART_LAYOUT = 3500;
  66. var PRIORITY_VISUAL_COMPONENT = 4000;
  67. // FIXME
  68. // necessary?
  69. var PRIORITY_VISUAL_BRUSH = 5000;
  70. export var PRIORITY = {
  71. PROCESSOR: {
  72. FILTER: PRIORITY_PROCESSOR_FILTER,
  73. SERIES_FILTER: PRIORITY_PROCESSOR_SERIES_FILTER,
  74. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  75. },
  76. VISUAL: {
  77. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  78. PROGRESSIVE_LAYOUT: PRIORITY_VISUAL_PROGRESSIVE_LAYOUT,
  79. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  80. CHART: PRIORITY_VISUAL_CHART,
  81. POST_CHART_LAYOUT: PRIORITY_VISUAL_POST_CHART_LAYOUT,
  82. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  83. BRUSH: PRIORITY_VISUAL_BRUSH
  84. }
  85. };
  86. // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  87. // where they must not be invoked nestedly, except the only case: invoke
  88. // dispatchAction with updateMethod "none" in main process.
  89. // This flag is used to carry out this rule.
  90. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  91. var IN_MAIN_PROCESS = '__flagInMainProcess';
  92. var OPTION_UPDATED = '__optionUpdated';
  93. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  94. function createRegisterEventWithLowercaseName(method, ignoreDisposed) {
  95. return function (eventName, handler, context) {
  96. if (!ignoreDisposed && this._disposed) {
  97. disposedWarning(this.id);
  98. return;
  99. }
  100. // Event name is all lowercase
  101. eventName = eventName && eventName.toLowerCase();
  102. Eventful.prototype[method].call(this, eventName, handler, context);
  103. };
  104. }
  105. /**
  106. * @module echarts~MessageCenter
  107. */
  108. function MessageCenter() {
  109. Eventful.call(this);
  110. }
  111. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on', true);
  112. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off', true);
  113. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one', true);
  114. zrUtil.mixin(MessageCenter, Eventful);
  115. /**
  116. * @module echarts~ECharts
  117. */
  118. function ECharts(dom, theme, opts) {
  119. opts = opts || {};
  120. // Get theme by name
  121. if (typeof theme === 'string') {
  122. theme = themeStorage[theme];
  123. }
  124. /**
  125. * @type {string}
  126. */
  127. this.id;
  128. /**
  129. * Group id
  130. * @type {string}
  131. */
  132. this.group;
  133. /**
  134. * @type {HTMLElement}
  135. * @private
  136. */
  137. this._dom = dom;
  138. var defaultRenderer = 'canvas';
  139. if (__DEV__) {
  140. defaultRenderer = (
  141. typeof window === 'undefined' ? global : window
  142. ).__ECHARTS__DEFAULT__RENDERER__ || defaultRenderer;
  143. }
  144. /**
  145. * @type {module:zrender/ZRender}
  146. * @private
  147. */
  148. var zr = this._zr = zrender.init(dom, {
  149. renderer: opts.renderer || defaultRenderer,
  150. devicePixelRatio: opts.devicePixelRatio,
  151. width: opts.width,
  152. height: opts.height
  153. });
  154. /**
  155. * Expect 60 fps.
  156. * @type {Function}
  157. * @private
  158. */
  159. this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);
  160. var theme = zrUtil.clone(theme);
  161. theme && backwardCompat(theme, true);
  162. /**
  163. * @type {Object}
  164. * @private
  165. */
  166. this._theme = theme;
  167. /**
  168. * @type {Array.<module:echarts/view/Chart>}
  169. * @private
  170. */
  171. this._chartsViews = [];
  172. /**
  173. * @type {Object.<string, module:echarts/view/Chart>}
  174. * @private
  175. */
  176. this._chartsMap = {};
  177. /**
  178. * @type {Array.<module:echarts/view/Component>}
  179. * @private
  180. */
  181. this._componentsViews = [];
  182. /**
  183. * @type {Object.<string, module:echarts/view/Component>}
  184. * @private
  185. */
  186. this._componentsMap = {};
  187. /**
  188. * @type {module:echarts/CoordinateSystem}
  189. * @private
  190. */
  191. this._coordSysMgr = new CoordinateSystemManager();
  192. /**
  193. * @type {module:echarts/ExtensionAPI}
  194. * @private
  195. */
  196. var api = this._api = createExtensionAPI(this);
  197. // Sort on demand
  198. function prioritySortFunc(a, b) {
  199. return a.__prio - b.__prio;
  200. }
  201. timsort(visualFuncs, prioritySortFunc);
  202. timsort(dataProcessorFuncs, prioritySortFunc);
  203. /**
  204. * @type {module:echarts/stream/Scheduler}
  205. */
  206. this._scheduler = new Scheduler(this, api, dataProcessorFuncs, visualFuncs);
  207. Eventful.call(this, this._ecEventProcessor = new EventProcessor());
  208. /**
  209. * @type {module:echarts~MessageCenter}
  210. * @private
  211. */
  212. this._messageCenter = new MessageCenter();
  213. // Init mouse events
  214. this._initEvents();
  215. // In case some people write `window.onresize = chart.resize`
  216. this.resize = zrUtil.bind(this.resize, this);
  217. // Can't dispatch action during rendering procedure
  218. this._pendingActions = [];
  219. zr.animation.on('frame', this._onframe, this);
  220. bindRenderedEvent(zr, this);
  221. // ECharts instance can be used as value.
  222. zrUtil.setAsPrimitive(this);
  223. }
  224. var echartsProto = ECharts.prototype;
  225. echartsProto._onframe = function () {
  226. if (this._disposed) {
  227. return;
  228. }
  229. var scheduler = this._scheduler;
  230. // Lazy update
  231. if (this[OPTION_UPDATED]) {
  232. var silent = this[OPTION_UPDATED].silent;
  233. this[IN_MAIN_PROCESS] = true;
  234. prepare(this);
  235. updateMethods.update.call(this);
  236. this[IN_MAIN_PROCESS] = false;
  237. this[OPTION_UPDATED] = false;
  238. flushPendingActions.call(this, silent);
  239. triggerUpdatedEvent.call(this, silent);
  240. }
  241. // Avoid do both lazy update and progress in one frame.
  242. else if (scheduler.unfinished) {
  243. // Stream progress.
  244. var remainTime = TEST_FRAME_REMAIN_TIME;
  245. var ecModel = this._model;
  246. var api = this._api;
  247. scheduler.unfinished = false;
  248. do {
  249. var startTime = +new Date();
  250. scheduler.performSeriesTasks(ecModel);
  251. // Currently dataProcessorFuncs do not check threshold.
  252. scheduler.performDataProcessorTasks(ecModel);
  253. updateStreamModes(this, ecModel);
  254. // Do not update coordinate system here. Because that coord system update in
  255. // each frame is not a good user experience. So we follow the rule that
  256. // the extent of the coordinate system is determin in the first frame (the
  257. // frame is executed immedietely after task reset.
  258. // this._coordSysMgr.update(ecModel, api);
  259. // console.log('--- ec frame visual ---', remainTime);
  260. scheduler.performVisualTasks(ecModel);
  261. renderSeries(this, this._model, api, 'remain');
  262. remainTime -= (+new Date() - startTime);
  263. }
  264. while (remainTime > 0 && scheduler.unfinished);
  265. // Call flush explicitly for trigger finished event.
  266. if (!scheduler.unfinished) {
  267. this._zr.flush();
  268. }
  269. // Else, zr flushing be ensue within the same frame,
  270. // because zr flushing is after onframe event.
  271. }
  272. };
  273. /**
  274. * @return {HTMLElement}
  275. */
  276. echartsProto.getDom = function () {
  277. return this._dom;
  278. };
  279. /**
  280. * @return {module:zrender~ZRender}
  281. */
  282. echartsProto.getZr = function () {
  283. return this._zr;
  284. };
  285. /**
  286. * Usage:
  287. * chart.setOption(option, notMerge, lazyUpdate);
  288. * chart.setOption(option, {
  289. * notMerge: ...,
  290. * lazyUpdate: ...,
  291. * silent: ...
  292. * });
  293. *
  294. * @param {Object} option
  295. * @param {Object|boolean} [opts] opts or notMerge.
  296. * @param {boolean} [opts.notMerge=false]
  297. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  298. */
  299. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  300. if (__DEV__) {
  301. assert(!this[IN_MAIN_PROCESS], '`setOption` should not be called during main process.');
  302. }
  303. if (this._disposed) {
  304. disposedWarning(this.id);
  305. return;
  306. }
  307. var silent;
  308. if (isObject(notMerge)) {
  309. lazyUpdate = notMerge.lazyUpdate;
  310. silent = notMerge.silent;
  311. notMerge = notMerge.notMerge;
  312. }
  313. this[IN_MAIN_PROCESS] = true;
  314. if (!this._model || notMerge) {
  315. var optionManager = new OptionManager(this._api);
  316. var theme = this._theme;
  317. var ecModel = this._model = new GlobalModel();
  318. ecModel.scheduler = this._scheduler;
  319. ecModel.init(null, null, theme, optionManager);
  320. }
  321. this._model.setOption(option, optionPreprocessorFuncs);
  322. if (lazyUpdate) {
  323. this[OPTION_UPDATED] = {silent: silent};
  324. this[IN_MAIN_PROCESS] = false;
  325. }
  326. else {
  327. prepare(this);
  328. updateMethods.update.call(this);
  329. // Ensure zr refresh sychronously, and then pixel in canvas can be
  330. // fetched after `setOption`.
  331. this._zr.flush();
  332. this[OPTION_UPDATED] = false;
  333. this[IN_MAIN_PROCESS] = false;
  334. flushPendingActions.call(this, silent);
  335. triggerUpdatedEvent.call(this, silent);
  336. }
  337. };
  338. /**
  339. * @DEPRECATED
  340. */
  341. echartsProto.setTheme = function () {
  342. console.error('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  343. };
  344. /**
  345. * @return {module:echarts/model/Global}
  346. */
  347. echartsProto.getModel = function () {
  348. return this._model;
  349. };
  350. /**
  351. * @return {Object}
  352. */
  353. echartsProto.getOption = function () {
  354. return this._model && this._model.getOption();
  355. };
  356. /**
  357. * @return {number}
  358. */
  359. echartsProto.getWidth = function () {
  360. return this._zr.getWidth();
  361. };
  362. /**
  363. * @return {number}
  364. */
  365. echartsProto.getHeight = function () {
  366. return this._zr.getHeight();
  367. };
  368. /**
  369. * @return {number}
  370. */
  371. echartsProto.getDevicePixelRatio = function () {
  372. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  373. };
  374. /**
  375. * Get canvas which has all thing rendered
  376. * @param {Object} opts
  377. * @param {string} [opts.backgroundColor]
  378. * @return {string}
  379. */
  380. echartsProto.getRenderedCanvas = function (opts) {
  381. if (!env.canvasSupported) {
  382. return;
  383. }
  384. opts = opts || {};
  385. opts.pixelRatio = opts.pixelRatio || 1;
  386. opts.backgroundColor = opts.backgroundColor
  387. || this._model.get('backgroundColor');
  388. var zr = this._zr;
  389. // var list = zr.storage.getDisplayList();
  390. // Stop animations
  391. // Never works before in init animation, so remove it.
  392. // zrUtil.each(list, function (el) {
  393. // el.stopAnimation(true);
  394. // });
  395. return zr.painter.getRenderedCanvas(opts);
  396. };
  397. /**
  398. * Get svg data url
  399. * @return {string}
  400. */
  401. echartsProto.getSvgDataURL = function () {
  402. if (!env.svgSupported) {
  403. return;
  404. }
  405. var zr = this._zr;
  406. var list = zr.storage.getDisplayList();
  407. // Stop animations
  408. zrUtil.each(list, function (el) {
  409. el.stopAnimation(true);
  410. });
  411. return zr.painter.toDataURL();
  412. };
  413. /**
  414. * @return {string}
  415. * @param {Object} opts
  416. * @param {string} [opts.type='png']
  417. * @param {string} [opts.pixelRatio=1]
  418. * @param {string} [opts.backgroundColor]
  419. * @param {string} [opts.excludeComponents]
  420. */
  421. echartsProto.getDataURL = function (opts) {
  422. if (this._disposed) {
  423. disposedWarning(this.id);
  424. return;
  425. }
  426. opts = opts || {};
  427. var excludeComponents = opts.excludeComponents;
  428. var ecModel = this._model;
  429. var excludesComponentViews = [];
  430. var self = this;
  431. each(excludeComponents, function (componentType) {
  432. ecModel.eachComponent({
  433. mainType: componentType
  434. }, function (component) {
  435. var view = self._componentsMap[component.__viewId];
  436. if (!view.group.ignore) {
  437. excludesComponentViews.push(view);
  438. view.group.ignore = true;
  439. }
  440. });
  441. });
  442. var url = this._zr.painter.getType() === 'svg'
  443. ? this.getSvgDataURL()
  444. : this.getRenderedCanvas(opts).toDataURL(
  445. 'image/' + (opts && opts.type || 'png')
  446. );
  447. each(excludesComponentViews, function (view) {
  448. view.group.ignore = false;
  449. });
  450. return url;
  451. };
  452. /**
  453. * @return {string}
  454. * @param {Object} opts
  455. * @param {string} [opts.type='png']
  456. * @param {string} [opts.pixelRatio=1]
  457. * @param {string} [opts.backgroundColor]
  458. */
  459. echartsProto.getConnectedDataURL = function (opts) {
  460. if (this._disposed) {
  461. disposedWarning(this.id);
  462. return;
  463. }
  464. if (!env.canvasSupported) {
  465. return;
  466. }
  467. var isSvg = opts.type === 'svg';
  468. var groupId = this.group;
  469. var mathMin = Math.min;
  470. var mathMax = Math.max;
  471. var MAX_NUMBER = Infinity;
  472. if (connectedGroups[groupId]) {
  473. var left = MAX_NUMBER;
  474. var top = MAX_NUMBER;
  475. var right = -MAX_NUMBER;
  476. var bottom = -MAX_NUMBER;
  477. var canvasList = [];
  478. var dpr = (opts && opts.pixelRatio) || 1;
  479. zrUtil.each(instances, function (chart, id) {
  480. if (chart.group === groupId) {
  481. var canvas = isSvg
  482. ? chart.getZr().painter.getSvgDom().innerHTML
  483. : chart.getRenderedCanvas(zrUtil.clone(opts));
  484. var boundingRect = chart.getDom().getBoundingClientRect();
  485. left = mathMin(boundingRect.left, left);
  486. top = mathMin(boundingRect.top, top);
  487. right = mathMax(boundingRect.right, right);
  488. bottom = mathMax(boundingRect.bottom, bottom);
  489. canvasList.push({
  490. dom: canvas,
  491. left: boundingRect.left,
  492. top: boundingRect.top
  493. });
  494. }
  495. });
  496. left *= dpr;
  497. top *= dpr;
  498. right *= dpr;
  499. bottom *= dpr;
  500. var width = right - left;
  501. var height = bottom - top;
  502. var targetCanvas = zrUtil.createCanvas();
  503. var zr = zrender.init(targetCanvas, {
  504. renderer: isSvg ? 'svg' : 'canvas'
  505. });
  506. zr.resize({
  507. width: width,
  508. height: height
  509. });
  510. if (isSvg) {
  511. var content = '';
  512. each(canvasList, function (item) {
  513. var x = item.left - left;
  514. var y = item.top - top;
  515. content += '<g transform="translate(' + x + ','
  516. + y + ')">' + item.dom + '</g>';
  517. });
  518. zr.painter.getSvgRoot().innerHTML = content;
  519. if (opts.connectedBackgroundColor) {
  520. zr.painter.setBackgroundColor(opts.connectedBackgroundColor);
  521. }
  522. zr.refreshImmediately();
  523. return zr.painter.toDataURL();
  524. }
  525. else {
  526. // Background between the charts
  527. if (opts.connectedBackgroundColor) {
  528. zr.add(new graphic.Rect({
  529. shape: {
  530. x: 0,
  531. y: 0,
  532. width: width,
  533. height: height
  534. },
  535. style: {
  536. fill: opts.connectedBackgroundColor
  537. }
  538. }));
  539. }
  540. each(canvasList, function (item) {
  541. var img = new graphic.Image({
  542. style: {
  543. x: item.left * dpr - left,
  544. y: item.top * dpr - top,
  545. image: item.dom
  546. }
  547. });
  548. zr.add(img);
  549. });
  550. zr.refreshImmediately();
  551. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  552. }
  553. }
  554. else {
  555. return this.getDataURL(opts);
  556. }
  557. };
  558. /**
  559. * Convert from logical coordinate system to pixel coordinate system.
  560. * See CoordinateSystem#convertToPixel.
  561. * @param {string|Object} finder
  562. * If string, e.g., 'geo', means {geoIndex: 0}.
  563. * If Object, could contain some of these properties below:
  564. * {
  565. * seriesIndex / seriesId / seriesName,
  566. * geoIndex / geoId, geoName,
  567. * bmapIndex / bmapId / bmapName,
  568. * xAxisIndex / xAxisId / xAxisName,
  569. * yAxisIndex / yAxisId / yAxisName,
  570. * gridIndex / gridId / gridName,
  571. * ... (can be extended)
  572. * }
  573. * @param {Array|number} value
  574. * @return {Array|number} result
  575. */
  576. echartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');
  577. /**
  578. * Convert from pixel coordinate system to logical coordinate system.
  579. * See CoordinateSystem#convertFromPixel.
  580. * @param {string|Object} finder
  581. * If string, e.g., 'geo', means {geoIndex: 0}.
  582. * If Object, could contain some of these properties below:
  583. * {
  584. * seriesIndex / seriesId / seriesName,
  585. * geoIndex / geoId / geoName,
  586. * bmapIndex / bmapId / bmapName,
  587. * xAxisIndex / xAxisId / xAxisName,
  588. * yAxisIndex / yAxisId / yAxisName
  589. * gridIndex / gridId / gridName,
  590. * ... (can be extended)
  591. * }
  592. * @param {Array|number} value
  593. * @return {Array|number} result
  594. */
  595. echartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');
  596. function doConvertPixel(methodName, finder, value) {
  597. if (this._disposed) {
  598. disposedWarning(this.id);
  599. return;
  600. }
  601. var ecModel = this._model;
  602. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  603. var result;
  604. finder = modelUtil.parseFinder(ecModel, finder);
  605. for (var i = 0; i < coordSysList.length; i++) {
  606. var coordSys = coordSysList[i];
  607. if (coordSys[methodName]
  608. && (result = coordSys[methodName](ecModel, finder, value)) != null
  609. ) {
  610. return result;
  611. }
  612. }
  613. if (__DEV__) {
  614. console.warn(
  615. 'No coordinate system that supports ' + methodName + ' found by the given finder.'
  616. );
  617. }
  618. }
  619. /**
  620. * Is the specified coordinate systems or components contain the given pixel point.
  621. * @param {string|Object} finder
  622. * If string, e.g., 'geo', means {geoIndex: 0}.
  623. * If Object, could contain some of these properties below:
  624. * {
  625. * seriesIndex / seriesId / seriesName,
  626. * geoIndex / geoId / geoName,
  627. * bmapIndex / bmapId / bmapName,
  628. * xAxisIndex / xAxisId / xAxisName,
  629. * yAxisIndex / yAxisId / yAxisName,
  630. * gridIndex / gridId / gridName,
  631. * ... (can be extended)
  632. * }
  633. * @param {Array|number} value
  634. * @return {boolean} result
  635. */
  636. echartsProto.containPixel = function (finder, value) {
  637. if (this._disposed) {
  638. disposedWarning(this.id);
  639. return;
  640. }
  641. var ecModel = this._model;
  642. var result;
  643. finder = modelUtil.parseFinder(ecModel, finder);
  644. zrUtil.each(finder, function (models, key) {
  645. key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {
  646. var coordSys = model.coordinateSystem;
  647. if (coordSys && coordSys.containPoint) {
  648. result |= !!coordSys.containPoint(value);
  649. }
  650. else if (key === 'seriesModels') {
  651. var view = this._chartsMap[model.__viewId];
  652. if (view && view.containPoint) {
  653. result |= view.containPoint(value, model);
  654. }
  655. else {
  656. if (__DEV__) {
  657. console.warn(key + ': ' + (view
  658. ? 'The found component do not support containPoint.'
  659. : 'No view mapping to the found component.'
  660. ));
  661. }
  662. }
  663. }
  664. else {
  665. if (__DEV__) {
  666. console.warn(key + ': containPoint is not supported');
  667. }
  668. }
  669. }, this);
  670. }, this);
  671. return !!result;
  672. };
  673. /**
  674. * Get visual from series or data.
  675. * @param {string|Object} finder
  676. * If string, e.g., 'series', means {seriesIndex: 0}.
  677. * If Object, could contain some of these properties below:
  678. * {
  679. * seriesIndex / seriesId / seriesName,
  680. * dataIndex / dataIndexInside
  681. * }
  682. * If dataIndex is not specified, series visual will be fetched,
  683. * but not data item visual.
  684. * If all of seriesIndex, seriesId, seriesName are not specified,
  685. * visual will be fetched from first series.
  686. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  687. */
  688. echartsProto.getVisual = function (finder, visualType) {
  689. var ecModel = this._model;
  690. finder = modelUtil.parseFinder(ecModel, finder, {defaultMainType: 'series'});
  691. var seriesModel = finder.seriesModel;
  692. if (__DEV__) {
  693. if (!seriesModel) {
  694. console.warn('There is no specified seires model');
  695. }
  696. }
  697. var data = seriesModel.getData();
  698. var dataIndexInside = finder.hasOwnProperty('dataIndexInside')
  699. ? finder.dataIndexInside
  700. : finder.hasOwnProperty('dataIndex')
  701. ? data.indexOfRawIndex(finder.dataIndex)
  702. : null;
  703. return dataIndexInside != null
  704. ? data.getItemVisual(dataIndexInside, visualType)
  705. : data.getVisual(visualType);
  706. };
  707. /**
  708. * Get view of corresponding component model
  709. * @param {module:echarts/model/Component} componentModel
  710. * @return {module:echarts/view/Component}
  711. */
  712. echartsProto.getViewOfComponentModel = function (componentModel) {
  713. return this._componentsMap[componentModel.__viewId];
  714. };
  715. /**
  716. * Get view of corresponding series model
  717. * @param {module:echarts/model/Series} seriesModel
  718. * @return {module:echarts/view/Chart}
  719. */
  720. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  721. return this._chartsMap[seriesModel.__viewId];
  722. };
  723. var updateMethods = {
  724. prepareAndUpdate: function (payload) {
  725. prepare(this);
  726. updateMethods.update.call(this, payload);
  727. },
  728. /**
  729. * @param {Object} payload
  730. * @private
  731. */
  732. update: function (payload) {
  733. // console.profile && console.profile('update');
  734. var ecModel = this._model;
  735. var api = this._api;
  736. var zr = this._zr;
  737. var coordSysMgr = this._coordSysMgr;
  738. var scheduler = this._scheduler;
  739. // update before setOption
  740. if (!ecModel) {
  741. return;
  742. }
  743. scheduler.restoreData(ecModel, payload);
  744. scheduler.performSeriesTasks(ecModel);
  745. // TODO
  746. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  747. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  748. // Create new coordinate system each update
  749. // In LineView may save the old coordinate system and use it to get the orignal point
  750. coordSysMgr.create(ecModel, api);
  751. scheduler.performDataProcessorTasks(ecModel, payload);
  752. // Current stream render is not supported in data process. So we can update
  753. // stream modes after data processing, where the filtered data is used to
  754. // deteming whether use progressive rendering.
  755. updateStreamModes(this, ecModel);
  756. // We update stream modes before coordinate system updated, then the modes info
  757. // can be fetched when coord sys updating (consider the barGrid extent fix). But
  758. // the drawback is the full coord info can not be fetched. Fortunately this full
  759. // coord is not requied in stream mode updater currently.
  760. coordSysMgr.update(ecModel, api);
  761. clearColorPalette(ecModel);
  762. scheduler.performVisualTasks(ecModel, payload);
  763. render(this, ecModel, api, payload);
  764. // Set background
  765. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  766. // In IE8
  767. if (!env.canvasSupported) {
  768. var colorArr = colorTool.parse(backgroundColor);
  769. backgroundColor = colorTool.stringify(colorArr, 'rgb');
  770. if (colorArr[3] === 0) {
  771. backgroundColor = 'transparent';
  772. }
  773. }
  774. else {
  775. zr.setBackgroundColor(backgroundColor);
  776. }
  777. performPostUpdateFuncs(ecModel, api);
  778. // console.profile && console.profileEnd('update');
  779. },
  780. /**
  781. * @param {Object} payload
  782. * @private
  783. */
  784. updateTransform: function (payload) {
  785. var ecModel = this._model;
  786. var ecIns = this;
  787. var api = this._api;
  788. // update before setOption
  789. if (!ecModel) {
  790. return;
  791. }
  792. // ChartView.markUpdateMethod(payload, 'updateTransform');
  793. var componentDirtyList = [];
  794. ecModel.eachComponent(function (componentType, componentModel) {
  795. var componentView = ecIns.getViewOfComponentModel(componentModel);
  796. if (componentView && componentView.__alive) {
  797. if (componentView.updateTransform) {
  798. var result = componentView.updateTransform(componentModel, ecModel, api, payload);
  799. result && result.update && componentDirtyList.push(componentView);
  800. }
  801. else {
  802. componentDirtyList.push(componentView);
  803. }
  804. }
  805. });
  806. var seriesDirtyMap = zrUtil.createHashMap();
  807. ecModel.eachSeries(function (seriesModel) {
  808. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  809. if (chartView.updateTransform) {
  810. var result = chartView.updateTransform(seriesModel, ecModel, api, payload);
  811. result && result.update && seriesDirtyMap.set(seriesModel.uid, 1);
  812. }
  813. else {
  814. seriesDirtyMap.set(seriesModel.uid, 1);
  815. }
  816. });
  817. clearColorPalette(ecModel);
  818. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  819. // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
  820. this._scheduler.performVisualTasks(
  821. ecModel, payload, {setDirty: true, dirtyMap: seriesDirtyMap}
  822. );
  823. // Currently, not call render of components. Geo render cost a lot.
  824. // renderComponents(ecIns, ecModel, api, payload, componentDirtyList);
  825. renderSeries(ecIns, ecModel, api, payload, seriesDirtyMap);
  826. performPostUpdateFuncs(ecModel, this._api);
  827. },
  828. /**
  829. * @param {Object} payload
  830. * @private
  831. */
  832. updateView: function (payload) {
  833. var ecModel = this._model;
  834. // update before setOption
  835. if (!ecModel) {
  836. return;
  837. }
  838. ChartView.markUpdateMethod(payload, 'updateView');
  839. clearColorPalette(ecModel);
  840. // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  841. this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
  842. render(this, this._model, this._api, payload);
  843. performPostUpdateFuncs(ecModel, this._api);
  844. },
  845. /**
  846. * @param {Object} payload
  847. * @private
  848. */
  849. updateVisual: function (payload) {
  850. updateMethods.update.call(this, payload);
  851. // var ecModel = this._model;
  852. // // update before setOption
  853. // if (!ecModel) {
  854. // return;
  855. // }
  856. // ChartView.markUpdateMethod(payload, 'updateVisual');
  857. // clearColorPalette(ecModel);
  858. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  859. // this._scheduler.performVisualTasks(ecModel, payload, {visualType: 'visual', setDirty: true});
  860. // render(this, this._model, this._api, payload);
  861. // performPostUpdateFuncs(ecModel, this._api);
  862. },
  863. /**
  864. * @param {Object} payload
  865. * @private
  866. */
  867. updateLayout: function (payload) {
  868. updateMethods.update.call(this, payload);
  869. // var ecModel = this._model;
  870. // // update before setOption
  871. // if (!ecModel) {
  872. // return;
  873. // }
  874. // ChartView.markUpdateMethod(payload, 'updateLayout');
  875. // // Keep pipe to the exist pipeline because it depends on the render task of the full pipeline.
  876. // // this._scheduler.performVisualTasks(ecModel, payload, 'layout', true);
  877. // this._scheduler.performVisualTasks(ecModel, payload, {setDirty: true});
  878. // render(this, this._model, this._api, payload);
  879. // performPostUpdateFuncs(ecModel, this._api);
  880. }
  881. };
  882. function prepare(ecIns) {
  883. var ecModel = ecIns._model;
  884. var scheduler = ecIns._scheduler;
  885. scheduler.restorePipelines(ecModel);
  886. scheduler.prepareStageTasks();
  887. prepareView(ecIns, 'component', ecModel, scheduler);
  888. prepareView(ecIns, 'chart', ecModel, scheduler);
  889. scheduler.plan();
  890. }
  891. /**
  892. * @private
  893. */
  894. function updateDirectly(ecIns, method, payload, mainType, subType) {
  895. var ecModel = ecIns._model;
  896. // broadcast
  897. if (!mainType) {
  898. // FIXME
  899. // Chart will not be update directly here, except set dirty.
  900. // But there is no such scenario now.
  901. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  902. return;
  903. }
  904. var query = {};
  905. query[mainType + 'Id'] = payload[mainType + 'Id'];
  906. query[mainType + 'Index'] = payload[mainType + 'Index'];
  907. query[mainType + 'Name'] = payload[mainType + 'Name'];
  908. var condition = {mainType: mainType, query: query};
  909. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  910. var excludeSeriesId = payload.excludeSeriesId;
  911. if (excludeSeriesId != null) {
  912. excludeSeriesId = zrUtil.createHashMap(modelUtil.normalizeToArray(excludeSeriesId));
  913. }
  914. // If dispatchAction before setOption, do nothing.
  915. ecModel && ecModel.eachComponent(condition, function (model) {
  916. if (!excludeSeriesId || excludeSeriesId.get(model.id) == null) {
  917. callView(ecIns[
  918. mainType === 'series' ? '_chartsMap' : '_componentsMap'
  919. ][model.__viewId]);
  920. }
  921. }, ecIns);
  922. function callView(view) {
  923. view && view.__alive && view[method] && view[method](
  924. view.__model, ecModel, ecIns._api, payload
  925. );
  926. }
  927. }
  928. /**
  929. * Resize the chart
  930. * @param {Object} opts
  931. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  932. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  933. * @param {boolean} [opts.silent=false]
  934. */
  935. echartsProto.resize = function (opts) {
  936. if (__DEV__) {
  937. assert(!this[IN_MAIN_PROCESS], '`resize` should not be called during main process.');
  938. }
  939. if (this._disposed) {
  940. disposedWarning(this.id);
  941. return;
  942. }
  943. this._zr.resize(opts);
  944. var ecModel = this._model;
  945. // Resize loading effect
  946. this._loadingFX && this._loadingFX.resize();
  947. if (!ecModel) {
  948. return;
  949. }
  950. var optionChanged = ecModel.resetOption('media');
  951. var silent = opts && opts.silent;
  952. this[IN_MAIN_PROCESS] = true;
  953. optionChanged && prepare(this);
  954. updateMethods.update.call(this);
  955. this[IN_MAIN_PROCESS] = false;
  956. flushPendingActions.call(this, silent);
  957. triggerUpdatedEvent.call(this, silent);
  958. };
  959. function updateStreamModes(ecIns, ecModel) {
  960. var chartsMap = ecIns._chartsMap;
  961. var scheduler = ecIns._scheduler;
  962. ecModel.eachSeries(function (seriesModel) {
  963. scheduler.updateStreamModes(seriesModel, chartsMap[seriesModel.__viewId]);
  964. });
  965. }
  966. /**
  967. * Show loading effect
  968. * @param {string} [name='default']
  969. * @param {Object} [cfg]
  970. */
  971. echartsProto.showLoading = function (name, cfg) {
  972. if (this._disposed) {
  973. disposedWarning(this.id);
  974. return;
  975. }
  976. if (isObject(name)) {
  977. cfg = name;
  978. name = '';
  979. }
  980. name = name || 'default';
  981. this.hideLoading();
  982. if (!loadingEffects[name]) {
  983. if (__DEV__) {
  984. console.warn('Loading effects ' + name + ' not exists.');
  985. }
  986. return;
  987. }
  988. var el = loadingEffects[name](this._api, cfg);
  989. var zr = this._zr;
  990. this._loadingFX = el;
  991. zr.add(el);
  992. };
  993. /**
  994. * Hide loading effect
  995. */
  996. echartsProto.hideLoading = function () {
  997. if (this._disposed) {
  998. disposedWarning(this.id);
  999. return;
  1000. }
  1001. this._loadingFX && this._zr.remove(this._loadingFX);
  1002. this._loadingFX = null;
  1003. };
  1004. /**
  1005. * @param {Object} eventObj
  1006. * @return {Object}
  1007. */
  1008. echartsProto.makeActionFromEvent = function (eventObj) {
  1009. var payload = zrUtil.extend({}, eventObj);
  1010. payload.type = eventActionMap[eventObj.type];
  1011. return payload;
  1012. };
  1013. /**
  1014. * @pubilc
  1015. * @param {Object} payload
  1016. * @param {string} [payload.type] Action type
  1017. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  1018. * @param {boolean} [opt.silent=false] Whether trigger events.
  1019. * @param {boolean} [opt.flush=undefined]
  1020. * true: Flush immediately, and then pixel in canvas can be fetched
  1021. * immediately. Caution: it might affect performance.
  1022. * false: Not flush.
  1023. * undefined: Auto decide whether perform flush.
  1024. */
  1025. echartsProto.dispatchAction = function (payload, opt) {
  1026. if (this._disposed) {
  1027. disposedWarning(this.id);
  1028. return;
  1029. }
  1030. if (!isObject(opt)) {
  1031. opt = {silent: !!opt};
  1032. }
  1033. if (!actions[payload.type]) {
  1034. return;
  1035. }
  1036. // Avoid dispatch action before setOption. Especially in `connect`.
  1037. if (!this._model) {
  1038. return;
  1039. }
  1040. // May dispatchAction in rendering procedure
  1041. if (this[IN_MAIN_PROCESS]) {
  1042. this._pendingActions.push(payload);
  1043. return;
  1044. }
  1045. doDispatchAction.call(this, payload, opt.silent);
  1046. if (opt.flush) {
  1047. this._zr.flush(true);
  1048. }
  1049. else if (opt.flush !== false && env.browser.weChat) {
  1050. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  1051. // hang when sliding page (on touch event), which cause that zr does not
  1052. // refresh util user interaction finished, which is not expected.
  1053. // But `dispatchAction` may be called too frequently when pan on touch
  1054. // screen, which impacts performance if do not throttle them.
  1055. this._throttledZrFlush();
  1056. }
  1057. flushPendingActions.call(this, opt.silent);
  1058. triggerUpdatedEvent.call(this, opt.silent);
  1059. };
  1060. function doDispatchAction(payload, silent) {
  1061. var payloadType = payload.type;
  1062. var escapeConnect = payload.escapeConnect;
  1063. var actionWrap = actions[payloadType];
  1064. var actionInfo = actionWrap.actionInfo;
  1065. var cptType = (actionInfo.update || 'update').split(':');
  1066. var updateMethod = cptType.pop();
  1067. cptType = cptType[0] != null && parseClassType(cptType[0]);
  1068. this[IN_MAIN_PROCESS] = true;
  1069. var payloads = [payload];
  1070. var batched = false;
  1071. // Batch action
  1072. if (payload.batch) {
  1073. batched = true;
  1074. payloads = zrUtil.map(payload.batch, function (item) {
  1075. item = zrUtil.defaults(zrUtil.extend({}, item), payload);
  1076. item.batch = null;
  1077. return item;
  1078. });
  1079. }
  1080. var eventObjBatch = [];
  1081. var eventObj;
  1082. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  1083. each(payloads, function (batchItem) {
  1084. // Action can specify the event by return it.
  1085. eventObj = actionWrap.action(batchItem, this._model, this._api);
  1086. // Emit event outside
  1087. eventObj = eventObj || zrUtil.extend({}, batchItem);
  1088. // Convert type to eventType
  1089. eventObj.type = actionInfo.event || eventObj.type;
  1090. eventObjBatch.push(eventObj);
  1091. // light update does not perform data process, layout and visual.
  1092. if (isHighDown) {
  1093. // method, payload, mainType, subType
  1094. updateDirectly(this, updateMethod, batchItem, 'series');
  1095. }
  1096. else if (cptType) {
  1097. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  1098. }
  1099. }, this);
  1100. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  1101. // Still dirty
  1102. if (this[OPTION_UPDATED]) {
  1103. // FIXME Pass payload ?
  1104. prepare(this);
  1105. updateMethods.update.call(this, payload);
  1106. this[OPTION_UPDATED] = false;
  1107. }
  1108. else {
  1109. updateMethods[updateMethod].call(this, payload);
  1110. }
  1111. }
  1112. // Follow the rule of action batch
  1113. if (batched) {
  1114. eventObj = {
  1115. type: actionInfo.event || payloadType,
  1116. escapeConnect: escapeConnect,
  1117. batch: eventObjBatch
  1118. };
  1119. }
  1120. else {
  1121. eventObj = eventObjBatch[0];
  1122. }
  1123. this[IN_MAIN_PROCESS] = false;
  1124. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  1125. }
  1126. function flushPendingActions(silent) {
  1127. var pendingActions = this._pendingActions;
  1128. while (pendingActions.length) {
  1129. var payload = pendingActions.shift();
  1130. doDispatchAction.call(this, payload, silent);
  1131. }
  1132. }
  1133. function triggerUpdatedEvent(silent) {
  1134. !silent && this.trigger('updated');
  1135. }
  1136. /**
  1137. * Event `rendered` is triggered when zr
  1138. * rendered. It is useful for realtime
  1139. * snapshot (reflect animation).
  1140. *
  1141. * Event `finished` is triggered when:
  1142. * (1) zrender rendering finished.
  1143. * (2) initial animation finished.
  1144. * (3) progressive rendering finished.
  1145. * (4) no pending action.
  1146. * (5) no delayed setOption needs to be processed.
  1147. */
  1148. function bindRenderedEvent(zr, ecIns) {
  1149. zr.on('rendered', function () {
  1150. ecIns.trigger('rendered');
  1151. // The `finished` event should not be triggered repeatly,
  1152. // so it should only be triggered when rendering indeed happend
  1153. // in zrender. (Consider the case that dipatchAction is keep
  1154. // triggering when mouse move).
  1155. if (
  1156. // Although zr is dirty if initial animation is not finished
  1157. // and this checking is called on frame, we also check
  1158. // animation finished for robustness.
  1159. zr.animation.isFinished()
  1160. && !ecIns[OPTION_UPDATED]
  1161. && !ecIns._scheduler.unfinished
  1162. && !ecIns._pendingActions.length
  1163. ) {
  1164. ecIns.trigger('finished');
  1165. }
  1166. });
  1167. }
  1168. /**
  1169. * @param {Object} params
  1170. * @param {number} params.seriesIndex
  1171. * @param {Array|TypedArray} params.data
  1172. */
  1173. echartsProto.appendData = function (params) {
  1174. if (this._disposed) {
  1175. disposedWarning(this.id);
  1176. return;
  1177. }
  1178. var seriesIndex = params.seriesIndex;
  1179. var ecModel = this.getModel();
  1180. var seriesModel = ecModel.getSeriesByIndex(seriesIndex);
  1181. if (__DEV__) {
  1182. assert(params.data && seriesModel);
  1183. }
  1184. seriesModel.appendData(params);
  1185. // Note: `appendData` does not support that update extent of coordinate
  1186. // system, util some scenario require that. In the expected usage of
  1187. // `appendData`, the initial extent of coordinate system should better
  1188. // be fixed by axis `min`/`max` setting or initial data, otherwise if
  1189. // the extent changed while `appendData`, the location of the painted
  1190. // graphic elements have to be changed, which make the usage of
  1191. // `appendData` meaningless.
  1192. this._scheduler.unfinished = true;
  1193. };
  1194. /**
  1195. * Register event
  1196. * @method
  1197. */
  1198. echartsProto.on = createRegisterEventWithLowercaseName('on', false);
  1199. echartsProto.off = createRegisterEventWithLowercaseName('off', false);
  1200. echartsProto.one = createRegisterEventWithLowercaseName('one', false);
  1201. /**
  1202. * Prepare view instances of charts and components
  1203. * @param {module:echarts/model/Global} ecModel
  1204. * @private
  1205. */
  1206. function prepareView(ecIns, type, ecModel, scheduler) {
  1207. var isComponent = type === 'component';
  1208. var viewList = isComponent ? ecIns._componentsViews : ecIns._chartsViews;
  1209. var viewMap = isComponent ? ecIns._componentsMap : ecIns._chartsMap;
  1210. var zr = ecIns._zr;
  1211. var api = ecIns._api;
  1212. for (var i = 0; i < viewList.length; i++) {
  1213. viewList[i].__alive = false;
  1214. }
  1215. isComponent
  1216. ? ecModel.eachComponent(function (componentType, model) {
  1217. componentType !== 'series' && doPrepare(model);
  1218. })
  1219. : ecModel.eachSeries(doPrepare);
  1220. function doPrepare(model) {
  1221. // Consider: id same and type changed.
  1222. var viewId = '_ec_' + model.id + '_' + model.type;
  1223. var view = viewMap[viewId];
  1224. if (!view) {
  1225. var classType = parseClassType(model.type);
  1226. var Clazz = isComponent
  1227. ? ComponentView.getClass(classType.main, classType.sub)
  1228. : ChartView.getClass(classType.sub);
  1229. if (__DEV__) {
  1230. assert(Clazz, classType.sub + ' does not exist.');
  1231. }
  1232. view = new Clazz();
  1233. view.init(ecModel, api);
  1234. viewMap[viewId] = view;
  1235. viewList.push(view);
  1236. zr.add(view.group);
  1237. }
  1238. model.__viewId = view.__id = viewId;
  1239. view.__alive = true;
  1240. view.__model = model;
  1241. view.group.__ecComponentInfo = {
  1242. mainType: model.mainType,
  1243. index: model.componentIndex
  1244. };
  1245. !isComponent && scheduler.prepareView(view, model, ecModel, api);
  1246. }
  1247. for (var i = 0; i < viewList.length;) {
  1248. var view = viewList[i];
  1249. if (!view.__alive) {
  1250. !isComponent && view.renderTask.dispose();
  1251. zr.remove(view.group);
  1252. view.dispose(ecModel, api);
  1253. viewList.splice(i, 1);
  1254. delete viewMap[view.__id];
  1255. view.__id = view.group.__ecComponentInfo = null;
  1256. }
  1257. else {
  1258. i++;
  1259. }
  1260. }
  1261. }
  1262. // /**
  1263. // * Encode visual infomation from data after data processing
  1264. // *
  1265. // * @param {module:echarts/model/Global} ecModel
  1266. // * @param {object} layout
  1267. // * @param {boolean} [layoutFilter] `true`: only layout,
  1268. // * `false`: only not layout,
  1269. // * `null`/`undefined`: all.
  1270. // * @param {string} taskBaseTag
  1271. // * @private
  1272. // */
  1273. // function startVisualEncoding(ecIns, ecModel, api, payload, layoutFilter) {
  1274. // each(visualFuncs, function (visual, index) {
  1275. // var isLayout = visual.isLayout;
  1276. // if (layoutFilter == null
  1277. // || (layoutFilter === false && !isLayout)
  1278. // || (layoutFilter === true && isLayout)
  1279. // ) {
  1280. // visual.func(ecModel, api, payload);
  1281. // }
  1282. // });
  1283. // }
  1284. function clearColorPalette(ecModel) {
  1285. ecModel.clearColorPalette();
  1286. ecModel.eachSeries(function (seriesModel) {
  1287. seriesModel.clearColorPalette();
  1288. });
  1289. }
  1290. function render(ecIns, ecModel, api, payload) {
  1291. renderComponents(ecIns, ecModel, api, payload);
  1292. each(ecIns._chartsViews, function (chart) {
  1293. chart.__alive = false;
  1294. });
  1295. renderSeries(ecIns, ecModel, api, payload);
  1296. // Remove groups of unrendered charts
  1297. each(ecIns._chartsViews, function (chart) {
  1298. if (!chart.__alive) {
  1299. chart.remove(ecModel, api);
  1300. }
  1301. });
  1302. }
  1303. function renderComponents(ecIns, ecModel, api, payload, dirtyList) {
  1304. each(dirtyList || ecIns._componentsViews, function (componentView) {
  1305. var componentModel = componentView.__model;
  1306. componentView.render(componentModel, ecModel, api, payload);
  1307. updateZ(componentModel, componentView);
  1308. });
  1309. }
  1310. /**
  1311. * Render each chart and component
  1312. * @private
  1313. */
  1314. function renderSeries(ecIns, ecModel, api, payload, dirtyMap) {
  1315. // Render all charts
  1316. var scheduler = ecIns._scheduler;
  1317. var unfinished;
  1318. ecModel.eachSeries(function (seriesModel) {
  1319. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  1320. chartView.__alive = true;
  1321. var renderTask = chartView.renderTask;
  1322. scheduler.updatePayload(renderTask, payload);
  1323. if (dirtyMap && dirtyMap.get(seriesModel.uid)) {
  1324. renderTask.dirty();
  1325. }
  1326. unfinished |= renderTask.perform(scheduler.getPerformArgs(renderTask));
  1327. chartView.group.silent = !!seriesModel.get('silent');
  1328. updateZ(seriesModel, chartView);
  1329. updateBlend(seriesModel, chartView);
  1330. });
  1331. scheduler.unfinished |= unfinished;
  1332. // If use hover layer
  1333. updateHoverLayerStatus(ecIns, ecModel);
  1334. // Add aria
  1335. aria(ecIns._zr.dom, ecModel);
  1336. }
  1337. function performPostUpdateFuncs(ecModel, api) {
  1338. each(postUpdateFuncs, function (func) {
  1339. func(ecModel, api);
  1340. });
  1341. }
  1342. var MOUSE_EVENT_NAMES = [
  1343. 'click', 'dblclick', 'mouseover', 'mouseout', 'mousemove',
  1344. 'mousedown', 'mouseup', 'globalout', 'contextmenu'
  1345. ];
  1346. /**
  1347. * @private
  1348. */
  1349. echartsProto._initEvents = function () {
  1350. each(MOUSE_EVENT_NAMES, function (eveName) {
  1351. var handler = function (e) {
  1352. var ecModel = this.getModel();
  1353. var el = e.target;
  1354. var params;
  1355. var isGlobalOut = eveName === 'globalout';
  1356. // no e.target when 'globalout'.
  1357. if (isGlobalOut) {
  1358. params = {};
  1359. }
  1360. else if (el && el.dataIndex != null) {
  1361. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  1362. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType, el) || {};
  1363. }
  1364. // If element has custom eventData of components
  1365. else if (el && el.eventData) {
  1366. params = zrUtil.extend({}, el.eventData);
  1367. }
  1368. // Contract: if params prepared in mouse event,
  1369. // these properties must be specified:
  1370. // {
  1371. // componentType: string (component main type)
  1372. // componentIndex: number
  1373. // }
  1374. // Otherwise event query can not work.
  1375. if (params) {
  1376. var componentType = params.componentType;
  1377. var componentIndex = params.componentIndex;
  1378. // Special handling for historic reason: when trigger by
  1379. // markLine/markPoint/markArea, the componentType is
  1380. // 'markLine'/'markPoint'/'markArea', but we should better
  1381. // enable them to be queried by seriesIndex, since their
  1382. // option is set in each series.
  1383. if (componentType === 'markLine'
  1384. || componentType === 'markPoint'
  1385. || componentType === 'markArea'
  1386. ) {
  1387. componentType = 'series';
  1388. componentIndex = params.seriesIndex;
  1389. }
  1390. var model = componentType && componentIndex != null
  1391. && ecModel.getComponent(componentType, componentIndex);
  1392. var view = model && this[
  1393. model.mainType === 'series' ? '_chartsMap' : '_componentsMap'
  1394. ][model.__viewId];
  1395. if (__DEV__) {
  1396. // `event.componentType` and `event[componentTpype + 'Index']` must not
  1397. // be missed, otherwise there is no way to distinguish source component.
  1398. // See `dataFormat.getDataParams`.
  1399. if (!isGlobalOut && !(model && view)) {
  1400. console.warn('model or view can not be found by params');
  1401. }
  1402. }
  1403. params.event = e;
  1404. params.type = eveName;
  1405. this._ecEventProcessor.eventInfo = {
  1406. targetEl: el,
  1407. packedEvent: params,
  1408. model: model,
  1409. view: view
  1410. };
  1411. this.trigger(eveName, params);
  1412. }
  1413. };
  1414. // Consider that some component (like tooltip, brush, ...)
  1415. // register zr event handler, but user event handler might
  1416. // do anything, such as call `setOption` or `dispatchAction`,
  1417. // which probably update any of the content and probably
  1418. // cause problem if it is called previous other inner handlers.
  1419. handler.zrEventfulCallAtLast = true;
  1420. this._zr.on(eveName, handler, this);
  1421. }, this);
  1422. each(eventActionMap, function (actionType, eventType) {
  1423. this._messageCenter.on(eventType, function (event) {
  1424. this.trigger(eventType, event);
  1425. }, this);
  1426. }, this);
  1427. };
  1428. /**
  1429. * @return {boolean}
  1430. */
  1431. echartsProto.isDisposed = function () {
  1432. return this._disposed;
  1433. };
  1434. /**
  1435. * Clear
  1436. */
  1437. echartsProto.clear = function () {
  1438. if (this._disposed) {
  1439. disposedWarning(this.id);
  1440. return;
  1441. }
  1442. this.setOption({ series: [] }, true);
  1443. };
  1444. /**
  1445. * Dispose instance
  1446. */
  1447. echartsProto.dispose = function () {
  1448. if (this._disposed) {
  1449. disposedWarning(this.id);
  1450. return;
  1451. }
  1452. this._disposed = true;
  1453. modelUtil.setAttribute(this.getDom(), DOM_ATTRIBUTE_KEY, '');
  1454. var api = this._api;
  1455. var ecModel = this._model;
  1456. each(this._componentsViews, function (component) {
  1457. component.dispose(ecModel, api);
  1458. });
  1459. each(this._chartsViews, function (chart) {
  1460. chart.dispose(ecModel, api);
  1461. });
  1462. // Dispose after all views disposed
  1463. this._zr.dispose();
  1464. delete instances[this.id];
  1465. };
  1466. zrUtil.mixin(ECharts, Eventful);
  1467. function disposedWarning(id) {
  1468. if (__DEV__) {
  1469. console.warn('Instance ' + id + ' has been disposed');
  1470. }
  1471. }
  1472. function updateHoverLayerStatus(ecIns, ecModel) {
  1473. var zr = ecIns._zr;
  1474. var storage = zr.storage;
  1475. var elCount = 0;
  1476. storage.traverse(function (el) {
  1477. elCount++;
  1478. });
  1479. if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
  1480. ecModel.eachSeries(function (seriesModel) {
  1481. if (seriesModel.preventUsingHoverLayer) {
  1482. return;
  1483. }
  1484. var chartView = ecIns._chartsMap[seriesModel.__viewId];
  1485. if (chartView.__alive) {
  1486. chartView.group.traverse(function (el) {
  1487. // Don't switch back.
  1488. el.useHoverLayer = true;
  1489. });
  1490. }
  1491. });
  1492. }
  1493. }
  1494. /**
  1495. * Update chart progressive and blend.
  1496. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  1497. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  1498. */
  1499. function updateBlend(seriesModel, chartView) {
  1500. var blendMode = seriesModel.get('blendMode') || null;
  1501. if (__DEV__) {
  1502. if (!env.canvasSupported && blendMode && blendMode !== 'source-over') {
  1503. console.warn('Only canvas support blendMode');
  1504. }
  1505. }
  1506. chartView.group.traverse(function (el) {
  1507. // FIXME marker and other components
  1508. if (!el.isGroup) {
  1509. // Only set if blendMode is changed. In case element is incremental and don't wan't to rerender.
  1510. if (el.style.blend !== blendMode) {
  1511. el.setStyle('blend', blendMode);
  1512. }
  1513. }
  1514. if (el.eachPendingDisplayable) {
  1515. el.eachPendingDisplayable(function (displayable) {
  1516. displayable.setStyle('blend', blendMode);
  1517. });
  1518. }
  1519. });
  1520. }
  1521. /**
  1522. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  1523. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  1524. */
  1525. function updateZ(model, view) {
  1526. var z = model.get('z');
  1527. var zlevel = model.get('zlevel');
  1528. // Set z and zlevel
  1529. view.group.traverse(function (el) {
  1530. if (el.type !== 'group') {
  1531. z != null && (el.z = z);
  1532. zlevel != null && (el.zlevel = zlevel);
  1533. }
  1534. });
  1535. }
  1536. function createExtensionAPI(ecInstance) {
  1537. var coordSysMgr = ecInstance._coordSysMgr;
  1538. return zrUtil.extend(new ExtensionAPI(ecInstance), {
  1539. // Inject methods
  1540. getCoordinateSystems: zrUtil.bind(
  1541. coordSysMgr.getCoordinateSystems, coordSysMgr
  1542. ),
  1543. getComponentByElement: function (el) {
  1544. while (el) {
  1545. var modelInfo = el.__ecComponentInfo;
  1546. if (modelInfo != null) {
  1547. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  1548. }
  1549. el = el.parent;
  1550. }
  1551. }
  1552. });
  1553. }
  1554. /**
  1555. * @class
  1556. * Usage of query:
  1557. * `chart.on('click', query, handler);`
  1558. * The `query` can be:
  1559. * + The component type query string, only `mainType` or `mainType.subType`,
  1560. * like: 'xAxis', 'series', 'xAxis.category' or 'series.line'.
  1561. * + The component query object, like:
  1562. * `{seriesIndex: 2}`, `{seriesName: 'xx'}`, `{seriesId: 'some'}`,
  1563. * `{xAxisIndex: 2}`, `{xAxisName: 'xx'}`, `{xAxisId: 'some'}`.
  1564. * + The data query object, like:
  1565. * `{dataIndex: 123}`, `{dataType: 'link'}`, `{name: 'some'}`.
  1566. * + The other query object (cmponent customized query), like:
  1567. * `{element: 'some'}` (only available in custom series).
  1568. *
  1569. * Caveat: If a prop in the `query` object is `null/undefined`, it is the
  1570. * same as there is no such prop in the `query` object.
  1571. */
  1572. function EventProcessor() {
  1573. // These info required: targetEl, packedEvent, model, view
  1574. this.eventInfo;
  1575. }
  1576. EventProcessor.prototype = {
  1577. constructor: EventProcessor,
  1578. normalizeQuery: function (query) {
  1579. var cptQuery = {};
  1580. var dataQuery = {};
  1581. var otherQuery = {};
  1582. // `query` is `mainType` or `mainType.subType` of component.
  1583. if (zrUtil.isString(query)) {
  1584. var condCptType = parseClassType(query);
  1585. // `.main` and `.sub` may be ''.
  1586. cptQuery.mainType = condCptType.main || null;
  1587. cptQuery.subType = condCptType.sub || null;
  1588. }
  1589. // `query` is an object, convert to {mainType, index, name, id}.
  1590. else {
  1591. // `xxxIndex`, `xxxName`, `xxxId`, `name`, `dataIndex`, `dataType` is reserved,
  1592. // can not be used in `compomentModel.filterForExposedEvent`.
  1593. var suffixes = ['Index', 'Name', 'Id'];
  1594. var dataKeys = {name: 1, dataIndex: 1, dataType: 1};
  1595. zrUtil.each(query, function (val, key) {
  1596. var reserved = false;
  1597. for (var i = 0; i < suffixes.length; i++) {
  1598. var propSuffix = suffixes[i];
  1599. var suffixPos = key.lastIndexOf(propSuffix);
  1600. if (suffixPos > 0 && suffixPos === key.length - propSuffix.length) {
  1601. var mainType = key.slice(0, suffixPos);
  1602. // Consider `dataIndex`.
  1603. if (mainType !== 'data') {
  1604. cptQuery.mainType = mainType;
  1605. cptQuery[propSuffix.toLowerCase()] = val;
  1606. reserved = true;
  1607. }
  1608. }
  1609. }
  1610. if (dataKeys.hasOwnProperty(key)) {
  1611. dataQuery[key] = val;
  1612. reserved = true;
  1613. }
  1614. if (!reserved) {
  1615. otherQuery[key] = val;
  1616. }
  1617. });
  1618. }
  1619. return {
  1620. cptQuery: cptQuery,
  1621. dataQuery: dataQuery,
  1622. otherQuery: otherQuery
  1623. };
  1624. },
  1625. filter: function (eventType, query, args) {
  1626. // They should be assigned before each trigger call.
  1627. var eventInfo = this.eventInfo;
  1628. if (!eventInfo) {
  1629. return true;
  1630. }
  1631. var targetEl = eventInfo.targetEl;
  1632. var packedEvent = eventInfo.packedEvent;
  1633. var model = eventInfo.model;
  1634. var view = eventInfo.view;
  1635. // For event like 'globalout'.
  1636. if (!model || !view) {
  1637. return true;
  1638. }
  1639. var cptQuery = query.cptQuery;
  1640. var dataQuery = query.dataQuery;
  1641. return check(cptQuery, model, 'mainType')
  1642. && check(cptQuery, model, 'subType')
  1643. && check(cptQuery, model, 'index', 'componentIndex')
  1644. && check(cptQuery, model, 'name')
  1645. && check(cptQuery, model, 'id')
  1646. && check(dataQuery, packedEvent, 'name')
  1647. && check(dataQuery, packedEvent, 'dataIndex')
  1648. && check(dataQuery, packedEvent, 'dataType')
  1649. && (!view.filterForExposedEvent || view.filterForExposedEvent(
  1650. eventType, query.otherQuery, targetEl, packedEvent
  1651. ));
  1652. function check(query, host, prop, propOnHost) {
  1653. return query[prop] == null || host[propOnHost || prop] === query[prop];
  1654. }
  1655. },
  1656. afterTrigger: function () {
  1657. // Make sure the eventInfo wont be used in next trigger.
  1658. this.eventInfo = null;
  1659. }
  1660. };
  1661. /**
  1662. * @type {Object} key: actionType.
  1663. * @inner
  1664. */
  1665. var actions = {};
  1666. /**
  1667. * Map eventType to actionType
  1668. * @type {Object}
  1669. */
  1670. var eventActionMap = {};
  1671. /**
  1672. * Data processor functions of each stage
  1673. * @type {Array.<Object.<string, Function>>}
  1674. * @inner
  1675. */
  1676. var dataProcessorFuncs = [];
  1677. /**
  1678. * @type {Array.<Function>}
  1679. * @inner
  1680. */
  1681. var optionPreprocessorFuncs = [];
  1682. /**
  1683. * @type {Array.<Function>}
  1684. * @inner
  1685. */
  1686. var postUpdateFuncs = [];
  1687. /**
  1688. * Visual encoding functions of each stage
  1689. * @type {Array.<Object.<string, Function>>}
  1690. */
  1691. var visualFuncs = [];
  1692. /**
  1693. * Theme storage
  1694. * @type {Object.<key, Object>}
  1695. */
  1696. var themeStorage = {};
  1697. /**
  1698. * Loading effects
  1699. */
  1700. var loadingEffects = {};
  1701. var instances = {};
  1702. var connectedGroups = {};
  1703. var idBase = new Date() - 0;
  1704. var groupIdBase = new Date() - 0;
  1705. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  1706. function enableConnect(chart) {
  1707. var STATUS_PENDING = 0;
  1708. var STATUS_UPDATING = 1;
  1709. var STATUS_UPDATED = 2;
  1710. var STATUS_KEY = '__connectUpdateStatus';
  1711. function updateConnectedChartsStatus(charts, status) {
  1712. for (var i = 0; i < charts.length; i++) {
  1713. var otherChart = charts[i];
  1714. otherChart[STATUS_KEY] = status;
  1715. }
  1716. }
  1717. each(eventActionMap, function (actionType, eventType) {
  1718. chart._messageCenter.on(eventType, function (event) {
  1719. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  1720. if (event && event.escapeConnect) {
  1721. return;
  1722. }
  1723. var action = chart.makeActionFromEvent(event);
  1724. var otherCharts = [];
  1725. each(instances, function (otherChart) {
  1726. if (otherChart !== chart && otherChart.group === chart.group) {
  1727. otherCharts.push(otherChart);
  1728. }
  1729. });
  1730. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  1731. each(otherCharts, function (otherChart) {
  1732. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  1733. otherChart.dispatchAction(action);
  1734. }
  1735. });
  1736. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  1737. }
  1738. });
  1739. });
  1740. }
  1741. /**
  1742. * @param {HTMLElement} dom
  1743. * @param {Object} [theme]
  1744. * @param {Object} opts
  1745. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  1746. * @param {string} [opts.renderer] Can choose 'canvas' or 'svg' to render the chart.
  1747. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  1748. * Can be 'auto' (the same as null/undefined)
  1749. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  1750. * Can be 'auto' (the same as null/undefined)
  1751. */
  1752. export function init(dom, theme, opts) {
  1753. if (__DEV__) {
  1754. // Check version
  1755. if ((zrender.version.replace('.', '') - 0) < (dependencies.zrender.replace('.', '') - 0)) {
  1756. throw new Error(
  1757. 'zrender/src ' + zrender.version
  1758. + ' is too old for ECharts ' + version
  1759. + '. Current version need ZRender '
  1760. + dependencies.zrender + '+'
  1761. );
  1762. }
  1763. if (!dom) {
  1764. throw new Error('Initialize failed: invalid dom.');
  1765. }
  1766. }
  1767. var existInstance = getInstanceByDom(dom);
  1768. if (existInstance) {
  1769. if (__DEV__) {
  1770. console.warn('There is a chart instance already initialized on the dom.');
  1771. }
  1772. return existInstance;
  1773. }
  1774. if (__DEV__) {
  1775. if (zrUtil.isDom(dom)
  1776. && dom.nodeName.toUpperCase() !== 'CANVAS'
  1777. && (
  1778. (!dom.clientWidth && (!opts || opts.width == null))
  1779. || (!dom.clientHeight && (!opts || opts.height == null))
  1780. )
  1781. ) {
  1782. console.warn('Can\'t get DOM width or height. Please check '
  1783. + 'dom.clientWidth and dom.clientHeight. They should not be 0.'
  1784. + 'For example, you may need to call this in the callback '
  1785. + 'of window.onload.');
  1786. }
  1787. }
  1788. var chart = new ECharts(dom, theme, opts);
  1789. chart.id = 'ec_' + idBase++;
  1790. instances[chart.id] = chart;
  1791. modelUtil.setAttribute(dom, DOM_ATTRIBUTE_KEY, chart.id);
  1792. enableConnect(chart);
  1793. return chart;
  1794. }
  1795. /**
  1796. * @return {string|Array.<module:echarts~ECharts>} groupId
  1797. */
  1798. export function connect(groupId) {
  1799. // Is array of charts
  1800. if (zrUtil.isArray(groupId)) {
  1801. var charts = groupId;
  1802. groupId = null;
  1803. // If any chart has group
  1804. each(charts, function (chart) {
  1805. if (chart.group != null) {
  1806. groupId = chart.group;
  1807. }
  1808. });
  1809. groupId = groupId || ('g_' + groupIdBase++);
  1810. each(charts, function (chart) {
  1811. chart.group = groupId;
  1812. });
  1813. }
  1814. connectedGroups[groupId] = true;
  1815. return groupId;
  1816. }
  1817. /**
  1818. * @DEPRECATED
  1819. * @return {string} groupId
  1820. */
  1821. export function disConnect(groupId) {
  1822. connectedGroups[groupId] = false;
  1823. }
  1824. /**
  1825. * @return {string} groupId
  1826. */
  1827. export var disconnect = disConnect;
  1828. /**
  1829. * Dispose a chart instance
  1830. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  1831. */
  1832. export function dispose(chart) {
  1833. if (typeof chart === 'string') {
  1834. chart = instances[chart];
  1835. }
  1836. else if (!(chart instanceof ECharts)) {
  1837. // Try to treat as dom
  1838. chart = getInstanceByDom(chart);
  1839. }
  1840. if ((chart instanceof ECharts) && !chart.isDisposed()) {
  1841. chart.dispose();
  1842. }
  1843. }
  1844. /**
  1845. * @param {HTMLElement} dom
  1846. * @return {echarts~ECharts}
  1847. */
  1848. export function getInstanceByDom(dom) {
  1849. return instances[modelUtil.getAttribute(dom, DOM_ATTRIBUTE_KEY)];
  1850. }
  1851. /**
  1852. * @param {string} key
  1853. * @return {echarts~ECharts}
  1854. */
  1855. export function getInstanceById(key) {
  1856. return instances[key];
  1857. }
  1858. /**
  1859. * Register theme
  1860. */
  1861. export function registerTheme(name, theme) {
  1862. themeStorage[name] = theme;
  1863. }
  1864. /**
  1865. * Register option preprocessor
  1866. * @param {Function} preprocessorFunc
  1867. */
  1868. export function registerPreprocessor(preprocessorFunc) {
  1869. optionPreprocessorFuncs.push(preprocessorFunc);
  1870. }
  1871. /**
  1872. * @param {number} [priority=1000]
  1873. * @param {Object|Function} processor
  1874. */
  1875. export function registerProcessor(priority, processor) {
  1876. normalizeRegister(dataProcessorFuncs, priority, processor, PRIORITY_PROCESSOR_FILTER);
  1877. }
  1878. /**
  1879. * Register postUpdater
  1880. * @param {Function} postUpdateFunc
  1881. */
  1882. export function registerPostUpdate(postUpdateFunc) {
  1883. postUpdateFuncs.push(postUpdateFunc);
  1884. }
  1885. /**
  1886. * Usage:
  1887. * registerAction('someAction', 'someEvent', function () { ... });
  1888. * registerAction('someAction', function () { ... });
  1889. * registerAction(
  1890. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  1891. * function () { ... }
  1892. * );
  1893. *
  1894. * @param {(string|Object)} actionInfo
  1895. * @param {string} actionInfo.type
  1896. * @param {string} [actionInfo.event]
  1897. * @param {string} [actionInfo.update]
  1898. * @param {string} [eventName]
  1899. * @param {Function} action
  1900. */
  1901. export function registerAction(actionInfo, eventName, action) {
  1902. if (typeof eventName === 'function') {
  1903. action = eventName;
  1904. eventName = '';
  1905. }
  1906. var actionType = isObject(actionInfo)
  1907. ? actionInfo.type
  1908. : ([actionInfo, actionInfo = {
  1909. event: eventName
  1910. }][0]);
  1911. // Event name is all lowercase
  1912. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  1913. eventName = actionInfo.event;
  1914. // Validate action type and event name.
  1915. assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  1916. if (!actions[actionType]) {
  1917. actions[actionType] = {action: action, actionInfo: actionInfo};
  1918. }
  1919. eventActionMap[eventName] = actionType;
  1920. }
  1921. /**
  1922. * @param {string} type
  1923. * @param {*} CoordinateSystem
  1924. */
  1925. export function registerCoordinateSystem(type, CoordinateSystem) {
  1926. CoordinateSystemManager.register(type, CoordinateSystem);
  1927. }
  1928. /**
  1929. * Get dimensions of specified coordinate system.
  1930. * @param {string} type
  1931. * @return {Array.<string|Object>}
  1932. */
  1933. export function getCoordinateSystemDimensions(type) {
  1934. var coordSysCreator = CoordinateSystemManager.get(type);
  1935. if (coordSysCreator) {
  1936. return coordSysCreator.getDimensionsInfo
  1937. ? coordSysCreator.getDimensionsInfo()
  1938. : coordSysCreator.dimensions.slice();
  1939. }
  1940. }
  1941. /**
  1942. * Layout is a special stage of visual encoding
  1943. * Most visual encoding like color are common for different chart
  1944. * But each chart has it's own layout algorithm
  1945. *
  1946. * @param {number} [priority=1000]
  1947. * @param {Function} layoutTask
  1948. */
  1949. export function registerLayout(priority, layoutTask) {
  1950. normalizeRegister(visualFuncs, priority, layoutTask, PRIORITY_VISUAL_LAYOUT, 'layout');
  1951. }
  1952. /**
  1953. * @param {number} [priority=3000]
  1954. * @param {module:echarts/stream/Task} visualTask
  1955. */
  1956. export function registerVisual(priority, visualTask) {
  1957. normalizeRegister(visualFuncs, priority, visualTask, PRIORITY_VISUAL_CHART, 'visual');
  1958. }
  1959. /**
  1960. * @param {Object|Function} fn: {seriesType, createOnAllSeries, performRawSeries, reset}
  1961. */
  1962. function normalizeRegister(targetList, priority, fn, defaultPriority, visualType) {
  1963. if (isFunction(priority) || isObject(priority)) {
  1964. fn = priority;
  1965. priority = defaultPriority;
  1966. }
  1967. if (__DEV__) {
  1968. if (isNaN(priority) || priority == null) {
  1969. throw new Error('Illegal priority');
  1970. }
  1971. // Check duplicate
  1972. each(targetList, function (wrap) {
  1973. assert(wrap.__raw !== fn);
  1974. });
  1975. }
  1976. var stageHandler = Scheduler.wrapStageHandler(fn, visualType);
  1977. stageHandler.__prio = priority;
  1978. stageHandler.__raw = fn;
  1979. targetList.push(stageHandler);
  1980. return stageHandler;
  1981. }
  1982. /**
  1983. * @param {string} name
  1984. */
  1985. export function registerLoading(name, loadingFx) {
  1986. loadingEffects[name] = loadingFx;
  1987. }
  1988. /**
  1989. * @param {Object} opts
  1990. * @param {string} [superClass]
  1991. */
  1992. export function extendComponentModel(opts/*, superClass*/) {
  1993. // var Clazz = ComponentModel;
  1994. // if (superClass) {
  1995. // var classType = parseClassType(superClass);
  1996. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  1997. // }
  1998. return ComponentModel.extend(opts);
  1999. }
  2000. /**
  2001. * @param {Object} opts
  2002. * @param {string} [superClass]
  2003. */
  2004. export function extendComponentView(opts/*, superClass*/) {
  2005. // var Clazz = ComponentView;
  2006. // if (superClass) {
  2007. // var classType = parseClassType(superClass);
  2008. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  2009. // }
  2010. return ComponentView.extend(opts);
  2011. }
  2012. /**
  2013. * @param {Object} opts
  2014. * @param {string} [superClass]
  2015. */
  2016. export function extendSeriesModel(opts/*, superClass*/) {
  2017. // var Clazz = SeriesModel;
  2018. // if (superClass) {
  2019. // superClass = 'series.' + superClass.replace('series.', '');
  2020. // var classType = parseClassType(superClass);
  2021. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  2022. // }
  2023. return SeriesModel.extend(opts);
  2024. }
  2025. /**
  2026. * @param {Object} opts
  2027. * @param {string} [superClass]
  2028. */
  2029. export function extendChartView(opts/*, superClass*/) {
  2030. // var Clazz = ChartView;
  2031. // if (superClass) {
  2032. // superClass = superClass.replace('series.', '');
  2033. // var classType = parseClassType(superClass);
  2034. // Clazz = ChartView.getClass(classType.main, true);
  2035. // }
  2036. return ChartView.extend(opts);
  2037. }
  2038. /**
  2039. * ZRender need a canvas context to do measureText.
  2040. * But in node environment canvas may be created by node-canvas.
  2041. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  2042. *
  2043. * Be careful of using it in the browser.
  2044. *
  2045. * @param {Function} creator
  2046. * @example
  2047. * var Canvas = require('canvas');
  2048. * var echarts = require('echarts');
  2049. * echarts.setCanvasCreator(function () {
  2050. * // Small size is enough.
  2051. * return new Canvas(32, 32);
  2052. * });
  2053. */
  2054. export function setCanvasCreator(creator) {
  2055. zrUtil.$override('createCanvas', creator);
  2056. }
  2057. /**
  2058. * @param {string} mapName
  2059. * @param {Array.<Object>|Object|string} geoJson
  2060. * @param {Object} [specialAreas]
  2061. *
  2062. * @example GeoJSON
  2063. * $.get('USA.json', function (geoJson) {
  2064. * echarts.registerMap('USA', geoJson);
  2065. * // Or
  2066. * echarts.registerMap('USA', {
  2067. * geoJson: geoJson,
  2068. * specialAreas: {}
  2069. * })
  2070. * });
  2071. *
  2072. * $.get('airport.svg', function (svg) {
  2073. * echarts.registerMap('airport', {
  2074. * svg: svg
  2075. * }
  2076. * });
  2077. *
  2078. * echarts.registerMap('eu', [
  2079. * {svg: eu-topographic.svg},
  2080. * {geoJSON: eu.json}
  2081. * ])
  2082. */
  2083. export function registerMap(mapName, geoJson, specialAreas) {
  2084. mapDataStorage.registerMap(mapName, geoJson, specialAreas);
  2085. }
  2086. /**
  2087. * @param {string} mapName
  2088. * @return {Object}
  2089. */
  2090. export function getMap(mapName) {
  2091. // For backward compatibility, only return the first one.
  2092. var records = mapDataStorage.retrieveMap(mapName);
  2093. return records && records[0] && {
  2094. geoJson: records[0].geoJSON,
  2095. specialAreas: records[0].specialAreas
  2096. };
  2097. }
  2098. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  2099. registerPreprocessor(backwardCompat);
  2100. registerProcessor(PRIORITY_PROCESSOR_DATASTACK, dataStack);
  2101. registerLoading('default', loadingDefault);
  2102. // Default actions
  2103. registerAction({
  2104. type: 'highlight',
  2105. event: 'highlight',
  2106. update: 'highlight'
  2107. }, zrUtil.noop);
  2108. registerAction({
  2109. type: 'downplay',
  2110. event: 'downplay',
  2111. update: 'downplay'
  2112. }, zrUtil.noop);
  2113. // Default theme
  2114. registerTheme('light', lightTheme);
  2115. registerTheme('dark', darkTheme);
  2116. // For backward compatibility, where the namespace `dataTool` will
  2117. // be mounted on `echarts` is the extension `dataTool` is imported.
  2118. export var dataTool = {};