echarts.js 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953
  1. var _config = require("./config");
  2. var __DEV__ = _config.__DEV__;
  3. var zrender = require("zrender/lib/zrender");
  4. var zrUtil = require("zrender/lib/core/util");
  5. var colorTool = require("zrender/lib/tool/color");
  6. var env = require("zrender/lib/core/env");
  7. var timsort = require("zrender/lib/core/timsort");
  8. var Eventful = require("zrender/lib/mixin/Eventful");
  9. var GlobalModel = require("./model/Global");
  10. var ExtensionAPI = require("./ExtensionAPI");
  11. var CoordinateSystemManager = require("./CoordinateSystem");
  12. var OptionManager = require("./model/OptionManager");
  13. var backwardCompat = require("./preprocessor/backwardCompat");
  14. var ComponentModel = require("./model/Component");
  15. var SeriesModel = require("./model/Series");
  16. var ComponentView = require("./view/Component");
  17. var ChartView = require("./view/Chart");
  18. var graphic = require("./util/graphic");
  19. var modelUtil = require("./util/model");
  20. var _throttle = require("./util/throttle");
  21. var throttle = _throttle.throttle;
  22. var seriesColor = require("./visual/seriesColor");
  23. var loadingDefault = require("./loading/default");
  24. /*!
  25. * ECharts, a javascript interactive chart library.
  26. *
  27. * Copyright (c) 2015, Baidu Inc.
  28. * All rights reserved.
  29. *
  30. * LICENSE
  31. * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
  32. */
  33. var each = zrUtil.each;
  34. var parseClassType = ComponentModel.parseClassType;
  35. var version = '3.8.5';
  36. var dependencies = {
  37. zrender: '3.7.4'
  38. };
  39. var PRIORITY_PROCESSOR_FILTER = 1000;
  40. var PRIORITY_PROCESSOR_STATISTIC = 5000;
  41. var PRIORITY_VISUAL_LAYOUT = 1000;
  42. var PRIORITY_VISUAL_GLOBAL = 2000;
  43. var PRIORITY_VISUAL_CHART = 3000;
  44. var PRIORITY_VISUAL_COMPONENT = 4000; // FIXME
  45. // necessary?
  46. var PRIORITY_VISUAL_BRUSH = 5000;
  47. var PRIORITY = {
  48. PROCESSOR: {
  49. FILTER: PRIORITY_PROCESSOR_FILTER,
  50. STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  51. },
  52. VISUAL: {
  53. LAYOUT: PRIORITY_VISUAL_LAYOUT,
  54. GLOBAL: PRIORITY_VISUAL_GLOBAL,
  55. CHART: PRIORITY_VISUAL_CHART,
  56. COMPONENT: PRIORITY_VISUAL_COMPONENT,
  57. BRUSH: PRIORITY_VISUAL_BRUSH
  58. }
  59. }; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
  60. // where they must not be invoked nestedly, except the only case: invoke
  61. // dispatchAction with updateMethod "none" in main process.
  62. // This flag is used to carry out this rule.
  63. // All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).
  64. var IN_MAIN_PROCESS = '__flagInMainProcess';
  65. var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
  66. var OPTION_UPDATED = '__optionUpdated';
  67. var ACTION_REG = /^[a-zA-Z0-9_]+$/;
  68. function createRegisterEventWithLowercaseName(method) {
  69. return function (eventName, handler, context) {
  70. // Event name is all lowercase
  71. eventName = eventName && eventName.toLowerCase();
  72. Eventful.prototype[method].call(this, eventName, handler, context);
  73. };
  74. }
  75. /**
  76. * @module echarts~MessageCenter
  77. */
  78. function MessageCenter() {
  79. Eventful.call(this);
  80. }
  81. MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
  82. MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
  83. MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
  84. zrUtil.mixin(MessageCenter, Eventful);
  85. /**
  86. * @module echarts~ECharts
  87. */
  88. function ECharts(dom, theme, opts) {
  89. opts = opts || {}; // Get theme by name
  90. if (typeof theme === 'string') {
  91. theme = themeStorage[theme];
  92. }
  93. /**
  94. * @type {string}
  95. */
  96. this.id;
  97. /**
  98. * Group id
  99. * @type {string}
  100. */
  101. this.group;
  102. /**
  103. * @type {HTMLElement}
  104. * @private
  105. */
  106. this._dom = dom;
  107. var defaultRenderer = 'canvas';
  108. /**
  109. * @type {module:zrender/ZRender}
  110. * @private
  111. */
  112. var zr = this._zr = zrender.init(dom, {
  113. renderer: opts.renderer || defaultRenderer,
  114. devicePixelRatio: opts.devicePixelRatio,
  115. width: opts.width,
  116. height: opts.height
  117. });
  118. /**
  119. * Expect 60 pfs.
  120. * @type {Function}
  121. * @private
  122. */
  123. this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);
  124. var theme = zrUtil.clone(theme);
  125. theme && backwardCompat(theme, true);
  126. /**
  127. * @type {Object}
  128. * @private
  129. */
  130. this._theme = theme;
  131. /**
  132. * @type {Array.<module:echarts/view/Chart>}
  133. * @private
  134. */
  135. this._chartsViews = [];
  136. /**
  137. * @type {Object.<string, module:echarts/view/Chart>}
  138. * @private
  139. */
  140. this._chartsMap = {};
  141. /**
  142. * @type {Array.<module:echarts/view/Component>}
  143. * @private
  144. */
  145. this._componentsViews = [];
  146. /**
  147. * @type {Object.<string, module:echarts/view/Component>}
  148. * @private
  149. */
  150. this._componentsMap = {};
  151. /**
  152. * @type {module:echarts/CoordinateSystem}
  153. * @private
  154. */
  155. this._coordSysMgr = new CoordinateSystemManager();
  156. /**
  157. * @type {module:echarts/ExtensionAPI}
  158. * @private
  159. */
  160. this._api = createExtensionAPI(this);
  161. Eventful.call(this);
  162. /**
  163. * @type {module:echarts~MessageCenter}
  164. * @private
  165. */
  166. this._messageCenter = new MessageCenter(); // Init mouse events
  167. this._initEvents(); // In case some people write `window.onresize = chart.resize`
  168. this.resize = zrUtil.bind(this.resize, this); // Can't dispatch action during rendering procedure
  169. this._pendingActions = []; // Sort on demand
  170. function prioritySortFunc(a, b) {
  171. return a.prio - b.prio;
  172. }
  173. timsort(visualFuncs, prioritySortFunc);
  174. timsort(dataProcessorFuncs, prioritySortFunc);
  175. zr.animation.on('frame', this._onframe, this); // ECharts instance can be used as value.
  176. zrUtil.setAsPrimitive(this);
  177. }
  178. var echartsProto = ECharts.prototype;
  179. echartsProto._onframe = function () {
  180. // Lazy update
  181. if (this[OPTION_UPDATED]) {
  182. var silent = this[OPTION_UPDATED].silent;
  183. this[IN_MAIN_PROCESS] = true;
  184. updateMethods.prepareAndUpdate.call(this);
  185. this[IN_MAIN_PROCESS] = false;
  186. this[OPTION_UPDATED] = false;
  187. flushPendingActions.call(this, silent);
  188. triggerUpdatedEvent.call(this, silent);
  189. }
  190. };
  191. /**
  192. * @return {HTMLElement}
  193. */
  194. echartsProto.getDom = function () {
  195. return this._dom;
  196. };
  197. /**
  198. * @return {module:zrender~ZRender}
  199. */
  200. echartsProto.getZr = function () {
  201. return this._zr;
  202. };
  203. /**
  204. * Usage:
  205. * chart.setOption(option, notMerge, lazyUpdate);
  206. * chart.setOption(option, {
  207. * notMerge: ...,
  208. * lazyUpdate: ...,
  209. * silent: ...
  210. * });
  211. *
  212. * @param {Object} option
  213. * @param {Object|boolean} [opts] opts or notMerge.
  214. * @param {boolean} [opts.notMerge=false]
  215. * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
  216. */
  217. echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  218. var silent;
  219. if (zrUtil.isObject(notMerge)) {
  220. lazyUpdate = notMerge.lazyUpdate;
  221. silent = notMerge.silent;
  222. notMerge = notMerge.notMerge;
  223. }
  224. this[IN_MAIN_PROCESS] = true;
  225. if (!this._model || notMerge) {
  226. var optionManager = new OptionManager(this._api);
  227. var theme = this._theme;
  228. var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);
  229. ecModel.init(null, null, theme, optionManager);
  230. }
  231. this._model.setOption(option, optionPreprocessorFuncs);
  232. if (lazyUpdate) {
  233. this[OPTION_UPDATED] = {
  234. silent: silent
  235. };
  236. this[IN_MAIN_PROCESS] = false;
  237. } else {
  238. updateMethods.prepareAndUpdate.call(this); // Ensure zr refresh sychronously, and then pixel in canvas can be
  239. // fetched after `setOption`.
  240. this._zr.flush();
  241. this[OPTION_UPDATED] = false;
  242. this[IN_MAIN_PROCESS] = false;
  243. flushPendingActions.call(this, silent);
  244. triggerUpdatedEvent.call(this, silent);
  245. }
  246. };
  247. /**
  248. * @DEPRECATED
  249. */
  250. echartsProto.setTheme = function () {
  251. console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
  252. };
  253. /**
  254. * @return {module:echarts/model/Global}
  255. */
  256. echartsProto.getModel = function () {
  257. return this._model;
  258. };
  259. /**
  260. * @return {Object}
  261. */
  262. echartsProto.getOption = function () {
  263. return this._model && this._model.getOption();
  264. };
  265. /**
  266. * @return {number}
  267. */
  268. echartsProto.getWidth = function () {
  269. return this._zr.getWidth();
  270. };
  271. /**
  272. * @return {number}
  273. */
  274. echartsProto.getHeight = function () {
  275. return this._zr.getHeight();
  276. };
  277. /**
  278. * @return {number}
  279. */
  280. echartsProto.getDevicePixelRatio = function () {
  281. return this._zr.painter.dpr || window.devicePixelRatio || 1;
  282. };
  283. /**
  284. * Get canvas which has all thing rendered
  285. * @param {Object} opts
  286. * @param {string} [opts.backgroundColor]
  287. * @return {string}
  288. */
  289. echartsProto.getRenderedCanvas = function (opts) {
  290. if (!env.canvasSupported) {
  291. return;
  292. }
  293. opts = opts || {};
  294. opts.pixelRatio = opts.pixelRatio || 1;
  295. opts.backgroundColor = opts.backgroundColor || this._model.get('backgroundColor');
  296. var zr = this._zr;
  297. var list = zr.storage.getDisplayList(); // Stop animations
  298. zrUtil.each(list, function (el) {
  299. el.stopAnimation(true);
  300. });
  301. return zr.painter.getRenderedCanvas(opts);
  302. };
  303. /**
  304. * Get svg data url
  305. * @return {string}
  306. */
  307. echartsProto.getSvgDataUrl = function () {
  308. if (!env.svgSupported) {
  309. return;
  310. }
  311. var zr = this._zr;
  312. var list = zr.storage.getDisplayList(); // Stop animations
  313. zrUtil.each(list, function (el) {
  314. el.stopAnimation(true);
  315. });
  316. return zr.painter.pathToSvg();
  317. };
  318. /**
  319. * @return {string}
  320. * @param {Object} opts
  321. * @param {string} [opts.type='png']
  322. * @param {string} [opts.pixelRatio=1]
  323. * @param {string} [opts.backgroundColor]
  324. * @param {string} [opts.excludeComponents]
  325. */
  326. echartsProto.getDataURL = function (opts) {
  327. opts = opts || {};
  328. var excludeComponents = opts.excludeComponents;
  329. var ecModel = this._model;
  330. var excludesComponentViews = [];
  331. var self = this;
  332. each(excludeComponents, function (componentType) {
  333. ecModel.eachComponent({
  334. mainType: componentType
  335. }, function (component) {
  336. var view = self._componentsMap[component.__viewId];
  337. if (!view.group.ignore) {
  338. excludesComponentViews.push(view);
  339. view.group.ignore = true;
  340. }
  341. });
  342. });
  343. var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataUrl() : this.getRenderedCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));
  344. each(excludesComponentViews, function (view) {
  345. view.group.ignore = false;
  346. });
  347. return url;
  348. };
  349. /**
  350. * @return {string}
  351. * @param {Object} opts
  352. * @param {string} [opts.type='png']
  353. * @param {string} [opts.pixelRatio=1]
  354. * @param {string} [opts.backgroundColor]
  355. */
  356. echartsProto.getConnectedDataURL = function (opts) {
  357. if (!env.canvasSupported) {
  358. return;
  359. }
  360. var groupId = this.group;
  361. var mathMin = Math.min;
  362. var mathMax = Math.max;
  363. var MAX_NUMBER = Infinity;
  364. if (connectedGroups[groupId]) {
  365. var left = MAX_NUMBER;
  366. var top = MAX_NUMBER;
  367. var right = -MAX_NUMBER;
  368. var bottom = -MAX_NUMBER;
  369. var canvasList = [];
  370. var dpr = opts && opts.pixelRatio || 1;
  371. zrUtil.each(instances, function (chart, id) {
  372. if (chart.group === groupId) {
  373. var canvas = chart.getRenderedCanvas(zrUtil.clone(opts));
  374. var boundingRect = chart.getDom().getBoundingClientRect();
  375. left = mathMin(boundingRect.left, left);
  376. top = mathMin(boundingRect.top, top);
  377. right = mathMax(boundingRect.right, right);
  378. bottom = mathMax(boundingRect.bottom, bottom);
  379. canvasList.push({
  380. dom: canvas,
  381. left: boundingRect.left,
  382. top: boundingRect.top
  383. });
  384. }
  385. });
  386. left *= dpr;
  387. top *= dpr;
  388. right *= dpr;
  389. bottom *= dpr;
  390. var width = right - left;
  391. var height = bottom - top;
  392. var targetCanvas = zrUtil.createCanvas();
  393. targetCanvas.width = width;
  394. targetCanvas.height = height;
  395. var zr = zrender.init(targetCanvas);
  396. each(canvasList, function (item) {
  397. var img = new graphic.Image({
  398. style: {
  399. x: item.left * dpr - left,
  400. y: item.top * dpr - top,
  401. image: item.dom
  402. }
  403. });
  404. zr.add(img);
  405. });
  406. zr.refreshImmediately();
  407. return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  408. } else {
  409. return this.getDataURL(opts);
  410. }
  411. };
  412. /**
  413. * Convert from logical coordinate system to pixel coordinate system.
  414. * See CoordinateSystem#convertToPixel.
  415. * @param {string|Object} finder
  416. * If string, e.g., 'geo', means {geoIndex: 0}.
  417. * If Object, could contain some of these properties below:
  418. * {
  419. * seriesIndex / seriesId / seriesName,
  420. * geoIndex / geoId, geoName,
  421. * bmapIndex / bmapId / bmapName,
  422. * xAxisIndex / xAxisId / xAxisName,
  423. * yAxisIndex / yAxisId / yAxisName,
  424. * gridIndex / gridId / gridName,
  425. * ... (can be extended)
  426. * }
  427. * @param {Array|number} value
  428. * @return {Array|number} result
  429. */
  430. echartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');
  431. /**
  432. * Convert from pixel coordinate system to logical coordinate system.
  433. * See CoordinateSystem#convertFromPixel.
  434. * @param {string|Object} finder
  435. * If string, e.g., 'geo', means {geoIndex: 0}.
  436. * If Object, could contain some of these properties below:
  437. * {
  438. * seriesIndex / seriesId / seriesName,
  439. * geoIndex / geoId / geoName,
  440. * bmapIndex / bmapId / bmapName,
  441. * xAxisIndex / xAxisId / xAxisName,
  442. * yAxisIndex / yAxisId / yAxisName
  443. * gridIndex / gridId / gridName,
  444. * ... (can be extended)
  445. * }
  446. * @param {Array|number} value
  447. * @return {Array|number} result
  448. */
  449. echartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');
  450. function doConvertPixel(methodName, finder, value) {
  451. var ecModel = this._model;
  452. var coordSysList = this._coordSysMgr.getCoordinateSystems();
  453. var result;
  454. finder = modelUtil.parseFinder(ecModel, finder);
  455. for (var i = 0; i < coordSysList.length; i++) {
  456. var coordSys = coordSysList[i];
  457. if (coordSys[methodName] && (result = coordSys[methodName](ecModel, finder, value)) != null) {
  458. return result;
  459. }
  460. }
  461. }
  462. /**
  463. * Is the specified coordinate systems or components contain the given pixel point.
  464. * @param {string|Object} finder
  465. * If string, e.g., 'geo', means {geoIndex: 0}.
  466. * If Object, could contain some of these properties below:
  467. * {
  468. * seriesIndex / seriesId / seriesName,
  469. * geoIndex / geoId / geoName,
  470. * bmapIndex / bmapId / bmapName,
  471. * xAxisIndex / xAxisId / xAxisName,
  472. * yAxisIndex / yAxisId / yAxisName,
  473. * gridIndex / gridId / gridName,
  474. * ... (can be extended)
  475. * }
  476. * @param {Array|number} value
  477. * @return {boolean} result
  478. */
  479. echartsProto.containPixel = function (finder, value) {
  480. var ecModel = this._model;
  481. var result;
  482. finder = modelUtil.parseFinder(ecModel, finder);
  483. zrUtil.each(finder, function (models, key) {
  484. key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {
  485. var coordSys = model.coordinateSystem;
  486. if (coordSys && coordSys.containPoint) {
  487. result |= !!coordSys.containPoint(value);
  488. } else if (key === 'seriesModels') {
  489. var view = this._chartsMap[model.__viewId];
  490. if (view && view.containPoint) {
  491. result |= view.containPoint(value, model);
  492. } else {}
  493. } else {}
  494. }, this);
  495. }, this);
  496. return !!result;
  497. };
  498. /**
  499. * Get visual from series or data.
  500. * @param {string|Object} finder
  501. * If string, e.g., 'series', means {seriesIndex: 0}.
  502. * If Object, could contain some of these properties below:
  503. * {
  504. * seriesIndex / seriesId / seriesName,
  505. * dataIndex / dataIndexInside
  506. * }
  507. * If dataIndex is not specified, series visual will be fetched,
  508. * but not data item visual.
  509. * If all of seriesIndex, seriesId, seriesName are not specified,
  510. * visual will be fetched from first series.
  511. * @param {string} visualType 'color', 'symbol', 'symbolSize'
  512. */
  513. echartsProto.getVisual = function (finder, visualType) {
  514. var ecModel = this._model;
  515. finder = modelUtil.parseFinder(ecModel, finder, {
  516. defaultMainType: 'series'
  517. });
  518. var seriesModel = finder.seriesModel;
  519. var data = seriesModel.getData();
  520. var dataIndexInside = finder.hasOwnProperty('dataIndexInside') ? finder.dataIndexInside : finder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(finder.dataIndex) : null;
  521. return dataIndexInside != null ? data.getItemVisual(dataIndexInside, visualType) : data.getVisual(visualType);
  522. };
  523. /**
  524. * Get view of corresponding component model
  525. * @param {module:echarts/model/Component} componentModel
  526. * @return {module:echarts/view/Component}
  527. */
  528. echartsProto.getViewOfComponentModel = function (componentModel) {
  529. return this._componentsMap[componentModel.__viewId];
  530. };
  531. /**
  532. * Get view of corresponding series model
  533. * @param {module:echarts/model/Series} seriesModel
  534. * @return {module:echarts/view/Chart}
  535. */
  536. echartsProto.getViewOfSeriesModel = function (seriesModel) {
  537. return this._chartsMap[seriesModel.__viewId];
  538. };
  539. var updateMethods = {
  540. /**
  541. * @param {Object} payload
  542. * @private
  543. */
  544. update: function (payload) {
  545. // console.profile && console.profile('update');
  546. var ecModel = this._model;
  547. var api = this._api;
  548. var coordSysMgr = this._coordSysMgr;
  549. var zr = this._zr; // update before setOption
  550. if (!ecModel) {
  551. return;
  552. } // Fixme First time update ?
  553. ecModel.restoreData(); // TODO
  554. // Save total ecModel here for undo/redo (after restoring data and before processing data).
  555. // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
  556. // Create new coordinate system each update
  557. // In LineView may save the old coordinate system and use it to get the orignal point
  558. coordSysMgr.create(this._model, this._api);
  559. processData.call(this, ecModel, api);
  560. stackSeriesData.call(this, ecModel);
  561. coordSysMgr.update(ecModel, api);
  562. doVisualEncoding.call(this, ecModel, payload);
  563. doRender.call(this, ecModel, payload); // Set background
  564. var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
  565. var painter = zr.painter; // TODO all use clearColor ?
  566. if (painter.isSingleCanvas && painter.isSingleCanvas()) {
  567. zr.configLayer(0, {
  568. clearColor: backgroundColor
  569. });
  570. } else {
  571. // In IE8
  572. if (!env.canvasSupported) {
  573. var colorArr = colorTool.parse(backgroundColor);
  574. backgroundColor = colorTool.stringify(colorArr, 'rgb');
  575. if (colorArr[3] === 0) {
  576. backgroundColor = 'transparent';
  577. }
  578. }
  579. if (backgroundColor.colorStops || backgroundColor.image) {
  580. // Gradient background
  581. // FIXME Fixed layer?
  582. zr.configLayer(0, {
  583. clearColor: backgroundColor
  584. });
  585. this[HAS_GRADIENT_OR_PATTERN_BG] = true;
  586. this._dom.style.background = 'transparent';
  587. } else {
  588. if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
  589. zr.configLayer(0, {
  590. clearColor: null
  591. });
  592. }
  593. this[HAS_GRADIENT_OR_PATTERN_BG] = false;
  594. this._dom.style.background = backgroundColor;
  595. }
  596. }
  597. each(postUpdateFuncs, function (func) {
  598. func(ecModel, api);
  599. }); // console.profile && console.profileEnd('update');
  600. },
  601. /**
  602. * @param {Object} payload
  603. * @private
  604. */
  605. updateView: function (payload) {
  606. var ecModel = this._model; // update before setOption
  607. if (!ecModel) {
  608. return;
  609. }
  610. ecModel.eachSeries(function (seriesModel) {
  611. seriesModel.getData().clearAllVisual();
  612. });
  613. doVisualEncoding.call(this, ecModel, payload);
  614. invokeUpdateMethod.call(this, 'updateView', ecModel, payload);
  615. },
  616. /**
  617. * @param {Object} payload
  618. * @private
  619. */
  620. updateVisual: function (payload) {
  621. var ecModel = this._model; // update before setOption
  622. if (!ecModel) {
  623. return;
  624. }
  625. ecModel.eachSeries(function (seriesModel) {
  626. seriesModel.getData().clearAllVisual();
  627. });
  628. doVisualEncoding.call(this, ecModel, payload, true);
  629. invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);
  630. },
  631. /**
  632. * @param {Object} payload
  633. * @private
  634. */
  635. updateLayout: function (payload) {
  636. var ecModel = this._model; // update before setOption
  637. if (!ecModel) {
  638. return;
  639. }
  640. doLayout.call(this, ecModel, payload);
  641. invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);
  642. },
  643. /**
  644. * @param {Object} payload
  645. * @private
  646. */
  647. prepareAndUpdate: function (payload) {
  648. var ecModel = this._model;
  649. prepareView.call(this, 'component', ecModel);
  650. prepareView.call(this, 'chart', ecModel);
  651. updateMethods.update.call(this, payload);
  652. }
  653. };
  654. /**
  655. * @private
  656. */
  657. function updateDirectly(ecIns, method, payload, mainType, subType) {
  658. var ecModel = ecIns._model; // broadcast
  659. if (!mainType) {
  660. each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
  661. return;
  662. }
  663. var query = {};
  664. query[mainType + 'Id'] = payload[mainType + 'Id'];
  665. query[mainType + 'Index'] = payload[mainType + 'Index'];
  666. query[mainType + 'Name'] = payload[mainType + 'Name'];
  667. var condition = {
  668. mainType: mainType,
  669. query: query
  670. };
  671. subType && (condition.subType = subType); // subType may be '' by parseClassType;
  672. // If dispatchAction before setOption, do nothing.
  673. ecModel && ecModel.eachComponent(condition, function (model, index) {
  674. callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);
  675. }, ecIns);
  676. function callView(view) {
  677. view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);
  678. }
  679. }
  680. /**
  681. * Resize the chart
  682. * @param {Object} opts
  683. * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
  684. * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
  685. * @param {boolean} [opts.silent=false]
  686. */
  687. echartsProto.resize = function (opts) {
  688. this[IN_MAIN_PROCESS] = true;
  689. this._zr.resize(opts);
  690. var optionChanged = this._model && this._model.resetOption('media');
  691. var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';
  692. updateMethods[updateMethod].call(this); // Resize loading effect
  693. this._loadingFX && this._loadingFX.resize();
  694. this[IN_MAIN_PROCESS] = false;
  695. var silent = opts && opts.silent;
  696. flushPendingActions.call(this, silent);
  697. triggerUpdatedEvent.call(this, silent);
  698. };
  699. /**
  700. * Show loading effect
  701. * @param {string} [name='default']
  702. * @param {Object} [cfg]
  703. */
  704. echartsProto.showLoading = function (name, cfg) {
  705. if (zrUtil.isObject(name)) {
  706. cfg = name;
  707. name = '';
  708. }
  709. name = name || 'default';
  710. this.hideLoading();
  711. if (!loadingEffects[name]) {
  712. return;
  713. }
  714. var el = loadingEffects[name](this._api, cfg);
  715. var zr = this._zr;
  716. this._loadingFX = el;
  717. zr.add(el);
  718. };
  719. /**
  720. * Hide loading effect
  721. */
  722. echartsProto.hideLoading = function () {
  723. this._loadingFX && this._zr.remove(this._loadingFX);
  724. this._loadingFX = null;
  725. };
  726. /**
  727. * @param {Object} eventObj
  728. * @return {Object}
  729. */
  730. echartsProto.makeActionFromEvent = function (eventObj) {
  731. var payload = zrUtil.extend({}, eventObj);
  732. payload.type = eventActionMap[eventObj.type];
  733. return payload;
  734. };
  735. /**
  736. * @pubilc
  737. * @param {Object} payload
  738. * @param {string} [payload.type] Action type
  739. * @param {Object|boolean} [opt] If pass boolean, means opt.silent
  740. * @param {boolean} [opt.silent=false] Whether trigger events.
  741. * @param {boolean} [opt.flush=undefined]
  742. * true: Flush immediately, and then pixel in canvas can be fetched
  743. * immediately. Caution: it might affect performance.
  744. * false: Not not flush.
  745. * undefined: Auto decide whether perform flush.
  746. */
  747. echartsProto.dispatchAction = function (payload, opt) {
  748. if (!zrUtil.isObject(opt)) {
  749. opt = {
  750. silent: !!opt
  751. };
  752. }
  753. if (!actions[payload.type]) {
  754. return;
  755. } // Avoid dispatch action before setOption. Especially in `connect`.
  756. if (!this._model) {
  757. return;
  758. } // May dispatchAction in rendering procedure
  759. if (this[IN_MAIN_PROCESS]) {
  760. this._pendingActions.push(payload);
  761. return;
  762. }
  763. doDispatchAction.call(this, payload, opt.silent);
  764. if (opt.flush) {
  765. this._zr.flush(true);
  766. } else if (opt.flush !== false && env.browser.weChat) {
  767. // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
  768. // hang when sliding page (on touch event), which cause that zr does not
  769. // refresh util user interaction finished, which is not expected.
  770. // But `dispatchAction` may be called too frequently when pan on touch
  771. // screen, which impacts performance if do not throttle them.
  772. this._throttledZrFlush();
  773. }
  774. flushPendingActions.call(this, opt.silent);
  775. triggerUpdatedEvent.call(this, opt.silent);
  776. };
  777. function doDispatchAction(payload, silent) {
  778. var payloadType = payload.type;
  779. var escapeConnect = payload.escapeConnect;
  780. var actionWrap = actions[payloadType];
  781. var actionInfo = actionWrap.actionInfo;
  782. var cptType = (actionInfo.update || 'update').split(':');
  783. var updateMethod = cptType.pop();
  784. cptType = cptType[0] != null && parseClassType(cptType[0]);
  785. this[IN_MAIN_PROCESS] = true;
  786. var payloads = [payload];
  787. var batched = false; // Batch action
  788. if (payload.batch) {
  789. batched = true;
  790. payloads = zrUtil.map(payload.batch, function (item) {
  791. item = zrUtil.defaults(zrUtil.extend({}, item), payload);
  792. item.batch = null;
  793. return item;
  794. });
  795. }
  796. var eventObjBatch = [];
  797. var eventObj;
  798. var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  799. each(payloads, function (batchItem) {
  800. // Action can specify the event by return it.
  801. eventObj = actionWrap.action(batchItem, this._model, this._api); // Emit event outside
  802. eventObj = eventObj || zrUtil.extend({}, batchItem); // Convert type to eventType
  803. eventObj.type = actionInfo.event || eventObj.type;
  804. eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.
  805. if (isHighDown) {
  806. // method, payload, mainType, subType
  807. updateDirectly(this, updateMethod, batchItem, 'series');
  808. } else if (cptType) {
  809. updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
  810. }
  811. }, this);
  812. if (updateMethod !== 'none' && !isHighDown && !cptType) {
  813. // Still dirty
  814. if (this[OPTION_UPDATED]) {
  815. // FIXME Pass payload ?
  816. updateMethods.prepareAndUpdate.call(this, payload);
  817. this[OPTION_UPDATED] = false;
  818. } else {
  819. updateMethods[updateMethod].call(this, payload);
  820. }
  821. } // Follow the rule of action batch
  822. if (batched) {
  823. eventObj = {
  824. type: actionInfo.event || payloadType,
  825. escapeConnect: escapeConnect,
  826. batch: eventObjBatch
  827. };
  828. } else {
  829. eventObj = eventObjBatch[0];
  830. }
  831. this[IN_MAIN_PROCESS] = false;
  832. !silent && this._messageCenter.trigger(eventObj.type, eventObj);
  833. }
  834. function flushPendingActions(silent) {
  835. var pendingActions = this._pendingActions;
  836. while (pendingActions.length) {
  837. var payload = pendingActions.shift();
  838. doDispatchAction.call(this, payload, silent);
  839. }
  840. }
  841. function triggerUpdatedEvent(silent) {
  842. !silent && this.trigger('updated');
  843. }
  844. /**
  845. * Register event
  846. * @method
  847. */
  848. echartsProto.on = createRegisterEventWithLowercaseName('on');
  849. echartsProto.off = createRegisterEventWithLowercaseName('off');
  850. echartsProto.one = createRegisterEventWithLowercaseName('one');
  851. /**
  852. * @param {string} methodName
  853. * @private
  854. */
  855. function invokeUpdateMethod(methodName, ecModel, payload) {
  856. var api = this._api; // Update all components
  857. each(this._componentsViews, function (component) {
  858. var componentModel = component.__model;
  859. component[methodName](componentModel, ecModel, api, payload);
  860. updateZ(componentModel, component);
  861. }, this); // Upate all charts
  862. ecModel.eachSeries(function (seriesModel, idx) {
  863. var chart = this._chartsMap[seriesModel.__viewId];
  864. chart[methodName](seriesModel, ecModel, api, payload);
  865. updateZ(seriesModel, chart);
  866. updateProgressiveAndBlend(seriesModel, chart);
  867. }, this); // If use hover layer
  868. updateHoverLayerStatus(this._zr, ecModel); // Post render
  869. each(postUpdateFuncs, function (func) {
  870. func(ecModel, api);
  871. });
  872. }
  873. /**
  874. * Prepare view instances of charts and components
  875. * @param {module:echarts/model/Global} ecModel
  876. * @private
  877. */
  878. function prepareView(type, ecModel) {
  879. var isComponent = type === 'component';
  880. var viewList = isComponent ? this._componentsViews : this._chartsViews;
  881. var viewMap = isComponent ? this._componentsMap : this._chartsMap;
  882. var zr = this._zr;
  883. for (var i = 0; i < viewList.length; i++) {
  884. viewList[i].__alive = false;
  885. }
  886. ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {
  887. if (isComponent) {
  888. if (componentType === 'series') {
  889. return;
  890. }
  891. } else {
  892. model = componentType;
  893. } // Consider: id same and type changed.
  894. var viewId = '_ec_' + model.id + '_' + model.type;
  895. var view = viewMap[viewId];
  896. if (!view) {
  897. var classType = parseClassType(model.type);
  898. var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : ChartView.getClass(classType.sub);
  899. if (Clazz) {
  900. view = new Clazz();
  901. view.init(ecModel, this._api);
  902. viewMap[viewId] = view;
  903. viewList.push(view);
  904. zr.add(view.group);
  905. } else {
  906. // Error
  907. return;
  908. }
  909. }
  910. model.__viewId = view.__id = viewId;
  911. view.__alive = true;
  912. view.__model = model;
  913. view.group.__ecComponentInfo = {
  914. mainType: model.mainType,
  915. index: model.componentIndex
  916. };
  917. }, this);
  918. for (var i = 0; i < viewList.length;) {
  919. var view = viewList[i];
  920. if (!view.__alive) {
  921. zr.remove(view.group);
  922. view.dispose(ecModel, this._api);
  923. viewList.splice(i, 1);
  924. delete viewMap[view.__id];
  925. view.__id = view.group.__ecComponentInfo = null;
  926. } else {
  927. i++;
  928. }
  929. }
  930. }
  931. /**
  932. * Processor data in each series
  933. *
  934. * @param {module:echarts/model/Global} ecModel
  935. * @private
  936. */
  937. function processData(ecModel, api) {
  938. each(dataProcessorFuncs, function (process) {
  939. process.func(ecModel, api);
  940. });
  941. }
  942. /**
  943. * @private
  944. */
  945. function stackSeriesData(ecModel) {
  946. var stackedDataMap = {};
  947. ecModel.eachSeries(function (series) {
  948. var stack = series.get('stack');
  949. var data = series.getData();
  950. if (stack && data.type === 'list') {
  951. var previousStack = stackedDataMap[stack]; // Avoid conflict with Object.prototype
  952. if (stackedDataMap.hasOwnProperty(stack) && previousStack) {
  953. data.stackedOn = previousStack;
  954. }
  955. stackedDataMap[stack] = data;
  956. }
  957. });
  958. }
  959. /**
  960. * Layout before each chart render there series, special visual encoding stage
  961. *
  962. * @param {module:echarts/model/Global} ecModel
  963. * @private
  964. */
  965. function doLayout(ecModel, payload) {
  966. var api = this._api;
  967. each(visualFuncs, function (visual) {
  968. if (visual.isLayout) {
  969. visual.func(ecModel, api, payload);
  970. }
  971. });
  972. }
  973. /**
  974. * Encode visual infomation from data after data processing
  975. *
  976. * @param {module:echarts/model/Global} ecModel
  977. * @param {object} layout
  978. * @param {boolean} [excludesLayout]
  979. * @private
  980. */
  981. function doVisualEncoding(ecModel, payload, excludesLayout) {
  982. var api = this._api;
  983. ecModel.clearColorPalette();
  984. ecModel.eachSeries(function (seriesModel) {
  985. seriesModel.clearColorPalette();
  986. });
  987. each(visualFuncs, function (visual) {
  988. (!excludesLayout || !visual.isLayout) && visual.func(ecModel, api, payload);
  989. });
  990. }
  991. /**
  992. * Render each chart and component
  993. * @private
  994. */
  995. function doRender(ecModel, payload) {
  996. var api = this._api; // Render all components
  997. each(this._componentsViews, function (componentView) {
  998. var componentModel = componentView.__model;
  999. componentView.render(componentModel, ecModel, api, payload);
  1000. updateZ(componentModel, componentView);
  1001. }, this);
  1002. each(this._chartsViews, function (chart) {
  1003. chart.__alive = false;
  1004. }, this); // Render all charts
  1005. ecModel.eachSeries(function (seriesModel, idx) {
  1006. var chartView = this._chartsMap[seriesModel.__viewId];
  1007. chartView.__alive = true;
  1008. chartView.render(seriesModel, ecModel, api, payload);
  1009. chartView.group.silent = !!seriesModel.get('silent');
  1010. updateZ(seriesModel, chartView);
  1011. updateProgressiveAndBlend(seriesModel, chartView);
  1012. }, this); // If use hover layer
  1013. updateHoverLayerStatus(this._zr, ecModel); // Remove groups of unrendered charts
  1014. each(this._chartsViews, function (chart) {
  1015. if (!chart.__alive) {
  1016. chart.remove(ecModel, api);
  1017. }
  1018. }, this);
  1019. }
  1020. var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];
  1021. /**
  1022. * @private
  1023. */
  1024. echartsProto._initEvents = function () {
  1025. each(MOUSE_EVENT_NAMES, function (eveName) {
  1026. this._zr.on(eveName, function (e) {
  1027. var ecModel = this.getModel();
  1028. var el = e.target;
  1029. var params; // no e.target when 'globalout'.
  1030. if (eveName === 'globalout') {
  1031. params = {};
  1032. } else if (el && el.dataIndex != null) {
  1033. var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
  1034. params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
  1035. } // If element has custom eventData of components
  1036. else if (el && el.eventData) {
  1037. params = zrUtil.extend({}, el.eventData);
  1038. }
  1039. if (params) {
  1040. params.event = e;
  1041. params.type = eveName;
  1042. this.trigger(eveName, params);
  1043. }
  1044. }, this);
  1045. }, this);
  1046. each(eventActionMap, function (actionType, eventType) {
  1047. this._messageCenter.on(eventType, function (event) {
  1048. this.trigger(eventType, event);
  1049. }, this);
  1050. }, this);
  1051. };
  1052. /**
  1053. * @return {boolean}
  1054. */
  1055. echartsProto.isDisposed = function () {
  1056. return this._disposed;
  1057. };
  1058. /**
  1059. * Clear
  1060. */
  1061. echartsProto.clear = function () {
  1062. this.setOption({
  1063. series: []
  1064. }, true);
  1065. };
  1066. /**
  1067. * Dispose instance
  1068. */
  1069. echartsProto.dispose = function () {
  1070. if (this._disposed) {
  1071. return;
  1072. }
  1073. this._disposed = true;
  1074. var api = this._api;
  1075. var ecModel = this._model;
  1076. each(this._componentsViews, function (component) {
  1077. component.dispose(ecModel, api);
  1078. });
  1079. each(this._chartsViews, function (chart) {
  1080. chart.dispose(ecModel, api);
  1081. }); // Dispose after all views disposed
  1082. this._zr.dispose();
  1083. delete instances[this.id];
  1084. };
  1085. zrUtil.mixin(ECharts, Eventful);
  1086. function updateHoverLayerStatus(zr, ecModel) {
  1087. var storage = zr.storage;
  1088. var elCount = 0;
  1089. storage.traverse(function (el) {
  1090. if (!el.isGroup) {
  1091. elCount++;
  1092. }
  1093. });
  1094. if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
  1095. storage.traverse(function (el) {
  1096. if (!el.isGroup) {
  1097. el.useHoverLayer = true;
  1098. }
  1099. });
  1100. }
  1101. }
  1102. /**
  1103. * Update chart progressive and blend.
  1104. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  1105. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  1106. */
  1107. function updateProgressiveAndBlend(seriesModel, chartView) {
  1108. // Progressive configuration
  1109. var elCount = 0;
  1110. chartView.group.traverse(function (el) {
  1111. if (el.type !== 'group' && !el.ignore) {
  1112. elCount++;
  1113. }
  1114. });
  1115. var frameDrawNum = +seriesModel.get('progressive');
  1116. var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node;
  1117. if (needProgressive) {
  1118. chartView.group.traverse(function (el) {
  1119. // FIXME marker and other components
  1120. if (!el.isGroup) {
  1121. el.progressive = needProgressive ? Math.floor(elCount++ / frameDrawNum) : -1;
  1122. if (needProgressive) {
  1123. el.stopAnimation(true);
  1124. }
  1125. }
  1126. });
  1127. } // Blend configration
  1128. var blendMode = seriesModel.get('blendMode') || null;
  1129. chartView.group.traverse(function (el) {
  1130. // FIXME marker and other components
  1131. if (!el.isGroup) {
  1132. el.setStyle('blend', blendMode);
  1133. }
  1134. });
  1135. }
  1136. /**
  1137. * @param {module:echarts/model/Series|module:echarts/model/Component} model
  1138. * @param {module:echarts/view/Component|module:echarts/view/Chart} view
  1139. */
  1140. function updateZ(model, view) {
  1141. var z = model.get('z');
  1142. var zlevel = model.get('zlevel'); // Set z and zlevel
  1143. view.group.traverse(function (el) {
  1144. if (el.type !== 'group') {
  1145. z != null && (el.z = z);
  1146. zlevel != null && (el.zlevel = zlevel);
  1147. }
  1148. });
  1149. }
  1150. function createExtensionAPI(ecInstance) {
  1151. var coordSysMgr = ecInstance._coordSysMgr;
  1152. return zrUtil.extend(new ExtensionAPI(ecInstance), {
  1153. // Inject methods
  1154. getCoordinateSystems: zrUtil.bind(coordSysMgr.getCoordinateSystems, coordSysMgr),
  1155. getComponentByElement: function (el) {
  1156. while (el) {
  1157. var modelInfo = el.__ecComponentInfo;
  1158. if (modelInfo != null) {
  1159. return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
  1160. }
  1161. el = el.parent;
  1162. }
  1163. }
  1164. });
  1165. }
  1166. /**
  1167. * @type {Object} key: actionType.
  1168. * @inner
  1169. */
  1170. var actions = {};
  1171. /**
  1172. * Map eventType to actionType
  1173. * @type {Object}
  1174. */
  1175. var eventActionMap = {};
  1176. /**
  1177. * Data processor functions of each stage
  1178. * @type {Array.<Object.<string, Function>>}
  1179. * @inner
  1180. */
  1181. var dataProcessorFuncs = [];
  1182. /**
  1183. * @type {Array.<Function>}
  1184. * @inner
  1185. */
  1186. var optionPreprocessorFuncs = [];
  1187. /**
  1188. * @type {Array.<Function>}
  1189. * @inner
  1190. */
  1191. var postUpdateFuncs = [];
  1192. /**
  1193. * Visual encoding functions of each stage
  1194. * @type {Array.<Object.<string, Function>>}
  1195. * @inner
  1196. */
  1197. var visualFuncs = [];
  1198. /**
  1199. * Theme storage
  1200. * @type {Object.<key, Object>}
  1201. */
  1202. var themeStorage = {};
  1203. /**
  1204. * Loading effects
  1205. */
  1206. var loadingEffects = {};
  1207. var instances = {};
  1208. var connectedGroups = {};
  1209. var idBase = new Date() - 0;
  1210. var groupIdBase = new Date() - 0;
  1211. var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
  1212. var mapDataStores = {};
  1213. function enableConnect(chart) {
  1214. var STATUS_PENDING = 0;
  1215. var STATUS_UPDATING = 1;
  1216. var STATUS_UPDATED = 2;
  1217. var STATUS_KEY = '__connectUpdateStatus';
  1218. function updateConnectedChartsStatus(charts, status) {
  1219. for (var i = 0; i < charts.length; i++) {
  1220. var otherChart = charts[i];
  1221. otherChart[STATUS_KEY] = status;
  1222. }
  1223. }
  1224. zrUtil.each(eventActionMap, function (actionType, eventType) {
  1225. chart._messageCenter.on(eventType, function (event) {
  1226. if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
  1227. if (event && event.escapeConnect) {
  1228. return;
  1229. }
  1230. var action = chart.makeActionFromEvent(event);
  1231. var otherCharts = [];
  1232. zrUtil.each(instances, function (otherChart) {
  1233. if (otherChart !== chart && otherChart.group === chart.group) {
  1234. otherCharts.push(otherChart);
  1235. }
  1236. });
  1237. updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
  1238. each(otherCharts, function (otherChart) {
  1239. if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
  1240. otherChart.dispatchAction(action);
  1241. }
  1242. });
  1243. updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
  1244. }
  1245. });
  1246. });
  1247. }
  1248. /**
  1249. * @param {HTMLElement} dom
  1250. * @param {Object} [theme]
  1251. * @param {Object} opts
  1252. * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
  1253. * @param {string} [opts.renderer] Currently only 'canvas' is supported.
  1254. * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
  1255. * Can be 'auto' (the same as null/undefined)
  1256. * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
  1257. * Can be 'auto' (the same as null/undefined)
  1258. */
  1259. function init(dom, theme, opts) {
  1260. var existInstance = getInstanceByDom(dom);
  1261. if (existInstance) {
  1262. return existInstance;
  1263. }
  1264. var chart = new ECharts(dom, theme, opts);
  1265. chart.id = 'ec_' + idBase++;
  1266. instances[chart.id] = chart;
  1267. if (dom.setAttribute) {
  1268. dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);
  1269. } else {
  1270. dom[DOM_ATTRIBUTE_KEY] = chart.id;
  1271. }
  1272. enableConnect(chart);
  1273. return chart;
  1274. }
  1275. /**
  1276. * @return {string|Array.<module:echarts~ECharts>} groupId
  1277. */
  1278. function connect(groupId) {
  1279. // Is array of charts
  1280. if (zrUtil.isArray(groupId)) {
  1281. var charts = groupId;
  1282. groupId = null; // If any chart has group
  1283. zrUtil.each(charts, function (chart) {
  1284. if (chart.group != null) {
  1285. groupId = chart.group;
  1286. }
  1287. });
  1288. groupId = groupId || 'g_' + groupIdBase++;
  1289. zrUtil.each(charts, function (chart) {
  1290. chart.group = groupId;
  1291. });
  1292. }
  1293. connectedGroups[groupId] = true;
  1294. return groupId;
  1295. }
  1296. /**
  1297. * @DEPRECATED
  1298. * @return {string} groupId
  1299. */
  1300. function disConnect(groupId) {
  1301. connectedGroups[groupId] = false;
  1302. }
  1303. /**
  1304. * @return {string} groupId
  1305. */
  1306. var disconnect = disConnect;
  1307. /**
  1308. * Dispose a chart instance
  1309. * @param {module:echarts~ECharts|HTMLDomElement|string} chart
  1310. */
  1311. function dispose(chart) {
  1312. if (typeof chart === 'string') {
  1313. chart = instances[chart];
  1314. } else if (!(chart instanceof ECharts)) {
  1315. // Try to treat as dom
  1316. chart = getInstanceByDom(chart);
  1317. }
  1318. if (chart instanceof ECharts && !chart.isDisposed()) {
  1319. chart.dispose();
  1320. }
  1321. }
  1322. /**
  1323. * @param {HTMLElement} dom
  1324. * @return {echarts~ECharts}
  1325. */
  1326. function getInstanceByDom(dom) {
  1327. var key;
  1328. if (dom.getAttribute) {
  1329. key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
  1330. } else {
  1331. key = dom[DOM_ATTRIBUTE_KEY];
  1332. }
  1333. return instances[key];
  1334. }
  1335. /**
  1336. * @param {string} key
  1337. * @return {echarts~ECharts}
  1338. */
  1339. function getInstanceById(key) {
  1340. return instances[key];
  1341. }
  1342. /**
  1343. * Register theme
  1344. */
  1345. function registerTheme(name, theme) {
  1346. themeStorage[name] = theme;
  1347. }
  1348. /**
  1349. * Register option preprocessor
  1350. * @param {Function} preprocessorFunc
  1351. */
  1352. function registerPreprocessor(preprocessorFunc) {
  1353. optionPreprocessorFuncs.push(preprocessorFunc);
  1354. }
  1355. /**
  1356. * @param {number} [priority=1000]
  1357. * @param {Function} processorFunc
  1358. */
  1359. function registerProcessor(priority, processorFunc) {
  1360. if (typeof priority === 'function') {
  1361. processorFunc = priority;
  1362. priority = PRIORITY_PROCESSOR_FILTER;
  1363. }
  1364. dataProcessorFuncs.push({
  1365. prio: priority,
  1366. func: processorFunc
  1367. });
  1368. }
  1369. /**
  1370. * Register postUpdater
  1371. * @param {Function} postUpdateFunc
  1372. */
  1373. function registerPostUpdate(postUpdateFunc) {
  1374. postUpdateFuncs.push(postUpdateFunc);
  1375. }
  1376. /**
  1377. * Usage:
  1378. * registerAction('someAction', 'someEvent', function () { ... });
  1379. * registerAction('someAction', function () { ... });
  1380. * registerAction(
  1381. * {type: 'someAction', event: 'someEvent', update: 'updateView'},
  1382. * function () { ... }
  1383. * );
  1384. *
  1385. * @param {(string|Object)} actionInfo
  1386. * @param {string} actionInfo.type
  1387. * @param {string} [actionInfo.event]
  1388. * @param {string} [actionInfo.update]
  1389. * @param {string} [eventName]
  1390. * @param {Function} action
  1391. */
  1392. function registerAction(actionInfo, eventName, action) {
  1393. if (typeof eventName === 'function') {
  1394. action = eventName;
  1395. eventName = '';
  1396. }
  1397. var actionType = zrUtil.isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {
  1398. event: eventName
  1399. }][0]; // Event name is all lowercase
  1400. actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  1401. eventName = actionInfo.event; // Validate action type and event name.
  1402. zrUtil.assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));
  1403. if (!actions[actionType]) {
  1404. actions[actionType] = {
  1405. action: action,
  1406. actionInfo: actionInfo
  1407. };
  1408. }
  1409. eventActionMap[eventName] = actionType;
  1410. }
  1411. /**
  1412. * @param {string} type
  1413. * @param {*} CoordinateSystem
  1414. */
  1415. function registerCoordinateSystem(type, CoordinateSystem) {
  1416. CoordinateSystemManager.register(type, CoordinateSystem);
  1417. }
  1418. /**
  1419. * Get dimensions of specified coordinate system.
  1420. * @param {string} type
  1421. * @return {Array.<string|Object>}
  1422. */
  1423. function getCoordinateSystemDimensions(type) {
  1424. var coordSysCreator = CoordinateSystemManager.get(type);
  1425. if (coordSysCreator) {
  1426. return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();
  1427. }
  1428. }
  1429. /**
  1430. * Layout is a special stage of visual encoding
  1431. * Most visual encoding like color are common for different chart
  1432. * But each chart has it's own layout algorithm
  1433. *
  1434. * @param {number} [priority=1000]
  1435. * @param {Function} layoutFunc
  1436. */
  1437. function registerLayout(priority, layoutFunc) {
  1438. if (typeof priority === 'function') {
  1439. layoutFunc = priority;
  1440. priority = PRIORITY_VISUAL_LAYOUT;
  1441. }
  1442. visualFuncs.push({
  1443. prio: priority,
  1444. func: layoutFunc,
  1445. isLayout: true
  1446. });
  1447. }
  1448. /**
  1449. * @param {number} [priority=3000]
  1450. * @param {Function} visualFunc
  1451. */
  1452. function registerVisual(priority, visualFunc) {
  1453. if (typeof priority === 'function') {
  1454. visualFunc = priority;
  1455. priority = PRIORITY_VISUAL_CHART;
  1456. }
  1457. visualFuncs.push({
  1458. prio: priority,
  1459. func: visualFunc
  1460. });
  1461. }
  1462. /**
  1463. * @param {string} name
  1464. */
  1465. function registerLoading(name, loadingFx) {
  1466. loadingEffects[name] = loadingFx;
  1467. }
  1468. /**
  1469. * @param {Object} opts
  1470. * @param {string} [superClass]
  1471. */
  1472. function extendComponentModel(opts
  1473. /*, superClass*/
  1474. ) {
  1475. // var Clazz = ComponentModel;
  1476. // if (superClass) {
  1477. // var classType = parseClassType(superClass);
  1478. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  1479. // }
  1480. return ComponentModel.extend(opts);
  1481. }
  1482. /**
  1483. * @param {Object} opts
  1484. * @param {string} [superClass]
  1485. */
  1486. function extendComponentView(opts
  1487. /*, superClass*/
  1488. ) {
  1489. // var Clazz = ComponentView;
  1490. // if (superClass) {
  1491. // var classType = parseClassType(superClass);
  1492. // Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  1493. // }
  1494. return ComponentView.extend(opts);
  1495. }
  1496. /**
  1497. * @param {Object} opts
  1498. * @param {string} [superClass]
  1499. */
  1500. function extendSeriesModel(opts
  1501. /*, superClass*/
  1502. ) {
  1503. // var Clazz = SeriesModel;
  1504. // if (superClass) {
  1505. // superClass = 'series.' + superClass.replace('series.', '');
  1506. // var classType = parseClassType(superClass);
  1507. // Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  1508. // }
  1509. return SeriesModel.extend(opts);
  1510. }
  1511. /**
  1512. * @param {Object} opts
  1513. * @param {string} [superClass]
  1514. */
  1515. function extendChartView(opts
  1516. /*, superClass*/
  1517. ) {
  1518. // var Clazz = ChartView;
  1519. // if (superClass) {
  1520. // superClass = superClass.replace('series.', '');
  1521. // var classType = parseClassType(superClass);
  1522. // Clazz = ChartView.getClass(classType.main, true);
  1523. // }
  1524. return ChartView.extend(opts);
  1525. }
  1526. /**
  1527. * ZRender need a canvas context to do measureText.
  1528. * But in node environment canvas may be created by node-canvas.
  1529. * So we need to specify how to create a canvas instead of using document.createElement('canvas')
  1530. *
  1531. * Be careful of using it in the browser.
  1532. *
  1533. * @param {Function} creator
  1534. * @example
  1535. * var Canvas = require('canvas');
  1536. * var echarts = require('echarts');
  1537. * echarts.setCanvasCreator(function () {
  1538. * // Small size is enough.
  1539. * return new Canvas(32, 32);
  1540. * });
  1541. */
  1542. function setCanvasCreator(creator) {
  1543. zrUtil.$override('createCanvas', creator);
  1544. }
  1545. /**
  1546. * @param {string} mapName
  1547. * @param {Object|string} geoJson
  1548. * @param {Object} [specialAreas]
  1549. *
  1550. * @example
  1551. * $.get('USA.json', function (geoJson) {
  1552. * echarts.registerMap('USA', geoJson);
  1553. * // Or
  1554. * echarts.registerMap('USA', {
  1555. * geoJson: geoJson,
  1556. * specialAreas: {}
  1557. * })
  1558. * });
  1559. */
  1560. function registerMap(mapName, geoJson, specialAreas) {
  1561. if (geoJson.geoJson && !geoJson.features) {
  1562. specialAreas = geoJson.specialAreas;
  1563. geoJson = geoJson.geoJson;
  1564. }
  1565. if (typeof geoJson === 'string') {
  1566. geoJson = typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(geoJson) : new Function('return (' + geoJson + ');')();
  1567. }
  1568. mapDataStores[mapName] = {
  1569. geoJson: geoJson,
  1570. specialAreas: specialAreas
  1571. };
  1572. }
  1573. /**
  1574. * @param {string} mapName
  1575. * @return {Object}
  1576. */
  1577. function getMap(mapName) {
  1578. return mapDataStores[mapName];
  1579. }
  1580. registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
  1581. registerPreprocessor(backwardCompat);
  1582. registerLoading('default', loadingDefault); // Default actions
  1583. registerAction({
  1584. type: 'highlight',
  1585. event: 'highlight',
  1586. update: 'highlight'
  1587. }, zrUtil.noop);
  1588. registerAction({
  1589. type: 'downplay',
  1590. event: 'downplay',
  1591. update: 'downplay'
  1592. }, zrUtil.noop); // For backward compatibility, where the namespace `dataTool` will
  1593. // be mounted on `echarts` is the extension `dataTool` is imported.
  1594. var dataTool = {};
  1595. exports.version = version;
  1596. exports.dependencies = dependencies;
  1597. exports.PRIORITY = PRIORITY;
  1598. exports.init = init;
  1599. exports.connect = connect;
  1600. exports.disConnect = disConnect;
  1601. exports.disconnect = disconnect;
  1602. exports.dispose = dispose;
  1603. exports.getInstanceByDom = getInstanceByDom;
  1604. exports.getInstanceById = getInstanceById;
  1605. exports.registerTheme = registerTheme;
  1606. exports.registerPreprocessor = registerPreprocessor;
  1607. exports.registerProcessor = registerProcessor;
  1608. exports.registerPostUpdate = registerPostUpdate;
  1609. exports.registerAction = registerAction;
  1610. exports.registerCoordinateSystem = registerCoordinateSystem;
  1611. exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
  1612. exports.registerLayout = registerLayout;
  1613. exports.registerVisual = registerVisual;
  1614. exports.registerLoading = registerLoading;
  1615. exports.extendComponentModel = extendComponentModel;
  1616. exports.extendComponentView = extendComponentView;
  1617. exports.extendSeriesModel = extendSeriesModel;
  1618. exports.extendChartView = extendChartView;
  1619. exports.setCanvasCreator = setCanvasCreator;
  1620. exports.registerMap = registerMap;
  1621. exports.getMap = getMap;
  1622. exports.dataTool = dataTool;
  1623. var ___ec_export = require("./export");
  1624. (function () {
  1625. for (var key in ___ec_export) {
  1626. if (___ec_export.hasOwnProperty(key)) {
  1627. exports[key] = ___ec_export[key];
  1628. }
  1629. }
  1630. })();