378 lines
11 KiB
JavaScript
378 lines
11 KiB
JavaScript
|
|
/*
|
|
* 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 _util = require("zrender/lib/core/util");
|
|
|
|
var isTypedArray = _util.isTypedArray;
|
|
var extend = _util.extend;
|
|
var assert = _util.assert;
|
|
var each = _util.each;
|
|
var isObject = _util.isObject;
|
|
|
|
var _model = require("../../util/model");
|
|
|
|
var getDataItemValue = _model.getDataItemValue;
|
|
var isDataItemOption = _model.isDataItemOption;
|
|
|
|
var _number = require("../../util/number");
|
|
|
|
var parseDate = _number.parseDate;
|
|
|
|
var Source = require("../Source");
|
|
|
|
var _sourceType = require("./sourceType");
|
|
|
|
var SOURCE_FORMAT_TYPED_ARRAY = _sourceType.SOURCE_FORMAT_TYPED_ARRAY;
|
|
var SOURCE_FORMAT_ARRAY_ROWS = _sourceType.SOURCE_FORMAT_ARRAY_ROWS;
|
|
var SOURCE_FORMAT_ORIGINAL = _sourceType.SOURCE_FORMAT_ORIGINAL;
|
|
var SOURCE_FORMAT_OBJECT_ROWS = _sourceType.SOURCE_FORMAT_OBJECT_ROWS;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
// TODO
|
|
// ??? refactor? check the outer usage of data provider.
|
|
// merge with defaultDimValueGetter?
|
|
|
|
/**
|
|
* If normal array used, mutable chunk size is supported.
|
|
* If typed array used, chunk size must be fixed.
|
|
*/
|
|
function DefaultDataProvider(source, dimSize) {
|
|
if (!Source.isInstance(source)) {
|
|
source = Source.seriesDataToSource(source);
|
|
}
|
|
|
|
this._source = source;
|
|
var data = this._data = source.data;
|
|
var sourceFormat = source.sourceFormat; // Typed array. TODO IE10+?
|
|
|
|
if (sourceFormat === SOURCE_FORMAT_TYPED_ARRAY) {
|
|
this._offset = 0;
|
|
this._dimSize = dimSize;
|
|
this._data = data;
|
|
}
|
|
|
|
var methods = providerMethods[sourceFormat === SOURCE_FORMAT_ARRAY_ROWS ? sourceFormat + '_' + source.seriesLayoutBy : sourceFormat];
|
|
extend(this, methods);
|
|
}
|
|
|
|
var providerProto = DefaultDataProvider.prototype; // If data is pure without style configuration
|
|
|
|
providerProto.pure = false; // If data is persistent and will not be released after use.
|
|
|
|
providerProto.persistent = true; // ???! FIXME legacy data provider do not has method getSource
|
|
|
|
providerProto.getSource = function () {
|
|
return this._source;
|
|
};
|
|
|
|
var providerMethods = {
|
|
'arrayRows_column': {
|
|
pure: true,
|
|
count: function () {
|
|
return Math.max(0, this._data.length - this._source.startIndex);
|
|
},
|
|
getItem: function (idx) {
|
|
return this._data[idx + this._source.startIndex];
|
|
},
|
|
appendData: appendDataSimply
|
|
},
|
|
'arrayRows_row': {
|
|
pure: true,
|
|
count: function () {
|
|
var row = this._data[0];
|
|
return row ? Math.max(0, row.length - this._source.startIndex) : 0;
|
|
},
|
|
getItem: function (idx) {
|
|
idx += this._source.startIndex;
|
|
var item = [];
|
|
var data = this._data;
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
var row = data[i];
|
|
item.push(row ? row[idx] : null);
|
|
}
|
|
|
|
return item;
|
|
},
|
|
appendData: function () {
|
|
throw new Error('Do not support appendData when set seriesLayoutBy: "row".');
|
|
}
|
|
},
|
|
'objectRows': {
|
|
pure: true,
|
|
count: countSimply,
|
|
getItem: getItemSimply,
|
|
appendData: appendDataSimply
|
|
},
|
|
'keyedColumns': {
|
|
pure: true,
|
|
count: function () {
|
|
var dimName = this._source.dimensionsDefine[0].name;
|
|
var col = this._data[dimName];
|
|
return col ? col.length : 0;
|
|
},
|
|
getItem: function (idx) {
|
|
var item = [];
|
|
var dims = this._source.dimensionsDefine;
|
|
|
|
for (var i = 0; i < dims.length; i++) {
|
|
var col = this._data[dims[i].name];
|
|
item.push(col ? col[idx] : null);
|
|
}
|
|
|
|
return item;
|
|
},
|
|
appendData: function (newData) {
|
|
var data = this._data;
|
|
each(newData, function (newCol, key) {
|
|
var oldCol = data[key] || (data[key] = []);
|
|
|
|
for (var i = 0; i < (newCol || []).length; i++) {
|
|
oldCol.push(newCol[i]);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
'original': {
|
|
count: countSimply,
|
|
getItem: getItemSimply,
|
|
appendData: appendDataSimply
|
|
},
|
|
'typedArray': {
|
|
persistent: false,
|
|
pure: true,
|
|
count: function () {
|
|
return this._data ? this._data.length / this._dimSize : 0;
|
|
},
|
|
getItem: function (idx, out) {
|
|
idx = idx - this._offset;
|
|
out = out || [];
|
|
var offset = this._dimSize * idx;
|
|
|
|
for (var i = 0; i < this._dimSize; i++) {
|
|
out[i] = this._data[offset + i];
|
|
}
|
|
|
|
return out;
|
|
},
|
|
appendData: function (newData) {
|
|
this._data = newData;
|
|
},
|
|
// Clean self if data is already used.
|
|
clean: function () {
|
|
// PENDING
|
|
this._offset += this.count();
|
|
this._data = null;
|
|
}
|
|
}
|
|
};
|
|
|
|
function countSimply() {
|
|
return this._data.length;
|
|
}
|
|
|
|
function getItemSimply(idx) {
|
|
return this._data[idx];
|
|
}
|
|
|
|
function appendDataSimply(newData) {
|
|
for (var i = 0; i < newData.length; i++) {
|
|
this._data.push(newData[i]);
|
|
}
|
|
}
|
|
|
|
var rawValueGetters = {
|
|
arrayRows: getRawValueSimply,
|
|
objectRows: function (dataItem, dataIndex, dimIndex, dimName) {
|
|
return dimIndex != null ? dataItem[dimName] : dataItem;
|
|
},
|
|
keyedColumns: getRawValueSimply,
|
|
original: function (dataItem, dataIndex, dimIndex, dimName) {
|
|
// FIXME
|
|
// In some case (markpoint in geo (geo-map.html)), dataItem
|
|
// is {coord: [...]}
|
|
var value = getDataItemValue(dataItem);
|
|
return dimIndex == null || !(value instanceof Array) ? value : value[dimIndex];
|
|
},
|
|
typedArray: getRawValueSimply
|
|
};
|
|
|
|
function getRawValueSimply(dataItem, dataIndex, dimIndex, dimName) {
|
|
return dimIndex != null ? dataItem[dimIndex] : dataItem;
|
|
}
|
|
|
|
var defaultDimValueGetters = {
|
|
arrayRows: getDimValueSimply,
|
|
objectRows: function (dataItem, dimName, dataIndex, dimIndex) {
|
|
return converDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
|
|
},
|
|
keyedColumns: getDimValueSimply,
|
|
original: function (dataItem, dimName, dataIndex, dimIndex) {
|
|
// Performance sensitive, do not use modelUtil.getDataItemValue.
|
|
// If dataItem is an plain object with no value field, the var `value`
|
|
// will be assigned with the object, but it will be tread correctly
|
|
// in the `convertDataValue`.
|
|
var value = dataItem && (dataItem.value == null ? dataItem : dataItem.value); // If any dataItem is like { value: 10 }
|
|
|
|
if (!this._rawData.pure && isDataItemOption(dataItem)) {
|
|
this.hasItemOption = true;
|
|
}
|
|
|
|
return converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
|
|
: value, this._dimensionInfos[dimName]);
|
|
},
|
|
typedArray: function (dataItem, dimName, dataIndex, dimIndex) {
|
|
return dataItem[dimIndex];
|
|
}
|
|
};
|
|
|
|
function getDimValueSimply(dataItem, dimName, dataIndex, dimIndex) {
|
|
return converDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
|
|
}
|
|
/**
|
|
* This helper method convert value in data.
|
|
* @param {string|number|Date} value
|
|
* @param {Object|string} [dimInfo] If string (like 'x'), dimType defaults 'number'.
|
|
* If "dimInfo.ordinalParseAndSave", ordinal value can be parsed.
|
|
*/
|
|
|
|
|
|
function converDataValue(value, dimInfo) {
|
|
// Performance sensitive.
|
|
var dimType = dimInfo && dimInfo.type;
|
|
|
|
if (dimType === 'ordinal') {
|
|
// If given value is a category string
|
|
var ordinalMeta = dimInfo && dimInfo.ordinalMeta;
|
|
return ordinalMeta ? ordinalMeta.parseAndCollect(value) : value;
|
|
}
|
|
|
|
if (dimType === 'time' // spead up when using timestamp
|
|
&& typeof value !== 'number' && value != null && value !== '-') {
|
|
value = +parseDate(value);
|
|
} // dimType defaults 'number'.
|
|
// If dimType is not ordinal and value is null or undefined or NaN or '-',
|
|
// parse to NaN.
|
|
|
|
|
|
return value == null || value === '' ? NaN // If string (like '-'), using '+' parse to NaN
|
|
// If object, also parse to NaN
|
|
: +value;
|
|
} // ??? FIXME can these logic be more neat: getRawValue, getRawDataItem,
|
|
// Consider persistent.
|
|
// Caution: why use raw value to display on label or tooltip?
|
|
// A reason is to avoid format. For example time value we do not know
|
|
// how to format is expected. More over, if stack is used, calculated
|
|
// value may be 0.91000000001, which have brings trouble to display.
|
|
// TODO: consider how to treat null/undefined/NaN when display?
|
|
|
|
/**
|
|
* @param {module:echarts/data/List} data
|
|
* @param {number} dataIndex
|
|
* @param {string|number} [dim] dimName or dimIndex
|
|
* @return {Array.<number>|string|number} can be null/undefined.
|
|
*/
|
|
|
|
|
|
function retrieveRawValue(data, dataIndex, dim) {
|
|
if (!data) {
|
|
return;
|
|
} // Consider data may be not persistent.
|
|
|
|
|
|
var dataItem = data.getRawDataItem(dataIndex);
|
|
|
|
if (dataItem == null) {
|
|
return;
|
|
}
|
|
|
|
var sourceFormat = data.getProvider().getSource().sourceFormat;
|
|
var dimName;
|
|
var dimIndex;
|
|
var dimInfo = data.getDimensionInfo(dim);
|
|
|
|
if (dimInfo) {
|
|
dimName = dimInfo.name;
|
|
dimIndex = dimInfo.index;
|
|
}
|
|
|
|
return rawValueGetters[sourceFormat](dataItem, dataIndex, dimIndex, dimName);
|
|
}
|
|
/**
|
|
* Compatible with some cases (in pie, map) like:
|
|
* data: [{name: 'xx', value: 5, selected: true}, ...]
|
|
* where only sourceFormat is 'original' and 'objectRows' supported.
|
|
*
|
|
* ??? TODO
|
|
* Supported detail options in data item when using 'arrayRows'.
|
|
*
|
|
* @param {module:echarts/data/List} data
|
|
* @param {number} dataIndex
|
|
* @param {string} attr like 'selected'
|
|
*/
|
|
|
|
|
|
function retrieveRawAttr(data, dataIndex, attr) {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
|
|
var sourceFormat = data.getProvider().getSource().sourceFormat;
|
|
|
|
if (sourceFormat !== SOURCE_FORMAT_ORIGINAL && sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS) {
|
|
return;
|
|
}
|
|
|
|
var dataItem = data.getRawDataItem(dataIndex);
|
|
|
|
if (sourceFormat === SOURCE_FORMAT_ORIGINAL && !isObject(dataItem)) {
|
|
dataItem = null;
|
|
}
|
|
|
|
if (dataItem) {
|
|
return dataItem[attr];
|
|
}
|
|
}
|
|
|
|
exports.DefaultDataProvider = DefaultDataProvider;
|
|
exports.defaultDimValueGetters = defaultDimValueGetters;
|
|
exports.retrieveRawValue = retrieveRawValue;
|
|
exports.retrieveRawAttr = retrieveRawAttr; |