var _config = require("./config");

var __DEV__ = _config.__DEV__;

var zrender = require("zrender/lib/zrender");

var zrUtil = require("zrender/lib/core/util");

var colorTool = require("zrender/lib/tool/color");

var env = require("zrender/lib/core/env");

var timsort = require("zrender/lib/core/timsort");

var Eventful = require("zrender/lib/mixin/Eventful");

var GlobalModel = require("./model/Global");

var ExtensionAPI = require("./ExtensionAPI");

var CoordinateSystemManager = require("./CoordinateSystem");

var OptionManager = require("./model/OptionManager");

var backwardCompat = require("./preprocessor/backwardCompat");

var ComponentModel = require("./model/Component");

var SeriesModel = require("./model/Series");

var ComponentView = require("./view/Component");

var ChartView = require("./view/Chart");

var graphic = require("./util/graphic");

var modelUtil = require("./util/model");

var _throttle = require("./util/throttle");

var throttle = _throttle.throttle;

var seriesColor = require("./visual/seriesColor");

var loadingDefault = require("./loading/default");

/*!
 * ECharts, a javascript interactive chart library.
 *
 * Copyright (c) 2015, Baidu Inc.
 * All rights reserved.
 *
 * LICENSE
 * https://github.com/ecomfe/echarts/blob/master/LICENSE.txt
 */
var each = zrUtil.each;
var parseClassType = ComponentModel.parseClassType;
var version = '3.8.5';
var dependencies = {
  zrender: '3.7.4'
};
var PRIORITY_PROCESSOR_FILTER = 1000;
var PRIORITY_PROCESSOR_STATISTIC = 5000;
var PRIORITY_VISUAL_LAYOUT = 1000;
var PRIORITY_VISUAL_GLOBAL = 2000;
var PRIORITY_VISUAL_CHART = 3000;
var PRIORITY_VISUAL_COMPONENT = 4000; // FIXME
// necessary?

var PRIORITY_VISUAL_BRUSH = 5000;
var PRIORITY = {
  PROCESSOR: {
    FILTER: PRIORITY_PROCESSOR_FILTER,
    STATISTIC: PRIORITY_PROCESSOR_STATISTIC
  },
  VISUAL: {
    LAYOUT: PRIORITY_VISUAL_LAYOUT,
    GLOBAL: PRIORITY_VISUAL_GLOBAL,
    CHART: PRIORITY_VISUAL_CHART,
    COMPONENT: PRIORITY_VISUAL_COMPONENT,
    BRUSH: PRIORITY_VISUAL_BRUSH
  }
}; // Main process have three entries: `setOption`, `dispatchAction` and `resize`,
// where they must not be invoked nestedly, except the only case: invoke
// dispatchAction with updateMethod "none" in main process.
// This flag is used to carry out this rule.
// All events will be triggered out side main process (i.e. when !this[IN_MAIN_PROCESS]).

var IN_MAIN_PROCESS = '__flagInMainProcess';
var HAS_GRADIENT_OR_PATTERN_BG = '__hasGradientOrPatternBg';
var OPTION_UPDATED = '__optionUpdated';
var ACTION_REG = /^[a-zA-Z0-9_]+$/;

function createRegisterEventWithLowercaseName(method) {
  return function (eventName, handler, context) {
    // Event name is all lowercase
    eventName = eventName && eventName.toLowerCase();
    Eventful.prototype[method].call(this, eventName, handler, context);
  };
}
/**
 * @module echarts~MessageCenter
 */


function MessageCenter() {
  Eventful.call(this);
}

MessageCenter.prototype.on = createRegisterEventWithLowercaseName('on');
MessageCenter.prototype.off = createRegisterEventWithLowercaseName('off');
MessageCenter.prototype.one = createRegisterEventWithLowercaseName('one');
zrUtil.mixin(MessageCenter, Eventful);
/**
 * @module echarts~ECharts
 */

function ECharts(dom, theme, opts) {
  opts = opts || {}; // Get theme by name

  if (typeof theme === 'string') {
    theme = themeStorage[theme];
  }
  /**
   * @type {string}
   */


  this.id;
  /**
   * Group id
   * @type {string}
   */

  this.group;
  /**
   * @type {HTMLElement}
   * @private
   */

  this._dom = dom;
  var defaultRenderer = 'canvas';

  /**
   * @type {module:zrender/ZRender}
   * @private
   */
  var zr = this._zr = zrender.init(dom, {
    renderer: opts.renderer || defaultRenderer,
    devicePixelRatio: opts.devicePixelRatio,
    width: opts.width,
    height: opts.height
  });
  /**
   * Expect 60 pfs.
   * @type {Function}
   * @private
   */

  this._throttledZrFlush = throttle(zrUtil.bind(zr.flush, zr), 17);
  var theme = zrUtil.clone(theme);
  theme && backwardCompat(theme, true);
  /**
   * @type {Object}
   * @private
   */

  this._theme = theme;
  /**
   * @type {Array.<module:echarts/view/Chart>}
   * @private
   */

  this._chartsViews = [];
  /**
   * @type {Object.<string, module:echarts/view/Chart>}
   * @private
   */

  this._chartsMap = {};
  /**
   * @type {Array.<module:echarts/view/Component>}
   * @private
   */

  this._componentsViews = [];
  /**
   * @type {Object.<string, module:echarts/view/Component>}
   * @private
   */

  this._componentsMap = {};
  /**
   * @type {module:echarts/CoordinateSystem}
   * @private
   */

  this._coordSysMgr = new CoordinateSystemManager();
  /**
   * @type {module:echarts/ExtensionAPI}
   * @private
   */

  this._api = createExtensionAPI(this);
  Eventful.call(this);
  /**
   * @type {module:echarts~MessageCenter}
   * @private
   */

  this._messageCenter = new MessageCenter(); // Init mouse events

  this._initEvents(); // In case some people write `window.onresize = chart.resize`


  this.resize = zrUtil.bind(this.resize, this); // Can't dispatch action during rendering procedure

  this._pendingActions = []; // Sort on demand

  function prioritySortFunc(a, b) {
    return a.prio - b.prio;
  }

  timsort(visualFuncs, prioritySortFunc);
  timsort(dataProcessorFuncs, prioritySortFunc);
  zr.animation.on('frame', this._onframe, this); // ECharts instance can be used as value.

  zrUtil.setAsPrimitive(this);
}

var echartsProto = ECharts.prototype;

echartsProto._onframe = function () {
  // Lazy update
  if (this[OPTION_UPDATED]) {
    var silent = this[OPTION_UPDATED].silent;
    this[IN_MAIN_PROCESS] = true;
    updateMethods.prepareAndUpdate.call(this);
    this[IN_MAIN_PROCESS] = false;
    this[OPTION_UPDATED] = false;
    flushPendingActions.call(this, silent);
    triggerUpdatedEvent.call(this, silent);
  }
};
/**
 * @return {HTMLElement}
 */


echartsProto.getDom = function () {
  return this._dom;
};
/**
 * @return {module:zrender~ZRender}
 */


echartsProto.getZr = function () {
  return this._zr;
};
/**
 * Usage:
 * chart.setOption(option, notMerge, lazyUpdate);
 * chart.setOption(option, {
 *     notMerge: ...,
 *     lazyUpdate: ...,
 *     silent: ...
 * });
 *
 * @param {Object} option
 * @param {Object|boolean} [opts] opts or notMerge.
 * @param {boolean} [opts.notMerge=false]
 * @param {boolean} [opts.lazyUpdate=false] Useful when setOption frequently.
 */


echartsProto.setOption = function (option, notMerge, lazyUpdate) {
  var silent;

  if (zrUtil.isObject(notMerge)) {
    lazyUpdate = notMerge.lazyUpdate;
    silent = notMerge.silent;
    notMerge = notMerge.notMerge;
  }

  this[IN_MAIN_PROCESS] = true;

  if (!this._model || notMerge) {
    var optionManager = new OptionManager(this._api);
    var theme = this._theme;
    var ecModel = this._model = new GlobalModel(null, null, theme, optionManager);
    ecModel.init(null, null, theme, optionManager);
  }

  this._model.setOption(option, optionPreprocessorFuncs);

  if (lazyUpdate) {
    this[OPTION_UPDATED] = {
      silent: silent
    };
    this[IN_MAIN_PROCESS] = false;
  } else {
    updateMethods.prepareAndUpdate.call(this); // Ensure zr refresh sychronously, and then pixel in canvas can be
    // fetched after `setOption`.

    this._zr.flush();

    this[OPTION_UPDATED] = false;
    this[IN_MAIN_PROCESS] = false;
    flushPendingActions.call(this, silent);
    triggerUpdatedEvent.call(this, silent);
  }
};
/**
 * @DEPRECATED
 */


echartsProto.setTheme = function () {
  console.log('ECharts#setTheme() is DEPRECATED in ECharts 3.0');
};
/**
 * @return {module:echarts/model/Global}
 */


echartsProto.getModel = function () {
  return this._model;
};
/**
 * @return {Object}
 */


echartsProto.getOption = function () {
  return this._model && this._model.getOption();
};
/**
 * @return {number}
 */


echartsProto.getWidth = function () {
  return this._zr.getWidth();
};
/**
 * @return {number}
 */


echartsProto.getHeight = function () {
  return this._zr.getHeight();
};
/**
 * @return {number}
 */


echartsProto.getDevicePixelRatio = function () {
  return this._zr.painter.dpr || window.devicePixelRatio || 1;
};
/**
 * Get canvas which has all thing rendered
 * @param {Object} opts
 * @param {string} [opts.backgroundColor]
 * @return {string}
 */


echartsProto.getRenderedCanvas = function (opts) {
  if (!env.canvasSupported) {
    return;
  }

  opts = opts || {};
  opts.pixelRatio = opts.pixelRatio || 1;
  opts.backgroundColor = opts.backgroundColor || this._model.get('backgroundColor');
  var zr = this._zr;
  var list = zr.storage.getDisplayList(); // Stop animations

  zrUtil.each(list, function (el) {
    el.stopAnimation(true);
  });
  return zr.painter.getRenderedCanvas(opts);
};
/**
 * Get svg data url
 * @return {string}
 */


echartsProto.getSvgDataUrl = function () {
  if (!env.svgSupported) {
    return;
  }

  var zr = this._zr;
  var list = zr.storage.getDisplayList(); // Stop animations

  zrUtil.each(list, function (el) {
    el.stopAnimation(true);
  });
  return zr.painter.pathToSvg();
};
/**
 * @return {string}
 * @param {Object} opts
 * @param {string} [opts.type='png']
 * @param {string} [opts.pixelRatio=1]
 * @param {string} [opts.backgroundColor]
 * @param {string} [opts.excludeComponents]
 */


echartsProto.getDataURL = function (opts) {
  opts = opts || {};
  var excludeComponents = opts.excludeComponents;
  var ecModel = this._model;
  var excludesComponentViews = [];
  var self = this;
  each(excludeComponents, function (componentType) {
    ecModel.eachComponent({
      mainType: componentType
    }, function (component) {
      var view = self._componentsMap[component.__viewId];

      if (!view.group.ignore) {
        excludesComponentViews.push(view);
        view.group.ignore = true;
      }
    });
  });
  var url = this._zr.painter.getType() === 'svg' ? this.getSvgDataUrl() : this.getRenderedCanvas(opts).toDataURL('image/' + (opts && opts.type || 'png'));
  each(excludesComponentViews, function (view) {
    view.group.ignore = false;
  });
  return url;
};
/**
 * @return {string}
 * @param {Object} opts
 * @param {string} [opts.type='png']
 * @param {string} [opts.pixelRatio=1]
 * @param {string} [opts.backgroundColor]
 */


echartsProto.getConnectedDataURL = function (opts) {
  if (!env.canvasSupported) {
    return;
  }

  var groupId = this.group;
  var mathMin = Math.min;
  var mathMax = Math.max;
  var MAX_NUMBER = Infinity;

  if (connectedGroups[groupId]) {
    var left = MAX_NUMBER;
    var top = MAX_NUMBER;
    var right = -MAX_NUMBER;
    var bottom = -MAX_NUMBER;
    var canvasList = [];
    var dpr = opts && opts.pixelRatio || 1;
    zrUtil.each(instances, function (chart, id) {
      if (chart.group === groupId) {
        var canvas = chart.getRenderedCanvas(zrUtil.clone(opts));
        var boundingRect = chart.getDom().getBoundingClientRect();
        left = mathMin(boundingRect.left, left);
        top = mathMin(boundingRect.top, top);
        right = mathMax(boundingRect.right, right);
        bottom = mathMax(boundingRect.bottom, bottom);
        canvasList.push({
          dom: canvas,
          left: boundingRect.left,
          top: boundingRect.top
        });
      }
    });
    left *= dpr;
    top *= dpr;
    right *= dpr;
    bottom *= dpr;
    var width = right - left;
    var height = bottom - top;
    var targetCanvas = zrUtil.createCanvas();
    targetCanvas.width = width;
    targetCanvas.height = height;
    var zr = zrender.init(targetCanvas);
    each(canvasList, function (item) {
      var img = new graphic.Image({
        style: {
          x: item.left * dpr - left,
          y: item.top * dpr - top,
          image: item.dom
        }
      });
      zr.add(img);
    });
    zr.refreshImmediately();
    return targetCanvas.toDataURL('image/' + (opts && opts.type || 'png'));
  } else {
    return this.getDataURL(opts);
  }
};
/**
 * Convert from logical coordinate system to pixel coordinate system.
 * See CoordinateSystem#convertToPixel.
 * @param {string|Object} finder
 *        If string, e.g., 'geo', means {geoIndex: 0}.
 *        If Object, could contain some of these properties below:
 *        {
 *            seriesIndex / seriesId / seriesName,
 *            geoIndex / geoId, geoName,
 *            bmapIndex / bmapId / bmapName,
 *            xAxisIndex / xAxisId / xAxisName,
 *            yAxisIndex / yAxisId / yAxisName,
 *            gridIndex / gridId / gridName,
 *            ... (can be extended)
 *        }
 * @param {Array|number} value
 * @return {Array|number} result
 */


echartsProto.convertToPixel = zrUtil.curry(doConvertPixel, 'convertToPixel');
/**
 * Convert from pixel coordinate system to logical coordinate system.
 * See CoordinateSystem#convertFromPixel.
 * @param {string|Object} finder
 *        If string, e.g., 'geo', means {geoIndex: 0}.
 *        If Object, could contain some of these properties below:
 *        {
 *            seriesIndex / seriesId / seriesName,
 *            geoIndex / geoId / geoName,
 *            bmapIndex / bmapId / bmapName,
 *            xAxisIndex / xAxisId / xAxisName,
 *            yAxisIndex / yAxisId / yAxisName
 *            gridIndex / gridId / gridName,
 *            ... (can be extended)
 *        }
 * @param {Array|number} value
 * @return {Array|number} result
 */

echartsProto.convertFromPixel = zrUtil.curry(doConvertPixel, 'convertFromPixel');

function doConvertPixel(methodName, finder, value) {
  var ecModel = this._model;

  var coordSysList = this._coordSysMgr.getCoordinateSystems();

  var result;
  finder = modelUtil.parseFinder(ecModel, finder);

  for (var i = 0; i < coordSysList.length; i++) {
    var coordSys = coordSysList[i];

    if (coordSys[methodName] && (result = coordSys[methodName](ecModel, finder, value)) != null) {
      return result;
    }
  }
}
/**
 * Is the specified coordinate systems or components contain the given pixel point.
 * @param {string|Object} finder
 *        If string, e.g., 'geo', means {geoIndex: 0}.
 *        If Object, could contain some of these properties below:
 *        {
 *            seriesIndex / seriesId / seriesName,
 *            geoIndex / geoId / geoName,
 *            bmapIndex / bmapId / bmapName,
 *            xAxisIndex / xAxisId / xAxisName,
 *            yAxisIndex / yAxisId / yAxisName,
 *            gridIndex / gridId / gridName,
 *            ... (can be extended)
 *        }
 * @param {Array|number} value
 * @return {boolean} result
 */


echartsProto.containPixel = function (finder, value) {
  var ecModel = this._model;
  var result;
  finder = modelUtil.parseFinder(ecModel, finder);
  zrUtil.each(finder, function (models, key) {
    key.indexOf('Models') >= 0 && zrUtil.each(models, function (model) {
      var coordSys = model.coordinateSystem;

      if (coordSys && coordSys.containPoint) {
        result |= !!coordSys.containPoint(value);
      } else if (key === 'seriesModels') {
        var view = this._chartsMap[model.__viewId];

        if (view && view.containPoint) {
          result |= view.containPoint(value, model);
        } else {}
      } else {}
    }, this);
  }, this);
  return !!result;
};
/**
 * Get visual from series or data.
 * @param {string|Object} finder
 *        If string, e.g., 'series', means {seriesIndex: 0}.
 *        If Object, could contain some of these properties below:
 *        {
 *            seriesIndex / seriesId / seriesName,
 *            dataIndex / dataIndexInside
 *        }
 *        If dataIndex is not specified, series visual will be fetched,
 *        but not data item visual.
 *        If all of seriesIndex, seriesId, seriesName are not specified,
 *        visual will be fetched from first series.
 * @param {string} visualType 'color', 'symbol', 'symbolSize'
 */


echartsProto.getVisual = function (finder, visualType) {
  var ecModel = this._model;
  finder = modelUtil.parseFinder(ecModel, finder, {
    defaultMainType: 'series'
  });
  var seriesModel = finder.seriesModel;
  var data = seriesModel.getData();
  var dataIndexInside = finder.hasOwnProperty('dataIndexInside') ? finder.dataIndexInside : finder.hasOwnProperty('dataIndex') ? data.indexOfRawIndex(finder.dataIndex) : null;
  return dataIndexInside != null ? data.getItemVisual(dataIndexInside, visualType) : data.getVisual(visualType);
};
/**
 * Get view of corresponding component model
 * @param  {module:echarts/model/Component} componentModel
 * @return {module:echarts/view/Component}
 */


echartsProto.getViewOfComponentModel = function (componentModel) {
  return this._componentsMap[componentModel.__viewId];
};
/**
 * Get view of corresponding series model
 * @param  {module:echarts/model/Series} seriesModel
 * @return {module:echarts/view/Chart}
 */


echartsProto.getViewOfSeriesModel = function (seriesModel) {
  return this._chartsMap[seriesModel.__viewId];
};

var updateMethods = {
  /**
   * @param {Object} payload
   * @private
   */
  update: function (payload) {
    // console.profile && console.profile('update');
    var ecModel = this._model;
    var api = this._api;
    var coordSysMgr = this._coordSysMgr;
    var zr = this._zr; // update before setOption

    if (!ecModel) {
      return;
    } // Fixme First time update ?


    ecModel.restoreData(); // TODO
    // Save total ecModel here for undo/redo (after restoring data and before processing data).
    // Undo (restoration of total ecModel) can be carried out in 'action' or outside API call.
    // Create new coordinate system each update
    // In LineView may save the old coordinate system and use it to get the orignal point

    coordSysMgr.create(this._model, this._api);
    processData.call(this, ecModel, api);
    stackSeriesData.call(this, ecModel);
    coordSysMgr.update(ecModel, api);
    doVisualEncoding.call(this, ecModel, payload);
    doRender.call(this, ecModel, payload); // Set background

    var backgroundColor = ecModel.get('backgroundColor') || 'transparent';
    var painter = zr.painter; // TODO all use clearColor ?

    if (painter.isSingleCanvas && painter.isSingleCanvas()) {
      zr.configLayer(0, {
        clearColor: backgroundColor
      });
    } else {
      // In IE8
      if (!env.canvasSupported) {
        var colorArr = colorTool.parse(backgroundColor);
        backgroundColor = colorTool.stringify(colorArr, 'rgb');

        if (colorArr[3] === 0) {
          backgroundColor = 'transparent';
        }
      }

      if (backgroundColor.colorStops || backgroundColor.image) {
        // Gradient background
        // FIXME Fixed layer？
        zr.configLayer(0, {
          clearColor: backgroundColor
        });
        this[HAS_GRADIENT_OR_PATTERN_BG] = true;
        this._dom.style.background = 'transparent';
      } else {
        if (this[HAS_GRADIENT_OR_PATTERN_BG]) {
          zr.configLayer(0, {
            clearColor: null
          });
        }

        this[HAS_GRADIENT_OR_PATTERN_BG] = false;
        this._dom.style.background = backgroundColor;
      }
    }

    each(postUpdateFuncs, function (func) {
      func(ecModel, api);
    }); // console.profile && console.profileEnd('update');
  },

  /**
   * @param {Object} payload
   * @private
   */
  updateView: function (payload) {
    var ecModel = this._model; // update before setOption

    if (!ecModel) {
      return;
    }

    ecModel.eachSeries(function (seriesModel) {
      seriesModel.getData().clearAllVisual();
    });
    doVisualEncoding.call(this, ecModel, payload);
    invokeUpdateMethod.call(this, 'updateView', ecModel, payload);
  },

  /**
   * @param {Object} payload
   * @private
   */
  updateVisual: function (payload) {
    var ecModel = this._model; // update before setOption

    if (!ecModel) {
      return;
    }

    ecModel.eachSeries(function (seriesModel) {
      seriesModel.getData().clearAllVisual();
    });
    doVisualEncoding.call(this, ecModel, payload, true);
    invokeUpdateMethod.call(this, 'updateVisual', ecModel, payload);
  },

  /**
   * @param {Object} payload
   * @private
   */
  updateLayout: function (payload) {
    var ecModel = this._model; // update before setOption

    if (!ecModel) {
      return;
    }

    doLayout.call(this, ecModel, payload);
    invokeUpdateMethod.call(this, 'updateLayout', ecModel, payload);
  },

  /**
   * @param {Object} payload
   * @private
   */
  prepareAndUpdate: function (payload) {
    var ecModel = this._model;
    prepareView.call(this, 'component', ecModel);
    prepareView.call(this, 'chart', ecModel);
    updateMethods.update.call(this, payload);
  }
};
/**
 * @private
 */

function updateDirectly(ecIns, method, payload, mainType, subType) {
  var ecModel = ecIns._model; // broadcast

  if (!mainType) {
    each(ecIns._componentsViews.concat(ecIns._chartsViews), callView);
    return;
  }

  var query = {};
  query[mainType + 'Id'] = payload[mainType + 'Id'];
  query[mainType + 'Index'] = payload[mainType + 'Index'];
  query[mainType + 'Name'] = payload[mainType + 'Name'];
  var condition = {
    mainType: mainType,
    query: query
  };
  subType && (condition.subType = subType); // subType may be '' by parseClassType;
  // If dispatchAction before setOption, do nothing.

  ecModel && ecModel.eachComponent(condition, function (model, index) {
    callView(ecIns[mainType === 'series' ? '_chartsMap' : '_componentsMap'][model.__viewId]);
  }, ecIns);

  function callView(view) {
    view && view.__alive && view[method] && view[method](view.__model, ecModel, ecIns._api, payload);
  }
}
/**
 * Resize the chart
 * @param {Object} opts
 * @param {number} [opts.width] Can be 'auto' (the same as null/undefined)
 * @param {number} [opts.height] Can be 'auto' (the same as null/undefined)
 * @param {boolean} [opts.silent=false]
 */


echartsProto.resize = function (opts) {
  this[IN_MAIN_PROCESS] = true;

  this._zr.resize(opts);

  var optionChanged = this._model && this._model.resetOption('media');

  var updateMethod = optionChanged ? 'prepareAndUpdate' : 'update';
  updateMethods[updateMethod].call(this); // Resize loading effect

  this._loadingFX && this._loadingFX.resize();
  this[IN_MAIN_PROCESS] = false;
  var silent = opts && opts.silent;
  flushPendingActions.call(this, silent);
  triggerUpdatedEvent.call(this, silent);
};
/**
 * Show loading effect
 * @param  {string} [name='default']
 * @param  {Object} [cfg]
 */


echartsProto.showLoading = function (name, cfg) {
  if (zrUtil.isObject(name)) {
    cfg = name;
    name = 'default';
  }

  this.hideLoading();
  var el = loadingDefault(this._api, cfg);
  var zr = this._zr;
  this._loadingFX = el;
  zr.add(el);
};
/**
 * Hide loading effect
 */


echartsProto.hideLoading = function () {
  this._loadingFX && this._zr.remove(this._loadingFX);
  this._loadingFX = null;
};
/**
 * @param {Object} eventObj
 * @return {Object}
 */


echartsProto.makeActionFromEvent = function (eventObj) {
  var payload = zrUtil.extend({}, eventObj);
  payload.type = eventActionMap[eventObj.type];
  return payload;
};
/**
 * @pubilc
 * @param {Object} payload
 * @param {string} [payload.type] Action type
 * @param {Object|boolean} [opt] If pass boolean, means opt.silent
 * @param {boolean} [opt.silent=false] Whether trigger events.
 * @param {boolean} [opt.flush=undefined]
 *                  true: Flush immediately, and then pixel in canvas can be fetched
 *                      immediately. Caution: it might affect performance.
 *                  false: Not not flush.
 *                  undefined: Auto decide whether perform flush.
 */


echartsProto.dispatchAction = function (payload, opt) {
  if (!zrUtil.isObject(opt)) {
    opt = {
      silent: !!opt
    };
  }

  if (!actions[payload.type]) {
    return;
  } // Avoid dispatch action before setOption. Especially in `connect`.


  if (!this._model) {
    return;
  } // May dispatchAction in rendering procedure


  if (this[IN_MAIN_PROCESS]) {
    this._pendingActions.push(payload);

    return;
  }

  doDispatchAction.call(this, payload, opt.silent);

  if (opt.flush) {
    this._zr.flush(true);
  } else if (opt.flush !== false && env.browser.weChat) {
    // In WeChat embeded browser, `requestAnimationFrame` and `setInterval`
    // hang when sliding page (on touch event), which cause that zr does not
    // refresh util user interaction finished, which is not expected.
    // But `dispatchAction` may be called too frequently when pan on touch
    // screen, which impacts performance if do not throttle them.
    this._throttledZrFlush();
  }

  flushPendingActions.call(this, opt.silent);
  triggerUpdatedEvent.call(this, opt.silent);
};

function doDispatchAction(payload, silent) {
  var payloadType = payload.type;
  var escapeConnect = payload.escapeConnect;
  var actionWrap = actions[payloadType];
  var actionInfo = actionWrap.actionInfo;
  var cptType = (actionInfo.update || 'update').split(':');
  var updateMethod = cptType.pop();
  cptType = cptType[0] != null && parseClassType(cptType[0]);
  this[IN_MAIN_PROCESS] = true;
  var payloads = [payload];
  var batched = false; // Batch action

  if (payload.batch) {
    batched = true;
    payloads = zrUtil.map(payload.batch, function (item) {
      item = zrUtil.defaults(zrUtil.extend({}, item), payload);
      item.batch = null;
      return item;
    });
  }

  var eventObjBatch = [];
  var eventObj;
  var isHighDown = payloadType === 'highlight' || payloadType === 'downplay';
  each(payloads, function (batchItem) {
    // Action can specify the event by return it.
    eventObj = actionWrap.action(batchItem, this._model, this._api); // Emit event outside

    eventObj = eventObj || zrUtil.extend({}, batchItem); // Convert type to eventType

    eventObj.type = actionInfo.event || eventObj.type;
    eventObjBatch.push(eventObj); // light update does not perform data process, layout and visual.

    if (isHighDown) {
      // method, payload, mainType, subType
      updateDirectly(this, updateMethod, batchItem, 'series');
    } else if (cptType) {
      updateDirectly(this, updateMethod, batchItem, cptType.main, cptType.sub);
    }
  }, this);

  if (updateMethod !== 'none' && !isHighDown && !cptType) {
    // Still dirty
    if (this[OPTION_UPDATED]) {
      // FIXME Pass payload ?
      updateMethods.prepareAndUpdate.call(this, payload);
      this[OPTION_UPDATED] = false;
    } else {
      updateMethods[updateMethod].call(this, payload);
    }
  } // Follow the rule of action batch


  if (batched) {
    eventObj = {
      type: actionInfo.event || payloadType,
      escapeConnect: escapeConnect,
      batch: eventObjBatch
    };
  } else {
    eventObj = eventObjBatch[0];
  }

  this[IN_MAIN_PROCESS] = false;
  !silent && this._messageCenter.trigger(eventObj.type, eventObj);
}

function flushPendingActions(silent) {
  var pendingActions = this._pendingActions;

  while (pendingActions.length) {
    var payload = pendingActions.shift();
    doDispatchAction.call(this, payload, silent);
  }
}

function triggerUpdatedEvent(silent) {
  !silent && this.trigger('updated');
}
/**
 * Register event
 * @method
 */


echartsProto.on = createRegisterEventWithLowercaseName('on');
echartsProto.off = createRegisterEventWithLowercaseName('off');
echartsProto.one = createRegisterEventWithLowercaseName('one');
/**
 * @param {string} methodName
 * @private
 */

function invokeUpdateMethod(methodName, ecModel, payload) {
  var api = this._api; // Update all components

  each(this._componentsViews, function (component) {
    var componentModel = component.__model;
    component[methodName](componentModel, ecModel, api, payload);
    updateZ(componentModel, component);
  }, this); // Upate all charts

  ecModel.eachSeries(function (seriesModel, idx) {
    var chart = this._chartsMap[seriesModel.__viewId];
    chart[methodName](seriesModel, ecModel, api, payload);
    updateZ(seriesModel, chart);
    updateProgressiveAndBlend(seriesModel, chart);
  }, this); // If use hover layer

  updateHoverLayerStatus(this._zr, ecModel); // Post render

  each(postUpdateFuncs, function (func) {
    func(ecModel, api);
  });
}
/**
 * Prepare view instances of charts and components
 * @param  {module:echarts/model/Global} ecModel
 * @private
 */


function prepareView(type, ecModel) {
  var isComponent = type === 'component';
  var viewList = isComponent ? this._componentsViews : this._chartsViews;
  var viewMap = isComponent ? this._componentsMap : this._chartsMap;
  var zr = this._zr;

  for (var i = 0; i < viewList.length; i++) {
    viewList[i].__alive = false;
  }

  ecModel[isComponent ? 'eachComponent' : 'eachSeries'](function (componentType, model) {
    if (isComponent) {
      if (componentType === 'series') {
        return;
      }
    } else {
      model = componentType;
    } // Consider: id same and type changed.


    var viewId = '_ec_' + model.id + '_' + model.type;
    var view = viewMap[viewId];

    if (!view) {
      var classType = parseClassType(model.type);
      var Clazz = isComponent ? ComponentView.getClass(classType.main, classType.sub) : ChartView.getClass(classType.sub);

      if (Clazz) {
        view = new Clazz();
        view.init(ecModel, this._api);
        viewMap[viewId] = view;
        viewList.push(view);
        zr.add(view.group);
      } else {
        // Error
        return;
      }
    }

    model.__viewId = view.__id = viewId;
    view.__alive = true;
    view.__model = model;
    view.group.__ecComponentInfo = {
      mainType: model.mainType,
      index: model.componentIndex
    };
  }, this);

  for (var i = 0; i < viewList.length;) {
    var view = viewList[i];

    if (!view.__alive) {
      zr.remove(view.group);
      view.dispose(ecModel, this._api);
      viewList.splice(i, 1);
      delete viewMap[view.__id];
      view.__id = view.group.__ecComponentInfo = null;
    } else {
      i++;
    }
  }
}
/**
 * Processor data in each series
 *
 * @param {module:echarts/model/Global} ecModel
 * @private
 */


function processData(ecModel, api) {
  each(dataProcessorFuncs, function (process) {
    process.func(ecModel, api);
  });
}
/**
 * @private
 */


function stackSeriesData(ecModel) {
  var stackedDataMap = {};
  ecModel.eachSeries(function (series) {
    var stack = series.get('stack');
    var data = series.getData();

    if (stack && data.type === 'list') {
      var previousStack = stackedDataMap[stack]; // Avoid conflict with Object.prototype

      if (stackedDataMap.hasOwnProperty(stack) && previousStack) {
        data.stackedOn = previousStack;
      }

      stackedDataMap[stack] = data;
    }
  });
}
/**
 * Layout before each chart render there series, special visual encoding stage
 *
 * @param {module:echarts/model/Global} ecModel
 * @private
 */


function doLayout(ecModel, payload) {
  var api = this._api;
  each(visualFuncs, function (visual) {
    if (visual.isLayout) {
      visual.func(ecModel, api, payload);
    }
  });
}
/**
 * Encode visual infomation from data after data processing
 *
 * @param {module:echarts/model/Global} ecModel
 * @param {object} layout
 * @param {boolean} [excludesLayout]
 * @private
 */


function doVisualEncoding(ecModel, payload, excludesLayout) {
  var api = this._api;
  ecModel.clearColorPalette();
  ecModel.eachSeries(function (seriesModel) {
    seriesModel.clearColorPalette();
  });
  each(visualFuncs, function (visual) {
    (!excludesLayout || !visual.isLayout) && visual.func(ecModel, api, payload);
  });
}
/**
 * Render each chart and component
 * @private
 */


function doRender(ecModel, payload) {
  var api = this._api; // Render all components

  each(this._componentsViews, function (componentView) {
    var componentModel = componentView.__model;
    componentView.render(componentModel, ecModel, api, payload);
    updateZ(componentModel, componentView);
  }, this);
  each(this._chartsViews, function (chart) {
    chart.__alive = false;
  }, this); // Render all charts

  ecModel.eachSeries(function (seriesModel, idx) {
    var chartView = this._chartsMap[seriesModel.__viewId];
    chartView.__alive = true;
    chartView.render(seriesModel, ecModel, api, payload);
    chartView.group.silent = !!seriesModel.get('silent');
    updateZ(seriesModel, chartView);
    updateProgressiveAndBlend(seriesModel, chartView);
  }, this); // If use hover layer

  updateHoverLayerStatus(this._zr, ecModel); // Remove groups of unrendered charts

  each(this._chartsViews, function (chart) {
    if (!chart.__alive) {
      chart.remove(ecModel, api);
    }
  }, this);
}

var MOUSE_EVENT_NAMES = ['click', 'dblclick', 'mouseover', 'mouseout', 'mousemove', 'mousedown', 'mouseup', 'globalout', 'contextmenu'];
/**
 * @private
 */

echartsProto._initEvents = function () {
  each(MOUSE_EVENT_NAMES, function (eveName) {
    this._zr.on(eveName, function (e) {
      var ecModel = this.getModel();
      var el = e.target;
      var params; // no e.target when 'globalout'.

      if (eveName === 'globalout') {
        params = {};
      } else if (el && el.dataIndex != null) {
        var dataModel = el.dataModel || ecModel.getSeriesByIndex(el.seriesIndex);
        params = dataModel && dataModel.getDataParams(el.dataIndex, el.dataType) || {};
      } // If element has custom eventData of components
      else if (el && el.eventData) {
          params = zrUtil.extend({}, el.eventData);
        }

      if (params) {
        params.event = e;
        params.type = eveName;
        this.trigger(eveName, params);
      }
    }, this);
  }, this);
  each(eventActionMap, function (actionType, eventType) {
    this._messageCenter.on(eventType, function (event) {
      this.trigger(eventType, event);
    }, this);
  }, this);
};
/**
 * @return {boolean}
 */


echartsProto.isDisposed = function () {
  return this._disposed;
};
/**
 * Clear
 */


echartsProto.clear = function () {
  this.setOption({
    series: []
  }, true);
};
/**
 * Dispose instance
 */


echartsProto.dispose = function () {
  if (this._disposed) {
    return;
  }

  this._disposed = true;
  var api = this._api;
  var ecModel = this._model;
  each(this._componentsViews, function (component) {
    component.dispose(ecModel, api);
  });
  each(this._chartsViews, function (chart) {
    chart.dispose(ecModel, api);
  }); // Dispose after all views disposed

  this._zr.dispose();

  delete instances[this.id];
};

zrUtil.mixin(ECharts, Eventful);

function updateHoverLayerStatus(zr, ecModel) {
  var storage = zr.storage;
  var elCount = 0;
  storage.traverse(function (el) {
    if (!el.isGroup) {
      elCount++;
    }
  });

  if (elCount > ecModel.get('hoverLayerThreshold') && !env.node) {
    storage.traverse(function (el) {
      if (!el.isGroup) {
        el.useHoverLayer = true;
      }
    });
  }
}
/**
 * Update chart progressive and blend.
 * @param {module:echarts/model/Series|module:echarts/model/Component} model
 * @param {module:echarts/view/Component|module:echarts/view/Chart} view
 */


function updateProgressiveAndBlend(seriesModel, chartView) {
  // Progressive configuration
  var elCount = 0;
  chartView.group.traverse(function (el) {
    if (el.type !== 'group' && !el.ignore) {
      elCount++;
    }
  });
  var frameDrawNum = +seriesModel.get('progressive');
  var needProgressive = elCount > seriesModel.get('progressiveThreshold') && frameDrawNum && !env.node;

  if (needProgressive) {
    chartView.group.traverse(function (el) {
      // FIXME marker and other components
      if (!el.isGroup) {
        el.progressive = needProgressive ? Math.floor(elCount++ / frameDrawNum) : -1;

        if (needProgressive) {
          el.stopAnimation(true);
        }
      }
    });
  } // Blend configration


  var blendMode = seriesModel.get('blendMode') || null;
  chartView.group.traverse(function (el) {
    // FIXME marker and other components
    if (!el.isGroup) {
      el.setStyle('blend', blendMode);
    }
  });
}
/**
 * @param {module:echarts/model/Series|module:echarts/model/Component} model
 * @param {module:echarts/view/Component|module:echarts/view/Chart} view
 */


function updateZ(model, view) {
  var z = model.get('z');
  var zlevel = model.get('zlevel'); // Set z and zlevel

  view.group.traverse(function (el) {
    if (el.type !== 'group') {
      z != null && (el.z = z);
      zlevel != null && (el.zlevel = zlevel);
    }
  });
}

function createExtensionAPI(ecInstance) {
  var coordSysMgr = ecInstance._coordSysMgr;
  return zrUtil.extend(new ExtensionAPI(ecInstance), {
    // Inject methods
    getCoordinateSystems: zrUtil.bind(coordSysMgr.getCoordinateSystems, coordSysMgr),
    getComponentByElement: function (el) {
      while (el) {
        var modelInfo = el.__ecComponentInfo;

        if (modelInfo != null) {
          return ecInstance._model.getComponent(modelInfo.mainType, modelInfo.index);
        }

        el = el.parent;
      }
    }
  });
}
/**
 * @type {Object} key: actionType.
 * @inner
 */


var actions = {};
/**
 * Map eventType to actionType
 * @type {Object}
 */

var eventActionMap = {};
/**
 * Data processor functions of each stage
 * @type {Array.<Object.<string, Function>>}
 * @inner
 */

var dataProcessorFuncs = [];
/**
 * @type {Array.<Function>}
 * @inner
 */

var optionPreprocessorFuncs = [];
/**
 * @type {Array.<Function>}
 * @inner
 */

var postUpdateFuncs = [];
/**
 * Visual encoding functions of each stage
 * @type {Array.<Object.<string, Function>>}
 * @inner
 */

var visualFuncs = [];
/**
 * Theme storage
 * @type {Object.<key, Object>}
 */

var themeStorage = {};
var instances = {};
var connectedGroups = {};
var idBase = new Date() - 0;
var groupIdBase = new Date() - 0;
var DOM_ATTRIBUTE_KEY = '_echarts_instance_';
var mapDataStores = {};

function enableConnect(chart) {
  var STATUS_PENDING = 0;
  var STATUS_UPDATING = 1;
  var STATUS_UPDATED = 2;
  var STATUS_KEY = '__connectUpdateStatus';

  function updateConnectedChartsStatus(charts, status) {
    for (var i = 0; i < charts.length; i++) {
      var otherChart = charts[i];
      otherChart[STATUS_KEY] = status;
    }
  }

  zrUtil.each(eventActionMap, function (actionType, eventType) {
    chart._messageCenter.on(eventType, function (event) {
      if (connectedGroups[chart.group] && chart[STATUS_KEY] !== STATUS_PENDING) {
        if (event && event.escapeConnect) {
          return;
        }

        var action = chart.makeActionFromEvent(event);
        var otherCharts = [];
        zrUtil.each(instances, function (otherChart) {
          if (otherChart !== chart && otherChart.group === chart.group) {
            otherCharts.push(otherChart);
          }
        });
        updateConnectedChartsStatus(otherCharts, STATUS_PENDING);
        each(otherCharts, function (otherChart) {
          if (otherChart[STATUS_KEY] !== STATUS_UPDATING) {
            otherChart.dispatchAction(action);
          }
        });
        updateConnectedChartsStatus(otherCharts, STATUS_UPDATED);
      }
    });
  });
}
/**
 * @param {HTMLElement} dom
 * @param {Object} [theme]
 * @param {Object} opts
 * @param {number} [opts.devicePixelRatio] Use window.devicePixelRatio by default
 * @param {string} [opts.renderer] Currently only 'canvas' is supported.
 * @param {number} [opts.width] Use clientWidth of the input `dom` by default.
 *                              Can be 'auto' (the same as null/undefined)
 * @param {number} [opts.height] Use clientHeight of the input `dom` by default.
 *                               Can be 'auto' (the same as null/undefined)
 */


function init(dom, theme, opts) {
  var existInstance = getInstanceByDom(dom);

  if (existInstance) {
    return existInstance;
  }

  var chart = new ECharts(dom, theme, opts);
  chart.id = 'ec_' + idBase++;
  instances[chart.id] = chart;

  if (dom.setAttribute) {
    dom.setAttribute(DOM_ATTRIBUTE_KEY, chart.id);
  } else {
    dom[DOM_ATTRIBUTE_KEY] = chart.id;
  }

  enableConnect(chart);
  return chart;
}
/**
 * @return {string|Array.<module:echarts~ECharts>} groupId
 */


function connect(groupId) {
  // Is array of charts
  if (zrUtil.isArray(groupId)) {
    var charts = groupId;
    groupId = null; // If any chart has group

    zrUtil.each(charts, function (chart) {
      if (chart.group != null) {
        groupId = chart.group;
      }
    });
    groupId = groupId || 'g_' + groupIdBase++;
    zrUtil.each(charts, function (chart) {
      chart.group = groupId;
    });
  }

  connectedGroups[groupId] = true;
  return groupId;
}
/**
 * @DEPRECATED
 * @return {string} groupId
 */


function disConnect(groupId) {
  connectedGroups[groupId] = false;
}
/**
 * @return {string} groupId
 */


var disconnect = disConnect;
/**
 * Dispose a chart instance
 * @param  {module:echarts~ECharts|HTMLDomElement|string} chart
 */

function dispose(chart) {
  if (typeof chart === 'string') {
    chart = instances[chart];
  } else if (!(chart instanceof ECharts)) {
    // Try to treat as dom
    chart = getInstanceByDom(chart);
  }

  if (chart instanceof ECharts && !chart.isDisposed()) {
    chart.dispose();
  }
}
/**
 * @param  {HTMLElement} dom
 * @return {echarts~ECharts}
 */


function getInstanceByDom(dom) {
  var key;

  if (dom.getAttribute) {
    key = dom.getAttribute(DOM_ATTRIBUTE_KEY);
  } else {
    key = dom[DOM_ATTRIBUTE_KEY];
  }

  return instances[key];
}
/**
 * @param {string} key
 * @return {echarts~ECharts}
 */


function getInstanceById(key) {
  return instances[key];
}
/**
 * Register theme
 */


function registerTheme(name, theme) {
  themeStorage[name] = theme;
}
/**
 * Register option preprocessor
 * @param {Function} preprocessorFunc
 */


function registerPreprocessor(preprocessorFunc) {
  optionPreprocessorFuncs.push(preprocessorFunc);
}
/**
 * @param {number} [priority=1000]
 * @param {Function} processorFunc
 */


function registerProcessor(priority, processorFunc) {
  if (typeof priority === 'function') {
    processorFunc = priority;
    priority = PRIORITY_PROCESSOR_FILTER;
  }

  dataProcessorFuncs.push({
    prio: priority,
    func: processorFunc
  });
}
/**
 * Register postUpdater
 * @param {Function} postUpdateFunc
 */


function registerPostUpdate(postUpdateFunc) {
  postUpdateFuncs.push(postUpdateFunc);
}
/**
 * Usage:
 * registerAction('someAction', 'someEvent', function () { ... });
 * registerAction('someAction', function () { ... });
 * registerAction(
 *     {type: 'someAction', event: 'someEvent', update: 'updateView'},
 *     function () { ... }
 * );
 *
 * @param {(string|Object)} actionInfo
 * @param {string} actionInfo.type
 * @param {string} [actionInfo.event]
 * @param {string} [actionInfo.update]
 * @param {string} [eventName]
 * @param {Function} action
 */


function registerAction(actionInfo, eventName, action) {
  if (typeof eventName === 'function') {
    action = eventName;
    eventName = '';
  }

  var actionType = zrUtil.isObject(actionInfo) ? actionInfo.type : [actionInfo, actionInfo = {
    event: eventName
  }][0]; // Event name is all lowercase

  actionInfo.event = (actionInfo.event || actionType).toLowerCase();
  eventName = actionInfo.event; // Validate action type and event name.

  zrUtil.assert(ACTION_REG.test(actionType) && ACTION_REG.test(eventName));

  if (!actions[actionType]) {
    actions[actionType] = {
      action: action,
      actionInfo: actionInfo
    };
  }

  eventActionMap[eventName] = actionType;
}
/**
 * @param {string} type
 * @param {*} CoordinateSystem
 */


function registerCoordinateSystem(type, CoordinateSystem) {
  CoordinateSystemManager.register(type, CoordinateSystem);
}
/**
 * Get dimensions of specified coordinate system.
 * @param {string} type
 * @return {Array.<string|Object>}
 */


function getCoordinateSystemDimensions(type) {
  var coordSysCreator = CoordinateSystemManager.get(type);

  if (coordSysCreator) {
    return coordSysCreator.getDimensionsInfo ? coordSysCreator.getDimensionsInfo() : coordSysCreator.dimensions.slice();
  }
}
/**
 * Layout is a special stage of visual encoding
 * Most visual encoding like color are common for different chart
 * But each chart has it's own layout algorithm
 *
 * @param {number} [priority=1000]
 * @param {Function} layoutFunc
 */


function registerLayout(priority, layoutFunc) {
  if (typeof priority === 'function') {
    layoutFunc = priority;
    priority = PRIORITY_VISUAL_LAYOUT;
  }

  visualFuncs.push({
    prio: priority,
    func: layoutFunc,
    isLayout: true
  });
}
/**
 * @param {number} [priority=3000]
 * @param {Function} visualFunc
 */


function registerVisual(priority, visualFunc) {
  if (typeof priority === 'function') {
    visualFunc = priority;
    priority = PRIORITY_VISUAL_CHART;
  }

  visualFuncs.push({
    prio: priority,
    func: visualFunc
  });
}
/**
 * @param {Object} opts
 * @param {string} [superClass]
 */


function extendComponentModel(opts
/*, superClass*/
) {
  // var Clazz = ComponentModel;
  // if (superClass) {
  //     var classType = parseClassType(superClass);
  //     Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  // }
  return ComponentModel.extend(opts);
}
/**
 * @param {Object} opts
 * @param {string} [superClass]
 */


function extendComponentView(opts
/*, superClass*/
) {
  // var Clazz = ComponentView;
  // if (superClass) {
  //     var classType = parseClassType(superClass);
  //     Clazz = ComponentView.getClass(classType.main, classType.sub, true);
  // }
  return ComponentView.extend(opts);
}
/**
 * @param {Object} opts
 * @param {string} [superClass]
 */


function extendSeriesModel(opts
/*, superClass*/
) {
  // var Clazz = SeriesModel;
  // if (superClass) {
  //     superClass = 'series.' + superClass.replace('series.', '');
  //     var classType = parseClassType(superClass);
  //     Clazz = ComponentModel.getClass(classType.main, classType.sub, true);
  // }
  return SeriesModel.extend(opts);
}
/**
 * @param {Object} opts
 * @param {string} [superClass]
 */


function extendChartView(opts
/*, superClass*/
) {
  // var Clazz = ChartView;
  // if (superClass) {
  //     superClass = superClass.replace('series.', '');
  //     var classType = parseClassType(superClass);
  //     Clazz = ChartView.getClass(classType.main, true);
  // }
  return ChartView.extend(opts);
}
/**
 * ZRender need a canvas context to do measureText.
 * But in node environment canvas may be created by node-canvas.
 * So we need to specify how to create a canvas instead of using document.createElement('canvas')
 *
 * Be careful of using it in the browser.
 *
 * @param {Function} creator
 * @example
 *     var Canvas = require('canvas');
 *     var echarts = require('echarts');
 *     echarts.setCanvasCreator(function () {
 *         // Small size is enough.
 *         return new Canvas(32, 32);
 *     });
 */


function setCanvasCreator(creator) {
  zrUtil.$override('createCanvas', creator);
}
/**
 * @param {string} mapName
 * @param {Object|string} geoJson
 * @param {Object} [specialAreas]
 *
 * @example
 *     $.get('USA.json', function (geoJson) {
 *         echarts.registerMap('USA', geoJson);
 *         // Or
 *         echarts.registerMap('USA', {
 *             geoJson: geoJson,
 *             specialAreas: {}
 *         })
 *     });
 */


function registerMap(mapName, geoJson, specialAreas) {
  if (geoJson.geoJson && !geoJson.features) {
    specialAreas = geoJson.specialAreas;
    geoJson = geoJson.geoJson;
  }

  if (typeof geoJson === 'string') {
    geoJson = typeof JSON !== 'undefined' && JSON.parse ? JSON.parse(geoJson) : new Function('return (' + geoJson + ');')();
  }

  mapDataStores[mapName] = {
    geoJson: geoJson,
    specialAreas: specialAreas
  };
}
/**
 * @param {string} mapName
 * @return {Object}
 */


function getMap(mapName) {
  return mapDataStores[mapName];
}

registerVisual(PRIORITY_VISUAL_GLOBAL, seriesColor);
registerPreprocessor(backwardCompat); // Default actions

registerAction({
  type: 'highlight',
  event: 'highlight',
  update: 'highlight'
}, zrUtil.noop);
registerAction({
  type: 'downplay',
  event: 'downplay',
  update: 'downplay'
}, zrUtil.noop); // For backward compatibility, where the namespace `dataTool` will
// be mounted on `echarts` is the extension `dataTool` is imported.

var dataTool = {};
exports.version = version;
exports.dependencies = dependencies;
exports.PRIORITY = PRIORITY;
exports.init = init;
exports.connect = connect;
exports.disConnect = disConnect;
exports.disconnect = disconnect;
exports.dispose = dispose;
exports.getInstanceByDom = getInstanceByDom;
exports.getInstanceById = getInstanceById;
exports.registerTheme = registerTheme;
exports.registerPreprocessor = registerPreprocessor;
exports.registerProcessor = registerProcessor;
exports.registerPostUpdate = registerPostUpdate;
exports.registerAction = registerAction;
exports.registerCoordinateSystem = registerCoordinateSystem;
exports.getCoordinateSystemDimensions = getCoordinateSystemDimensions;
exports.registerLayout = registerLayout;
exports.registerVisual = registerVisual;
exports.extendComponentModel = extendComponentModel;
exports.extendComponentView = extendComponentView;
exports.extendSeriesModel = extendSeriesModel;
exports.extendChartView = extendChartView;
exports.setCanvasCreator = setCanvasCreator;
exports.registerMap = registerMap;
exports.getMap = getMap;
exports.dataTool = dataTool;
var ___ec_export = require("./export");
(function () {
    for (var key in ___ec_export) {
        if (___ec_export.hasOwnProperty(key)) {
            exports[key] = ___ec_export[key];
        }
    }
})();