1 | /**
|
2 | * Base class for all Axis
|
3 | */
|
4 | import { __extends } from "tslib";
|
5 | /**
|
6 | * ============================================================================
|
7 | * IMPORTS
|
8 | * ============================================================================
|
9 | * @hidden
|
10 | */
|
11 | import { Component } from "../../core/Component";
|
12 | import { Container } from "../../core/Container";
|
13 | import { DataItem } from "../../core/DataItem";
|
14 | import { AxisBreak } from "./AxisBreak";
|
15 | import { Label } from "../../core/elements/Label";
|
16 | import { Tooltip } from "../../core/elements/Tooltip";
|
17 | import { SortedListTemplate } from "../../core/utils/SortedList";
|
18 | import { List, ListTemplate, ListDisposer } from "../../core/utils/List";
|
19 | import { Disposer, MultiDisposer } from "../../core/utils/Disposer";
|
20 | import { registry } from "../../core/Registry";
|
21 | import { InterfaceColorSet } from "../../core/utils/InterfaceColorSet";
|
22 | import * as $iter from "../../core/utils/Iterator";
|
23 | import * as $math from "../../core/utils/Math";
|
24 | import * as $utils from "../../core/utils/Utils";
|
25 | import * as $number from "../../core/utils/Number";
|
26 | import * as $array from "../../core/utils/Array";
|
27 | import * as $type from "../../core/utils/Type";
|
28 | import { defaultRules, ResponsiveBreakpoints } from "../../core/utils/Responsive";
|
29 | /**
|
30 | * ============================================================================
|
31 | * DATA ITEM
|
32 | * ============================================================================
|
33 | * @hidden
|
34 | */
|
35 | /**
|
36 | * Defines a [[DataItem]] for [[Axis]].
|
37 | *
|
38 | * @see {@link DataItem}
|
39 | */
|
40 | var AxisDataItem = /** @class */ (function (_super) {
|
41 | __extends(AxisDataItem, _super);
|
42 | /**
|
43 | * Constructor
|
44 | */
|
45 | function AxisDataItem() {
|
46 | var _this = _super.call(this) || this;
|
47 | _this.className = "AxisDataItem";
|
48 | _this.applyTheme();
|
49 | return _this;
|
50 | }
|
51 | Object.defineProperty(AxisDataItem.prototype, "grid", {
|
52 | /**
|
53 | * @return Grid element
|
54 | */
|
55 | get: function () {
|
56 | if (!this._grid) {
|
57 | var component_1 = this.component;
|
58 | if (component_1) {
|
59 | var template = void 0;
|
60 | var grid_1;
|
61 | if (this.isRange) {
|
62 | template = component_1.axisRanges.template.grid;
|
63 | if (template.disabled) {
|
64 | return;
|
65 | }
|
66 | else {
|
67 | grid_1 = template.clone();
|
68 | }
|
69 | }
|
70 | else {
|
71 | template = component_1.renderer.grid.template;
|
72 | if (template.disabled) {
|
73 | return;
|
74 | }
|
75 | else {
|
76 | grid_1 = component_1.renderer.grid.create();
|
77 | this._disposers.push(new Disposer(function () {
|
78 | component_1.renderer.grid.removeValue(grid_1);
|
79 | }));
|
80 | }
|
81 | }
|
82 | this.grid = grid_1;
|
83 | grid_1.shouldClone = false;
|
84 | this._disposers.push(grid_1);
|
85 | grid_1.axis = this.component;
|
86 | }
|
87 | }
|
88 | return this._grid;
|
89 | },
|
90 | /**
|
91 | * A [[Grid]] element associated with this data item.
|
92 | *
|
93 | * If there is no grid element associated with data item, a new one is
|
94 | * created and returned.
|
95 | *
|
96 | * @param grid Grid element
|
97 | */
|
98 | set: function (grid) {
|
99 | if (this._grid && this._grid != grid) {
|
100 | $array.remove(this.sprites, this._grid);
|
101 | this._grid.dataItem = undefined;
|
102 | }
|
103 | if (grid) {
|
104 | if (grid.dataItem && grid.dataItem != this) {
|
105 | $array.remove(grid.dataItem.sprites, grid);
|
106 | grid.dataItem.grid = undefined;
|
107 | }
|
108 | this.addSprite(grid);
|
109 | }
|
110 | this._grid = grid;
|
111 | },
|
112 | enumerable: true,
|
113 | configurable: true
|
114 | });
|
115 | Object.defineProperty(AxisDataItem.prototype, "tick", {
|
116 | /**
|
117 | * @return Tick element
|
118 | */
|
119 | get: function () {
|
120 | if (!this._tick) {
|
121 | var component_2 = this.component;
|
122 | if (component_2) {
|
123 | var template = void 0;
|
124 | var tick_1;
|
125 | if (this.isRange) {
|
126 | template = component_2.axisRanges.template.tick;
|
127 | if (template.disabled) {
|
128 | return;
|
129 | }
|
130 | else {
|
131 | tick_1 = template.clone();
|
132 | }
|
133 | }
|
134 | else {
|
135 | template = component_2.renderer.ticks.template;
|
136 | if (template.disabled) {
|
137 | return;
|
138 | }
|
139 | else {
|
140 | tick_1 = component_2.renderer.ticks.create();
|
141 | this._disposers.push(new Disposer(function () {
|
142 | component_2.renderer.ticks.removeValue(tick_1);
|
143 | }));
|
144 | }
|
145 | }
|
146 | this.tick = tick_1;
|
147 | tick_1.axis = this.component;
|
148 | tick_1.shouldClone = false;
|
149 | this._disposers.push(tick_1);
|
150 | }
|
151 | }
|
152 | return this._tick;
|
153 | },
|
154 | /**
|
155 | * An [[AxisTick]] element associated with this data item.
|
156 | *
|
157 | * If there is no tick element associated with data item, a new one is
|
158 | * created and returned.
|
159 | *
|
160 | * @param tick Tick element
|
161 | */
|
162 | set: function (tick) {
|
163 | if (this._tick && this._tick != tick) {
|
164 | $array.remove(this.sprites, this._tick);
|
165 | this._tick.dataItem = undefined;
|
166 | }
|
167 | if (tick) {
|
168 | if (tick.dataItem && tick.dataItem != this) {
|
169 | $array.remove(tick.dataItem.sprites, tick);
|
170 | tick.dataItem.tick = undefined;
|
171 | }
|
172 | this.addSprite(tick);
|
173 | }
|
174 | this._tick = tick;
|
175 | },
|
176 | enumerable: true,
|
177 | configurable: true
|
178 | });
|
179 | Object.defineProperty(AxisDataItem.prototype, "label", {
|
180 | /**
|
181 | * @return Label element
|
182 | */
|
183 | get: function () {
|
184 | if (!this._label) {
|
185 | var component_3 = this.component;
|
186 | if (component_3) {
|
187 | var template = void 0;
|
188 | var label_1;
|
189 | if (this.isRange) {
|
190 | template = component_3.axisRanges.template.label;
|
191 | if (template.disabled) {
|
192 | return;
|
193 | }
|
194 | else {
|
195 | label_1 = template.clone();
|
196 | }
|
197 | }
|
198 | else {
|
199 | template = component_3.renderer.labels.template;
|
200 | if (template.disabled) {
|
201 | return;
|
202 | }
|
203 | else {
|
204 | label_1 = component_3.renderer.labels.create();
|
205 | this._disposers.push(new Disposer(function () {
|
206 | component_3.renderer.labels.removeValue(label_1);
|
207 | }));
|
208 | }
|
209 | }
|
210 | this._disposers.push(label_1);
|
211 | this.label = label_1;
|
212 | label_1.shouldClone = false;
|
213 | label_1.axis = this.component;
|
214 | label_1.virtualParent = component_3;
|
215 | }
|
216 | }
|
217 | return this._label;
|
218 | },
|
219 | /**
|
220 | * An [[AxisLabel]] element associated with this data item.
|
221 | *
|
222 | * If there is no label element associated with data item, a new one is
|
223 | * created and returned.
|
224 | *
|
225 | * @param label Label element
|
226 | */
|
227 | set: function (label) {
|
228 | if (this._label && this._label != label) {
|
229 | $array.remove(this.sprites, this._label);
|
230 | this._label.dataItem = undefined;
|
231 | }
|
232 | if (label) {
|
233 | if (label.dataItem && label.dataItem != this) {
|
234 | $array.remove(label.dataItem.sprites, label);
|
235 | label.dataItem.label = undefined;
|
236 | }
|
237 | this.addSprite(label);
|
238 | }
|
239 | this._label = label;
|
240 | },
|
241 | enumerable: true,
|
242 | configurable: true
|
243 | });
|
244 | Object.defineProperty(AxisDataItem.prototype, "axisFill", {
|
245 | /**
|
246 | * @return Label element
|
247 | */
|
248 | get: function () {
|
249 | if (!this._axisFill) {
|
250 | var component_4 = this.component;
|
251 | if (component_4) {
|
252 | var template = void 0;
|
253 | var axisFill_1;
|
254 | if (this.isRange) {
|
255 | template = component_4.axisRanges.template.axisFill;
|
256 | if (!this.isTemplate && template.disabled) {
|
257 | return;
|
258 | }
|
259 | else {
|
260 | axisFill_1 = template.clone();
|
261 | }
|
262 | }
|
263 | else {
|
264 | template = component_4.renderer.axisFills.template;
|
265 | if (template.disabled) {
|
266 | return;
|
267 | }
|
268 | else {
|
269 | axisFill_1 = component_4.renderer.axisFills.create();
|
270 | this._disposers.push(new Disposer(function () {
|
271 | component_4.renderer.axisFills.removeValue(axisFill_1);
|
272 | }));
|
273 | }
|
274 | }
|
275 | this.axisFill = axisFill_1;
|
276 | axisFill_1.shouldClone = false;
|
277 | this._disposers.push(axisFill_1);
|
278 | }
|
279 | }
|
280 | return this._axisFill;
|
281 | },
|
282 | /**
|
283 | * An [[AxisFill]] associated element with this data item.
|
284 | *
|
285 | * If there is no fill element associated with data item, a new one is
|
286 | * created and returned.
|
287 | *
|
288 | * @param label Label element
|
289 | */
|
290 | set: function (axisFill) {
|
291 | if (this._axisFill && this._axisFill != axisFill) {
|
292 | $array.remove(this.sprites, this._axisFill);
|
293 | this._axisFill.dataItem = undefined;
|
294 | }
|
295 | if (axisFill) {
|
296 | if (axisFill.dataItem && axisFill.dataItem != this) {
|
297 | $array.remove(axisFill.dataItem.sprites, axisFill);
|
298 | axisFill.dataItem.axisFill = undefined;
|
299 | }
|
300 | axisFill.axis = this.component;
|
301 | this.addSprite(axisFill);
|
302 | }
|
303 | this._axisFill = axisFill;
|
304 | },
|
305 | enumerable: true,
|
306 | configurable: true
|
307 | });
|
308 | Object.defineProperty(AxisDataItem.prototype, "text", {
|
309 | /**
|
310 | * @return Text label
|
311 | */
|
312 | get: function () {
|
313 | return this._text;
|
314 | },
|
315 | /**
|
316 | * Text to be used as data item's label.
|
317 | *
|
318 | * @param text Text label
|
319 | */
|
320 | set: function (text) {
|
321 | this._text = text;
|
322 | if (this._label) { // do not use getter, it will create unwanted instances!
|
323 | this._label.text = text;
|
324 | }
|
325 | },
|
326 | enumerable: true,
|
327 | configurable: true
|
328 | });
|
329 | Object.defineProperty(AxisDataItem.prototype, "mask", {
|
330 | /**
|
331 | * Data item's mask.
|
332 | *
|
333 | * @return Mask
|
334 | */
|
335 | get: function () {
|
336 | return this._mask;
|
337 | },
|
338 | enumerable: true,
|
339 | configurable: true
|
340 | });
|
341 | Object.defineProperty(AxisDataItem.prototype, "contents", {
|
342 | /**
|
343 | * Returns a [[Container]] to place all visual elements, related to data item
|
344 | * in.
|
345 | *
|
346 | * If there is no Container, a new one is created.
|
347 | *
|
348 | * @return Contents container
|
349 | */
|
350 | get: function () {
|
351 | if (!this._contents) {
|
352 | var contents = new Container();
|
353 | this.addSprite(contents);
|
354 | contents.isMeasured = false;
|
355 | this._contents = contents;
|
356 | var component = this.component;
|
357 | if (component) {
|
358 | var mask = component.renderer.createFill(this.component);
|
359 | mask.disabled = false;
|
360 | mask.axis = component;
|
361 | this.addSprite(mask);
|
362 | this._mask = mask;
|
363 | contents.mask = mask;
|
364 | }
|
365 | }
|
366 | return this._contents;
|
367 | },
|
368 | enumerable: true,
|
369 | configurable: true
|
370 | });
|
371 | Object.defineProperty(AxisDataItem.prototype, "axisBreak", {
|
372 | /**
|
373 | * @return Axis break
|
374 | */
|
375 | get: function () {
|
376 | return this._axisBreak;
|
377 | },
|
378 | /**
|
379 | * An [[AxisBreak]] this data item falls within.
|
380 | *
|
381 | * @param axisBreak Axis break
|
382 | */
|
383 | set: function (axisBreak) {
|
384 | if (this._axisBreak) {
|
385 | this._axisBreak.dataItems.removeValue(this);
|
386 | }
|
387 | if (axisBreak) {
|
388 | axisBreak.dataItems.push(this);
|
389 | }
|
390 | this._axisBreak = axisBreak;
|
391 | },
|
392 | enumerable: true,
|
393 | configurable: true
|
394 | });
|
395 | /**
|
396 | * Re-draws the element.
|
397 | *
|
398 | * @ignore Exclude from docs
|
399 | */
|
400 | AxisDataItem.prototype.validate = function () {
|
401 | if (this.component) {
|
402 | this.component.validateDataElement(this);
|
403 | }
|
404 | };
|
405 | /**
|
406 | * Appends data item's elements to the parent [[Container]].
|
407 | *
|
408 | * @ignore Exclude from docs
|
409 | */
|
410 | AxisDataItem.prototype.appendChildren = function () {
|
411 | if (this.component) {
|
412 | this.component.appendDataItem(this);
|
413 | }
|
414 | };
|
415 | /**
|
416 | * Checks if data item has particular property set.
|
417 | *
|
418 | * @param prop Property name
|
419 | * @return Property set?
|
420 | */
|
421 | AxisDataItem.prototype.hasProperty = function (prop) {
|
422 | return prop == "component" ? true : _super.prototype.hasProperty.call(this, prop);
|
423 | };
|
424 | /**
|
425 | * Copies all parameters from another [[AxisDataItem]].
|
426 | *
|
427 | * @param source Source AxisDataItem
|
428 | */
|
429 | AxisDataItem.prototype.copyFrom = function (source) {
|
430 | _super.prototype.copyFrom.call(this, source);
|
431 | this.text = source.text;
|
432 | if (source.bullet) {
|
433 | this.bullet = source.bullet.clone();
|
434 | }
|
435 | this.minPosition = source.minPosition;
|
436 | this.maxPosition = source.maxPosition;
|
437 | };
|
438 | /**
|
439 | * Sets visibility of the Data Item.
|
440 | *
|
441 | * @param value Data Item
|
442 | */
|
443 | AxisDataItem.prototype.setVisibility = function (value, noChangeValues) {
|
444 | _super.prototype.setVisibility.call(this, value, noChangeValues);
|
445 | if (this._contents) {
|
446 | this._contents.visible = value;
|
447 | }
|
448 | };
|
449 | Object.defineProperty(AxisDataItem.prototype, "bullet", {
|
450 | /**
|
451 | * @return Bullet
|
452 | */
|
453 | get: function () {
|
454 | return this._bullet;
|
455 | },
|
456 | /**
|
457 | * Set it to an instance of any [[Sprite]]. It will be displayed as an axis
|
458 | * bullet in the middle of the cell, or specific value.
|
459 | *
|
460 | * If you need position bullet relatively to the cell, use [[AxisBullet]]
|
461 | * instead. It has a `location` property which can be used to indicate
|
462 | * precise relative location within cell/range.
|
463 | *
|
464 | * Also, [[AxisBullet]] is a [[Container]] so you can push any other element
|
465 | * into it.
|
466 | *
|
467 | * NOTE: `location` is relative to the parent axis range's scope, i.e.
|
468 | * between its `date` and `endDate` for [[DateAxis]], or `value`/`endValue`
|
469 | * ([[ValueAxis]]), or `category`/`endCategory` ([[categoryAxis]]).
|
470 | *
|
471 | * ```TypeScript
|
472 | * let range = dateAxis.axisRanges.create();
|
473 | * range.date = new Date(2018, 0, 5);
|
474 | *
|
475 | * let flag = new am4plugins_bullets.FlagBullet();
|
476 | * flag.label.text = "Hello";
|
477 | *
|
478 | * range.bullet = flag;
|
479 | * ```
|
480 | * ```JavaScript
|
481 | * var range = dateAxis.axisRanges.create();
|
482 | * range.date = new Date(2018, 0, 5);
|
483 | *
|
484 | * var flag = new am4plugins_bullets.FlagBullet();
|
485 | * flag.label.text = "Hello";
|
486 | *
|
487 | * range.bullet = flag;
|
488 | * ```
|
489 | * ```JSON
|
490 | * {
|
491 | * // ...
|
492 | * "xAxes": [{
|
493 | * "type": "DateAxis",
|
494 | * // ...
|
495 | * "axisRanges": [{
|
496 | * "date": new Date(2018, 0, 5),
|
497 | * "bullet: {
|
498 | * "type": "FlagBullet",
|
499 | * "label": {
|
500 | * "text": "Hello"
|
501 | * }
|
502 | * }
|
503 | * }]
|
504 | * }]
|
505 | * }
|
506 | * ```
|
507 | *
|
508 | * @since 4.5.9
|
509 | * @param value Bullet
|
510 | */
|
511 | set: function (value) {
|
512 | if (this._bullet && this._bullet != value) {
|
513 | $array.remove(this.sprites, this._bullet);
|
514 | this._bullet.dataItem = undefined;
|
515 | }
|
516 | this._bullet = value;
|
517 | if (value) {
|
518 | this.addSprite(value);
|
519 | }
|
520 | },
|
521 | enumerable: true,
|
522 | configurable: true
|
523 | });
|
524 | return AxisDataItem;
|
525 | }(DataItem));
|
526 | export { AxisDataItem };
|
527 | /**
|
528 | * ============================================================================
|
529 | * REQUISITES
|
530 | * ============================================================================
|
531 | * @hidden
|
532 | */
|
533 | /**
|
534 | * Defines named positions for data item's location within [[Axis]].
|
535 | */
|
536 | export var AxisItemLocation;
|
537 | (function (AxisItemLocation) {
|
538 | AxisItemLocation[AxisItemLocation["Start"] = 0] = "Start";
|
539 | AxisItemLocation[AxisItemLocation["Middle"] = 0.5] = "Middle";
|
540 | AxisItemLocation[AxisItemLocation["End"] = 1] = "End";
|
541 | })(AxisItemLocation || (AxisItemLocation = {}));
|
542 | /**
|
543 | * ============================================================================
|
544 | * MAIN CLASS
|
545 | * ============================================================================
|
546 | * @hidden
|
547 | */
|
548 | /**
|
549 | * A base class for all Axis elements.
|
550 | *
|
551 | * @see {@link IAxisEvents} for a list of available Events
|
552 | * @see {@link IAxisAdapters} for a list of available Adapters
|
553 | */
|
554 | var Axis = /** @class */ (function (_super) {
|
555 | __extends(Axis, _super);
|
556 | /**
|
557 | * Constructor
|
558 | */
|
559 | function Axis() {
|
560 | var _this =
|
561 | // Init
|
562 | _super.call(this) || this;
|
563 | /**
|
564 | * Number of Grid elements on the axis.
|
565 | */
|
566 | _this._gridCount = 10;
|
567 | /**
|
568 | * A list of [[XYSeries]] that are using this Axis.
|
569 | */
|
570 | _this._series = new List();
|
571 | /**
|
572 | * Specifies if axis should be automatically disposed when removing from
|
573 | * chart's axis list.
|
574 | *
|
575 | * @default true
|
576 | */
|
577 | _this.autoDispose = true;
|
578 | /**
|
579 | * @ignore
|
580 | */
|
581 | _this._axisItemCount = 0;
|
582 | if (_this.constructor === Axis) {
|
583 | throw new Error("'Axis' cannot be instantiated directly. Please use a specific axis type.");
|
584 | }
|
585 | _this.hideTooltipWhileZooming = true;
|
586 | _this.minWidth = 0.0001;
|
587 | _this.minHeight = 0.0001;
|
588 | _this.className = "Axis";
|
589 | _this.shouldClone = false;
|
590 | _this.setPropertyValue("cursorTooltipEnabled", true);
|
591 | _this.toggleZoomOutButton = true;
|
592 | _this.zoomable = true;
|
593 | var interfaceColors = new InterfaceColorSet();
|
594 | // Create title
|
595 | _this.title = new Label();
|
596 | _this.title.shouldClone = false;
|
597 | _this._disposers.push(_this.title);
|
598 | _this.setPropertyValue("startLocation", 0);
|
599 | _this.setPropertyValue("endLocation", 1);
|
600 | // Data item iterator
|
601 | _this._dataItemsIterator = new $iter.ListIterator(_this.dataItems, function () { return _this.dataItems.create(); });
|
602 | _this._dataItemsIterator.createNewItems = true;
|
603 | // Create tooltip
|
604 | var tooltip = new Tooltip();
|
605 | _this._disposers.push(tooltip);
|
606 | tooltip.label.padding(5, 10, 5, 10);
|
607 | tooltip.background.pointerLength = 5;
|
608 | tooltip.fitPointerToBounds = true;
|
609 | tooltip.background.filters.clear();
|
610 | // Set virtual parentfor the tooltip so that it can properly inheirt
|
611 | // formatters from the axis.
|
612 | tooltip.virtualParent = _this;
|
613 | // Create background element for the tooltip
|
614 | var background = tooltip.background;
|
615 | background.cornerRadius = 0;
|
616 | background.fill = interfaceColors.getFor("alternativeBackground");
|
617 | background.stroke = background.fill;
|
618 | background.strokeWidth = 1;
|
619 | background.fillOpacity = 1;
|
620 | tooltip.label.fill = interfaceColors.getFor("alternativeText");
|
621 | _this.tooltip = tooltip;
|
622 | // Accessibility
|
623 | _this.readerHidden = true;
|
624 | _this.events.on("rangechangestarted", function () {
|
625 | _this.series.each(function (series) {
|
626 | if (series.hideTooltipWhileZooming) {
|
627 | series.tooltip.hide();
|
628 | series.tooltip.preventShow = true;
|
629 | }
|
630 | });
|
631 | if (_this.hideTooltipWhileZooming) {
|
632 | _this.tooltip.hide();
|
633 | _this.tooltip.preventShow = true;
|
634 | }
|
635 | }, undefined, false);
|
636 | _this.events.on("rangechangeended", function () {
|
637 | _this.series.each(function (series) {
|
638 | if (series.hideTooltipWhileZooming) {
|
639 | series.tooltip.hide();
|
640 | series.tooltip.preventShow = false;
|
641 | }
|
642 | });
|
643 | if (_this.hideTooltipWhileZooming) {
|
644 | _this.tooltip.hide();
|
645 | _this.tooltip.preventShow = false;
|
646 | }
|
647 | }, undefined, false);
|
648 | _this.applyTheme();
|
649 | return _this;
|
650 | }
|
651 | /**
|
652 | * Holds reference to a function that accepts a DataItem and its index as
|
653 | * parameters.
|
654 | *
|
655 | * It can either return a fill opacity for a fill, or manipulate data item
|
656 | * directly, to create various highlighting scenarios.
|
657 | *
|
658 | * For example, you can set it up to highlight only weekends on a
|
659 | * [[DateAxis]].
|
660 | */
|
661 | Axis.prototype.fillRule = function (dataItem, index) {
|
662 | if (!$type.isNumber(index)) {
|
663 | index = dataItem.index;
|
664 | }
|
665 | if (index / 2 == Math.round(index / 2)) {
|
666 | dataItem.axisFill.__disabled = true;
|
667 | dataItem.axisFill.opacity = 0;
|
668 | }
|
669 | else {
|
670 | dataItem.axisFill.opacity = 1;
|
671 | dataItem.axisFill.__disabled = false;
|
672 | }
|
673 | };
|
674 | /**
|
675 | * Returns a new/empty DataItem of the type appropriate for this object.
|
676 | *
|
677 | * @see {@link DataItem}
|
678 | * @return Data Item
|
679 | */
|
680 | Axis.prototype.createDataItem = function () {
|
681 | return new AxisDataItem();
|
682 | };
|
683 | /**
|
684 | * Invalidates layout.
|
685 | *
|
686 | * @ignore Exclude from docs
|
687 | */
|
688 | Axis.prototype.invalidateLayout = function () {
|
689 | _super.prototype.invalidateLayout.call(this);
|
690 | // this puts series after axis in invalidation order also makes series update it's data items in case widht/height of a series is not 100%
|
691 | $iter.each(this.series.iterator(), function (series) {
|
692 | series.invalidateLayout();
|
693 | });
|
694 | };
|
695 | /**
|
696 | * Invalidates series of this axis.
|
697 | */
|
698 | Axis.prototype.invalidateSeries = function () {
|
699 | // this puts series after axis in invalidation order also makes series update it's data items in case widht/height of a series is not 100%
|
700 | $iter.each(this.series.iterator(), function (series) {
|
701 | series.invalidate();
|
702 | });
|
703 | };
|
704 | /**
|
705 | * Override to cancel super call for data element validation.
|
706 | * @ignore
|
707 | */
|
708 | Axis.prototype.validateDataElements = function () {
|
709 | this._axisItemCount = 0;
|
710 | if (this.ghostLabel) {
|
711 | this.renderer.updateLabelElement(this.ghostLabel, this.start, this.end);
|
712 | this.ghostLabel.validate();
|
713 | }
|
714 | };
|
715 | /**
|
716 | * Recalculates the number of grid items on the axis.
|
717 | */
|
718 | Axis.prototype.updateGridCount = function () {
|
719 | if (this.renderer) {
|
720 | var gridCount = this.axisLength / this.renderer.minGridDistance;
|
721 | if (gridCount != this._gridCount) {
|
722 | this._gridCount = gridCount;
|
723 | this.clearCache();
|
724 | }
|
725 | }
|
726 | };
|
727 | /**
|
728 | * Redraws the element.
|
729 | *
|
730 | * @ignore Exclude from docs
|
731 | */
|
732 | Axis.prototype.validateLayout = function () {
|
733 | this.axisFullLength = this.axisLength / (this.end - this.start);
|
734 | _super.prototype.validateLayout.call(this);
|
735 | this.updateGridCount();
|
736 | var renderer = this.renderer;
|
737 | if (renderer) {
|
738 | renderer.updateAxisLine();
|
739 | renderer.updateTooltip();
|
740 | renderer.updateBaseGridElement();
|
741 | }
|
742 | if (this._prevLength != this.axisLength) {
|
743 | this.dispatchImmediately("lengthchanged");
|
744 | this._prevLength = this.axisLength;
|
745 | }
|
746 | };
|
747 | /**
|
748 | * Initializes Axis' renderer.
|
749 | *
|
750 | * @ignore Exclude from docs
|
751 | */
|
752 | Axis.prototype.initRenderer = function () {
|
753 | };
|
754 | /**
|
755 | * Adds a data item to the Axis.
|
756 | *
|
757 | * @param dataItem Data item
|
758 | */
|
759 | Axis.prototype.appendDataItem = function (dataItem) {
|
760 | var renderer = this.renderer;
|
761 | var tick = dataItem.tick;
|
762 | if (tick) {
|
763 | if (tick.above) {
|
764 | tick.parent = renderer.bulletsContainer;
|
765 | }
|
766 | else {
|
767 | tick.parent = renderer.gridContainer;
|
768 | }
|
769 | }
|
770 | if (dataItem.label) {
|
771 | dataItem.label.parent = renderer;
|
772 | }
|
773 | var axisFill = dataItem.axisFill;
|
774 | if (axisFill) {
|
775 | if (axisFill.above) {
|
776 | axisFill.parent = renderer.bulletsContainer;
|
777 | }
|
778 | else {
|
779 | axisFill.parent = renderer.gridContainer;
|
780 | }
|
781 | }
|
782 | var grid = dataItem.grid;
|
783 | if (grid) {
|
784 | if (grid.above) {
|
785 | grid.parent = renderer.bulletsContainer;
|
786 | }
|
787 | else {
|
788 | grid.parent = renderer.gridContainer;
|
789 | }
|
790 | }
|
791 | if (dataItem.bullet) {
|
792 | dataItem.bullet.parent = renderer.bulletsContainer;
|
793 | }
|
794 | };
|
795 | /**
|
796 | * Redraws Axis' related items.
|
797 | *
|
798 | * @ignore Exclude from docs
|
799 | */
|
800 | Axis.prototype.validate = function () {
|
801 | _super.prototype.validate.call(this);
|
802 | this.validateLayout();
|
803 | this.renderer.updateGridContainer();
|
804 | };
|
805 | /**
|
806 | * Redars Axis ranges.
|
807 | *
|
808 | * @ignore Exclude from docs
|
809 | */
|
810 | Axis.prototype.validateAxisRanges = function () {
|
811 | var _this = this;
|
812 | $iter.each(this.axisRanges.iterator(), function (axisRange) {
|
813 | _this.appendDataItem(axisRange);
|
814 | _this.validateDataElement(axisRange);
|
815 | if (axisRange.grid) {
|
816 | axisRange.grid.validate();
|
817 | }
|
818 | if (axisRange.tick) {
|
819 | axisRange.tick.validate();
|
820 | }
|
821 | if (axisRange.axisFill) {
|
822 | axisRange.axisFill.validate();
|
823 | }
|
824 | if (axisRange.label) {
|
825 | axisRange.label.validate();
|
826 | }
|
827 | });
|
828 | };
|
829 | /**
|
830 | * Invalidates all axis breaks, so they are redrawn.
|
831 | *
|
832 | * @ignore Exclude from docs
|
833 | */
|
834 | Axis.prototype.validateBreaks = function () {
|
835 | if (this._axisBreaks) {
|
836 | $iter.each(this._axisBreaks.iterator(), function (axisBreak) {
|
837 | axisBreak.invalidate();
|
838 | });
|
839 | }
|
840 | };
|
841 | /**
|
842 | * Associates an Axis break with this Axis, after it is inserted into
|
843 | * `axisBreaks`.
|
844 | *
|
845 | * @ignore Exclude from docs
|
846 | * @param event Event
|
847 | */
|
848 | Axis.prototype.processBreak = function (event) {
|
849 | var axisBreak = event.newValue;
|
850 | axisBreak.parent = this.renderer.breakContainer;
|
851 | axisBreak.axis = this;
|
852 | };
|
853 | /**
|
854 | * Registers a [[XYSeries]] element with this Axis.
|
855 | *
|
856 | * Returns a [[Disposer]] for all events, added to Series for watching
|
857 | * changes in Axis, and vice versa.
|
858 | * @ignore
|
859 | * @param series Series
|
860 | * @return Event disposer
|
861 | */
|
862 | Axis.prototype.registerSeries = function (series) {
|
863 | var _this = this;
|
864 | this.series.moveValue(series);
|
865 | return new MultiDisposer([
|
866 | new Disposer(function () {
|
867 | _this.series.removeValue(series);
|
868 | }),
|
869 | this.events.on("lengthchanged", series.invalidate, series, false),
|
870 | this.events.on("lengthchanged", series.createMask, series, false),
|
871 | this.events.on("startchanged", series.invalidate, series, false),
|
872 | this.events.on("endchanged", series.invalidate, series, false),
|
873 | ]);
|
874 | };
|
875 | Object.defineProperty(Axis.prototype, "renderer", {
|
876 | /**
|
877 | * @return Renderer
|
878 | */
|
879 | get: function () {
|
880 | return this._renderer;
|
881 | },
|
882 | /**
|
883 | * An [[AxisRenderer]] to be used to render this Axis.
|
884 | *
|
885 | * Please note that most of the settings, related to Axis' appearance are set
|
886 | * via its renderer. Not directly on the Axis.
|
887 | *
|
888 | * E.g.:
|
889 | *
|
890 | * ```TypeScript
|
891 | * axis.renderer.inside = true;
|
892 | * axis.renderer.minLabelPosition = 0.1;
|
893 | * axis.renderer.maxLabelPosition = 0.9;
|
894 | * ```
|
895 | * ```JavaScript
|
896 | * axis.renderer.inside = true;
|
897 | * axis.renderer.minLabelPosition = 0.1;
|
898 | * axis.renderer.maxLabelPosition = 0.9;
|
899 | * ```
|
900 | *
|
901 | * @see {@link https://www.amcharts.com/docs/v4/concepts/axes/} for more info
|
902 | * @param renderer Renderer
|
903 | */
|
904 | set: function (renderer) {
|
905 | if (renderer != this._renderer) {
|
906 | this._renderer = renderer;
|
907 | renderer.chart = this.chart;
|
908 | renderer.axis = this;
|
909 | renderer.parent = this;
|
910 | this.title.parent = this; // we add title to axis and set layout in renderer to avoid one extra container, as otherwise axis container would be used for holding renderer only
|
911 | this.initRenderer();
|
912 | this._disposers.push(renderer.gridContainer.events.on("maxsizechanged", this.invalidate, this, false));
|
913 | var ghostLabel_1 = this.renderer.labels.create();
|
914 | this._disposers.push(ghostLabel_1);
|
915 | ghostLabel_1.dataItem = this.dataItems.template.clone(); // just for the adapters not to fail
|
916 | ghostLabel_1.text = "L";
|
917 | ghostLabel_1.parent = this.renderer;
|
918 | ghostLabel_1.shouldClone = false;
|
919 | ghostLabel_1.fillOpacity = 0;
|
920 | ghostLabel_1.opacity = 0;
|
921 | ghostLabel_1.strokeOpacity = 0;
|
922 | ghostLabel_1.interactionsEnabled = false;
|
923 | ghostLabel_1.validate();
|
924 | this.ghostLabel = ghostLabel_1;
|
925 | this.events.on("beforedatavalidated", function () {
|
926 | ghostLabel_1.text = "L";
|
927 | }, undefined, false);
|
928 | }
|
929 | },
|
930 | enumerable: true,
|
931 | configurable: true
|
932 | });
|
933 | /**
|
934 | * Converts a relative position to angle. (for circular axes)
|
935 | *
|
936 | * @param position Position (0-1)
|
937 | * @return Angle
|
938 | */
|
939 | Axis.prototype.positionToAngle = function (position) {
|
940 | return this.renderer.positionToAngle(position);
|
941 | };
|
942 | /**
|
943 | * Converts pixel coordinates to a relative position. (0-1)
|
944 | *
|
945 | * @param point Coorinates (px)
|
946 | * @return Position (0-1)
|
947 | */
|
948 | Axis.prototype.pointToPosition = function (point) {
|
949 | return this.renderer.pointToPosition(point);
|
950 | };
|
951 | /**
|
952 | * Converts relative position to coordinate.
|
953 | *
|
954 | * @since 4.7.15
|
955 | * @param position (0-1)
|
956 | * @return coordinate (px)
|
957 | */
|
958 | Axis.prototype.positionToCoordinate = function (position) {
|
959 | return this.renderer.positionToCoordinate(position);
|
960 | };
|
961 | /**
|
962 | * [getAnyRangePath description]
|
963 | *
|
964 | * @ignore Exclude from docs
|
965 | * @todo Description
|
966 | * @param start [description]
|
967 | * @param end [description]
|
968 | * @return [description]
|
969 | */
|
970 | Axis.prototype.getAnyRangePath = function (start, end) {
|
971 | return this.renderer.getPositionRangePath(start, end);
|
972 | };
|
973 | /**
|
974 | * Converts any positional parameter to a relative position on axis.
|
975 | *
|
976 | * @todo Description (review)
|
977 | * @param value Pisition
|
978 | * @return Position (0-1)
|
979 | */
|
980 | Axis.prototype.anyToPosition = function (value) {
|
981 | return 0;
|
982 | };
|
983 | /**
|
984 | * Converts any positional parameter to a relative position on axis.
|
985 | *
|
986 | * @todo Description (review)
|
987 | * @param value Pisition
|
988 | * @return Orientation point
|
989 | */
|
990 | Axis.prototype.anyToPoint = function (value) {
|
991 | return { x: 0, y: 0, angle: 0 };
|
992 | };
|
993 | /**
|
994 | * [getPositionRangePath description]
|
995 | *
|
996 | * @ignore Exclude from docs
|
997 | * @todo Description
|
998 | * @param startPosition [description]
|
999 | * @param endPosition [description]
|
1000 | * @return [description]
|
1001 | */
|
1002 | Axis.prototype.getPositionRangePath = function (startPosition, endPosition) {
|
1003 | if (this.renderer) {
|
1004 | return this.renderer.getPositionRangePath(startPosition, endPosition);
|
1005 | }
|
1006 | return "";
|
1007 | };
|
1008 | Object.defineProperty(Axis.prototype, "axisLength", {
|
1009 | /**
|
1010 | * Actual axis length in pixels.
|
1011 | *
|
1012 | * @return Axis length (px)
|
1013 | */
|
1014 | get: function () {
|
1015 | if (this.renderer) {
|
1016 | return this.renderer.axisLength;
|
1017 | }
|
1018 | return 0;
|
1019 | },
|
1020 | enumerable: true,
|
1021 | configurable: true
|
1022 | });
|
1023 | Object.defineProperty(Axis.prototype, "cursorTooltipEnabled", {
|
1024 | /**
|
1025 | * @return Display tooltip?
|
1026 | */
|
1027 | get: function () {
|
1028 | return this.getPropertyValue("cursorTooltipEnabled");
|
1029 | },
|
1030 | /**
|
1031 | * Indicates if axis should display a tooltip for chart's cursor.
|
1032 | *
|
1033 | * @param value Display tooltip?
|
1034 | */
|
1035 | set: function (value) {
|
1036 | if (this.setPropertyValue("cursorTooltipEnabled", value)) {
|
1037 | if (value && this.renderer) {
|
1038 | this.renderer.updateTooltip();
|
1039 | }
|
1040 | else if (this.tooltip) {
|
1041 | this.tooltip.hide(0);
|
1042 | }
|
1043 | }
|
1044 | },
|
1045 | enumerable: true,
|
1046 | configurable: true
|
1047 | });
|
1048 | Object.defineProperty(Axis.prototype, "toggleZoomOutButton", {
|
1049 | /**
|
1050 | * @return Toggle zoom out button?
|
1051 | */
|
1052 | get: function () {
|
1053 | return this.getPropertyValue("toggleZoomOutButton");
|
1054 | },
|
1055 | /**
|
1056 | * Normally, when axis is zoomed in, a zoom out button is shown by a chart,
|
1057 | * and vice versa: when axis is zoomed out completely, zoom out button is
|
1058 | * hidden.
|
1059 | *
|
1060 | * Setting this to `false` will disable this behavior. Zooming in our out
|
1061 | * this axis will not reveal or hide zoom out button.
|
1062 | *
|
1063 | * @default true
|
1064 | * @since 4.6.2
|
1065 | * @param value Toggle zoom out button?
|
1066 | */
|
1067 | set: function (value) {
|
1068 | this.setPropertyValue("toggleZoomOutButton", value);
|
1069 | },
|
1070 | enumerable: true,
|
1071 | configurable: true
|
1072 | });
|
1073 | /**
|
1074 | * Hides element's [[Tooltip]].
|
1075 | *
|
1076 | * @see {@link Tooltip}
|
1077 | */
|
1078 | Axis.prototype.hideTooltip = function (duration) {
|
1079 | _super.prototype.hideTooltip.call(this, duration);
|
1080 | this._tooltipPosition = undefined;
|
1081 | };
|
1082 | /**
|
1083 | * Shows Axis tooltip at specific relative position within Axis. (0-1)
|
1084 | *
|
1085 | * @param position Position (0-1)
|
1086 | * @param local or global position
|
1087 | */
|
1088 | Axis.prototype.showTooltipAtPosition = function (position, local) {
|
1089 | var tooltip = this._tooltip;
|
1090 | if (!tooltip || this.dataItems.length <= 0) {
|
1091 | this._tooltipPosition = undefined;
|
1092 | }
|
1093 | else {
|
1094 | if (!local) {
|
1095 | position = this.toAxisPosition(position);
|
1096 | }
|
1097 | if (!$type.isNumber(position) || position < this.start || position > this.end) {
|
1098 | tooltip.hide(0);
|
1099 | this._tooltipPosition = undefined;
|
1100 | return;
|
1101 | }
|
1102 | var renderer = this.renderer;
|
1103 | //@todo: think of how to solve this better
|
1104 | if (!tooltip.parent) {
|
1105 | tooltip.parent = this.tooltipContainer;
|
1106 | }
|
1107 | var tooltipLocation = renderer.tooltipLocation;
|
1108 | var startPosition = this.getCellStartPosition(position);
|
1109 | var endPosition = this.getCellEndPosition(position);
|
1110 | if (this.tooltipPosition == "fixed") {
|
1111 | position = startPosition + (endPosition - startPosition) * tooltipLocation;
|
1112 | }
|
1113 | position = $math.fitToRange(position, this.start, this.end);
|
1114 | if (this._tooltipPosition != position) {
|
1115 | this._tooltipPosition = position;
|
1116 | var tooltipLocation2 = renderer.tooltipLocation2;
|
1117 | var startPoint = renderer.positionToPoint(startPosition, tooltipLocation2);
|
1118 | var endPoint = renderer.positionToPoint(endPosition, tooltipLocation2);
|
1119 | // save values so cursor could use them
|
1120 | this.currentItemStartPoint = startPoint;
|
1121 | this.currentItemEndPoint = endPoint;
|
1122 | if (renderer.fullWidthTooltip) {
|
1123 | tooltip.width = endPoint.x - startPoint.x;
|
1124 | tooltip.height = endPoint.y - startPoint.y;
|
1125 | }
|
1126 | var point = renderer.positionToPoint(position, tooltipLocation2);
|
1127 | var globalPoint = $utils.spritePointToSvg(point, this.renderer.line);
|
1128 | tooltip.text = this.getTooltipText(position);
|
1129 | if (tooltip.text) {
|
1130 | tooltip.delayedPointTo(globalPoint);
|
1131 | tooltip.show();
|
1132 | }
|
1133 | }
|
1134 | if (!this.cursorTooltipEnabled || this.tooltip.disabled) {
|
1135 | tooltip.hide(0);
|
1136 | }
|
1137 | }
|
1138 | };
|
1139 | /**
|
1140 | * Converts relative position (0-1) to Axis position with zoom level and
|
1141 | * inversed taken into account.
|
1142 | *
|
1143 | * @param position Global position (0-1)
|
1144 | * @return Position within Axis (0-1)
|
1145 | */
|
1146 | Axis.prototype.toAxisPosition = function (position) {
|
1147 | position = this.renderer.toAxisPosition(position);
|
1148 | if (position == undefined) {
|
1149 | return;
|
1150 | }
|
1151 | position = position * (this.end - this.start);
|
1152 | if (this.renderer.inversed) {
|
1153 | position = this.end - position;
|
1154 | }
|
1155 | else {
|
1156 | position = this.start + position;
|
1157 | }
|
1158 | return position;
|
1159 | };
|
1160 | /**
|
1161 | * Converts position on the axis with zoom level and
|
1162 | * inversed taken into account to global position.
|
1163 | *
|
1164 | * @param position Axis position (0-1)
|
1165 | * @return Global position (0-1)
|
1166 | */
|
1167 | Axis.prototype.toGlobalPosition = function (position) {
|
1168 | if (this.renderer.inversed) {
|
1169 | position = this.end - position;
|
1170 | }
|
1171 | else {
|
1172 | position = position - this.start;
|
1173 | }
|
1174 | return position / (this.end - this.start);
|
1175 | };
|
1176 | /**
|
1177 | * Returns text to be used for cursor's Axis tooltip.
|
1178 | *
|
1179 | * This is a placeholder to override for extending classes.
|
1180 | *
|
1181 | * @ignore Exclude from docs
|
1182 | * @param position Position coordinate (px)
|
1183 | * @return Label text
|
1184 | */
|
1185 | Axis.prototype.getTooltipText = function (position) {
|
1186 | return;
|
1187 | };
|
1188 | /**
|
1189 | * Updates Axis' tooltip's position and possibly size, and pointer (stem)
|
1190 | * place.
|
1191 | *
|
1192 | * @ignore Exclude from docs
|
1193 | * @param pointerOrientation Pointer (stem) orientation
|
1194 | * @param boundingRectangle A rectangle for tooltip to fit within
|
1195 | */
|
1196 | Axis.prototype.updateTooltip = function (pointerOrientation, boundingRectangle) {
|
1197 | var tooltip = this._tooltip;
|
1198 | if (tooltip) {
|
1199 | tooltip.fixDoc = false;
|
1200 | tooltip.pointerOrientation = pointerOrientation;
|
1201 | tooltip.setBounds($utils.spriteRectToSvg(boundingRectangle, this.renderer.line));
|
1202 | }
|
1203 | };
|
1204 | /**
|
1205 | * [roundPosition description]
|
1206 | *
|
1207 | * @ignore Exclude from docs
|
1208 | * @todo Description
|
1209 | * @param position Relative position
|
1210 | * @param location Location on axis
|
1211 | * @return Rounded position
|
1212 | */
|
1213 | Axis.prototype.roundPosition = function (position, location, axisLocation) {
|
1214 | return position;
|
1215 | };
|
1216 | /**
|
1217 | * [getCellStartPosition description]
|
1218 | *
|
1219 | * @ignore Exclude from docs
|
1220 | * @todo Description
|
1221 | * @param position [description]
|
1222 | * @return [description]
|
1223 | */
|
1224 | Axis.prototype.getCellStartPosition = function (position) {
|
1225 | return position;
|
1226 | };
|
1227 | /**
|
1228 | * [getCellEndPosition description]
|
1229 | *
|
1230 | * @ignore Exclude from docs
|
1231 | * @todo Description
|
1232 | * @param position [description]
|
1233 | * @return [description]
|
1234 | */
|
1235 | Axis.prototype.getCellEndPosition = function (position) {
|
1236 | return position;
|
1237 | };
|
1238 | Object.defineProperty(Axis.prototype, "axisRanges", {
|
1239 | /**
|
1240 | * A list of axis ranges for this Axis.
|
1241 | *
|
1242 | * @return Axis ranges
|
1243 | */
|
1244 | get: function () {
|
1245 | if (!this._axisRanges) {
|
1246 | var dataItem = this.createDataItem();
|
1247 | dataItem.isRange = true;
|
1248 | dataItem.axisFill = this.renderer.axisFills.template.clone();
|
1249 | dataItem.grid = this.renderer.grid.template.clone();
|
1250 | dataItem.tick = this.renderer.ticks.template.clone();
|
1251 | dataItem.label = this.renderer.labels.template.clone();
|
1252 | dataItem.isTemplate = true;
|
1253 | dataItem.component = this;
|
1254 | dataItem.axisFill.disabled = false;
|
1255 | dataItem.tick.disabled = false;
|
1256 | dataItem.grid.disabled = false;
|
1257 | dataItem.label.disabled = false;
|
1258 | this._axisRanges = new ListTemplate(dataItem);
|
1259 | this._axisRanges.events.on("inserted", this.processAxisRange, this, false);
|
1260 | this._disposers.push(new ListDisposer(this._axisRanges));
|
1261 | this._disposers.push(this._axisRanges.template);
|
1262 | }
|
1263 | return this._axisRanges;
|
1264 | },
|
1265 | enumerable: true,
|
1266 | configurable: true
|
1267 | });
|
1268 | /**
|
1269 | * Decorates an axis range after it has been added to the axis range list.
|
1270 | *
|
1271 | * @param event Event
|
1272 | */
|
1273 | Axis.prototype.processAxisRange = function (event) {
|
1274 | var axisRange = event.newValue;
|
1275 | axisRange.component = this;
|
1276 | axisRange.isRange = true;
|
1277 | };
|
1278 | Object.defineProperty(Axis.prototype, "axisBreaks", {
|
1279 | /**
|
1280 | * A list of axis breaks on this Axis.
|
1281 | *
|
1282 | * @return Axis breaks.
|
1283 | */
|
1284 | get: function () {
|
1285 | if (!this._axisBreaks) {
|
1286 | this._axisBreaks = new SortedListTemplate(this.createAxisBreak(), function (a, b) {
|
1287 | return $number.order(a.adjustedStartValue, b.adjustedStartValue);
|
1288 | });
|
1289 | this._axisBreaks.events.on("inserted", this.processBreak, this, false);
|
1290 | this._disposers.push(new ListDisposer(this._axisBreaks));
|
1291 | this._disposers.push(this._axisBreaks.template);
|
1292 | }
|
1293 | return this._axisBreaks;
|
1294 | },
|
1295 | enumerable: true,
|
1296 | configurable: true
|
1297 | });
|
1298 | /**
|
1299 | * Creates a new axis break.
|
1300 | *
|
1301 | * @return Axis break
|
1302 | */
|
1303 | Axis.prototype.createAxisBreak = function () {
|
1304 | return new AxisBreak();
|
1305 | };
|
1306 | Object.defineProperty(Axis.prototype, "series", {
|
1307 | /**
|
1308 | * A list of Series currently associated with this Axis.
|
1309 | *
|
1310 | * @return Series
|
1311 | */
|
1312 | get: function () {
|
1313 | if (!this._series) {
|
1314 | this._series = new List();
|
1315 | }
|
1316 | return this._series;
|
1317 | },
|
1318 | enumerable: true,
|
1319 | configurable: true
|
1320 | });
|
1321 | /**
|
1322 | * Processes Series' data items.
|
1323 | *
|
1324 | * This is a placeholder to override for extending classes.
|
1325 | *
|
1326 | * @ignore Exclude from docs
|
1327 | */
|
1328 | Axis.prototype.processSeriesDataItems = function () {
|
1329 | };
|
1330 | /**
|
1331 | * Processes Series' single data item.
|
1332 | *
|
1333 | * This is a placeholder to override for extending classes.
|
1334 | *
|
1335 | * @ignore Exclude from docs
|
1336 | * @param dataItem Data item
|
1337 | */
|
1338 | Axis.prototype.processSeriesDataItem = function (dataItem, axisLetter) {
|
1339 | };
|
1340 | /**
|
1341 | * Post-processes Serie's data items.
|
1342 | *
|
1343 | * This is a placeholder to override for extending classes.
|
1344 | *
|
1345 | * @ignore Exclude from docs
|
1346 | */
|
1347 | Axis.prototype.postProcessSeriesDataItems = function (series) {
|
1348 | };
|
1349 | /**
|
1350 | * Post-processes Serie's single data item.
|
1351 | *
|
1352 | * This is a placeholder to override for extending classes.
|
1353 | *
|
1354 | * @ignore Exclude from docs
|
1355 | * @param dataItem Data item
|
1356 | */
|
1357 | Axis.prototype.postProcessSeriesDataItem = function (dataItem) {
|
1358 | };
|
1359 | //
|
1360 | /**
|
1361 | * Updates Axis based on all Series that might influence it.
|
1362 | *
|
1363 | * Called by Series after Series data is validated.
|
1364 | *
|
1365 | * This is a placeholder to override for extending classes.
|
1366 | *
|
1367 | * @ignore Exclude from docs
|
1368 | */
|
1369 | Axis.prototype.updateAxisBySeries = function () {
|
1370 | };
|
1371 | /**
|
1372 | * Hides unused data items.
|
1373 | *
|
1374 | * @ignore Exclude from docs
|
1375 | */
|
1376 | Axis.prototype.hideUnusedDataItems = function () {
|
1377 | var _this = this;
|
1378 | // hide all unused
|
1379 | var dataItemsIterator = this._dataItemsIterator;
|
1380 | dataItemsIterator.createNewItems = false;
|
1381 | $iter.each(dataItemsIterator.iterator(), function (dataItem) {
|
1382 | _this.validateDataElement(dataItem); // solves shrinking
|
1383 | dataItem.__disabled = true;
|
1384 | });
|
1385 | dataItemsIterator.clear();
|
1386 | dataItemsIterator.createNewItems = true;
|
1387 | };
|
1388 | /**
|
1389 | * Returns a Series' data item that corresponds to specific position on Axis.
|
1390 | *
|
1391 | * This is a placeholder to override for extending classes.
|
1392 | *
|
1393 | * @ignore Exclude from docs
|
1394 | * @param series Series
|
1395 | * @param position Position (0-1)
|
1396 | * @param findNearest Should axis try to find nearest tooltip if there is no data item at exact position
|
1397 | * @return Data item
|
1398 | */
|
1399 | Axis.prototype.getSeriesDataItem = function (series, position, findNearest) {
|
1400 | return;
|
1401 | };
|
1402 | /**
|
1403 | * Returns an angle that corresponds to specific position on axis.
|
1404 | *
|
1405 | * This is a placeholder to override for extending classes.
|
1406 | *
|
1407 | * @ignore Exclude from docs
|
1408 | * @todo Description (review)
|
1409 | * @param dataItem Data item
|
1410 | * @param key ???
|
1411 | * @param location Location
|
1412 | * @param stackKey ???
|
1413 | * @return Angle
|
1414 | */
|
1415 | Axis.prototype.getAngle = function (dataItem, key, location, stackKey, range) {
|
1416 | return;
|
1417 | };
|
1418 | /**
|
1419 | * [getX description]
|
1420 | *
|
1421 | * This is a placeholder to override for extending classes.
|
1422 | *
|
1423 | * @ignore Exclude from docs
|
1424 | * @todo Description (review)
|
1425 | * @param dataItem [description]
|
1426 | * @param key [description]
|
1427 | * @param location [description]
|
1428 | * @param stackKey [description]
|
1429 | * @return [description]
|
1430 | */
|
1431 | Axis.prototype.getX = function (dataItem, key, location, stackKey, range) {
|
1432 | return;
|
1433 | };
|
1434 | /**
|
1435 | * [getX description]
|
1436 | *
|
1437 | * This is a placeholder to override for extending classes.
|
1438 | *
|
1439 | * @ignore Exclude from docs
|
1440 | * @todo Description (review)
|
1441 | * @param dataItem [description]
|
1442 | * @param key [description]
|
1443 | * @param location [description]
|
1444 | * @param stackKey [description]
|
1445 | * @return [description]
|
1446 | */
|
1447 | Axis.prototype.getPositionX = function (dataItem, key, location, stackKey, range) {
|
1448 | return;
|
1449 | };
|
1450 | /**
|
1451 | * [getY description]
|
1452 | *
|
1453 | * This is a placeholder to override for extending classes.
|
1454 | *
|
1455 | * @ignore Exclude from docs
|
1456 | * @todo Description (review)
|
1457 | * @param dataItem [description]
|
1458 | * @param key [description]
|
1459 | * @param location [description]
|
1460 | * @param stackKey [description]
|
1461 | * @return [description]
|
1462 | */
|
1463 | Axis.prototype.getY = function (dataItem, key, location, stackKey, range) {
|
1464 | return;
|
1465 | };
|
1466 | /**
|
1467 | * [getY description]
|
1468 | *
|
1469 | * This is a placeholder to override for extending classes.
|
1470 | *
|
1471 | * @ignore Exclude from docs
|
1472 | * @todo Description (review)
|
1473 | * @param dataItem [description]
|
1474 | * @param key [description]
|
1475 | * @param location [description]
|
1476 | * @param stackKey [description]
|
1477 | * @return [description]
|
1478 | */
|
1479 | Axis.prototype.getPositionY = function (dataItem, key, location, stackKey, range) {
|
1480 | return;
|
1481 | };
|
1482 | Object.defineProperty(Axis.prototype, "basePoint", {
|
1483 | /**
|
1484 | * Coordinates of the actual axis start.
|
1485 | *
|
1486 | * @ignore Exclude from docs
|
1487 | * @return Base point coordinates
|
1488 | */
|
1489 | get: function () {
|
1490 | return { x: 0, y: 0 };
|
1491 | },
|
1492 | enumerable: true,
|
1493 | configurable: true
|
1494 | });
|
1495 | /**
|
1496 | * [dataChangeUpdate description]
|
1497 | *
|
1498 | * This is a placeholder to override for extending classes.
|
1499 | *
|
1500 | * @ignore Exclude from docs
|
1501 | * @todo Description
|
1502 | */
|
1503 | Axis.prototype.dataChangeUpdate = function () {
|
1504 | };
|
1505 | /**
|
1506 | * [dataChangeUpdate description]
|
1507 | *
|
1508 | *
|
1509 | * @ignore Exclude from docs
|
1510 | * @todo Description
|
1511 | */
|
1512 | Axis.prototype.seriesDataChangeUpdate = function (series) {
|
1513 | };
|
1514 | /**
|
1515 | * Removes axis breaks that fall between `min` and `max` (???)
|
1516 | *
|
1517 | * @ignore Exclude from docs
|
1518 | * @todo Description (review)
|
1519 | * @param min Start value
|
1520 | * @param max End value
|
1521 | * @return Spread o
|
1522 | */
|
1523 | Axis.prototype.adjustDifference = function (min, max) {
|
1524 | var difference = max - min;
|
1525 | if ($type.isNumber(difference)) {
|
1526 | if (this._axisBreaks) {
|
1527 | $iter.eachContinue(this._axisBreaks.iterator(), function (axisBreak) {
|
1528 | var startValue = axisBreak.adjustedStartValue;
|
1529 | var endValue = axisBreak.adjustedEndValue;
|
1530 | if ($type.isNumber(startValue) && $type.isNumber(endValue)) {
|
1531 | // breaks are sorted, we don't need go further anymore
|
1532 | if (startValue > max) {
|
1533 | return false;
|
1534 | }
|
1535 | if (endValue >= min) {
|
1536 | if ($type.isNumber(startValue) && $type.isNumber(endValue)) {
|
1537 | var breakSize = axisBreak.breakSize;
|
1538 | var intersection = $math.intersection({ start: startValue, end: endValue }, { start: min, end: max });
|
1539 | if (intersection) {
|
1540 | difference -= (intersection.end - intersection.start) * (1 - breakSize);
|
1541 | }
|
1542 | }
|
1543 | }
|
1544 | return true;
|
1545 | }
|
1546 | });
|
1547 | }
|
1548 | return difference;
|
1549 | }
|
1550 | };
|
1551 | /**
|
1552 | * Checks if specific value falls within a break.
|
1553 | *
|
1554 | * Returns [[AxisBreak]] the value falls into.
|
1555 | *
|
1556 | * @param value Value to check
|
1557 | * @return Axis break
|
1558 | */
|
1559 | Axis.prototype.isInBreak = function (value) {
|
1560 | if (this._axisBreaks) {
|
1561 | return $iter.find(this._axisBreaks.iterator(), function (axisBreak) {
|
1562 | return value >= axisBreak.adjustedStartValue &&
|
1563 | value <= axisBreak.adjustedEndValue;
|
1564 | });
|
1565 | }
|
1566 | };
|
1567 | /**
|
1568 | * [fixAxisBreaks description]
|
1569 | *
|
1570 | * @ignore Exclude from docs
|
1571 | * @todo Description
|
1572 | */
|
1573 | Axis.prototype.fixAxisBreaks = function () {
|
1574 | var _this = this;
|
1575 | if (this._axisBreaks) {
|
1576 | var axisBreaks = this._axisBreaks;
|
1577 | if (axisBreaks.length > 0) {
|
1578 | // first make sure that startValue is <= end value
|
1579 | // This needs to make a copy of axisBreaks because it mutates the list while traversing
|
1580 | // TODO very inefficient
|
1581 | $array.each($iter.toArray(axisBreaks.iterator()), function (axisBreak) {
|
1582 | var startValue = $math.min(axisBreak.startValue, axisBreak.endValue);
|
1583 | var endValue = $math.max(axisBreak.startValue, axisBreak.endValue);
|
1584 | axisBreak.adjustedStartValue = startValue;
|
1585 | axisBreak.adjustedEndValue = endValue;
|
1586 | _this._axisBreaks.update(axisBreak);
|
1587 | });
|
1588 | var firstAxisBreak = axisBreaks.first;
|
1589 | var previousEndValue_1 = Math.min(firstAxisBreak.startValue, firstAxisBreak.endValue);
|
1590 | // process breaks
|
1591 | // TODO does this need to call axisBreaks.update ?
|
1592 | $iter.each(axisBreaks.iterator(), function (axisBreak) {
|
1593 | var startValue = axisBreak.adjustedStartValue;
|
1594 | var endValue = axisBreak.adjustedEndValue;
|
1595 | // breaks can't overlap
|
1596 | // if break starts before previous break ends
|
1597 | if (startValue < previousEndValue_1) {
|
1598 | startValue = previousEndValue_1;
|
1599 | if (endValue < previousEndValue_1) {
|
1600 | endValue = previousEndValue_1;
|
1601 | }
|
1602 | }
|
1603 | axisBreak.adjustedStartValue = startValue;
|
1604 | axisBreak.adjustedEndValue = endValue;
|
1605 | });
|
1606 | }
|
1607 | }
|
1608 | };
|
1609 | Object.defineProperty(Axis.prototype, "startIndex", {
|
1610 | /**
|
1611 | * @ignore Exclude from docs
|
1612 | * @return [description]
|
1613 | */
|
1614 | get: function () {
|
1615 | return 0;
|
1616 | },
|
1617 | /**
|
1618 | * We need start/end indexes of axes to be 0 - `dataItems.length`.
|
1619 | *
|
1620 | * Yes, also for category axis, this helps to avoid jumping of categories
|
1621 | * while scrolling and does not do a lot of extra work as we use
|
1622 | * protected `_startIndex` and `_endIndex` when working with items.
|
1623 | *
|
1624 | * @hidden
|
1625 | */
|
1626 | /**
|
1627 | * [startIndex description]
|
1628 | *
|
1629 | * @ignore Exclude from docs
|
1630 | * @todo Description
|
1631 | * @param value [description]
|
1632 | */
|
1633 | set: function (value) {
|
1634 | },
|
1635 | enumerable: true,
|
1636 | configurable: true
|
1637 | });
|
1638 | Object.defineProperty(Axis.prototype, "endIndex", {
|
1639 | /**
|
1640 | * @ignore Exclude from docs
|
1641 | * @return [description]
|
1642 | */
|
1643 | get: function () {
|
1644 | return this.dataItems.length;
|
1645 | },
|
1646 | /**
|
1647 | * [endIndex description]
|
1648 | *
|
1649 | * @ignore Exclude from docs
|
1650 | * @todo Description
|
1651 | * @param value [description]
|
1652 | */
|
1653 | set: function (value) {
|
1654 | },
|
1655 | enumerable: true,
|
1656 | configurable: true
|
1657 | });
|
1658 | /**
|
1659 | * Returns a formatted label based on position.
|
1660 | *
|
1661 | * Individual axis types should override this method to generate a label
|
1662 | * that is relevant to axis type.
|
1663 | *
|
1664 | * Please note that `position` represents position within axis which may be
|
1665 | * zoomed and not correspond to Cursor's `position`.
|
1666 | *
|
1667 | * To convert Cursor's `position` to Axis' `position` use `toAxisPosition()` method.
|
1668 | *
|
1669 | * @see {@link https://www.amcharts.com/docs/v4/tutorials/tracking-cursors-position-via-api/#Tracking_Cursor_s_position} For more information about cursor tracking.
|
1670 | * @param position Relative position on axis (0-1)
|
1671 | * @return Position label
|
1672 | */
|
1673 | Axis.prototype.getPositionLabel = function (position) {
|
1674 | return Math.round(position * 100) + "%x";
|
1675 | };
|
1676 | Object.defineProperty(Axis.prototype, "chart", {
|
1677 | /**
|
1678 | * @return Chart
|
1679 | */
|
1680 | get: function () {
|
1681 | return this._chart;
|
1682 | },
|
1683 | /**
|
1684 | * A Chart this Axis belongs to.
|
1685 | *
|
1686 | * @param value Chart
|
1687 | */
|
1688 | set: function (value) {
|
1689 | this._chart = value;
|
1690 | },
|
1691 | enumerable: true,
|
1692 | configurable: true
|
1693 | });
|
1694 | /**
|
1695 | * Creates a data item for a Series range.
|
1696 | *
|
1697 | * @param series Target Series
|
1698 | * @return Range data item
|
1699 | */
|
1700 | Axis.prototype.createSeriesRange = function (series) {
|
1701 | var range = this.axisRanges.create();
|
1702 | range.component = this;
|
1703 | range.axisFill = this.renderer.axisFills.template.clone();
|
1704 | range.axisFill.disabled = false;
|
1705 | range.axisFill.fillOpacity = 0;
|
1706 | range.grid = this.renderer.grid.template.clone();
|
1707 | range.grid.disabled = true;
|
1708 | range.tick = this.renderer.ticks.template.clone();
|
1709 | range.tick.disabled = true;
|
1710 | range.label = this.renderer.labels.template.clone();
|
1711 | range.label.disabled = true;
|
1712 | range.addDisposer(new Disposer(function () {
|
1713 | series.axisRanges.removeValue(range);
|
1714 | }));
|
1715 | series.axisRanges.push(range);
|
1716 | return range;
|
1717 | };
|
1718 | /**
|
1719 | * Copies all properties and related data from a different instance of Axis.
|
1720 | *
|
1721 | * @param source Source Axis
|
1722 | */
|
1723 | Axis.prototype.copyFrom = function (source) {
|
1724 | _super.prototype.copyFrom.call(this, source);
|
1725 | if (this.renderer) {
|
1726 | this.renderer.copyFrom(source.renderer);
|
1727 | }
|
1728 | else {
|
1729 | if (source.renderer) {
|
1730 | this.renderer = source.renderer.clone();
|
1731 | this._disposers.push(this.renderer);
|
1732 | }
|
1733 | }
|
1734 | if (source.title) {
|
1735 | if (!this.title) {
|
1736 | this.title = source.title.clone();
|
1737 | this.title.parent = this;
|
1738 | }
|
1739 | else {
|
1740 | this.title.copyFrom(source.title);
|
1741 | }
|
1742 | this._disposers.push(this.title);
|
1743 | }
|
1744 | };
|
1745 | /**
|
1746 | * Resets internal iterator.
|
1747 | */
|
1748 | Axis.prototype.resetIterators = function () {
|
1749 | this._dataItemsIterator.reset();
|
1750 | };
|
1751 | /**
|
1752 | * Processes JSON-based config before it is applied to the object.
|
1753 | *
|
1754 | * @ignore Exclude from docs
|
1755 | * @param config Config
|
1756 | */
|
1757 | Axis.prototype.processConfig = function (config) {
|
1758 | if (config) {
|
1759 | // Set up axis ranges
|
1760 | if ($type.hasValue(config.axisRanges) && $type.isArray(config.axisRanges)) {
|
1761 | for (var i = 0, len = config.axisRanges.length; i < len; i++) {
|
1762 | var range = config.axisRanges[i];
|
1763 | // If `series` is set, we know it's a series range
|
1764 | if ($type.hasValue(range["series"])) {
|
1765 | if ($type.isString(range["series"])) {
|
1766 | if (this.map.hasKey(range["series"])) {
|
1767 | //range["series"] = this.map.getKey(range["series"]);
|
1768 | config.axisRanges[i] = this.createSeriesRange(this.map.getKey(range["series"]));
|
1769 | delete (range["series"]);
|
1770 | config.axisRanges[i].config = range;
|
1771 | }
|
1772 | }
|
1773 | }
|
1774 | }
|
1775 | }
|
1776 | }
|
1777 | _super.prototype.processConfig.call(this, config);
|
1778 | };
|
1779 | /**
|
1780 | * Ordering function used in JSON setup.
|
1781 | *
|
1782 | * @param a Item A
|
1783 | * @param b Item B
|
1784 | * @return Order
|
1785 | */
|
1786 | Axis.prototype.configOrder = function (a, b) {
|
1787 | if (a == b) {
|
1788 | return 0;
|
1789 | }
|
1790 | // last
|
1791 | else if (a == "title") {
|
1792 | return 1;
|
1793 | }
|
1794 | else if (b == "title") {
|
1795 | return -1;
|
1796 | }
|
1797 | // first
|
1798 | else if (a == "component") {
|
1799 | return -1;
|
1800 | }
|
1801 | else if (b == "component") {
|
1802 | return 1;
|
1803 | }
|
1804 | else {
|
1805 | return _super.prototype.configOrder.call(this, a, b);
|
1806 | }
|
1807 | };
|
1808 | Object.defineProperty(Axis.prototype, "startLocation", {
|
1809 | /**
|
1810 | * @return Location (0-1)
|
1811 | */
|
1812 | get: function () {
|
1813 | return this.getPropertyValue("startLocation");
|
1814 | },
|
1815 | /**
|
1816 | * Axis start location. Works on Date/Category axis, doesn't work on Value axis.
|
1817 | *
|
1818 | * * 0 - Full first cell is shown.
|
1819 | * * 0.5 - Half of first cell is shown.
|
1820 | * * 1 - None of the first cell is visible. (you probably don't want that)
|
1821 | *
|
1822 | * @param value Location (0-1)
|
1823 | */
|
1824 | set: function (value) {
|
1825 | this.setPropertyValue("startLocation", value, true);
|
1826 | },
|
1827 | enumerable: true,
|
1828 | configurable: true
|
1829 | });
|
1830 | Object.defineProperty(Axis.prototype, "endLocation", {
|
1831 | /**
|
1832 | * @return Location (0-1)
|
1833 | */
|
1834 | get: function () {
|
1835 | return this.getPropertyValue("endLocation");
|
1836 | },
|
1837 | /**
|
1838 | * Axis end location. Works on Date/Category axis, doesn't work on Value axis.
|
1839 | *
|
1840 | * * 0 - None of the last cell is shown. (don't do that)
|
1841 | * * 0.5 - Half of the last cell is shown.
|
1842 | * * 1 - Full last cell is shown.
|
1843 | *
|
1844 | * @param value Location (0-1)
|
1845 | */
|
1846 | set: function (value) {
|
1847 | this.setPropertyValue("endLocation", value, true);
|
1848 | },
|
1849 | enumerable: true,
|
1850 | configurable: true
|
1851 | });
|
1852 | Axis.prototype.setDisabled = function (value) {
|
1853 | var changed = _super.prototype.setDisabled.call(this, value);
|
1854 | if (this.renderer) {
|
1855 | this.renderer.gridContainer.disabled = value;
|
1856 | }
|
1857 | return changed;
|
1858 | };
|
1859 | Object.defineProperty(Axis.prototype, "title", {
|
1860 | /**
|
1861 | * @return Title label
|
1862 | */
|
1863 | get: function () {
|
1864 | return this._title;
|
1865 | },
|
1866 | /**
|
1867 | * A reference to a [[Label]] element which serves as a title to the axis.
|
1868 | *
|
1869 | * When axis is created it aleready has an element, so you can just modify
|
1870 | * it.
|
1871 | *
|
1872 | * Or you can replace it with your own instance of `Label`.
|
1873 | *
|
1874 | * @param value Title label
|
1875 | */
|
1876 | set: function (value) {
|
1877 | if (this._title && this._title != value) {
|
1878 | this._title.dispose();
|
1879 | }
|
1880 | if (value) {
|
1881 | this._title = value;
|
1882 | value.parent = this;
|
1883 | value.shouldClone = false;
|
1884 | }
|
1885 | },
|
1886 | enumerable: true,
|
1887 | configurable: true
|
1888 | });
|
1889 | Object.defineProperty(Axis.prototype, "hideTooltipWhileZooming", {
|
1890 | /**
|
1891 | * @return Hide tooltip while zooming?
|
1892 | */
|
1893 | get: function () {
|
1894 | return this.getPropertyValue("hideTooltipWhileZooming");
|
1895 | },
|
1896 | /**
|
1897 | * Indicates if axis' tooltip should be hidden while axis range is animating
|
1898 | * (zooming)
|
1899 | *
|
1900 | * @default true
|
1901 | * @since 4.7.16
|
1902 | * @param value Hide tooltip while zooming?
|
1903 | */
|
1904 | set: function (value) {
|
1905 | this.setPropertyValue("hideTooltipWhileZooming", value);
|
1906 | },
|
1907 | enumerable: true,
|
1908 | configurable: true
|
1909 | });
|
1910 | Object.defineProperty(Axis.prototype, "zoomable", {
|
1911 | /**
|
1912 | * @return Zoomable?
|
1913 | */
|
1914 | get: function () {
|
1915 | return this.getPropertyValue("zoomable");
|
1916 | },
|
1917 | /**
|
1918 | * Should the axis be zoomed with scrollbar/cursor?
|
1919 | *
|
1920 | * @default true
|
1921 | * @since 4.9.28
|
1922 | * @param value Zoomable?
|
1923 | */
|
1924 | set: function (value) {
|
1925 | this.setPropertyValue("zoomable", value);
|
1926 | },
|
1927 | enumerable: true,
|
1928 | configurable: true
|
1929 | });
|
1930 | return Axis;
|
1931 | }(Component));
|
1932 | export { Axis };
|
1933 | /**
|
1934 | * Register class in system, so that it can be instantiated using its name from
|
1935 | * anywhere.
|
1936 | *
|
1937 | * @ignore
|
1938 | */
|
1939 | registry.registeredClasses["Axis"] = Axis;
|
1940 | registry.registeredClasses["AxisDataItem"] = AxisDataItem;
|
1941 | /**
|
1942 | * Add default responsive rules
|
1943 | */
|
1944 | /**
|
1945 | * Disable axis tooltips.
|
1946 | */
|
1947 | defaultRules.push({
|
1948 | relevant: ResponsiveBreakpoints.maybeXS,
|
1949 | state: function (target, stateId) {
|
1950 | if (target instanceof Axis && target.tooltip) {
|
1951 | var state = target.states.create(stateId);
|
1952 | state.properties.cursorTooltipEnabled = false;
|
1953 | return state;
|
1954 | }
|
1955 | return null;
|
1956 | }
|
1957 | });
|
1958 | //# sourceMappingURL=Axis.js.map |
\ | No newline at end of file |