/**
* Link lists and struct (graph or tree)
*/
define(function (require) {
var zrUtil = require('zrender/core/util');
var each = zrUtil.each;
var DATAS = '\0__link_datas';
var MAIN_DATA = '\0__link_mainData';
// Caution:
// In most case, either list or its shallow clones (see list.cloneShallow)
// is active in echarts process. So considering heap memory consumption,
// we do not clone tree or graph, but share them among list and its shallow clones.
// But in some rare case, we have to keep old list (like do animation in chart). So
// please take care that both the old list and the new list share the same tree/graph.
/**
* @param {Object} opt
* @param {module:echarts/data/List} opt.mainData
* @param {Object} [opt.struct] For example, instance of Graph or Tree.
* @param {string} [opt.structAttr] designation: list[structAttr] = struct;
* @param {Object} [opt.datas] {dataType: data},
* like: {node: nodeList, edge: edgeList}.
* Should contain mainData.
* @param {Object} [opt.datasAttr] {dataType: attr},
* designation: struct[datasAttr[dataType]] = list;
*/
function linkList(opt) {
var mainData = opt.mainData;
var datas = opt.datas;
if (!datas) {
datas = {main: mainData};
opt.datasAttr = {main: 'data'};
}
opt.datas = opt.mainData = null;
linkAll(mainData, datas, opt);
// Porxy data original methods.
each(datas, function (data) {
each(mainData.TRANSFERABLE_METHODS, function (methodName) {
data.wrapMethod(methodName, zrUtil.curry(transferInjection, opt));
});
});
// Beyond transfer, additional features should be added to `cloneShallow`.
mainData.wrapMethod('cloneShallow', zrUtil.curry(cloneShallowInjection, opt));
// Only mainData trigger change, because struct.update may trigger
// another changable methods, which may bring about dead lock.
each(mainData.CHANGABLE_METHODS, function (methodName) {
mainData.wrapMethod(methodName, zrUtil.curry(changeInjection, opt));
});
// Make sure datas contains mainData.
zrUtil.assert(datas[mainData.dataType] === mainData);
}
function transferInjection(opt, res) {
if (isMainData(this)) {
// Transfer datas to new main data.
var datas = zrUtil.extend({}, this[DATAS]);
datas[this.dataType] = res;
linkAll(res, datas, opt);
}
else {
// Modify the reference in main data to point newData.
linkSingle(res, this.dataType, this[MAIN_DATA], opt);
}
return res;
}
function changeInjection(opt, res) {
opt.struct && opt.struct.update(this);
return res;
}
function cloneShallowInjection(opt, res) {
// cloneShallow, which brings about some fragilities, may be inappropriate
// to be exposed as an API. So for implementation simplicity we can make
// the restriction that cloneShallow of not-mainData should not be invoked
// outside, but only be invoked here.
each(res[DATAS], function (data, dataType) {
data !== res && linkSingle(data.cloneShallow(), dataType, res, opt);
});
return res;
}
/**
* Supplement method to List.
*
* @public
* @param {string} [dataType] If not specified, return mainData.
* @return {module:echarts/data/List}
*/
function getLinkedData(dataType) {
var mainData = this[MAIN_DATA];
return (dataType == null || mainData == null)
? mainData
: mainData[DATAS][dataType];
}
function isMainData(data) {
return data[MAIN_DATA] === data;
}
function linkAll(mainData, datas, opt) {
mainData[DATAS] = {};
each(datas, function (data, dataType) {
linkSingle(data, dataType, mainData, opt);
});
}
function linkSingle(data, dataType, mainData, opt) {
mainData[DATAS][dataType] = data;
data[MAIN_DATA] = mainData;
data.dataType = dataType;
if (opt.struct) {
data[opt.structAttr] = opt.struct;
opt.struct[opt.datasAttr[dataType]] = data;
}
// Supplement method.
data.getLinkedData = getLinkedData;
}
return linkList;
});
|