blob: 9df9338b62ca581dea32c818ae684ad7567baa8b [file] [log] [blame]
/**
* List for data storage
* @module echarts/data/List
*/
import { __DEV__ } from '../config';
import * as zrUtil from 'zrender/src/core/util';
import Model from '../model/Model';
import DataDiffer from './DataDiffer';
import * as modelUtil from '../util/model';
var isObject = zrUtil.isObject;
var UNDEFINED = 'undefined';
var globalObj = typeof window === UNDEFINED ? global : window;
var dataCtors = {
'float': typeof globalObj.Float64Array === UNDEFINED ? Array : globalObj.Float64Array,
'int': typeof globalObj.Int32Array === UNDEFINED ? Array : globalObj.Int32Array,
// Ordinal data type can be string or int
'ordinal': Array,
'number': Array,
'time': Array
};
var TRANSFERABLE_PROPERTIES = ['stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'];
function transferProperties(a, b) {
zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
if (b.hasOwnProperty(propName)) {
a[propName] = b[propName];
}
});
a.__wrappedMethods = b.__wrappedMethods;
}
function DefaultDataProvider(dataArray) {
this._array = dataArray || [];
}
DefaultDataProvider.prototype.pure = false;
DefaultDataProvider.prototype.count = function () {
return this._array.length;
};
DefaultDataProvider.prototype.getItem = function (idx) {
return this._array[idx];
};
/**
* @constructor
* @alias module:echarts/data/List
*
* @param {Array.<string|Object>} dimensions
* For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
* Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
* @param {module:echarts/model/Model} hostModel
*/
var List = function (dimensions, hostModel) {
dimensions = dimensions || ['x', 'y'];
var dimensionInfos = {};
var dimensionNames = [];
for (var i = 0; i < dimensions.length; i++) {
var dimensionName;
var dimensionInfo = {};
if (typeof dimensions[i] === 'string') {
dimensionName = dimensions[i];
dimensionInfo = {
name: dimensionName,
coordDim: dimensionName,
coordDimIndex: 0,
stackable: false,
// Type can be 'float', 'int', 'number'
// Default is number, Precision of float may not enough
type: 'number'
};
} else {
dimensionInfo = dimensions[i];
dimensionName = dimensionInfo.name;
dimensionInfo.type = dimensionInfo.type || 'number';
if (!dimensionInfo.coordDim) {
dimensionInfo.coordDim = dimensionName;
dimensionInfo.coordDimIndex = 0;
}
}
dimensionInfo.otherDims = dimensionInfo.otherDims || {};
dimensionNames.push(dimensionName);
dimensionInfos[dimensionName] = dimensionInfo;
}
/**
* @readOnly
* @type {Array.<string>}
*/
this.dimensions = dimensionNames;
/**
* Infomation of each data dimension, like data type.
* @type {Object}
*/
this._dimensionInfos = dimensionInfos;
/**
* @type {module:echarts/model/Model}
*/
this.hostModel = hostModel;
/**
* @type {module:echarts/model/Model}
*/
this.dataType;
/**
* Indices stores the indices of data subset after filtered.
* This data subset will be used in chart.
* @type {Array.<number>}
* @readOnly
*/
this.indices = [];
/**
* Data storage
* @type {Object.<key, TypedArray|Array>}
* @private
*/
this._storage = {};
/**
* @type {Array.<string>}
*/
this._nameList = [];
/**
* @type {Array.<string>}
*/
this._idList = [];
/**
* Models of data option is stored sparse for optimizing memory cost
* @type {Array.<module:echarts/model/Model>}
* @private
*/
this._optionModels = [];
/**
* @param {module:echarts/data/List}
*/
this.stackedOn = null;
/**
* Global visual properties after visual coding
* @type {Object}
* @private
*/
this._visual = {};
/**
* Globel layout properties.
* @type {Object}
* @private
*/
this._layout = {};
/**
* Item visual properties after visual coding
* @type {Array.<Object>}
* @private
*/
this._itemVisuals = [];
/**
* Item layout properties after layout
* @type {Array.<Object>}
* @private
*/
this._itemLayouts = [];
/**
* Graphic elemnents
* @type {Array.<module:zrender/Element>}
* @private
*/
this._graphicEls = [];
/**
* @type {Array.<Array|Object>}
* @private
*/
this._rawData;
/**
* @type {Object}
* @private
*/
this._extent;
};
var listProto = List.prototype;
listProto.type = 'list';
/**
* If each data item has it's own option
* @type {boolean}
*/
listProto.hasItemOption = true;
/**
* Get dimension name
* @param {string|number} dim
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
* @return {string} Concrete dim name.
*/
listProto.getDimension = function (dim) {
if (!isNaN(dim)) {
dim = this.dimensions[dim] || dim;
}
return dim;
};
/**
* Get type and stackable info of particular dimension
* @param {string|number} dim
* Dimension can be concrete names like x, y, z, lng, lat, angle, radius
* Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
*/
listProto.getDimensionInfo = function (dim) {
return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);
};
/**
* Initialize from data
* @param {Array.<Object|number|Array>} data
* @param {Array.<string>} [nameList]
* @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
*/
listProto.initData = function (data, nameList, dimValueGetter) {
data = data || [];
var isDataArray = zrUtil.isArray(data);
if (isDataArray) {
data = new DefaultDataProvider(data);
}
this._rawData = data; // Clear
var storage = this._storage = {};
var indices = this.indices = [];
var dimensions = this.dimensions;
var dimensionInfoMap = this._dimensionInfos;
var size = data.count();
var idList = [];
var nameRepeatCount = {};
var nameDimIdx;
nameList = nameList || []; // Init storage
for (var i = 0; i < dimensions.length; i++) {
var dimInfo = dimensionInfoMap[dimensions[i]];
dimInfo.otherDims.itemName === 0 && (nameDimIdx = i);
var DataCtor = dataCtors[dimInfo.type];
storage[dimensions[i]] = new DataCtor(size);
}
var self = this;
if (!dimValueGetter) {
self.hasItemOption = false;
} // Default dim value getter
dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
var value = modelUtil.getDataItemValue(dataItem); // If any dataItem is like { value: 10 }
if (modelUtil.isDataItemOption(dataItem)) {
self.hasItemOption = true;
}
return modelUtil.converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
: value, dimensionInfoMap[dimName]);
};
for (var i = 0; i < size; i++) {
// NOTICE: Try not to write things into dataItem
var dataItem = data.getItem(i); // Each data item is value
// [1, 2]
// 2
// Bar chart, line chart which uses category axis
// only gives the 'y' value. 'x' value is the indices of cateogry
// Use a tempValue to normalize the value to be a (x, y) value
// Store the data by dimensions
for (var k = 0; k < dimensions.length; k++) {
var dim = dimensions[k];
var dimStorage = storage[dim]; // PENDING NULL is empty or zero
dimStorage[i] = dimValueGetter(dataItem, dim, i, k);
}
indices.push(i);
} // Use the name in option and create id
for (var i = 0; i < size; i++) {
var dataItem = data.getItem(i);
if (!nameList[i] && dataItem) {
if (dataItem.name != null) {
nameList[i] = dataItem.name;
} else if (nameDimIdx != null) {
nameList[i] = storage[dimensions[nameDimIdx]][i];
}
}
var name = nameList[i] || ''; // Try using the id in option
var id = dataItem && dataItem.id;
if (!id && name) {
// Use name as id and add counter to avoid same name
nameRepeatCount[name] = nameRepeatCount[name] || 0;
id = name;
if (nameRepeatCount[name] > 0) {
id += '__ec__' + nameRepeatCount[name];
}
nameRepeatCount[name]++;
}
id && (idList[i] = id);
}
this._nameList = nameList;
this._idList = idList;
};
/**
* @return {number}
*/
listProto.count = function () {
return this.indices.length;
};
/**
* Get value. Return NaN if idx is out of range.
* @param {string} dim Dim must be concrete name.
* @param {number} idx
* @param {boolean} stack
* @return {number}
*/
listProto.get = function (dim, idx, stack) {
var storage = this._storage;
var dataIndex = this.indices[idx]; // If value not exists
if (dataIndex == null || !storage[dim]) {
return NaN;
}
var value = storage[dim][dataIndex]; // FIXME ordinal data type is not stackable
if (stack) {
var dimensionInfo = this._dimensionInfos[dim];
if (dimensionInfo && dimensionInfo.stackable) {
var stackedOn = this.stackedOn;
while (stackedOn) {
// Get no stacked data of stacked on
var stackedValue = stackedOn.get(dim, idx); // Considering positive stack, negative stack and empty data
if (value >= 0 && stackedValue > 0 || // Positive stack
value <= 0 && stackedValue < 0 // Negative stack
) {
value += stackedValue;
}
stackedOn = stackedOn.stackedOn;
}
}
}
return value;
};
/**
* Get value for multi dimensions.
* @param {Array.<string>} [dimensions] If ignored, using all dimensions.
* @param {number} idx
* @param {boolean} stack
* @return {number}
*/
listProto.getValues = function (dimensions, idx, stack) {
var values = [];
if (!zrUtil.isArray(dimensions)) {
stack = idx;
idx = dimensions;
dimensions = this.dimensions;
}
for (var i = 0, len = dimensions.length; i < len; i++) {
values.push(this.get(dimensions[i], idx, stack));
}
return values;
};
/**
* If value is NaN. Inlcuding '-'
* @param {string} dim
* @param {number} idx
* @return {number}
*/
listProto.hasValue = function (idx) {
var dimensions = this.dimensions;
var dimensionInfos = this._dimensionInfos;
for (var i = 0, len = dimensions.length; i < len; i++) {
if ( // Ordinal type can be string or number
dimensionInfos[dimensions[i]].type !== 'ordinal' && isNaN(this.get(dimensions[i], idx))) {
return false;
}
}
return true;
};
/**
* Get extent of data in one dimension
* @param {string} dim
* @param {boolean} stack
* @param {Function} filter
*/
listProto.getDataExtent = function (dim, stack, filter) {
dim = this.getDimension(dim);
var dimData = this._storage[dim];
var dimInfo = this.getDimensionInfo(dim);
stack = dimInfo && dimInfo.stackable && stack;
var dimExtent = (this._extent || (this._extent = {}))[dim + !!stack];
var value;
if (dimExtent) {
return dimExtent;
} // var dimInfo = this._dimensionInfos[dim];
if (dimData) {
var min = Infinity;
var max = -Infinity; // var isOrdinal = dimInfo.type === 'ordinal';
for (var i = 0, len = this.count(); i < len; i++) {
value = this.get(dim, i, stack); // FIXME
// if (isOrdinal && typeof value === 'string') {
// value = zrUtil.indexOf(dimData, value);
// }
if (!filter || filter(value, dim, i)) {
value < min && (min = value);
value > max && (max = value);
}
}
return this._extent[dim + !!stack] = [min, max];
} else {
return [Infinity, -Infinity];
}
};
/**
* Get sum of data in one dimension
* @param {string} dim
* @param {boolean} stack
*/
listProto.getSum = function (dim, stack) {
var dimData = this._storage[dim];
var sum = 0;
if (dimData) {
for (var i = 0, len = this.count(); i < len; i++) {
var value = this.get(dim, i, stack);
if (!isNaN(value)) {
sum += value;
}
}
}
return sum;
};
/**
* Retreive the index with given value
* @param {number} idx
* @param {number} value
* @return {number}
*/
// FIXME Precision of float value
listProto.indexOf = function (dim, value) {
var storage = this._storage;
var dimData = storage[dim];
var indices = this.indices;
if (dimData) {
for (var i = 0, len = indices.length; i < len; i++) {
var rawIndex = indices[i];
if (dimData[rawIndex] === value) {
return i;
}
}
}
return -1;
};
/**
* Retreive the index with given name
* @param {number} idx
* @param {number} name
* @return {number}
*/
listProto.indexOfName = function (name) {
var indices = this.indices;
var nameList = this._nameList;
for (var i = 0, len = indices.length; i < len; i++) {
var rawIndex = indices[i];
if (nameList[rawIndex] === name) {
return i;
}
}
return -1;
};
/**
* Retreive the index with given raw data index
* @param {number} idx
* @param {number} name
* @return {number}
*/
listProto.indexOfRawIndex = function (rawIndex) {
// Indices are ascending
var indices = this.indices; // If rawIndex === dataIndex
var rawDataIndex = indices[rawIndex];
if (rawDataIndex != null && rawDataIndex === rawIndex) {
return rawIndex;
}
var left = 0;
var right = indices.length - 1;
while (left <= right) {
var mid = (left + right) / 2 | 0;
if (indices[mid] < rawIndex) {
left = mid + 1;
} else if (indices[mid] > rawIndex) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
};
/**
* Retreive the index of nearest value
* @param {string} dim
* @param {number} value
* @param {boolean} stack If given value is after stacked
* @param {number} [maxDistance=Infinity]
* @return {Array.<number>} Considere multiple points has the same value.
*/
listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
var storage = this._storage;
var dimData = storage[dim];
var nearestIndices = [];
if (!dimData) {
return nearestIndices;
}
if (maxDistance == null) {
maxDistance = Infinity;
}
var minDist = Number.MAX_VALUE;
var minDiff = -1;
for (var i = 0, len = this.count(); i < len; i++) {
var diff = value - this.get(dim, i, stack);
var dist = Math.abs(diff);
if (diff <= maxDistance && dist <= minDist) {
// For the case of two data are same on xAxis, which has sequence data.
// Show the nearest index
// https://github.com/ecomfe/echarts/issues/2869
if (dist < minDist || diff >= 0 && minDiff < 0) {
minDist = dist;
minDiff = diff;
nearestIndices.length = 0;
}
nearestIndices.push(i);
}
}
return nearestIndices;
};
/**
* Get raw data index
* @param {number} idx
* @return {number}
*/
listProto.getRawIndex = function (idx) {
var rawIdx = this.indices[idx];
return rawIdx == null ? -1 : rawIdx;
};
/**
* Get raw data item
* @param {number} idx
* @return {number}
*/
listProto.getRawDataItem = function (idx) {
return this._rawData.getItem(this.getRawIndex(idx));
};
/**
* @param {number} idx
* @param {boolean} [notDefaultIdx=false]
* @return {string}
*/
listProto.getName = function (idx) {
return this._nameList[this.indices[idx]] || '';
};
/**
* @param {number} idx
* @param {boolean} [notDefaultIdx=false]
* @return {string}
*/
listProto.getId = function (idx) {
return this._idList[this.indices[idx]] || this.getRawIndex(idx) + '';
};
function normalizeDimensions(dimensions) {
if (!zrUtil.isArray(dimensions)) {
dimensions = [dimensions];
}
return dimensions;
}
/**
* Data iteration
* @param {string|Array.<string>}
* @param {Function} cb
* @param {boolean} [stack=false]
* @param {*} [context=this]
*
* @example
* list.each('x', function (x, idx) {});
* list.each(['x', 'y'], function (x, y, idx) {});
* list.each(function (idx) {})
*/
listProto.each = function (dims, cb, stack, context) {
if (typeof dims === 'function') {
context = stack;
stack = cb;
cb = dims;
dims = [];
}
dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
var value = [];
var dimSize = dims.length;
var indices = this.indices;
context = context || this;
for (var i = 0; i < indices.length; i++) {
// Simple optimization
switch (dimSize) {
case 0:
cb.call(context, i);
break;
case 1:
cb.call(context, this.get(dims[0], i, stack), i);
break;
case 2:
cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
break;
default:
for (var k = 0; k < dimSize; k++) {
value[k] = this.get(dims[k], i, stack);
} // Index
value[k] = i;
cb.apply(context, value);
}
}
};
/**
* Data filter
* @param {string|Array.<string>}
* @param {Function} cb
* @param {boolean} [stack=false]
* @param {*} [context=this]
*/
listProto.filterSelf = function (dimensions, cb, stack, context) {
if (typeof dimensions === 'function') {
context = stack;
stack = cb;
cb = dimensions;
dimensions = [];
}
dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
var newIndices = [];
var value = [];
var dimSize = dimensions.length;
var indices = this.indices;
context = context || this;
for (var i = 0; i < indices.length; i++) {
var keep; // Simple optimization
if (!dimSize) {
keep = cb.call(context, i);
} else if (dimSize === 1) {
keep = cb.call(context, this.get(dimensions[0], i, stack), i);
} else {
for (var k = 0; k < dimSize; k++) {
value[k] = this.get(dimensions[k], i, stack);
}
value[k] = i;
keep = cb.apply(context, value);
}
if (keep) {
newIndices.push(indices[i]);
}
}
this.indices = newIndices; // Reset data extent
this._extent = {};
return this;
};
/**
* Data mapping to a plain array
* @param {string|Array.<string>} [dimensions]
* @param {Function} cb
* @param {boolean} [stack=false]
* @param {*} [context=this]
* @return {Array}
*/
listProto.mapArray = function (dimensions, cb, stack, context) {
if (typeof dimensions === 'function') {
context = stack;
stack = cb;
cb = dimensions;
dimensions = [];
}
var result = [];
this.each(dimensions, function () {
result.push(cb && cb.apply(this, arguments));
}, stack, context);
return result;
};
function cloneListForMapAndSample(original, excludeDimensions) {
var allDimensions = original.dimensions;
var list = new List(zrUtil.map(allDimensions, original.getDimensionInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked
transferProperties(list, original);
var storage = list._storage = {};
var originalStorage = original._storage; // Init storage
for (var i = 0; i < allDimensions.length; i++) {
var dim = allDimensions[i];
var dimStore = originalStorage[dim];
if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
storage[dim] = new dimStore.constructor(originalStorage[dim].length);
} else {
// Direct reference for other dimensions
storage[dim] = originalStorage[dim];
}
}
return list;
}
/**
* Data mapping to a new List with given dimensions
* @param {string|Array.<string>} dimensions
* @param {Function} cb
* @param {boolean} [stack=false]
* @param {*} [context=this]
* @return {Array}
*/
listProto.map = function (dimensions, cb, stack, context) {
dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
var list = cloneListForMapAndSample(this, dimensions); // Following properties are all immutable.
// So we can reference to the same value
var indices = list.indices = this.indices;
var storage = list._storage;
var tmpRetValue = [];
this.each(dimensions, function () {
var idx = arguments[arguments.length - 1];
var retValue = cb && cb.apply(this, arguments);
if (retValue != null) {
// a number
if (typeof retValue === 'number') {
tmpRetValue[0] = retValue;
retValue = tmpRetValue;
}
for (var i = 0; i < retValue.length; i++) {
var dim = dimensions[i];
var dimStore = storage[dim];
var rawIdx = indices[idx];
if (dimStore) {
dimStore[rawIdx] = retValue[i];
}
}
}
}, stack, context);
return list;
};
/**
* Large data down sampling on given dimension
* @param {string} dimension
* @param {number} rate
* @param {Function} sampleValue
* @param {Function} sampleIndex Sample index for name and id
*/
listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
var list = cloneListForMapAndSample(this, [dimension]);
var storage = this._storage;
var targetStorage = list._storage;
var originalIndices = this.indices;
var indices = list.indices = [];
var frameValues = [];
var frameIndices = [];
var frameSize = Math.floor(1 / rate);
var dimStore = targetStorage[dimension];
var len = this.count(); // Copy data from original data
for (var i = 0; i < storage[dimension].length; i++) {
targetStorage[dimension][i] = storage[dimension][i];
}
for (var i = 0; i < len; i += frameSize) {
// Last frame
if (frameSize > len - i) {
frameSize = len - i;
frameValues.length = frameSize;
}
for (var k = 0; k < frameSize; k++) {
var idx = originalIndices[i + k];
frameValues[k] = dimStore[idx];
frameIndices[k] = idx;
}
var value = sampleValue(frameValues);
var idx = frameIndices[sampleIndex(frameValues, value) || 0]; // Only write value on the filtered data
dimStore[idx] = value;
indices.push(idx);
}
return list;
};
/**
* Get model of one data item.
*
* @param {number} idx
*/
// FIXME Model proxy ?
listProto.getItemModel = function (idx) {
var hostModel = this.hostModel;
idx = this.indices[idx];
return new Model(this._rawData.getItem(idx), hostModel, hostModel && hostModel.ecModel);
};
/**
* Create a data differ
* @param {module:echarts/data/List} otherList
* @return {module:echarts/data/DataDiffer}
*/
listProto.diff = function (otherList) {
var idList = this._idList;
var otherIdList = otherList && otherList._idList;
var val; // Use prefix to avoid index to be the same as otherIdList[idx],
// which will cause weird udpate animation.
var prefix = 'e\0\0';
return new DataDiffer(otherList ? otherList.indices : [], this.indices, function (idx) {
return (val = otherIdList[idx]) != null ? val : prefix + idx;
}, function (idx) {
return (val = idList[idx]) != null ? val : prefix + idx;
});
};
/**
* Get visual property.
* @param {string} key
*/
listProto.getVisual = function (key) {
var visual = this._visual;
return visual && visual[key];
};
/**
* Set visual property
* @param {string|Object} key
* @param {*} [value]
*
* @example
* setVisual('color', color);
* setVisual({
* 'color': color
* });
*/
listProto.setVisual = function (key, val) {
if (isObject(key)) {
for (var name in key) {
if (key.hasOwnProperty(name)) {
this.setVisual(name, key[name]);
}
}
return;
}
this._visual = this._visual || {};
this._visual[key] = val;
};
/**
* Set layout property.
* @param {string|Object} key
* @param {*} [val]
*/
listProto.setLayout = function (key, val) {
if (isObject(key)) {
for (var name in key) {
if (key.hasOwnProperty(name)) {
this.setLayout(name, key[name]);
}
}
return;
}
this._layout[key] = val;
};
/**
* Get layout property.
* @param {string} key.
* @return {*}
*/
listProto.getLayout = function (key) {
return this._layout[key];
};
/**
* Get layout of single data item
* @param {number} idx
*/
listProto.getItemLayout = function (idx) {
return this._itemLayouts[idx];
};
/**
* Set layout of single data item
* @param {number} idx
* @param {Object} layout
* @param {boolean=} [merge=false]
*/
listProto.setItemLayout = function (idx, layout, merge) {
this._itemLayouts[idx] = merge ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) : layout;
};
/**
* Clear all layout of single data item
*/
listProto.clearItemLayouts = function () {
this._itemLayouts.length = 0;
};
/**
* Get visual property of single data item
* @param {number} idx
* @param {string} key
* @param {boolean} [ignoreParent=false]
*/
listProto.getItemVisual = function (idx, key, ignoreParent) {
var itemVisual = this._itemVisuals[idx];
var val = itemVisual && itemVisual[key];
if (val == null && !ignoreParent) {
// Use global visual property
return this.getVisual(key);
}
return val;
};
/**
* Set visual property of single data item
*
* @param {number} idx
* @param {string|Object} key
* @param {*} [value]
*
* @example
* setItemVisual(0, 'color', color);
* setItemVisual(0, {
* 'color': color
* });
*/
listProto.setItemVisual = function (idx, key, value) {
var itemVisual = this._itemVisuals[idx] || {};
this._itemVisuals[idx] = itemVisual;
if (isObject(key)) {
for (var name in key) {
if (key.hasOwnProperty(name)) {
itemVisual[name] = key[name];
}
}
return;
}
itemVisual[key] = value;
};
/**
* Clear itemVisuals and list visual.
*/
listProto.clearAllVisual = function () {
this._visual = {};
this._itemVisuals = [];
};
var setItemDataAndSeriesIndex = function (child) {
child.seriesIndex = this.seriesIndex;
child.dataIndex = this.dataIndex;
child.dataType = this.dataType;
};
/**
* Set graphic element relative to data. It can be set as null
* @param {number} idx
* @param {module:zrender/Element} [el]
*/
listProto.setItemGraphicEl = function (idx, el) {
var hostModel = this.hostModel;
if (el) {
// Add data index and series index for indexing the data by element
// Useful in tooltip
el.dataIndex = idx;
el.dataType = this.dataType;
el.seriesIndex = hostModel && hostModel.seriesIndex;
if (el.type === 'group') {
el.traverse(setItemDataAndSeriesIndex, el);
}
}
this._graphicEls[idx] = el;
};
/**
* @param {number} idx
* @return {module:zrender/Element}
*/
listProto.getItemGraphicEl = function (idx) {
return this._graphicEls[idx];
};
/**
* @param {Function} cb
* @param {*} context
*/
listProto.eachItemGraphicEl = function (cb, context) {
zrUtil.each(this._graphicEls, function (el, idx) {
if (el) {
cb && cb.call(context, el, idx);
}
});
};
/**
* Shallow clone a new list except visual and layout properties, and graph elements.
* New list only change the indices.
*/
listProto.cloneShallow = function () {
var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
var list = new List(dimensionInfoList, this.hostModel); // FIXME
list._storage = this._storage;
transferProperties(list, this); // Clone will not change the data extent and indices
list.indices = this.indices.slice();
if (this._extent) {
list._extent = zrUtil.extend({}, this._extent);
}
return list;
};
/**
* Wrap some method to add more feature
* @param {string} methodName
* @param {Function} injectFunction
*/
listProto.wrapMethod = function (methodName, injectFunction) {
var originalMethod = this[methodName];
if (typeof originalMethod !== 'function') {
return;
}
this.__wrappedMethods = this.__wrappedMethods || [];
this.__wrappedMethods.push(methodName);
this[methodName] = function () {
var res = originalMethod.apply(this, arguments);
return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
};
}; // Methods that create a new list based on this list should be listed here.
// Notice that those method should `RETURN` the new list.
listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; // Methods that change indices of this list should be listed here.
listProto.CHANGABLE_METHODS = ['filterSelf'];
export default List;