You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							378 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							378 lines
						
					
					
						
							11 KiB
						
					
					
				
 | 
						|
/* | 
						|
* 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; |