UNPKG

67.2 kBJavaScriptView Raw
1import { __extends } from "tslib";
2/**
3 * ============================================================================
4 * IMPORTS
5 * ============================================================================
6 * @hidden
7 */
8import { Container } from "./Container";
9import { List, ListDisposer } from "./utils/List";
10import { OrderedListTemplate } from "./utils/SortedList";
11import { Dictionary } from "./utils/Dictionary";
12import { Disposer, MultiDisposer } from "./utils/Disposer";
13import { DataSource } from "./data/DataSource";
14import { Responsive } from "./utils/Responsive";
15import { system } from "./System";
16import { DataItem } from "./DataItem";
17import { registry } from "./Registry";
18import * as $math from "./utils/Math";
19import * as $array from "./utils/Array";
20import * as $ease from "./utils/Ease";
21import * as $utils from "./utils/Utils";
22import * as $iter from "./utils/Iterator";
23import * as $object from "./utils/Object";
24import * as $type from "./utils/Type";
25/**
26 * ============================================================================
27 * MAIN CLASS
28 * ============================================================================
29 * @hidden
30 */
31/**
32 * A Component represents an independent functional element or control, that
33 * can have it's own behavior, children, data, etc.
34 *
35 * A few examples of a Component: [[Legend]], [[Series]], [[Scrollbar]].
36 *
37 * @see {@link IComponentEvents} for a list of available events
38 * @see {@link IComponentAdapters} for a list of available Adapters
39 * @important
40 */
41var Component = /** @class */ (function (_super) {
42 __extends(Component, _super);
43 /**
44 * Constructor
45 */
46 function Component() {
47 var _this =
48 // Init
49 _super.call(this) || this;
50 /**
51 * Holds data field names.
52 *
53 * Data fields define connection beween [[DataItem]] and actual properties
54 * in raw data.
55 */
56 _this.dataFields = {};
57 /**
58 * A list of [[DataSource]] definitions of external data source.
59 *
60 * @ignore Exclude from docs
61 */
62 _this._dataSources = {};
63 /**
64 * This is used when only new data is invalidated (if added using `addData`
65 * method).
66 *
67 * @ignore Exclude from docs
68 */
69 _this._parseDataFrom = 0;
70 /**
71 * Holds the disposers for the dataItems and dataUsers
72 *
73 * @ignore Exclude from docs
74 */
75 _this._dataDisposers = [];
76 /**
77 * Currently selected "data set".
78 *
79 * If it's set to `""`, main data set (unaggregated data) is used.
80 */
81 _this._currentDataSetId = "";
82 /**
83 * [_start description]
84 *
85 * @ignore Exclude from docs
86 */
87 _this._start = 0;
88 /**
89 * [_end description]
90 *
91 * @ignore Exclude from docs
92 */
93 _this._end = 1;
94 /**
95 * If set to `true`, changing data range in element will not trigger
96 * `daterangechanged` event.
97 */
98 _this.skipRangeEvent = false;
99 /**
100 * Whenever selected scope changes (chart is zoomed or panned), for example
101 * by interaction from a Scrollbar, or API, a chart needs to reposition
102 * its contents.
103 *
104 * `rangeChangeDuration` influences how this is performed.
105 *
106 * If set to zero (0), the change will happen instantenously.
107 *
108 * If set to non-zero value, the chart will gradually animate into new
109 * position for the set amount of milliseconds.
110 *
111 * @default 0
112 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
113 */
114 _this.rangeChangeDuration = 0;
115 /**
116 * An easing function to use for range change animation.
117 *
118 * @see {@link Ease}
119 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
120 */
121 _this.rangeChangeEasing = $ease.cubicOut;
122 /**
123 * A duration (ms) of each data parsing step. A Component parses its data in
124 * chunks in order to avoid completely freezing the machine when large data
125 * sets are used. This setting will control how many milliseconds should pass
126 * when parsing data until parser stops for a brief moment to let other
127 * processes catch up.
128 */
129 _this.parsingStepDuration = 50;
130 /**
131 * [dataInvalid description]
132 *
133 * @ignore Exclude from docs
134 * @todo Description
135 */
136 _this.dataInvalid = false;
137 /**
138 *
139 * @ignore Exclude from docs
140 */
141 _this.rawDataInvalid = false;
142 /**
143 * [dataRangeInvalid description]
144 *
145 * @ignore Exclude from docs
146 * @todo Description
147 */
148 _this.dataRangeInvalid = false;
149 /**
150 * [dataItemsInvalid description]
151 *
152 * @ignore Exclude from docs
153 * @todo Description
154 */
155 _this.dataItemsInvalid = false;
156 /**
157 * If set to a non-zero number the element will "animate" data values of its
158 * children.
159 *
160 * This will happen on first load and whenever data values change.
161 *
162 * Enabling interpolation will mean that elements will transit smoothly into
163 * new values rather than updating instantly.
164 *
165 * @default 0
166 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
167 */
168 _this.interpolationDuration = 0;
169 /**
170 * An easing function to use for interpolating values when transiting from
171 * one source value to another.
172 *
173 * @default cubicOut
174 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
175 * @see {@link Ease}
176 */
177 _this.interpolationEasing = $ease.cubicOut;
178 /**
179 * Indicates whether transition between data item's values should start and
180 * play out all at once, or with a small delay (as defined by
181 * `sequencedInterpolationDelay`) for each subsequent data item.
182 *
183 * @default true
184 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
185 */
186 _this.sequencedInterpolation = true;
187 /**
188 * A delay (ms) to wait between animating each subsequent data item's
189 * interpolation animation.
190 *
191 * Relative only if `sequencedInterpolation = true`.
192 *
193 * @default 0
194 * @see {@link https://www.amcharts.com/docs/v4/concepts/animations/} for more info about animations
195 */
196 _this.sequencedInterpolationDelay = 0;
197 /**
198 * A progress (0-1) for the data validation process.
199 *
200 * @ignore Exclude from docs
201 */
202 _this.dataValidationProgress = 0;
203 _this._addAllDataItems = true;
204 _this._usesData = true;
205 _this.className = "Component";
206 _this.minZoomCount = 1;
207 _this.maxZoomCount = 0;
208 _this._dataItems = new OrderedListTemplate(_this.createDataItem());
209 _this._dataItems.events.on("inserted", _this.handleDataItemAdded, _this, false);
210 _this._dataItems.events.on("removed", _this.handleDataItemRemoved, _this, false);
211 _this._disposers.push(new ListDisposer(_this._dataItems));
212 _this._disposers.push(_this._dataItems.template);
213 _this.invalidateData();
214 // TODO what about remove ?
215 _this.dataUsers.events.on("inserted", _this.handleDataUserAdded, _this, false);
216 // Set up disposers
217 _this._disposers.push(new MultiDisposer(_this._dataDisposers));
218 _this._start = 0;
219 _this._end = 1;
220 _this.maxZoomDeclination = 1;
221 // Apply theme
222 _this.applyTheme();
223 return _this;
224 }
225 /**
226 * Returns a new/empty DataItem of the type appropriate for this object.
227 *
228 * @see {@link DataItem}
229 * @return Data Item
230 */
231 Component.prototype.createDataItem = function () {
232 return new DataItem();
233 };
234 /**
235 * [handleDataUserAdded description]
236 *
237 * @ignore Exclude from docs
238 * @todo Description
239 * @param event Event object
240 */
241 Component.prototype.handleDataUserAdded = function (event) {
242 var dataUser = event.newValue;
243 dataUser.dataProvider = this;
244 };
245 /**
246 * [handleDataItemValueChange description]
247 *
248 * @ignore Exclude from docs
249 * @todo Description
250 */
251 Component.prototype.handleDataItemValueChange = function (dataItem, name) {
252 if (!this.dataItemsInvalid) {
253 this.invalidateDataItems();
254 }
255 };
256 /**
257 * [handleDataItemWorkingValueChange description]
258 *
259 * @ignore Exclude from docs
260 */
261 Component.prototype.handleDataItemWorkingValueChange = function (dataItem, name) {
262 };
263 /**
264 * [handleDataItemWorkingLocationChange description]
265 *
266 * @ignore Exclude from docs
267 */
268 Component.prototype.handleDataItemWorkingLocationChange = function (dataItem, name) {
269 };
270 /**
271 * [handleDataItemCalculatedValueChange description]
272 *
273 * @ignore Exclude from docs
274 */
275 Component.prototype.handleDataItemCalculatedValueChange = function (dataItem, name) {
276 };
277 /**
278 * [handleDataItemPropertyChange description]
279 *
280 * @ignore Exclude from docs
281 */
282 Component.prototype.handleDataItemPropertyChange = function (dataItem, name) {
283 };
284 /**
285 * Populates a [[DataItem]] width data from data source.
286 *
287 * Loops through all the fields and if such a field is found in raw data
288 * object, a corresponding value on passed in `dataItem` is set.
289 *
290 * @ignore Exclude from docs
291 * @param item
292 */
293 Component.prototype.processDataItem = function (dataItem, dataContext) {
294 var _this = this;
295 if (dataItem) {
296 if (!dataContext) {
297 dataContext = {};
298 }
299 // store reference to original data item
300 dataItem.dataContext = dataContext;
301 var hasSomeValues_1 = false;
302 $object.each(this.dataFields, function (key, fieldValue) {
303 var fieldName = key;
304 var value = dataContext[fieldValue];
305 // Apply adapters to a retrieved value
306 if (_this._adapterO) {
307 if (_this._adapterO.isEnabled("dataContextValue")) {
308 value = _this._adapterO.apply("dataContextValue", {
309 field: fieldName,
310 value: value,
311 dataItem: dataItem
312 }).value;
313 }
314 }
315 if ($type.hasValue(value)) {
316 hasSomeValues_1 = true;
317 if (dataItem.hasChildren[fieldName]) {
318 var template = _this.createDataItem();
319 template.copyFrom(_this.mainDataSet.template);
320 var children = new OrderedListTemplate(template);
321 children.events.on("inserted", _this.handleDataItemAdded, _this, false);
322 children.events.on("removed", _this.handleDataItemRemoved, _this, false);
323 _this._dataDisposers.push(new ListDisposer(children));
324 var count = value.length;
325 for (var i = 0; i < count; i++) {
326 var rawDataItem = value[i];
327 var childDataItem = children.create();
328 childDataItem.parent = dataItem;
329 _this.processDataItem(childDataItem, rawDataItem);
330 }
331 var anyDataItem = dataItem;
332 anyDataItem[fieldName] = children;
333 }
334 else {
335 // data is converted to numbers/dates in each dataItem
336 dataItem[fieldName] = value;
337 }
338 }
339 });
340 $object.each(this.propertyFields, function (key, fieldValue) {
341 var f = key;
342 var value = dataContext[fieldValue];
343 if ($type.hasValue(value)) {
344 hasSomeValues_1 = true;
345 dataItem.setProperty(f, value);
346 }
347 });
348 // @todo we might need some flag which would tell whether we should create empty data items or not.
349 if (!this._addAllDataItems && !hasSomeValues_1) {
350 this.mainDataSet.remove(dataItem);
351 }
352 }
353 };
354 /**
355 *
356 * When validating raw data, instead of processing data item, we update it
357 *
358 * @ignore Exclude from docs
359 * @param item
360 */
361 Component.prototype.updateDataItem = function (dataItem) {
362 var _this = this;
363 if (dataItem) {
364 var dataContext_1 = dataItem.dataContext;
365 $object.each(this.dataFields, function (key, fieldValue) {
366 var fieldName = key;
367 var value = dataContext_1[fieldValue];
368 // Apply adapters to a retrieved value
369 if (_this._adapterO) {
370 value = _this._adapterO.apply("dataContextValue", {
371 field: fieldName,
372 value: value,
373 dataItem: dataItem
374 }).value;
375 }
376 if ($type.hasValue(value)) {
377 if (dataItem.hasChildren[fieldName]) {
378 var anyDataItem = dataItem;
379 var children = (anyDataItem[fieldName]);
380 children.each(function (child) {
381 _this.updateDataItem(child);
382 });
383 }
384 else {
385 // data is converted to numbers/dates in each dataItem
386 dataItem[fieldName] = value;
387 }
388 }
389 });
390 $object.each(this.propertyFields, function (key, fieldValue) {
391 var f = key;
392 var value = dataContext_1[fieldValue];
393 if ($type.hasValue(value)) {
394 dataItem.setProperty(f, value);
395 }
396 });
397 }
398 };
399 /**
400 * [validateDataElements description]
401 *
402 * @ignore Exclude from docs
403 * @todo Description
404 */
405 Component.prototype.validateDataElements = function () {
406 var count = this.endIndex;
407 for (var i = this.startIndex; i < count; i++) {
408 var dataItem = this.dataItems.getIndex(i);
409 // TODO is this correct
410 if (dataItem) {
411 this.validateDataElement(dataItem);
412 }
413 }
414 };
415 /**
416 * Validates this element and its related elements.
417 *
418 * @ignore Exclude from docs
419 */
420 Component.prototype.validate = function () {
421 this.validateDataElements();
422 _super.prototype.validate.call(this);
423 };
424 /**
425 * [validateDataElement description]
426 *
427 * @ignore Exclude from docs
428 * @param dataItem [description]
429 */
430 Component.prototype.validateDataElement = function (dataItem) {
431 };
432 /**
433 * Adds one or several (array) of data items to the existing data.
434 *
435 * @param rawDataItem One or many raw data item objects
436 */
437 Component.prototype.addData = function (rawDataItem, removeCount, skipRaw) {
438 var _this = this;
439 // need to check if data is invalid, as addData might be called multiple times
440 if (!this.dataInvalid && this.inited) {
441 this._parseDataFrom = this.data.length; // save length of parsed data
442 }
443 if (!skipRaw) {
444 if (rawDataItem instanceof Array) {
445 // can't use concat because new array is returned
446 $array.each(rawDataItem, function (dataItem) {
447 _this.data.push(dataItem);
448 });
449 }
450 else {
451 this.data.push(rawDataItem); // add to raw data array
452 }
453 }
454 if (this.inited) {
455 this.removeData(removeCount, skipRaw);
456 }
457 else {
458 if ($type.isNumber(removeCount)) {
459 while (removeCount > 0) {
460 this.data.shift();
461 removeCount--;
462 }
463 }
464 }
465 this.invalidateData();
466 };
467 /**
468 * Removes elements from the beginning of data
469 *
470 * @param count number of elements to remove
471 */
472 Component.prototype.removeData = function (count, skipRaw) {
473 if ($type.isNumber(count) && count > 0) {
474 while (count > 0) {
475 var dataItem = this.mainDataSet.getIndex(0);
476 if (dataItem) {
477 this.mainDataSet.remove(dataItem);
478 }
479 this.dataUsers.each(function (dataUser) {
480 if (!dataUser.data || dataUser.data.length == 0) {
481 var dataItem_1 = dataUser.mainDataSet.getIndex(0);
482 if (dataItem_1) {
483 dataUser.mainDataSet.remove(dataItem_1);
484 }
485 }
486 });
487 if (!skipRaw) {
488 this.data.shift();
489 }
490 if (this._parseDataFrom > 0) {
491 this._parseDataFrom--;
492 }
493 count--;
494 }
495 // changed from invalidateData since 4.7.19 to solve #51551
496 this.invalidateDataItems();
497 }
498 };
499 /**
500 * Triggers a data (re)parsing.
501 *
502 * @ignore Exclude from docs
503 */
504 Component.prototype.invalidateData = function () {
505 if (this.disabled || this.isTemplate) {
506 return;
507 }
508 //if(!this.dataInvalid){
509 registry.addToInvalidComponents(this);
510 system.requestFrame();
511 this.dataInvalid = true;
512 $iter.each(this.dataUsers.iterator(), function (x) {
513 x.invalidateDataItems();
514 });
515 //}
516 };
517 /**
518 * [invalidateDataUsers description]
519 *
520 * @ignore Exclude from docs
521 * @todo Description
522 */
523 Component.prototype.invalidateDataUsers = function () {
524 $iter.each(this.dataUsers.iterator(), function (x) {
525 x.invalidate();
526 });
527 };
528 /**
529 * Invalidates data values. When data array is not changed, but values within
530 * it changes, we invalidate data so that component would process changes.
531 *
532 * @ignore Exclude from docs
533 */
534 Component.prototype.invalidateDataItems = function () {
535 if (this.disabled || this.isTemplate) {
536 return;
537 }
538 //if(!this.dataItemsInvalid){
539 $array.move(registry.invalidDataItems, this);
540 system.requestFrame();
541 this.dataItemsInvalid = true;
542 $iter.each(this.dataUsers.iterator(), function (x) {
543 x.invalidateDataItems();
544 });
545 //}
546 };
547 /**
548 * Invalidates data range. This is done when data which must be shown
549 * changes (chart is zoomed for example).
550 *
551 * @ignore Exclude from docs
552 */
553 Component.prototype.invalidateDataRange = function () {
554 if (this.disabled || this.isTemplate) {
555 return;
556 }
557 //if(!this.dataRangeInvalid){
558 this.dataRangeInvalid = true;
559 $array.move(registry.invalidDataRange, this);
560 system.requestFrame();
561 //}
562 };
563 /**
564 * Processes data range.
565 *
566 * @todo Description
567 * @ignore Exclude from docs
568 */
569 Component.prototype.validateDataRange = function () {
570 $array.remove(registry.invalidDataRange, this);
571 this.dataRangeInvalid = false;
572 if (this.startIndex != this._prevStartIndex || this.endIndex != this._prevEndIndex) {
573 this.rangeChangeUpdate();
574 this.appendDataItems();
575 this.invalidate();
576 this.dispatchImmediately("datarangechanged");
577 }
578 };
579 /**
580 * [sliceData description]
581 *
582 * @todo Description
583 * @ignore Exclude from docs
584 */
585 Component.prototype.sliceData = function () {
586 this._workingStartIndex = this.startIndex;
587 this._workingEndIndex = this.endIndex;
588 };
589 /**
590 * [rangeChangeUpdate description]
591 *
592 * @todo Description
593 * @ignore Exclude from docs
594 */
595 Component.prototype.rangeChangeUpdate = function () {
596 this.sliceData();
597 this._prevStartIndex = this.startIndex;
598 this._prevEndIndex = this.endIndex;
599 };
600 /**
601 * [appendDataItems description]
602 *
603 * @todo Description
604 * @ignore Exclude from docs
605 */
606 Component.prototype.appendDataItems = function () {
607 // TODO use an iterator instead
608 var count = this.endIndex;
609 for (var i = this.startIndex; i < count; i++) {
610 // data item
611 var dataItem = this.dataItems.getIndex(i);
612 if (dataItem) {
613 dataItem.__disabled = false;
614 }
615 }
616 for (var i = 0; i < this.startIndex; i++) {
617 var dataItem = this.dataItems.getIndex(i);
618 if (dataItem) {
619 dataItem.__disabled = true;
620 }
621 }
622 for (var i = this.endIndex; i < this.dataItems.length; i++) {
623 var dataItem = this.dataItems.getIndex(i);
624 if (dataItem) {
625 dataItem.__disabled = true;
626 }
627 }
628 };
629 /**
630 * If you want to have a smooth transition from one data values to another, you change your raw data and then you must call this method.
631 * then instead of redrawing everything, the chart will check raw data and smoothly transit from previous to new data
632 */
633 Component.prototype.invalidateRawData = function () {
634 if (this.disabled || this.isTemplate) {
635 return;
636 }
637 //if(!this.rawDataInvalid){
638 $array.move(registry.invalidRawDatas, this);
639 system.requestFrame();
640 this.rawDataInvalid = true;
641 $iter.each(this.dataUsers.iterator(), function (x) {
642 x.invalidateRawData();
643 });
644 //}
645 };
646 /**
647 * @ignore
648 */
649 Component.prototype.validateRawData = function () {
650 var _this = this;
651 $array.remove(registry.invalidRawDatas, this);
652 $iter.each(this.mainDataSet.iterator(), function (dataItem) {
653 if (dataItem) {
654 _this.updateDataItem(dataItem);
655 }
656 });
657 };
658 /**
659 * Destroys this object and all related data.
660 */
661 Component.prototype.dispose = function () {
662 var _this = this;
663 this.mainDataSet.template.clones.clear();
664 $object.each(this._dataSources, function (key, source) {
665 _this.removeDispose(source);
666 });
667 this.disposeData();
668 _super.prototype.dispose.call(this);
669 };
670 /**
671 * @ignore
672 */
673 Component.prototype.disposeData = function () {
674 this.mainDataSet.template.clones.clear();
675 $array.each(this._dataDisposers, function (x) {
676 x.dispose();
677 });
678 // and for all components
679 $iter.each(this.dataUsers.iterator(), function (dataUser) {
680 dataUser.disposeData();
681 });
682 this._dataDisposers.length = 0;
683 this._startIndex = undefined;
684 this._endIndex = undefined;
685 // dispose old
686 this.mainDataSet.clear();
687 this.mainDataSet.template.clones.clear();
688 if (this._dataSets) {
689 this._dataSets.clear();
690 }
691 };
692 Component.prototype.getDataItem = function (dataContext) {
693 return this.mainDataSet.create();
694 };
695 /**
696 * Validates (processes) data.
697 *
698 * @ignore Exclude from docs
699 */
700 Component.prototype.validateData = function () {
701 this.dispatchImmediately("beforedatavalidated");
702 this.dataInvalid = false;
703 registry.removeFromInvalidComponents(this);
704 if (this.__disabled) {
705 return;
706 }
707 this.dataValidationProgress = 0;
708 // need this to slice new data
709 this._prevStartIndex = undefined;
710 this._prevEndIndex = undefined;
711 // todo: this needs some overthinking, maybe some extra settings like zoomOotonDataupdate like in v3 or so. some charts like pie chart probably should act like this always
712 this._startIndex = undefined;
713 this._endIndex = undefined;
714 if (this.dataFields.data && this.dataItem) {
715 var dataContext = this.dataItem.dataContext;
716 this._data = dataContext[this.dataFields.data];
717 }
718 // data items array is reset only if all data is validated, if _parseDataFrom is not 0, we append new data only
719 // check heatmap demo if uncommented
720 // fixed both issues by adding && this.data.length > 0
721 // check adding series example if changed
722 if (this._parseDataFrom === 0 && this.data.length > 0) {
723 this.disposeData();
724 }
725 if (this.data.length > 0) {
726 var preloader = this.preloader;
727 // and for all components
728 $iter.each(this.dataUsers.iterator(), function (dataUser) {
729 // todo: this needs some overthinking, maybe some extra settings like zoomOUtonDataupdate like in v3 or so. some charts like pie chart probably should act like this always
730 dataUser._startIndex = undefined;
731 dataUser._endIndex = undefined;
732 });
733 var counter = 0;
734 var startTime = Date.now();
735 // parse data
736 var i = this._parseDataFrom;
737 var n = this.data.length;
738 var _loop_1 = function () {
739 var rawDataItem = this_1.data[i];
740 if (this_1._usesData) {
741 var dataItem = this_1.getDataItem(rawDataItem);
742 this_1.processDataItem(dataItem, rawDataItem);
743 }
744 this_1.dataUsers.each(function (dataUser) {
745 if (dataUser.data.length == 0) { // checking if data is not set directly
746 var dataUserDataItem = dataUser.getDataItem(rawDataItem);
747 dataUser.processDataItem(dataUserDataItem, rawDataItem);
748 }
749 });
750 counter++;
751 // show preloader if this takes too many time
752 if (counter == 100) { // no need to check it on each data item
753 counter = 0;
754 var elapsed = Date.now() - startTime;
755 if (elapsed > this_1.parsingStepDuration) {
756 if (i < this_1.data.length - 10) {
757 this_1._parseDataFrom = i + 1;
758 // update preloader
759 if (preloader) {
760 if (i / this_1.data.length > 0.5 && !preloader.visible) {
761 // do not start showing
762 }
763 else {
764 preloader.progress = i / this_1.data.length;
765 }
766 }
767 this_1.dataValidationProgress = i / this_1.data.length;
768 i = this_1.data.length; // stops cycle
769 this_1.invalidateData();
770 return { value: void 0 };
771 }
772 }
773 }
774 };
775 var this_1 = this;
776 for (i; i < n; i++) {
777 var state_1 = _loop_1();
778 if (typeof state_1 === "object")
779 return state_1.value;
780 }
781 if (preloader) {
782 preloader.progress = 1;
783 }
784 this.dataUsers.each(function (dataUser) {
785 if (dataUser.hidden || (dataUser.appeared && !dataUser.visible && dataUser.stacked)) {
786 dataUser.hide(0);
787 }
788 });
789 }
790 this.dataValidationProgress = 1;
791 this._parseDataFrom = 0; // reset this index, it is set to dataItems.length if addData() method was used.
792 this.invalidateDataItems();
793 if (!this._internalDefaultsApplied) {
794 this.applyInternalDefaults();
795 }
796 this.dispatch("datavalidated"); // can't zoom chart if dispatched immediately
797 };
798 /**
799 * Validates (processes) data items.
800 *
801 * @ignore Exclude from docs
802 */
803 Component.prototype.validateDataItems = function () {
804 $array.remove(registry.invalidDataItems, this);
805 this.dataItemsInvalid = false;
806 this.invalidateDataRange();
807 this.invalidate();
808 this.dispatch("dataitemsvalidated");
809 };
810 Object.defineProperty(Component.prototype, "data", {
811 /**
812 * Returns element's source (raw) data.
813 *
814 * @return Data
815 */
816 get: function () {
817 if (!this._data) {
818 this._data = [];
819 }
820 if (!this._adapterO) {
821 return this._data;
822 }
823 else {
824 return this._adapterO.apply("data", this._data);
825 }
826 },
827 /**
828 * Sets source (raw) data for the element. The "data" is always an `Array`
829 * of objects.
830 *
831 * IMPORTANT: The order of data items in `data` array is important as it
832 * might affect chart look and behavior. [More details](https://www.amcharts.com/docs/v4/concepts/data/#Order_of_data_items).
833 *
834 * @param value Data
835 */
836 set: function (value) {
837 this.setData(value);
838 },
839 enumerable: true,
840 configurable: true
841 });
842 Component.prototype.setData = function (value) {
843 // array might be the same, but there might be items added
844 // todo: check if array changed, toString maybe?
845 if (!this.isDisposed()) {
846 this._parseDataFrom = 0;
847 this.disposeData();
848 this._data = value;
849 if (value && value.length > 0) {
850 this.invalidateData();
851 }
852 else {
853 this.dispatchImmediately("beforedatavalidated");
854 this.dispatch("datavalidated");
855 }
856 }
857 };
858 /**
859 * Returns (creates if necessary) a [[DataSource]] bound to any specific
860 * property.
861 *
862 * For example if I want to bind `data` to an external JSON file, I'd create
863 * a DataSource for it.
864 *
865 * @param property Property to bind external data to
866 * @return A DataSource for property
867 */
868 Component.prototype.getDataSource = function (property) {
869 var _this = this;
870 if (!$type.hasValue(this._dataSources[property])) {
871 this._dataSources[property] = new DataSource();
872 this._dataSources[property].component = this;
873 this.setDataSourceEvents(this._dataSources[property], property);
874 this._dataSources[property].adapter.add("dateFields", function (val) {
875 return _this.dataSourceDateFields(val);
876 });
877 this._dataSources[property].adapter.add("numberFields", function (val) {
878 return _this.dataSourceNumberFields(val);
879 });
880 this.events.on("inited", function () {
881 _this.loadData(property);
882 }, this, false);
883 }
884 return this._dataSources[property];
885 };
886 Object.defineProperty(Component.prototype, "dataSource", {
887 /**
888 * @return Data source
889 */
890 get: function () {
891 if (!this._dataSources["data"]) {
892 this.getDataSource("data");
893 }
894 return this._dataSources["data"];
895 },
896 /**
897 *A [[DataSource]] to be used for loading Component's data.
898 *
899 * @see {@link https://www.amcharts.com/docs/v4/concepts/loading-external-data/} for more on loading external data
900 * @param value Data source
901 */
902 set: function (value) {
903 var _this = this;
904 if (this._dataSources["data"]) {
905 this.removeDispose(this._dataSources["data"]);
906 }
907 this._dataSources["data"] = value;
908 this._dataSources["data"].component = this;
909 this.events.on("inited", function () {
910 _this.loadData("data");
911 }, this, false);
912 this.setDataSourceEvents(value, "data");
913 },
914 enumerable: true,
915 configurable: true
916 });
917 /**
918 * Initiates loading of the external data via [[DataSource]].
919 *
920 * @ignore Exclude from docs
921 */
922 Component.prototype.loadData = function (property) {
923 this._dataSources[property].load();
924 };
925 /**
926 * This function is called by the [[DataSource]]'s `dateFields` adapater
927 * so that particular chart types can popuplate this setting with their
928 * own type-specific data fields so they are parsed properly.
929 *
930 * @ignore Exclude from docs
931 * @param value Array of date fields
932 * @return Array of date fields populated with chart's date fields
933 */
934 Component.prototype.dataSourceDateFields = function (value) {
935 return value;
936 };
937 /**
938 * This function is called by the [[DataSource]]'s `numberFields` adapater
939 * so that particular chart types can popuplate this setting with their
940 * own type-specific data fields so they are parsed properly.
941 *
942 * @ignore Exclude from docs
943 * @param value Array of number fields
944 * @return Array of number fields populated with chart's number fields
945 */
946 Component.prototype.dataSourceNumberFields = function (value) {
947 return value;
948 };
949 /**
950 *
951 * @ignore Exclude from docs
952 * @todo Description
953 * @param list [description]
954 * @param dataFields [description]
955 * @param targetList [description]
956 * @return [description]
957 */
958 Component.prototype.populateDataSourceFields = function (list, dataFields, targetList) {
959 $array.each(targetList, function (value) {
960 if (dataFields[value] && $array.indexOf(list, dataFields[value]) === -1) {
961 list.push(dataFields[value]);
962 }
963 });
964 return list;
965 };
966 /**
967 * Sets events on a [[DataSource]].
968 *
969 * @ignore Exclude from docs
970 */
971 Component.prototype.setDataSourceEvents = function (ds, property) {
972 var _this = this;
973 ds.events.on("started", function (ev) {
974 var preloader = _this.preloader;
975 if (preloader) {
976 preloader.progress = 0;
977 //preloader.label.text = this.language.translate("Loading");
978 }
979 }, undefined, false);
980 ds.events.on("loadstarted", function (ev) {
981 var preloader = _this.preloader;
982 if (preloader) {
983 preloader.progress = 0.25;
984 }
985 }, undefined, false);
986 ds.events.on("loadended", function (ev) {
987 var preloader = _this.preloader;
988 if (preloader) {
989 preloader.progress = 0.5;
990 }
991 }, undefined, false);
992 ds.events.on("parseended", function (ev) {
993 var preloader = _this.preloader;
994 if (preloader) {
995 preloader.progress = 0.75;
996 }
997 }, undefined, false);
998 ds.events.on("ended", function (ev) {
999 var preloader = _this.preloader;
1000 if (preloader) {
1001 preloader.progress = 1;
1002 }
1003 }, undefined, false);
1004 ds.events.on("error", function (ev) {
1005 var preloader = _this.preloader;
1006 if (preloader) {
1007 preloader.progress = 1;
1008 }
1009 _this.openModal(ev.message);
1010 }, undefined, false);
1011 if (property) {
1012 ds.events.on("done", function (ev) {
1013 var preloader = _this.preloader;
1014 if (preloader) {
1015 preloader.progress = 1;
1016 }
1017 if (property == "data" && !$type.isArray(ev.data)) {
1018 ev.data = [ev.data];
1019 }
1020 if (ds.incremental && property == "data" && _this.data.length) {
1021 _this.addData(ev.data, ds.keepCount ? ev.data.length : 0);
1022 }
1023 else if (ds.updateCurrentData && property == "data" && _this.data.length) {
1024 // cycle through existing data items
1025 $array.each(_this.data, function (item, index) {
1026 if ($type.hasValue(ev.data[index])) {
1027 $object.each(item, function (key, val) {
1028 if ($type.hasValue(ev.data[index][key])) {
1029 item[key] = ev.data[index][key];
1030 }
1031 });
1032 }
1033 });
1034 _this.invalidateRawData();
1035 }
1036 else {
1037 _this[property] = ev.data;
1038 }
1039 });
1040 }
1041 };
1042 Object.defineProperty(Component.prototype, "responsive", {
1043 /**
1044 * @return Responsive rules handler
1045 */
1046 get: function () {
1047 if (!this._responsive) {
1048 this._responsive = new Responsive();
1049 this._responsive.component = this;
1050 }
1051 return this._responsive;
1052 },
1053 /**
1054 * A [[Responsive]] instance to be used when applying conditional
1055 * property values.
1056 *
1057 * NOTE: Responsive features are currently in development and may not work
1058 * as expected, if at all.
1059 *
1060 * @param value Responsive rules handler
1061 */
1062 set: function (value) {
1063 this._responsive = value;
1064 this._responsive.component = this;
1065 },
1066 enumerable: true,
1067 configurable: true
1068 });
1069 /**
1070 * Sets current zoom.
1071 *
1072 * The range uses relative values from 0 to 1, with 0 marking beginning and 1
1073 * marking end of the available data range.
1074 *
1075 * This method will not have any effect when called on a chart object.
1076 * Since the chart can have a number of axes and series, each with its own
1077 * data, the meaning of "range" is very ambiguous.
1078 *
1079 * To zoom the chart use `zoom*` methods on its respective axes.
1080 *
1081 * @param range Range
1082 * @param skipRangeEvent Should rangechanged event not be triggered?
1083 * @param instantly Do not animate?
1084 * @return Actual modidied range (taking `maxZoomFactor` into account)
1085 */
1086 Component.prototype.zoom = function (range, skipRangeEvent, instantly, declination) {
1087 var _this = this;
1088 if (skipRangeEvent === void 0) { skipRangeEvent = false; }
1089 if (instantly === void 0) { instantly = false; }
1090 var start = range.start;
1091 var end = range.end;
1092 var priority = range.priority;
1093 if (range.start == range.end) {
1094 range.start = range.start - 0.5 / this.maxZoomFactor;
1095 range.end = range.end + 0.5 / this.maxZoomFactor;
1096 }
1097 if (priority == "end" && end == 1 && start != 0) {
1098 if (start < this.start) {
1099 priority = "start";
1100 }
1101 }
1102 if (priority == "start" && start == 0) {
1103 if (end > this.end) {
1104 priority = "end";
1105 }
1106 }
1107 if (!$type.isNumber(declination)) {
1108 declination = this.maxZoomDeclination;
1109 }
1110 if (!$type.isNumber(start) || !$type.isNumber(end)) {
1111 return { start: this.start, end: this.end };
1112 }
1113 if (this._finalStart != start || this._finalEnd != end) {
1114 var maxZoomFactor = this.maxZoomFactor / this.minZoomCount;
1115 var minZoomFactor = this.maxZoomFactor / this.maxZoomCount;
1116 // most likely we are dragging left scrollbar grip here, so we tend to modify end
1117 if (priority == "start") {
1118 if (this.maxZoomCount > 0) {
1119 // add to the end
1120 if (1 / (end - start) < minZoomFactor) {
1121 end = start + 1 / minZoomFactor;
1122 }
1123 }
1124 // add to the end
1125 if (1 / (end - start) > maxZoomFactor) {
1126 end = start + 1 / maxZoomFactor;
1127 }
1128 //unless end is > 0
1129 if (end > 1 && end - start < 1 / maxZoomFactor) {
1130 //end = 1;
1131 start = end - 1 / maxZoomFactor;
1132 }
1133 }
1134 // most likely we are dragging right, so we modify left
1135 else {
1136 if (this.maxZoomCount > 0) {
1137 // add to the end
1138 if (1 / (end - start) < minZoomFactor) {
1139 start = end - 1 / minZoomFactor;
1140 }
1141 }
1142 // remove from start
1143 if (1 / (end - start) > maxZoomFactor) {
1144 if (start <= 0) {
1145 end = start + 1 / maxZoomFactor;
1146 }
1147 else {
1148 start = end - 1 / maxZoomFactor;
1149 }
1150 }
1151 if (start < 0 && end - start < 1 / maxZoomFactor) {
1152 //start = 0;
1153 end = start + 1 / maxZoomFactor;
1154 }
1155 }
1156 if (start < -declination) {
1157 start = -declination;
1158 }
1159 if (1 / (end - start) > maxZoomFactor) {
1160 end = start + 1 / maxZoomFactor;
1161 }
1162 if (end > 1 + declination) {
1163 end = 1 + declination;
1164 }
1165 if (1 / (end - start) > maxZoomFactor) {
1166 start = end - 1 / maxZoomFactor;
1167 }
1168 this._finalEnd = end;
1169 this._finalStart = start;
1170 this.skipRangeEvent = skipRangeEvent;
1171 this.dispatchImmediately("rangechangestarted");
1172 if (this.rangeChangeDuration > 0 && !instantly) {
1173 // todo: maybe move this to Animation
1174 var rangeChangeAnimation = this.rangeChangeAnimation;
1175 if (rangeChangeAnimation && rangeChangeAnimation.progress < 1) {
1176 var options = rangeChangeAnimation.animationOptions;
1177 if (options.length > 1) {
1178 if (options[0].to == start && options[1].to == end) {
1179 return { start: start, end: end };
1180 }
1181 else {
1182 if (!rangeChangeAnimation.isDisposed()) {
1183 rangeChangeAnimation.stop();
1184 }
1185 }
1186 }
1187 }
1188 if (this.rangeChangeAnimation) {
1189 this.rangeChangeAnimation.kill();
1190 }
1191 rangeChangeAnimation = this.animate([{ property: "start", to: start }, { property: "end", to: end }], this.rangeChangeDuration, this.rangeChangeEasing);
1192 this.rangeChangeAnimation = rangeChangeAnimation;
1193 if (rangeChangeAnimation && !rangeChangeAnimation.isFinished()) {
1194 rangeChangeAnimation.events.on("animationended", function () {
1195 _this.dispatchImmediately("rangechangeended");
1196 });
1197 }
1198 else {
1199 this.dispatchImmediately("rangechangeended");
1200 }
1201 }
1202 else {
1203 this.start = start;
1204 this.end = end;
1205 this.dispatch("rangechangeended");
1206 }
1207 }
1208 return { start: start, end: end };
1209 };
1210 /**
1211 * Zooms to specific data items using their index in data.
1212 *
1213 * This method will not have any effect when called on a chart object.
1214 * Since the chart can have a number of axes and series, each with its own
1215 * data, the meaning of "index" is very ambiguous.
1216 *
1217 * To zoom the chart use `zoom*` methods on its respective axes.
1218 *
1219 * @param startIndex Index of the starting data item
1220 * @param endIndex Index of the ending data item
1221 * @param skipRangeEvent Should rangechanged event not be triggered?
1222 * @param instantly Do not animate?
1223 */
1224 Component.prototype.zoomToIndexes = function (startIndex, endIndex, skipRangeEvent, instantly) {
1225 if (!$type.isNumber(startIndex) || !$type.isNumber(endIndex)) {
1226 return;
1227 }
1228 var start = startIndex / this.dataItems.length;
1229 var end = endIndex / this.dataItems.length;
1230 this.zoom({ start: start, end: end }, skipRangeEvent, instantly);
1231 };
1232 Object.defineProperty(Component.prototype, "zoomFactor", {
1233 /**
1234 * A current zoom factor (0-1). 1 meaning fully zoomed out. (showing all of
1235 * the available data)
1236 *
1237 * @return Zoom factor
1238 */
1239 get: function () {
1240 return $math.fitToRange(1 / (this.end - this.start), 1, this.maxZoomFactor);
1241 },
1242 enumerable: true,
1243 configurable: true
1244 });
1245 Object.defineProperty(Component.prototype, "maxZoomFactor", {
1246 /**
1247 * @return Maximum zoomFactor
1248 */
1249 get: function () {
1250 return this.getPropertyValue("maxZoomFactor");
1251 },
1252 /**
1253 * Max available `zoomFactor`.
1254 *
1255 * The element will not allow zoom to occur beyond this factor.
1256 *
1257 * [[DateAxis]] and [[CategoryAxis]] calculate this atutomatically so that
1258 * category axis could be zoomed to one category and date axis allows to be
1259 * zoomed up to one base interval.
1260 *
1261 * In case you want to restrict category or date axis to be zoomed to more
1262 * than one category or more than one base interval, use `minZoomCount`
1263 * property (set it to `> 1`).
1264 *
1265 * Default value of [[ValueAxis]]'s `maxZoomFactor` is `1000`.
1266 *
1267 * Feel free to modify it to allow bigger zoom or to restrict zooming.
1268 *
1269 * @param value Maximum zoomFactor
1270 */
1271 set: function (value) {
1272 if (this.setPropertyValue("maxZoomFactor", value)) {
1273 if (value == 1) {
1274 this.maxZoomDeclination = 0;
1275 }
1276 this.invalidateDataRange();
1277 }
1278 },
1279 enumerable: true,
1280 configurable: true
1281 });
1282 Object.defineProperty(Component.prototype, "maxZoomDeclination", {
1283 /**
1284 * @ignore
1285 * @return Maximum zoom declination
1286 */
1287 get: function () {
1288 return this.getPropertyValue("maxZoomDeclination");
1289 },
1290 /**
1291 * Max zoom declination.
1292 *
1293 * @ignore
1294 * @default 1
1295 * @param value Maximum zoom declination
1296 */
1297 set: function (value) {
1298 if (this.setPropertyValue("maxZoomDeclination", value)) {
1299 this.invalidateDataRange();
1300 }
1301 },
1302 enumerable: true,
1303 configurable: true
1304 });
1305 Object.defineProperty(Component.prototype, "startIndex", {
1306 /**
1307 * Current starting index.
1308 *
1309 * @return Start index
1310 */
1311 get: function () {
1312 if (!$type.isNumber(this._startIndex)) {
1313 this._startIndex = 0;
1314 }
1315 return this._startIndex;
1316 },
1317 /**
1318 * Sets current starting index.
1319 *
1320 * @ignore Exclude from docs
1321 * @param value Start index
1322 */
1323 set: function (value) {
1324 this._startIndex = $math.fitToRange(Math.round(value), 0, this.dataItems.length);
1325 //this._workingStartIndex = this._startIndex; // not good, breaks adjusted working start index of line series
1326 this.start = this.indexToPosition(this._startIndex);
1327 },
1328 enumerable: true,
1329 configurable: true
1330 });
1331 /**
1332 * @ignore
1333 * @todo:review description
1334 * returns item's relative position by the index of the item
1335 * @param index
1336 */
1337 Component.prototype.indexToPosition = function (index) {
1338 return index / this.dataItems.length;
1339 };
1340 Object.defineProperty(Component.prototype, "endIndex", {
1341 /**
1342 * Current ending index.
1343 *
1344 * @return End index
1345 */
1346 get: function () {
1347 var count = this.dataItems.length;
1348 if (!$type.isNumber(this._endIndex) || this._endIndex > count) {
1349 this._endIndex = count;
1350 }
1351 return this._endIndex;
1352 },
1353 /**
1354 * Sets current ending index.
1355 *
1356 * @ignore Exclude from docs
1357 * @param value End index
1358 */
1359 set: function (value) {
1360 this._endIndex = $math.fitToRange(Math.round(value), 0, this.dataItems.length);
1361 //this._workingEndIndex = this._endIndex; // not good, breaks adjusted workingend index of line series
1362 this.end = this.indexToPosition(this._endIndex);
1363 },
1364 enumerable: true,
1365 configurable: true
1366 });
1367 Object.defineProperty(Component.prototype, "start", {
1368 /**
1369 * @return Start (0-1)
1370 */
1371 get: function () {
1372 if (!this._adapterO) {
1373 return this._start;
1374 }
1375 else {
1376 return this._adapterO.apply("start", this._start);
1377 }
1378 },
1379 /**
1380 * Start of the current data range (zoom).
1381 *
1382 * These are relative values from 0 (beginning) to 1 (end).
1383 *
1384 * @param value Start (0-1)
1385 */
1386 set: function (value) {
1387 // value = $math.round(value, 10); not good
1388 //if (1 / (this.end - value) > this.maxZoomFactor) {
1389 // value = this.end - 1 / this.maxZoomFactor;
1390 //}
1391 if (this._start != value) {
1392 this._start = value;
1393 var startIndex = Math.max(0, Math.floor(this.dataItems.length * value) || 0);
1394 this._startIndex = Math.min(startIndex, this.dataItems.length);
1395 this.invalidateDataRange();
1396 this.invalidate();
1397 this.dispatchImmediately("startchanged");
1398 this.dispatch("startendchanged");
1399 }
1400 },
1401 enumerable: true,
1402 configurable: true
1403 });
1404 Object.defineProperty(Component.prototype, "end", {
1405 /**
1406 * @return End (0-1)
1407 */
1408 get: function () {
1409 if (!this._adapterO) {
1410 return this._end;
1411 }
1412 else {
1413 return this._adapterO.apply("end", this._end);
1414 }
1415 },
1416 /**
1417 * End of the current data range (zoom).
1418 *
1419 * These are relative values from 0 (beginning) to 1 (end).
1420 *
1421 * @param value End (0-1)
1422 */
1423 set: function (value) {
1424 // value = $math.round(value, 10); // not good
1425 //if (1 / (value - this.start) > this.maxZoomFactor) {
1426 // value = 1 / this.maxZoomFactor + this.start;
1427 //}
1428 if (this._end != value) {
1429 this._end = value;
1430 this._endIndex = Math.min(this.dataItems.length, Math.ceil(this.dataItems.length * value) || 0);
1431 this.invalidateDataRange();
1432 this.invalidate();
1433 this.dispatchImmediately("endchanged");
1434 this.dispatch("startendchanged");
1435 }
1436 },
1437 enumerable: true,
1438 configurable: true
1439 });
1440 /**
1441 * [removeFromInvalids description]
1442 *
1443 * @ignore Exclude from docs
1444 * @todo Description
1445 */
1446 Component.prototype.removeFromInvalids = function () {
1447 _super.prototype.removeFromInvalids.call(this);
1448 registry.removeFromInvalidComponents(this);
1449 $array.remove(registry.invalidDataItems, this);
1450 $array.remove(registry.invalidDataRange, this);
1451 $array.remove(registry.invalidRawDatas, this);
1452 };
1453 Object.defineProperty(Component.prototype, "dataItems", {
1454 /**
1455 * Returns a list of source [[DataItem]] objects currently used in the chart.
1456 *
1457 * @return List of data items
1458 */
1459 get: function () {
1460 if (this._currentDataSetId != "") {
1461 var dataItems = this.dataSets.getKey(this._currentDataSetId);
1462 if (dataItems) {
1463 return dataItems;
1464 }
1465 }
1466 return this._dataItems;
1467 },
1468 enumerable: true,
1469 configurable: true
1470 });
1471 Object.defineProperty(Component.prototype, "dataSets", {
1472 /**
1473 * Holds data items for data sets (usually aggregated data).
1474 *
1475 * @ignore
1476 * @since 4.7.0
1477 * @return Data sets
1478 */
1479 get: function () {
1480 if (!this._dataSets) {
1481 this._dataSets = new Dictionary();
1482 }
1483 return this._dataSets;
1484 },
1485 enumerable: true,
1486 configurable: true
1487 });
1488 /**
1489 * Makes the chart use particular data set.
1490 *
1491 * If `id` is not provided or there is no such data set, main data will be
1492 * used.
1493 *
1494 * @ignore
1495 * @since 4.7.0
1496 * @param id Data set id
1497 */
1498 Component.prototype.setDataSet = function (id) {
1499 if (this._currentDataSetId != id) {
1500 var dataSet = this.dataSets.getKey(id);
1501 if (!dataSet) {
1502 if (this._currentDataSetId != "") {
1503 this.dataItems.each(function (dataItem) {
1504 dataItem.__disabled = true;
1505 });
1506 this._currentDataSetId = "";
1507 this.invalidateDataRange();
1508 this._prevStartIndex = undefined;
1509 this.dataItems.each(function (dataItem) {
1510 dataItem.__disabled = false;
1511 });
1512 return true;
1513 }
1514 }
1515 else {
1516 this.dataItems.each(function (dataItem) {
1517 dataItem.__disabled = true;
1518 });
1519 this._currentDataSetId = id;
1520 this.invalidateDataRange();
1521 this._prevStartIndex = undefined;
1522 this.dataItems.each(function (dataItem) {
1523 dataItem.__disabled = false;
1524 });
1525 return true;
1526 }
1527 }
1528 return false;
1529 };
1530 Object.defineProperty(Component.prototype, "currentDataSetId", {
1531 /**
1532 * Returns id of the currently used data set, or `undefined` if main data set
1533 * is in use.
1534 *
1535 * @since 4.7.0
1536 * @return Current data set id
1537 */
1538 get: function () {
1539 return this._currentDataSetId;
1540 },
1541 enumerable: true,
1542 configurable: true
1543 });
1544 Object.defineProperty(Component.prototype, "mainDataSet", {
1545 /**
1546 * Returns reference to "main" data set (unaggregated data as it was supplied
1547 * in `data`).
1548 *
1549 * @since 4.7.0
1550 * @return Main data set
1551 */
1552 get: function () {
1553 return this._dataItems;
1554 },
1555 enumerable: true,
1556 configurable: true
1557 });
1558 /**
1559 * Updates the indexes for the dataItems
1560 *
1561 * @ignore Exclude from docs
1562 */
1563 Component.prototype._updateDataItemIndexes = function (startIndex) {
1564 var dataItems = this.mainDataSet.values;
1565 var length = dataItems.length;
1566 for (var i = startIndex; i < length; ++i) {
1567 dataItems[i]._index = i;
1568 }
1569 };
1570 /**
1571 * Processes newly added [[DataItem]] as well as triggers data re-validation.
1572 *
1573 * @ignore Exclude from docs
1574 * @param event [description]
1575 */
1576 Component.prototype.handleDataItemAdded = function (event) {
1577 event.newValue.component = this;
1578 this._updateDataItemIndexes(event.index);
1579 if (!this.dataItemsInvalid) {
1580 this.invalidateDataItems();
1581 }
1582 };
1583 /**
1584 * removes [[DataItem]] as well as triggers data re-validation.
1585 *
1586 * @ignore Exclude from docs
1587 * @param event [description]
1588 */
1589 Component.prototype.handleDataItemRemoved = function (event) {
1590 // event.oldValue.component = undefined; // not good, as some items might be not removed from component lists
1591 this._updateDataItemIndexes(event.index);
1592 if (!this.dataItemsInvalid) {
1593 this.invalidateDataItems();
1594 }
1595 };
1596 /**
1597 * Binds a data element's field to a specific field in raw data.
1598 * For example, for the very basic column chart you'd want to bind a `value`
1599 * field to a field in data, such as `price`.
1600 *
1601 * Some more advanced Components, like [[CandlestickSeries]] need several
1602 * data fields bound to data, such as ones for open, high, low and close
1603 * values.
1604 *
1605 * @todo Example
1606 * @param field Field name
1607 * @param value Field name in data
1608 */
1609 Component.prototype.bindDataField = function (field, value) {
1610 this.dataFields[field] = value;
1611 this.invalidateDataRange();
1612 };
1613 /**
1614 * Invalidates processed data.
1615 *
1616 * @ignore Exclude from docs
1617 */
1618 Component.prototype.invalidateProcessedData = function () {
1619 this.resetProcessedRange();
1620 this.invalidateDataRange();
1621 };
1622 /**
1623 * [resetProcessedRange description]
1624 *
1625 * @ignore Exclude from docs
1626 * @todo Description
1627 */
1628 Component.prototype.resetProcessedRange = function () {
1629 this._prevEndIndex = null;
1630 this._prevStartIndex = null;
1631 };
1632 Object.defineProperty(Component.prototype, "dataUsers", {
1633 /**
1634 * Returns all other [[Component]] objects that are using this element's
1635 * data.
1636 *
1637 * @ignore Exclude from docs
1638 * @todo Description (review)
1639 * @return [description]
1640 */
1641 get: function () {
1642 var _this = this;
1643 if (!this._dataUsers) {
1644 this._dataUsers = new List();
1645 //this._disposers.push(new ListDisposer(this._dataUsers));
1646 // TODO better way of handling this? e.g. move into another module ?
1647 this._disposers.push(new Disposer(function () {
1648 // TODO clear the list ?
1649 $iter.each(_this._dataUsers.iterator(), function (x) {
1650 x.dispose();
1651 });
1652 }));
1653 }
1654 return this._dataUsers;
1655 },
1656 enumerable: true,
1657 configurable: true
1658 });
1659 /**
1660 * Returns a clone of this element.
1661 *
1662 * @return Clone
1663 */
1664 Component.prototype.clone = function () {
1665 var component = _super.prototype.clone.call(this);
1666 component.dataFields = $utils.copyProperties(this.dataFields, {});
1667 return component;
1668 };
1669 /**
1670 * Copies all parameters from another [[Component]].
1671 *
1672 * @param source Source Component
1673 */
1674 Component.prototype.copyFrom = function (source) {
1675 _super.prototype.copyFrom.call(this, source);
1676 this.data = source.data;
1677 this.sequencedInterpolation = source.sequencedInterpolation;
1678 this.sequencedInterpolationDelay = source.sequencedInterpolationDelay;
1679 this.interpolationDuration = source.interpolationDuration;
1680 this.interpolationEasing = source.interpolationEasing;
1681 };
1682 /**
1683 * Invalidates the whole element, including all its children, causing
1684 * complete re-parsing of data and redraw.
1685 *
1686 * Use sparingly!
1687 */
1688 Component.prototype.reinit = function () {
1689 this._inited = false;
1690 this.deepInvalidate();
1691 };
1692 /**
1693 * Add an adapter for data.
1694 *
1695 * @return Exporting
1696 */
1697 Component.prototype.getExporting = function () {
1698 var _export = _super.prototype.getExporting.call(this);
1699 if (!_export.adapter.has("data", this._exportData, -1, this)) {
1700 _export.adapter.add("data", this._exportData, -1, this);
1701 this.events.on("datavalidated", function (ev) {
1702 _export.handleDataUpdated();
1703 });
1704 }
1705 return _export;
1706 };
1707 Component.prototype._exportData = function (arg) {
1708 arg.data = this.data;
1709 return arg;
1710 };
1711 Component.prototype.setDisabled = function (value) {
1712 var changed = _super.prototype.setDisabled.call(this, value);
1713 if (changed) {
1714 this.invalidateData();
1715 }
1716 return changed;
1717 };
1718 /**
1719 * @ignore
1720 */
1721 Component.prototype.setShowOnInit = function (value) {
1722 if (value != this.getPropertyValue("showOnInit")) {
1723 if (value && !this.inited && !this.hidden) {
1724 this._showOnInitDisposer2 = this.events.once("dataitemsvalidated", this.hideInitially, this, false);
1725 this._disposers.push(this._showOnInitDisposer2);
1726 }
1727 else {
1728 if (this._showOnInitDisposer2) {
1729 this.removeDispose(this._showOnInitDisposer2);
1730 }
1731 }
1732 }
1733 // important order here
1734 _super.prototype.setShowOnInit.call(this, value);
1735 };
1736 Component.prototype.setBaseId = function (value) {
1737 if (value != this._baseId) {
1738 if (this.dataInvalid) {
1739 this.dataInvalid = false;
1740 registry.removeFromInvalidComponents(this);
1741 this._baseId = value;
1742 this.invalidateData();
1743 }
1744 }
1745 _super.prototype.setBaseId.call(this, value);
1746 };
1747 Object.defineProperty(Component.prototype, "minZoomCount", {
1748 /**
1749 * @return Min zoom count
1750 */
1751 get: function () {
1752 return this.getPropertyValue("minZoomCount");
1753 },
1754 /**
1755 * Use this for [[CategoryAxis]] or [[DateAxis]].
1756 *
1757 * Allows restricting zoom in beyond certain number of categories or base
1758 * intervals.
1759 *
1760 * @default 1
1761 * @param value Min zoom count
1762 */
1763 set: function (value) {
1764 this.setPropertyValue("minZoomCount", value);
1765 },
1766 enumerable: true,
1767 configurable: true
1768 });
1769 Object.defineProperty(Component.prototype, "maxZoomCount", {
1770 /**
1771 * @return Max zoom count
1772 */
1773 get: function () {
1774 return this.getPropertyValue("maxZoomCount");
1775 },
1776 /**
1777 * Use this for [[CategoryAxis]] or [[DateAxis]].
1778 *
1779 * Limits how many categories or base intervals can be shown at the same
1780 * time.
1781 *
1782 * If there are more items in the chart, the chart will auto-zoom.
1783 *
1784 * @default 0 (no limit)
1785 * @since 4.6.2
1786 * @param value Max zoom count
1787 */
1788 set: function (value) {
1789 this.setPropertyValue("maxZoomCount", value);
1790 },
1791 enumerable: true,
1792 configurable: true
1793 });
1794 /**
1795 * Called during the System.update method
1796 *
1797 * @ignore Exclude from docs
1798 */
1799 Component.prototype._systemCheckIfValidate = function () {
1800 if (this.dataInvalid || (this.dataProvider && this.dataProvider.dataInvalid)) {
1801 return false;
1802 }
1803 else {
1804 return true;
1805 }
1806 };
1807 /**
1808 * Adds easing functions to "function" fields.
1809 *
1810 * @param field Field name
1811 * @return Assign as function?
1812 */
1813 Component.prototype.asFunction = function (field) {
1814 return field == "interpolationEasing" || field == "rangeChangeEasing" || _super.prototype.asIs.call(this, field);
1815 };
1816 return Component;
1817}(Container));
1818export { Component };
1819/**
1820 * Register class in system, so that it can be instantiated using its name from
1821 * anywhere.
1822 *
1823 * @ignore
1824 */
1825registry.registeredClasses["Component"] = Component;
1826//# sourceMappingURL=Component.js.map
\No newline at end of file