| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 | 
/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*/var _config = require("../config");var __DEV__ = _config.__DEV__;var zrUtil = require("zrender/lib/core/util");var graphicUtil = require("../util/graphic");var _labelHelper = require("./helper/labelHelper");var getDefaultLabel = _labelHelper.getDefaultLabel;var createListFromArray = require("./helper/createListFromArray");var _barGrid = require("../layout/barGrid");var getLayoutOnAxis = _barGrid.getLayoutOnAxis;var DataDiffer = require("../data/DataDiffer");var SeriesModel = require("../model/Series");var Model = require("../model/Model");var ChartView = require("../view/Chart");var _createClipPathFromCoordSys = require("./helper/createClipPathFromCoordSys");var createClipPath = _createClipPathFromCoordSys.createClipPath;var prepareCartesian2d = require("../coord/cartesian/prepareCustom");var prepareGeo = require("../coord/geo/prepareCustom");var prepareSingleAxis = require("../coord/single/prepareCustom");var preparePolar = require("../coord/polar/prepareCustom");var prepareCalendar = require("../coord/calendar/prepareCustom");/** Licensed to the Apache Software Foundation (ASF) under one* or more contributor license agreements.  See the NOTICE file* distributed with this work for additional information* regarding copyright ownership.  The ASF licenses this file* to you under the Apache License, Version 2.0 (the* "License"); you may not use this file except in compliance* with the License.  You may obtain a copy of the License at**   http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing,* software distributed under the License is distributed on an* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY* KIND, either express or implied.  See the License for the* specific language governing permissions and limitations* under the License.*/var CACHED_LABEL_STYLE_PROPERTIES = graphicUtil.CACHED_LABEL_STYLE_PROPERTIES;var ITEM_STYLE_NORMAL_PATH = ['itemStyle'];var ITEM_STYLE_EMPHASIS_PATH = ['emphasis', 'itemStyle'];var LABEL_NORMAL = ['label'];var LABEL_EMPHASIS = ['emphasis', 'label']; // Use prefix to avoid index to be the same as el.name,// which will cause weird udpate animation.var GROUP_DIFF_PREFIX = 'e\0\0';/** * To reduce total package size of each coordinate systems, the modules `prepareCustom` * of each coordinate systems are not required by each coordinate systems directly, but * required by the module `custom`. * * prepareInfoForCustomSeries {Function}: optional *     @return {Object} {coordSys: {...}, api: { *         coord: function (data, clamp) {}, // return point in global. *         size: function (dataSize, dataItem) {} // return size of each axis in coordSys. *     }} */var prepareCustoms = {  cartesian2d: prepareCartesian2d,  geo: prepareGeo,  singleAxis: prepareSingleAxis,  polar: preparePolar,  calendar: prepareCalendar}; // ------// Model// ------SeriesModel.extend({  type: 'series.custom',  dependencies: ['grid', 'polar', 'geo', 'singleAxis', 'calendar'],  defaultOption: {    coordinateSystem: 'cartesian2d',    // Can be set as 'none'    zlevel: 0,    z: 2,    legendHoverLink: true,    useTransform: true,    // Custom series will not clip by default.    // Some case will use custom series to draw label    // For example https://echarts.apache.org/examples/en/editor.html?c=custom-gantt-flight    // Only works on polar and cartesian2d coordinate system.    clip: false // Cartesian coordinate system    // xAxisIndex: 0,    // yAxisIndex: 0,    // Polar coordinate system    // polarIndex: 0,    // Geo coordinate system    // geoIndex: 0,    // label: {}    // itemStyle: {}  },  /**   * @override   */  getInitialData: function (option, ecModel) {    return createListFromArray(this.getSource(), this);  },  /**   * @override   */  getDataParams: function (dataIndex, dataType, el) {    var params = SeriesModel.prototype.getDataParams.apply(this, arguments);    el && (params.info = el.info);    return params;  }}); // -----// View// -----ChartView.extend({  type: 'custom',  /**   * @private   * @type {module:echarts/data/List}   */  _data: null,  /**   * @override   */  render: function (customSeries, ecModel, api, payload) {    var oldData = this._data;    var data = customSeries.getData();    var group = this.group;    var renderItem = makeRenderItem(customSeries, data, ecModel, api); // By default, merge mode is applied. In most cases, custom series is    // used in the scenario that data amount is not large but graphic elements    // is complicated, where merge mode is probably necessary for optimization.    // For example, reuse graphic elements and only update the transform when    // roam or data zoom according to `actionType`.    data.diff(oldData).add(function (newIdx) {      createOrUpdate(null, newIdx, renderItem(newIdx, payload), customSeries, group, data);    }).update(function (newIdx, oldIdx) {      var el = oldData.getItemGraphicEl(oldIdx);      createOrUpdate(el, newIdx, renderItem(newIdx, payload), customSeries, group, data);    }).remove(function (oldIdx) {      var el = oldData.getItemGraphicEl(oldIdx);      el && group.remove(el);    }).execute(); // Do clipping    var clipPath = customSeries.get('clip', true) ? createClipPath(customSeries.coordinateSystem, false, customSeries) : null;    if (clipPath) {      group.setClipPath(clipPath);    } else {      group.removeClipPath();    }    this._data = data;  },  incrementalPrepareRender: function (customSeries, ecModel, api) {    this.group.removeAll();    this._data = null;  },  incrementalRender: function (params, customSeries, ecModel, api, payload) {    var data = customSeries.getData();    var renderItem = makeRenderItem(customSeries, data, ecModel, api);    function setIncrementalAndHoverLayer(el) {      if (!el.isGroup) {        el.incremental = true;        el.useHoverLayer = true;      }    }    for (var idx = params.start; idx < params.end; idx++) {      var el = createOrUpdate(null, idx, renderItem(idx, payload), customSeries, this.group, data);      el.traverse(setIncrementalAndHoverLayer);    }  },  /**   * @override   */  dispose: zrUtil.noop,  /**   * @override   */  filterForExposedEvent: function (eventType, query, targetEl, packedEvent) {    var elementName = query.element;    if (elementName == null || targetEl.name === elementName) {      return true;    } // Enable to give a name on a group made by `renderItem`, and listen    // events that triggerd by its descendents.    while ((targetEl = targetEl.parent) && targetEl !== this.group) {      if (targetEl.name === elementName) {        return true;      }    }    return false;  }});function createEl(elOption) {  var graphicType = elOption.type;  var el; // Those graphic elements are not shapes. They should not be  // overwritten by users, so do them first.  if (graphicType === 'path') {    var shape = elOption.shape; // Using pathRect brings convenience to users sacle svg path.    var pathRect = shape.width != null && shape.height != null ? {      x: shape.x || 0,      y: shape.y || 0,      width: shape.width,      height: shape.height    } : null;    var pathData = getPathData(shape); // Path is also used for icon, so layout 'center' by default.    el = graphicUtil.makePath(pathData, null, pathRect, shape.layout || 'center');    el.__customPathData = pathData;  } else if (graphicType === 'image') {    el = new graphicUtil.Image({});    el.__customImagePath = elOption.style.image;  } else if (graphicType === 'text') {    el = new graphicUtil.Text({});    el.__customText = elOption.style.text;  } else if (graphicType === 'group') {    el = new graphicUtil.Group();  } else if (graphicType === 'compoundPath') {    throw new Error('"compoundPath" is not supported yet.');  } else {    var Clz = graphicUtil.getShapeClass(graphicType);    el = new Clz();  }  el.__customGraphicType = graphicType;  el.name = elOption.name;  return el;}function updateEl(el, dataIndex, elOption, animatableModel, data, isInit, isRoot) {  var transitionProps = {};  var elOptionStyle = elOption.style || {};  elOption.shape && (transitionProps.shape = zrUtil.clone(elOption.shape));  elOption.position && (transitionProps.position = elOption.position.slice());  elOption.scale && (transitionProps.scale = elOption.scale.slice());  elOption.origin && (transitionProps.origin = elOption.origin.slice());  elOption.rotation && (transitionProps.rotation = elOption.rotation);  if (el.type === 'image' && elOption.style) {    var targetStyle = transitionProps.style = {};    zrUtil.each(['x', 'y', 'width', 'height'], function (prop) {      prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);    });  }  if (el.type === 'text' && elOption.style) {    var targetStyle = transitionProps.style = {};    zrUtil.each(['x', 'y'], function (prop) {      prepareStyleTransition(prop, targetStyle, elOptionStyle, el.style, isInit);    }); // Compatible with previous: both support    // textFill and fill, textStroke and stroke in 'text' element.    !elOptionStyle.hasOwnProperty('textFill') && elOptionStyle.fill && (elOptionStyle.textFill = elOptionStyle.fill);    !elOptionStyle.hasOwnProperty('textStroke') && elOptionStyle.stroke && (elOptionStyle.textStroke = elOptionStyle.stroke);  }  if (el.type !== 'group') {    el.useStyle(elOptionStyle); // Init animation.    if (isInit) {      el.style.opacity = 0;      var targetOpacity = elOptionStyle.opacity;      targetOpacity == null && (targetOpacity = 1);      graphicUtil.initProps(el, {        style: {          opacity: targetOpacity        }      }, animatableModel, dataIndex);    }  }  if (isInit) {    el.attr(transitionProps);  } else {    graphicUtil.updateProps(el, transitionProps, animatableModel, dataIndex);  } // Merge by default.  // z2 must not be null/undefined, otherwise sort error may occur.  elOption.hasOwnProperty('z2') && el.attr('z2', elOption.z2 || 0);  elOption.hasOwnProperty('silent') && el.attr('silent', elOption.silent);  elOption.hasOwnProperty('invisible') && el.attr('invisible', elOption.invisible);  elOption.hasOwnProperty('ignore') && el.attr('ignore', elOption.ignore); // `elOption.info` enables user to mount some info on  // elements and use them in event handlers.  // Update them only when user specified, otherwise, remain.  elOption.hasOwnProperty('info') && el.attr('info', elOption.info); // If `elOption.styleEmphasis` is `false`, remove hover style. The  // logic is ensured by `graphicUtil.setElementHoverStyle`.  var styleEmphasis = elOption.styleEmphasis; // hoverStyle should always be set here, because if the hover style  // may already be changed, where the inner cache should be reset.  graphicUtil.setElementHoverStyle(el, styleEmphasis);  if (isRoot) {    graphicUtil.setAsHighDownDispatcher(el, styleEmphasis !== false);  }}function prepareStyleTransition(prop, targetStyle, elOptionStyle, oldElStyle, isInit) {  if (elOptionStyle[prop] != null && !isInit) {    targetStyle[prop] = elOptionStyle[prop];    elOptionStyle[prop] = oldElStyle[prop];  }}function makeRenderItem(customSeries, data, ecModel, api) {  var renderItem = customSeries.get('renderItem');  var coordSys = customSeries.coordinateSystem;  var prepareResult = {};  if (coordSys) {    prepareResult = coordSys.prepareCustoms ? coordSys.prepareCustoms() : prepareCustoms[coordSys.type](coordSys);  }  var userAPI = zrUtil.defaults({    getWidth: api.getWidth,    getHeight: api.getHeight,    getZr: api.getZr,    getDevicePixelRatio: api.getDevicePixelRatio,    value: value,    style: style,    styleEmphasis: styleEmphasis,    visual: visual,    barLayout: barLayout,    currentSeriesIndices: currentSeriesIndices,    font: font  }, prepareResult.api || {});  var userParams = {    // The life cycle of context: current round of rendering.    // The global life cycle is probably not necessary, because    // user can store global status by themselves.    context: {},    seriesId: customSeries.id,    seriesName: customSeries.name,    seriesIndex: customSeries.seriesIndex,    coordSys: prepareResult.coordSys,    dataInsideLength: data.count(),    encode: wrapEncodeDef(customSeries.getData())  }; // Do not support call `api` asynchronously without dataIndexInside input.  var currDataIndexInside;  var currDirty = true;  var currItemModel;  var currLabelNormalModel;  var currLabelEmphasisModel;  var currVisualColor;  return function (dataIndexInside, payload) {    currDataIndexInside = dataIndexInside;    currDirty = true;    return renderItem && renderItem(zrUtil.defaults({      dataIndexInside: dataIndexInside,      dataIndex: data.getRawIndex(dataIndexInside),      // Can be used for optimization when zoom or roam.      actionType: payload ? payload.type : null    }, userParams), userAPI);  }; // Do not update cache until api called.  function updateCache(dataIndexInside) {    dataIndexInside == null && (dataIndexInside = currDataIndexInside);    if (currDirty) {      currItemModel = data.getItemModel(dataIndexInside);      currLabelNormalModel = currItemModel.getModel(LABEL_NORMAL);      currLabelEmphasisModel = currItemModel.getModel(LABEL_EMPHASIS);      currVisualColor = data.getItemVisual(dataIndexInside, 'color');      currDirty = false;    }  }  /**   * @public   * @param {number|string} dim   * @param {number} [dataIndexInside=currDataIndexInside]   * @return {number|string} value   */  function value(dim, dataIndexInside) {    dataIndexInside == null && (dataIndexInside = currDataIndexInside);    return data.get(data.getDimension(dim || 0), dataIndexInside);  }  /**   * By default, `visual` is applied to style (to support visualMap).   * `visual.color` is applied at `fill`. If user want apply visual.color on `stroke`,   * it can be implemented as:   * `api.style({stroke: api.visual('color'), fill: null})`;   * @public   * @param {Object} [extra]   * @param {number} [dataIndexInside=currDataIndexInside]   */  function style(extra, dataIndexInside) {    dataIndexInside == null && (dataIndexInside = currDataIndexInside);    updateCache(dataIndexInside);    var itemStyle = currItemModel.getModel(ITEM_STYLE_NORMAL_PATH).getItemStyle();    currVisualColor != null && (itemStyle.fill = currVisualColor);    var opacity = data.getItemVisual(dataIndexInside, 'opacity');    opacity != null && (itemStyle.opacity = opacity);    var labelModel = extra ? applyExtraBefore(extra, currLabelNormalModel) : currLabelNormalModel;    graphicUtil.setTextStyle(itemStyle, labelModel, null, {      autoColor: currVisualColor,      isRectText: true    });    itemStyle.text = labelModel.getShallow('show') ? zrUtil.retrieve2(customSeries.getFormattedLabel(dataIndexInside, 'normal'), getDefaultLabel(data, dataIndexInside)) : null;    extra && applyExtraAfter(itemStyle, extra);    return itemStyle;  }  /**   * @public   * @param {Object} [extra]   * @param {number} [dataIndexInside=currDataIndexInside]   */  function styleEmphasis(extra, dataIndexInside) {    dataIndexInside == null && (dataIndexInside = currDataIndexInside);    updateCache(dataIndexInside);    var itemStyle = currItemModel.getModel(ITEM_STYLE_EMPHASIS_PATH).getItemStyle();    var labelModel = extra ? applyExtraBefore(extra, currLabelEmphasisModel) : currLabelEmphasisModel;    graphicUtil.setTextStyle(itemStyle, labelModel, null, {      isRectText: true    }, true);    itemStyle.text = labelModel.getShallow('show') ? zrUtil.retrieve3(customSeries.getFormattedLabel(dataIndexInside, 'emphasis'), customSeries.getFormattedLabel(dataIndexInside, 'normal'), getDefaultLabel(data, dataIndexInside)) : null;    extra && applyExtraAfter(itemStyle, extra);    return itemStyle;  }  /**   * @public   * @param {string} visualType   * @param {number} [dataIndexInside=currDataIndexInside]   */  function visual(visualType, dataIndexInside) {    dataIndexInside == null && (dataIndexInside = currDataIndexInside);    return data.getItemVisual(dataIndexInside, visualType);  }  /**   * @public   * @param {number} opt.count Positive interger.   * @param {number} [opt.barWidth]   * @param {number} [opt.barMaxWidth]   * @param {number} [opt.barMinWidth]   * @param {number} [opt.barGap]   * @param {number} [opt.barCategoryGap]   * @return {Object} {width, offset, offsetCenter} is not support, return undefined.   */  function barLayout(opt) {    if (coordSys.getBaseAxis) {      var baseAxis = coordSys.getBaseAxis();      return getLayoutOnAxis(zrUtil.defaults({        axis: baseAxis      }, opt), api);    }  }  /**   * @public   * @return {Array.<number>}   */  function currentSeriesIndices() {    return ecModel.getCurrentSeriesIndices();  }  /**   * @public   * @param {Object} opt   * @param {string} [opt.fontStyle]   * @param {number} [opt.fontWeight]   * @param {number} [opt.fontSize]   * @param {string} [opt.fontFamily]   * @return {string} font string   */  function font(opt) {    return graphicUtil.getFont(opt, ecModel);  }}function wrapEncodeDef(data) {  var encodeDef = {};  zrUtil.each(data.dimensions, function (dimName, dataDimIndex) {    var dimInfo = data.getDimensionInfo(dimName);    if (!dimInfo.isExtraCoord) {      var coordDim = dimInfo.coordDim;      var dataDims = encodeDef[coordDim] = encodeDef[coordDim] || [];      dataDims[dimInfo.coordDimIndex] = dataDimIndex;    }  });  return encodeDef;}function createOrUpdate(el, dataIndex, elOption, animatableModel, group, data) {  el = doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data, true);  el && data.setItemGraphicEl(dataIndex, el);  return el;}function doCreateOrUpdate(el, dataIndex, elOption, animatableModel, group, data, isRoot) {  // [Rule]  // By default, follow merge mode.  //     (It probably brings benifit for performance in some cases of large data, where  //     user program can be optimized to that only updated props needed to be re-calculated,  //     or according to `actionType` some calculation can be skipped.)  // If `renderItem` returns `null`/`undefined`/`false`, remove the previous el if existing.  //     (It seems that violate the "merge" principle, but most of users probably intuitively  //     regard "return;" as "show nothing element whatever", so make a exception to meet the  //     most cases.)  var simplyRemove = !elOption; // `null`/`undefined`/`false`  elOption = elOption || {};  var elOptionType = elOption.type;  var elOptionShape = elOption.shape;  var elOptionStyle = elOption.style;  if (el && (simplyRemove // || elOption.$merge === false  // If `elOptionType` is `null`, follow the merge principle.  || elOptionType != null && elOptionType !== el.__customGraphicType || elOptionType === 'path' && hasOwnPathData(elOptionShape) && getPathData(elOptionShape) !== el.__customPathData || elOptionType === 'image' && hasOwn(elOptionStyle, 'image') && elOptionStyle.image !== el.__customImagePath // FIXME test and remove this restriction?  || elOptionType === 'text' && hasOwn(elOptionShape, 'text') && elOptionStyle.text !== el.__customText)) {    group.remove(el);    el = null;  } // `elOption.type` is undefined when `renderItem` returns nothing.  if (simplyRemove) {    return;  }  var isInit = !el;  !el && (el = createEl(elOption));  updateEl(el, dataIndex, elOption, animatableModel, data, isInit, isRoot);  if (elOptionType === 'group') {    mergeChildren(el, dataIndex, elOption, animatableModel, data);  } // Always add whatever already added to ensure sequence.  group.add(el);  return el;} // Usage:// (1) By default, `elOption.$mergeChildren` is `'byIndex'`, which indicates that//     the existing children will not be removed, and enables the feature that//     update some of the props of some of the children simply by construct//     the returned children of `renderItem` like://     `var children = group.children = []; children[3] = {opacity: 0.5};`// (2) If `elOption.$mergeChildren` is `'byName'`, add/update/remove children//     by child.name. But that might be lower performance.// (3) If `elOption.$mergeChildren` is `false`, the existing children will be//     replaced totally.// (4) If `!elOption.children`, following the "merge" principle, nothing will happen.//// For implementation simpleness, do not provide a direct way to remove sinlge// child (otherwise the total indicies of the children array have to be modified).// User can remove a single child by set its `ignore` as `true` or replace// it by another element, where its `$merge` can be set as `true` if necessary.function mergeChildren(el, dataIndex, elOption, animatableModel, data) {  var newChildren = elOption.children;  var newLen = newChildren ? newChildren.length : 0;  var mergeChildren = elOption.$mergeChildren; // `diffChildrenByName` has been deprecated.  var byName = mergeChildren === 'byName' || elOption.diffChildrenByName;  var notMerge = mergeChildren === false; // For better performance on roam update, only enter if necessary.  if (!newLen && !byName && !notMerge) {    return;  }  if (byName) {    diffGroupChildren({      oldChildren: el.children() || [],      newChildren: newChildren || [],      dataIndex: dataIndex,      animatableModel: animatableModel,      group: el,      data: data    });    return;  }  notMerge && el.removeAll(); // Mapping children of a group simply by index, which  // might be better performance.  var index = 0;  for (; index < newLen; index++) {    newChildren[index] && doCreateOrUpdate(el.childAt(index), dataIndex, newChildren[index], animatableModel, el, data);  }}function diffGroupChildren(context) {  new DataDiffer(context.oldChildren, context.newChildren, getKey, getKey, context).add(processAddUpdate).update(processAddUpdate).remove(processRemove).execute();}function getKey(item, idx) {  var name = item && item.name;  return name != null ? name : GROUP_DIFF_PREFIX + idx;}function processAddUpdate(newIndex, oldIndex) {  var context = this.context;  var childOption = newIndex != null ? context.newChildren[newIndex] : null;  var child = oldIndex != null ? context.oldChildren[oldIndex] : null;  doCreateOrUpdate(child, context.dataIndex, childOption, context.animatableModel, context.group, context.data);} // `graphic#applyDefaultTextStyle` will cache// textFill, textStroke, textStrokeWidth.// We have to do this trick.function applyExtraBefore(extra, model) {  var dummyModel = new Model({}, model);  zrUtil.each(CACHED_LABEL_STYLE_PROPERTIES, function (stylePropName, modelPropName) {    if (extra.hasOwnProperty(stylePropName)) {      dummyModel.option[modelPropName] = extra[stylePropName];    }  });  return dummyModel;}function applyExtraAfter(itemStyle, extra) {  for (var key in extra) {    if (extra.hasOwnProperty(key) || !CACHED_LABEL_STYLE_PROPERTIES.hasOwnProperty(key)) {      itemStyle[key] = extra[key];    }  }}function processRemove(oldIndex) {  var context = this.context;  var child = context.oldChildren[oldIndex];  child && context.group.remove(child);}function getPathData(shape) {  // "d" follows the SVG convention.  return shape && (shape.pathData || shape.d);}function hasOwnPathData(shape) {  return shape && (shape.hasOwnProperty('pathData') || shape.hasOwnProperty('d'));}function hasOwn(host, prop) {  return host && host.hasOwnProperty(prop);}
 |