List.js 25.7 KB
Newer Older
L
lang 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/**
 * List for data storage
 * @module echarts/data/List
 */
define(function (require) {

    var UNDEFINED = 'undefined';
    var global = window;
    var Float32Array = typeof global.Float32Array === UNDEFINED
        ? Array : global.Float32Array;
    var Int32Array = typeof global.Int32Array === UNDEFINED
        ? Array : global.Int32Array;

    var dataCtors = {
        float: Float32Array,
        int: Int32Array,
L
lang 已提交
17 18
        // Ordinal data type can be string or int
        ordinal: Array,
L
lang 已提交
19
        'number': Array
L
lang 已提交
20
    };
L
lang 已提交
21

L
lang 已提交
22
    var Model = require('../model/Model');
L
lang 已提交
23
    var DataDiffer = require('./DataDiffer');
L
lang 已提交
24

L
lang 已提交
25 26
    var zrUtil = require('zrender/core/util');
    var isObject = zrUtil.isObject;
L
lang 已提交
27

L
lang 已提交
28
    var IMMUTABLE_PROPERTIES = [
L
lang 已提交
29 30
        'stackedOn', '_nameList', '_idList',
        '_rawData', '_valueProp', '_optionModels'
L
lang 已提交
31 32 33 34 35
    ];

    var transferImmuProperties = function (a, b) {
        zrUtil.each(IMMUTABLE_PROPERTIES, function (propName) {
            a[propName] = b[propName];
L
lang 已提交
36 37
        });
    };
L
lang 已提交
38

L
lang 已提交
39
    /**
L
lang 已提交
40 41
     * @constructor
     * @alias module:echarts/data/List
L
lang 已提交
42 43
     *
     * @param {Array.<string>} dimensions
L
lang 已提交
44
     * @param {module:echarts/model/Model} hostModel
L
lang 已提交
45
     */
L
lang 已提交
46
    var List = function (dimensions, hostModel) {
L
lang 已提交
47 48 49

        dimensions = dimensions || ['x', 'y'];

L
lang 已提交
50
        var dimensionInfos = {};
L
lang 已提交
51 52 53 54 55 56 57 58
        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,
L
lang 已提交
59
                    stackable: false,
L
lang 已提交
60
                    // Type can be 'float', 'int', 'number'
L
lang 已提交
61
                    // Default is number, Precision of float may not enough
L
lang 已提交
62 63
                    type: 'number'
                };
L
lang 已提交
64
            }
L
lang 已提交
65 66 67
            else {
                dimensionInfo = dimensions[i];
                dimensionName = dimensionInfo.name;
L
lang 已提交
68
                dimensionInfo.type = dimensionInfo.type || 'number';
L
lang 已提交
69 70
            }
            dimensionNames.push(dimensionName);
L
lang 已提交
71
            dimensionInfos[dimensionName] = dimensionInfo;
L
lang 已提交
72
        }
L
lang 已提交
73 74 75 76 77
        /**
         * @readOnly
         * @type {Array.<string>}
         */
        this.dimensions = dimensionNames;
P
pah100 已提交
78

L
lang 已提交
79 80
        /**
         * Infomation of each data dimension, like data type.
L
lang 已提交
81
         * @type {Object}
L
lang 已提交
82 83
         */
        this._dimensionInfos = dimensionInfos;
L
lang 已提交
84

L
lang 已提交
85 86 87
        /**
         * @type {module:echarts/model/Model}
         */
L
lang 已提交
88
        this.hostModel = hostModel;
L
lang 已提交
89

90
        /**
L
lang 已提交
91 92 93 94
         * Indices stores the indices of data subset after filtered.
         * This data subset will be used in chart.
         * @type {Array.<number>}
         * @readOnly
95
         */
L
lang 已提交
96
        this.indices = [];
97 98

        /**
L
lang 已提交
99 100 101
         * Data storage
         * @type {Object.<key, TypedArray|Array>}
         * @private
102
         */
L
lang 已提交
103
        this._storage = {};
104

L
lang 已提交
105 106 107 108
        /**
         * @type {Array.<string>}
         */
        this._nameList = [];
L
lang 已提交
109 110 111 112
        /**
         * @type {Array.<string>}
         */
        this._idList = [];
L
lang 已提交
113
        /**
L
lang 已提交
114 115 116
         * Models of data option is stored sparse for optimizing memory cost
         * @type {Array.<module:echarts/model/Model>}
         * @private
L
lang 已提交
117
         */
L
lang 已提交
118
        this._optionModels = [];
L
lang 已提交
119 120

        /**
L
lang 已提交
121
         * @param {module:echarts/data/List}
L
lang 已提交
122
         */
L
lang 已提交
123
        this.stackedOn = null;
L
lang 已提交
124

125
        /**
L
lang 已提交
126 127 128
         * Global visual properties after visual coding
         * @type {Object}
         * @private
129
         */
L
lang 已提交
130
        this._visual = {};
L
lang 已提交
131

L
lang 已提交
132
        /**
L
lang 已提交
133 134 135
         * Item visual properties after visual coding
         * @type {Array.<Object>}
         * @private
L
lang 已提交
136
         */
L
lang 已提交
137
        this._itemVisuals = [];
L
lang 已提交
138

L
lang 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151
        /**
         * Item layout properties after layout
         * @type {Array.<Object>}
         * @private
         */
        this._itemLayouts = [];

        /**
         * Graphic elemnents
         * @type {Array.<module:zrender/Element>}
         * @private
         */
        this._graphicEls = [];
L
lang 已提交
152 153 154

        /**
         * Raw data
L
lang 已提交
155
         * @private
L
lang 已提交
156 157
         */
        this._rawData = [];
L
lang 已提交
158 159 160 161 162 163

        /**
         * @private
         */
        this._valueProp;
    };
L
lang 已提交
164 165 166 167 168

    var listProto = List.prototype;

    listProto.type = 'list';

L
lang 已提交
169 170 171 172 173 174 175
    /**
     * Get type and stackable info of particular dimension
     */
    listProto.getDimensionInfo = function (dim) {
        return this._dimensionInfos[dim];
    };

L
lang 已提交
176 177
    /**
     * Initialize from data
L
lang 已提交
178 179
     * @param {Array.<Object|number|Array>} data
     * @param {Array.<string>} [nameList]
L
lang 已提交
180
     * @param {string} [valueProp='value']
L
lang 已提交
181
     */
L
lang 已提交
182 183 184
    listProto.initData = function (data, nameList, valueProp) {

        valueProp = valueProp || 'value';
L
lang 已提交
185 186

        this._rawData = data;
L
lang 已提交
187
        this._valueProp = valueProp;
L
lang 已提交
188

L
lang 已提交
189 190 191 192 193 194 195
        // Clear
        var optionModels = this._optionModels = [];
        var storage = this._storage = {};
        var indices = this.indices = [];

        var dimensions = this.dimensions;
        var size = data.length;
L
Add pie  
lang 已提交
196
        var dimensionInfoMap = this._dimensionInfos;
L
lang 已提交
197

L
lang 已提交
198 199 200
        var idList = [];
        var nameRepeatCount = {};

L
lang 已提交
201 202
        nameList = nameList || [];

L
lang 已提交
203 204
        // Init storage
        for (var i = 0; i < dimensions.length; i++) {
L
lang 已提交
205
            var dimInfo = dimensionInfoMap[dimensions[i]];
L
lang 已提交
206 207 208 209 210 211 212 213 214
            var DataCtor = dataCtors[dimInfo.type];
            storage[dimensions[i]] = new DataCtor(size);
        }

        // Special storage of indices of option model
        // It is used for indexing the model in List#_optionModels
        var optionModelIndices = storage.$optionModelIndices = new Int32Array(size);

        var tempValue = [];
L
Add pie  
lang 已提交
215 216
        var rawValueTo1D = false;
        var value1D = dimensions.length === 1;
L
lang 已提交
217 218

        // Use the first data to indicate data type;
L
lang 已提交
219
        var isValueArray = data[0] != null && zrUtil.isArray(
L
lang 已提交
220
            data[0][valueProp] == null ? data[0] : data[0][valueProp]
L
lang 已提交
221
        );
L
lang 已提交
222 223
        for (var idx = 0; idx < data.length; idx++) {
            var value = data[idx];
L
lang 已提交
224
            // Each data item contains `value` and other option
L
lang 已提交
225 226 227
            // {
            //  value: []
            // }
L
lang 已提交
228 229
            if (data[idx] != null && data[idx].hasOwnProperty(valueProp)) {
                value = data[idx][valueProp];
L
lang 已提交
230
                var model = new Model(data[idx], this.hostModel);
L
lang 已提交
231 232 233 234 235 236 237
                var modelIdx = optionModels.length;
                optionModelIndices[idx] = modelIdx;
                optionModels.push(model);
            }
            else {
                // Reference to the undefined
                optionModelIndices[idx] = -1;
L
lang 已提交
238
            }
L
lang 已提交
239 240
            // Bar chart, line chart which uses category axis
            // only gives the 'y' value. 'x' value is the indices of cateogry
L
Add pie  
lang 已提交
241
            // Use a tempValue to normalize the value to be a (x, y) value
L
lang 已提交
242
            if (!isValueArray) {
L
Add pie  
lang 已提交
243 244 245 246 247 248 249 250 251 252 253
                if (!value1D) {
                    tempValue[0] = idx;
                    tempValue[1] = value;
                    value = tempValue;
                    rawValueTo1D = true;
                }
                // Pie chart is 1D
                else {
                    tempValue[0] = value;
                    value = tempValue;
                }
254 255
            }

L
lang 已提交
256 257 258
            // Store the data by dimensions
            for (var k = 0; k < dimensions.length; k++) {
                var dim = dimensions[k];
L
lang 已提交
259
                var dimInfo = dimensionInfoMap[dim];
L
lang 已提交
260 261 262
                var dimStorage = storage[dim];
                var dimValue = value[k];
                // PENDING NULL is empty or zero
L
lang 已提交
263 264 265 266 267 268 269 270
                switch (dimInfo.type) {
                    case 'float':
                    case 'number':
                        dimValue = +dimValue;
                        break;
                    case 'int':
                        dimValue = dimValue | 0;
                        break;
271
                }
L
lang 已提交
272
                dimStorage[idx] = dimValue;
273 274
            }

L
lang 已提交
275 276
            indices.push(idx);
        }
L
lang 已提交
277

L
lang 已提交
278
        // Use the name in option and create id
279
        for (var i = 0; i < optionModelIndices.length; i++) {
L
lang 已提交
280
            var id = '';
L
lang 已提交
281
            if (!nameList[i]) {
282 283 284
                var modelIdx = optionModelIndices[i];
                var model = optionModels[modelIdx];
                if (model && model.option) {
L
lang 已提交
285 286 287 288 289 290 291 292 293 294 295 296
                    nameList[i] = model.option.name;
                    // Try using the id in option
                    id = model.option.id;
                }
            }
            var name = nameList[i] || '';
            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];
297
                }
L
lang 已提交
298
                nameRepeatCount[name]++;
299
            }
L
lang 已提交
300
            id && (idList[i] = id);
301 302
        }

L
lang 已提交
303
        this._nameList = nameList;
L
lang 已提交
304
        this._idList = idList;
L
lang 已提交
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
    };

    /**
     * @return {number}
     */
    listProto.count = function () {
        return this.indices.length;
    };

    /**
     * Get value
     * @param {string} dim
     * @param {number} idx
     * @param {boolean} stack
     * @return {number}
     */
    listProto.get = function (dim, idx, stack) {
        var storage = this._storage;
        var dataIndex = this.indices[idx];

        var value = storage[dim] && storage[dim][dataIndex];
L
lang 已提交
326
        var dimensionInfo = this._dimensionInfos[dim];
L
lang 已提交
327
        // FIXME ordinal data type is not stackable
L
lang 已提交
328 329 330 331
        if (
            stack && this.stackedOn
            && dimensionInfo && dimensionInfo.stackable
        ) {
L
lang 已提交
332
            var stackedValue = this.stackedOn.get(dim, idx, stack);
L
lang 已提交
333 334 335 336 337
            // Considering positive stack, negative stack and empty data
            if ((value >= 0 && stackedValue > 0)  // Positive stack
                || (value <= 0 && stackedValue < 0) // Negative stack
            ) {
                value += stackedValue;
L
lang 已提交
338 339 340 341
            }
        }
        return value;
    };
L
lang 已提交
342

L
lang 已提交
343 344 345 346 347 348 349 350
    /**
     * If value is NaN. Inlcuding '-'
     * @param {string} dim
     * @param {number} idx
     * @return {number}
     */
    listProto.hasValue = function (idx) {
        var dimensions = this.dimensions;
L
lang 已提交
351
        var dimensionInfos = this._dimensionInfos;
L
lang 已提交
352
        for (var i = 0, len = dimensions.length; i < len; i++) {
L
lang 已提交
353 354 355 356 357
            if (
                // Ordinal type can be string or number
                dimensionInfos[dimensions[i]].type !== 'ordinal'
                && isNaN(this.get(dimensions[i], idx))
            ) {
L
lang 已提交
358
                return false;
L
lang 已提交
359
            }
L
lang 已提交
360 361
        }
        return true;
L
lang 已提交
362
    };
L
lang 已提交
363

L
lang 已提交
364 365 366 367 368 369 370 371 372 373
    /**
     * Get extent of data in one dimension
     * @param {string} dim
     * @param {boolean} stack
     */
    listProto.getDataExtent = function (dim, stack) {
        var dimData = this._storage[dim];
        var min = Infinity;
        var max = -Infinity;
        var value;
L
lang 已提交
374
        // var dimInfo = this._dimensionInfos[dim];
L
lang 已提交
375
        if (dimData) {
L
lang 已提交
376 377
            // var isOrdinal = dimInfo.type === 'ordinal';
            for (var i = 0, len = this.count(); i < len; i++) {
L
lang 已提交
378
                value = this.get(dim, i, stack);
L
lang 已提交
379 380 381 382 383
                // FIXME
                // if (isOrdinal && typeof value === 'string') {
                //     value = zrUtil.indexOf(dimData, value);
                //     console.log(value);
                // }
L
lang 已提交
384 385 386 387 388 389
                value < min && (min = value);
                value > max && (max = value);
            }
        }
        return [min, max];
    };
L
lang 已提交
390

L
Add pie  
lang 已提交
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
    /**
     * 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;
    };
L
lang 已提交
409 410 411 412 413 414
    /**
     * Get raw value
     * @param {number} idx
     * @return {number}
     */
    listProto.getRawValue = function (idx) {
L
lang 已提交
415
        var itemOpt = this._rawData[idx];
L
lang 已提交
416 417 418
        var valueProp = this._valueProp;
        if (itemOpt && itemOpt.hasOwnProperty(valueProp)) {
            return itemOpt[valueProp];
L
lang 已提交
419
        }
L
lang 已提交
420
        return itemOpt;
L
lang 已提交
421
    };
L
lang 已提交
422

L
lang 已提交
423 424 425 426 427 428 429 430 431 432
    /**
     * 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];
L
lang 已提交
433
        var indices = this.indices;
L
lang 已提交
434 435

        if (dimData) {
L
lang 已提交
436 437 438
            for (var i = 0, len = indices.length; i < len; i++) {
                var rawIndex = indices[i];
                if (dimData[rawIndex] === value) {
L
lang 已提交
439 440 441 442 443 444 445
                    return i;
                }
            }
        }
        return -1;
    };

L
lang 已提交
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465
    /**
     * 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;
    };

L
lang 已提交
466 467 468 469
    /**
     * Retreive the index of nearest value
     * @param {number} idx
     * @param {number} value
L
lang 已提交
470
     * @param {boolean} stack If given value is after stacked
L
lang 已提交
471 472
     * @return {number}
     */
L
lang 已提交
473
    listProto.indexOfNearest = function (dim, value, stack) {
L
lang 已提交
474 475 476 477 478 479
        var storage = this._storage;
        var dimData = storage[dim];

        if (dimData) {
            var minDist = Number.MAX_VALUE;
            var nearestIdx = -1;
L
lang 已提交
480 481
            for (var i = 0, len = this.count(); i < len; i++) {
                var dist = Math.abs(this.get(dim, i, stack) - value);
L
lang 已提交
482 483 484 485 486 487 488 489
                if (dist <= minDist) {
                    minDist = dist;
                    nearestIdx = i;
                }
            }
            return nearestIdx;
        }
        return -1;
L
lang 已提交
490
    };
L
lang 已提交
491

L
lang 已提交
492 493
    /**
     * Get raw data index
L
lang 已提交
494 495
     * @param {number} idx
     * @return {number}
L
lang 已提交
496
     */
L
lang 已提交
497
    listProto.getRawIndex = function (idx) {
498 499
        var rawIdx = this.indices[idx];
        return rawIdx == null ? -1 : rawIdx;
L
lang 已提交
500
    };
L
lang 已提交
501

L
lang 已提交
502 503
    /**
     * @param {number} idx
504
     * @param {boolean} [notDefaultIdx=false]
L
lang 已提交
505 506
     * @return {string}
     */
L
lang 已提交
507 508 509 510 511 512 513 514 515 516 517
    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) + '');
L
lang 已提交
518 519 520
    };


L
lang 已提交
521 522 523 524 525 526
    function normalizeDimensions(dimensions) {
        if (typeof (dimensions) === 'string') {
            dimensions = [dimensions];
        }
        return dimensions;
    }
L
lang 已提交
527

L
lang 已提交
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546
    /**
     * 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 (dimensions, cb, stack, context) {
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }
L
lang 已提交
547

L
lang 已提交
548
        dimensions = normalizeDimensions(dimensions);
L
lang 已提交
549

L
lang 已提交
550 551 552
        var value = [];
        var dimSize = dimensions.length;
        var indices = this.indices;
L
lang 已提交
553

L
lang 已提交
554
        context = context || this;
L
lang 已提交
555

L
lang 已提交
556 557 558
        for (var i = 0; i < indices.length; i++) {
            if (dimSize === 0) {
                // FIXME Pass value as parameter ?
L
lang 已提交
559
                cb.call(context, i);
L
lang 已提交
560 561 562
            }
            // Simple optimization
            else if (dimSize === 1) {
L
lang 已提交
563
                cb.call(context, this.get(dimensions[0], i, stack), i);
L
lang 已提交
564 565 566
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
567
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
568 569 570 571 572 573 574
                }
                // Index
                value[k] = i;
                cb.apply(context, value);
            }
        }
    };
L
lang 已提交
575

L
lang 已提交
576 577 578 579 580 581 582 583
    /**
     * Data filter
     * @param {string|Array.<string>}
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
     */
    listProto.filterSelf = function (dimensions, cb, stack, context) {
L
lang 已提交
584 585 586 587 588 589 590
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }

L
lang 已提交
591 592 593 594 595 596 597 598 599 600 601 602 603
        dimensions = normalizeDimensions(dimensions);

        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 === 1) {
L
lang 已提交
604
                keep = cb.call(
L
lang 已提交
605
                    context, this.get(dimensions[0], i, stack), i
L
lang 已提交
606 607 608 609
                );
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
610
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
611 612 613 614 615 616 617 618
                }
                value[k] = i;
                keep = cb.apply(context, value);
            }
            if (keep) {
                newIndices.push(indices[i]);
            }
        }
P
pah100 已提交
619

L
lang 已提交
620
        this.indices = newIndices;
L
lang 已提交
621

L
lang 已提交
622 623
        return this;
    };
L
lang 已提交
624

L
lang 已提交
625
    /**
L
lang 已提交
626 627
     * Data mapping to a plain array
     * @param {string|Array.<string>} [dimensions]
L
lang 已提交
628 629 630
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
L
lang 已提交
631
     * @return {Array}
L
lang 已提交
632
     */
L
lang 已提交
633
    listProto.mapArray = function (dimensions, cb, stack, context) {
L
lang 已提交
634 635 636 637 638 639
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }
L
lang 已提交
640

L
lang 已提交
641 642 643 644 645 646 647
        var result = [];
        this.each(dimensions, function () {
            result.push(cb && cb.apply(this, arguments));
        }, stack, context);
        return result;
    };

L
lang 已提交
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665
    /**
     * 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) {
        var list = new List(
            zrUtil.map(dimensions, this.getDimensionInfo, this),
            this.hostModel
        );

        // Following properties are all immutable.
        // So we can reference to the same value
        var indices = list.indices = this.indices;

L
lang 已提交
666 667
        // FIXME If needs stackedOn, value may already been stacked
        transferImmuProperties(list, this);
L
lang 已提交
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708

        var storage = list._storage = {};
        var thisStorage = this._storage;

        // Init storage
        for (var i = 0; i < dimensions.length; i++) {
            var dim = dimensions[i];
            var dimStore = thisStorage[dim];
            if (dimStore) {
                storage[dim] = new dimStore.constructor(
                    thisStorage[dim].length
                );
            }
        }

        storage.$optionModelIndices = thisStorage.$optionModelIndices;

        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];
                    }
                }
            }
        });

        return list;
    };

L
lang 已提交
709 710 711 712
    var temporaryModel = new Model(null);
    /**
     * Get model of one data item.
     * It will create a temporary model if value on idx is not an option.
L
lang 已提交
713 714
     *
     * @param {number} idx
L
lang 已提交
715
     * @param {boolean} [createNew=false]
L
lang 已提交
716
     */
L
lang 已提交
717
    // FIXME Model proxy ?
L
lang 已提交
718
    listProto.getItemModel = function (idx, createNew) {
L
lang 已提交
719 720
        var storage = this._storage;
        var optionModelIndices = storage.$optionModelIndices;
L
lang 已提交
721
        var modelIndex = optionModelIndices && optionModelIndices[this.indices[idx]];
L
lang 已提交
722 723 724

        var model = this._optionModels[modelIndex];

L
lang 已提交
725
        var hostModel = this.hostModel;
L
lang 已提交
726
        if (!model) {
L
lang 已提交
727 728
            // Use a temporary model proxy if value on idx is not an option.
            // FIXME Create a new one may cause memory leak
L
lang 已提交
729 730 731 732 733 734 735
            if (createNew) {
                model = new Model(null, hostModel);
            }
            else {
                model = temporaryModel;
                model.parentModel = hostModel;
            }
L
lang 已提交
736 737 738 739 740 741 742 743 744 745
        }
        return model;
    };

    /**
     * Create a data differ
     * @param {module:echarts/data/List} oldList
     * @return {module:echarts/data/DataDiffer}
     */
    listProto.diff = function (oldList) {
L
lang 已提交
746
        var idList = this._idList;
747 748
        return new DataDiffer(
            oldList ? oldList.indices : [], this.indices, function (idx) {
L
lang 已提交
749
                return idList[idx] || (idx + '');
750 751
            }
        );
L
lang 已提交
752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    };

    /**
     * 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]);
L
lang 已提交
779 780
                }
            }
L
lang 已提交
781 782 783 784 785
            return;
        }
        this._visual = this._visual || {};
        this._visual[key] = val;
    };
L
lang 已提交
786

L
lang 已提交
787 788 789 790 791 792 793
    /**
     * Get layout of single data item
     * @param {number} idx
     */
    listProto.getItemLayout = function (idx) {
        return this._itemLayouts[idx];
    },
L
lang 已提交
794

L
lang 已提交
795 796 797 798
    /**
     * Set layout of single data item
     * @param {number} idx
     * @param {Object} layout
L
lang 已提交
799
     * @param {boolean=} [merge=false]
L
lang 已提交
800
     */
P
tree  
pah100 已提交
801 802 803 804
    listProto.setItemLayout = function (idx, layout, merge) {
        this._itemLayouts[idx] = merge
            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
            : layout;
L
lang 已提交
805
    },
L
lang 已提交
806

L
lang 已提交
807 808 809 810
    /**
     * Get visual property of single data item
     * @param {number} idx
     * @param {string} key
L
lang 已提交
811
     * @param {boolean} ignoreParent
L
lang 已提交
812
     */
L
lang 已提交
813
    listProto.getItemVisual = function (idx, key, ignoreParent) {
L
lang 已提交
814 815
        var itemVisual = this._itemVisuals[idx];
        var val = itemVisual && itemVisual[key];
L
lang 已提交
816
        if (val == null && !ignoreParent) {
L
lang 已提交
817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
            // 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)) {
P
pah100 已提交
843
                    itemVisual[name] = key[name];
L
lang 已提交
844
                }
L
lang 已提交
845
            }
L
lang 已提交
846
            return;
L
lang 已提交
847
        }
L
lang 已提交
848
        itemVisual[key] = value;
L
lang 已提交
849 850
    };

L
lang 已提交
851 852 853
    var setItemDataAndSeriesIndex = function (child) {
        child.seriesIndex = this.seriesIndex;
        child.dataIndex = this.dataIndex;
L
lang 已提交
854
    };
L
lang 已提交
855 856 857 858 859
    /**
     * @param {number} idx
     * @param {module:zrender/Element} el
     */
    listProto.setItemGraphicEl = function (idx, el) {
L
lang 已提交
860
        var hostModel = this.hostModel;
L
lang 已提交
861 862 863
        // Add data index and series index for indexing the data by element
        // Useful in tooltip
        el.dataIndex = idx;
L
lang 已提交
864
        el.seriesIndex = hostModel && hostModel.seriesIndex;
L
lang 已提交
865
        if (el.type === 'group') {
866
            el.traverse(setItemDataAndSeriesIndex, el);
L
lang 已提交
867
        }
L
lang 已提交
868

L
lang 已提交
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
        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) {
L
lang 已提交
885 886 887 888 889
        zrUtil.each(this._graphicEls, function (el, idx) {
            if (el) {
                cb && cb.call(context, el, idx);
            }
        });
L
lang 已提交
890 891 892 893 894 895 896
    };

    /**
     * Shallow clone a new list except visual and layout properties, and graph elements.
     * New list only change the indices.
     */
    listProto.cloneShallow = function () {
L
lang 已提交
897 898 899
        var dimensionInfoList = zrUtil.map(this.dimensions, function (dim) {
            return this._dimensionInfos[dim];
        }, this);
L
lang 已提交
900
        var list = new List(dimensionInfoList, this.hostModel);
L
lang 已提交
901 902 903

        // FIXME
        list._storage = this._storage;
L
lang 已提交
904 905

        transferImmuProperties(list, this);
L
lang 已提交
906 907

        list.indices = this.indices.slice();
P
pah100 已提交
908

L
lang 已提交
909 910
        return list;
    };
L
lang 已提交
911

L
lang 已提交
912 913
    return List;
});