UNPKG

@amcharts/amcharts4

Version:
1,404 lines (1,402 loc) 311 kB
/** * This is the main class that encapsulates every object on the chart. * * If it's an element that is to be displayed on the screen at some point, its * class must extend [[Sprite]] class. */ import { __extends, __values } from "tslib"; /** * ============================================================================ * IMPORTS * ============================================================================ * @hidden */ import { SpriteState } from "./SpriteState"; import { SpriteEventDispatcher } from "./SpriteEvents"; export { SpriteEventDispatcher }; import { BaseObjectEvents } from "./Base"; import { Adapter, globalAdapter } from "./utils/Adapter"; import { Dictionary, DictionaryTemplate, DictionaryDisposer } from "./utils/Dictionary"; import { ListDisposer, List } from "./utils/List"; import { MultiDisposer, Disposer, MutableValueDisposer } from "./utils/Disposer"; import { Animation, AnimationDisposer } from "./utils/Animation"; import { getGhostPaper } from "./rendering/Paper"; import { Container } from "./Container"; import { Pattern } from "./rendering/fills/Pattern"; import { LinearGradient } from "./rendering/fills/LinearGradient"; import { RadialGradient } from "./rendering/fills/RadialGradient"; import { Color, color, toColor } from "./utils/Color"; import { getInteraction } from "./interaction/Interaction"; import { MouseCursorStyle } from "./interaction/Mouse"; import { options } from "./Options"; import { registry } from "./Registry"; import { NumberFormatter } from "./formatters/NumberFormatter"; import { DateFormatter } from "./formatters/DateFormatter"; import { DurationFormatter } from "./formatters/DurationFormatter"; import { getTextFormatter } from "./formatters/TextFormatter"; import { Language } from "./utils/Language"; import { Export } from "./export/Export"; import * as $utils from "./utils/Utils"; import * as $math from "./utils/Math"; import * as $strings from "./utils/Strings"; import * as $array from "./utils/Array"; import * as $object from "./utils/Object"; import * as $type from "./utils/Type"; import * as $iter from "./utils/Iterator"; import { system } from "./System"; import { Percent, percent } from "./utils/Percent"; /** * Defines list ofvisual properties */ export var visualProperties = ["fill", "fillOpacity", "stroke", "strokeOpacity", "strokeWidth", "strokeDasharray", "strokeDashoffset", "strokeLinecap", "strokeLinejoin"]; // do not add opacity here, as it is used for showing/hiding ; /** * ============================================================================ * MAIN CLASS * ============================================================================ * @hidden */ /** * Sprite represents any displayable element. * * This is the main class that encapsulates every object on the chart. * * If it's an element that is to be displayed on the screen at some point, its * class must extend [[Sprite]] class. * * [[Sprite]] class represents the a hierarchical structure. Every object that * extends [[Sprite]] can have children, that would inherit their properties, * such as language, formatters, etc. * * @see {@link SpriteState} * @see {@link ISpriteEvents} for a list of available events * @see {@link ISpriteAdapters} for a list of available Adapters * * @todo Review child elements that need to go into `_disposers` * @important */ var Sprite = /** @class */ (function (_super) { __extends(Sprite, _super); /** * Constructor: * * Creates initial node * * Sets default properties * * Creates required default states * * Inits accessibility */ function Sprite() { var _this = // Init _super.call(this) || this; /** * Holds values for Sprite's properties. */ _this.properties = {}; /** * @ignore */ _this._eventDispatcher = new SpriteEventDispatcher(_this); /** * @ignore Exclude from docs * @todo Description */ _this._bindings = {}; /** * Holds indicator if this Sprite is a "template" to be used for creating * other Sprites from and should not be treated as full-fledged element. * * @ignore Exclude from docs */ _this._isTemplate = false; _this._isPath = false; /** * Holds indicator whether this sprite was already initialized. * * @ignore Exclude from docs */ _this._inited = false; /** * Holds indicator whether this sprite was already initialized and ready. * * @ignore Exclude from docs */ _this._ready = false; /** * If `sprite.hide()` is called and we have "hidden" state and * `transitionDuration > 0`, we set `isHiding` flag to `true` in order to * avoid restarting animations in case `hide()` method is called multiple * times. */ _this.isHiding = false; /** * If `sprite.hide()` is called, we set isHidden to true when sprite is hidden. * This was added becaus hidden state might have visibility set to true and so * there would not be possible to find out if a sprite is technically hidden or not. */ _this._isHidden = false; /** * This property indicates if Sprite is currently being revealed from hidden * state. This is used to prevent multiple calls to `sprite.show()` to * restart reveal animation. (if enabled) */ _this.isShowing = false; /** * Indicates if this element is a standalone instance. A "standalone * instance" means this is a autonomous object which maintains its own * set of controls like Preloader, Export, etc. * * @ignore Exclude from docs */ _this.isStandaloneInstance = false; /** * Indicates if togglable Sprite is currently active (toggled on). * * @ignore Exclude from docs */ _this._isActive = false; /** * A Sprite element to use as a mask for this Sprite. * * @ignore Exclude from docs */ _this._mask = new MutableValueDisposer(); /** * @ignore Exclude from docs * @todo Description */ _this._positionPrecision = 3; /** * An instance of [[Language]]. * * @ignore Exclude from docs */ _this._language = new MutableValueDisposer(); /** * Holds [[Export]] object. * * @ignore Exclude from docs */ _this._exporting = new MutableValueDisposer(); /** * Should this Sprite be included when exporting? */ _this._exportable = true; /** * Defines bounding box (square) for this element. * * @ignore Exclude from docs */ _this._bbox = { x: 0, y: 0, width: 0, height: 0 }; /** * Indicates if this element is invalid and should be re-validated (redrawn). * * @ignore Exclude from docs */ _this.invalid = false; /** * Indicates if this elements position is invalid and should be repositioned * * @ignore Exclude from docs */ _this.positionInvalid = false; /** * A collection of key/value pairs that can be used to bind specific Sprite * properties to [[DataItem]]. * * For example: `fill` property can be bound to `myCustomColor` field in * DataItem. The Sprite will automatically get the value for `fill` from its * DataItem. * * Can be set for each [[SpriteState]] individually to override default * bindings. * * @see {@link SpriteState} */ _this.propertyFields = {}; /** * Specifies if property changes on this object should be propagated to the * objects cloned from this object. * * This setting affects property changes *after* cloning, since at the moment * of cloning all of properties from source object are copied to the clone * anyway. * * @default false */ _this.applyOnClones = false; // unrotated unscaled _this._measuredWidthSelf = 0; _this._measuredHeightSelf = 0; // read only, sprite extreme coordinates /** * @ignore */ _this.maxLeft = 0; /** * @ignore */ _this.maxRight = 0; /** * @ignore */ _this.maxTop = 0; /** * @ignore */ _this.maxBottom = 0; // not rotated and not scaled /** * @ignore */ _this.maxLeftSelf = 0; /** * @ignore */ _this.maxRightSelf = 0; /** * @ignore */ _this.maxTopSelf = 0; /** * @ignore */ _this.maxBottomSelf = 0; _this._isDragged = false; _this._isResized = false; /** * @deprecated Moved to [[SpriteProperties]] */ _this._disabled = false; _this._internalDisabled = false; _this._updateDisabled = false; _this._internalDefaultsApplied = false; /** * Time in milliseconds after which rollout event happens when user rolls-out of the sprite. This helps to avoid flickering in some cases. */ _this.rollOutDelay = 0; /** * This flag is set to `true` for the initial sprite you create and place * to the div so that we could clear all additional * sprites/containers when this sprite is disposed. * * @ignore */ _this.isBaseSprite = false; /** * Indicates whether this sprite should be cloned when cloning its parent * container. We set this to `false` in those cases when a sprite is created * by the class, so that when cloning a duplicate sprite would not appear. */ _this.shouldClone = true; /** * A read-only flag which indicates if a sprite has completed its initial * animation (if `showOnInit = true`). * * In case `showOnInit = false`, `appeared` is set to `true` on init. * * @readonly */ _this.appeared = false; /** * [ex description] * * @todo Description * @ignore */ _this.ex = 0; /** * [ey description] * * @todo Description * @ignore */ _this.ey = 0; /** * Indicates if the sprite can be moved around when resizing it with two fingers (will only work if draggable = false) * @ignore */ _this.dragWhileResize = false; /** * @ignore */ _this.measureFailed = false; /** * If this flag is set to true, calling show() will not reveal the sprite. * * @ignore */ _this.preventShow = false; /** * When cloning a sprite, if the template has it's own tooltip assigned, this tooltip is also cloned by default. * This is not good for cpu and sometimes you might only need one single tooltip for all clones. Set this to false in order not to clone tooltip. */ _this.cloneTooltip = true; _this.className = "Sprite"; _this._disposers.push(_this._eventDispatcher); // Generate a unique ID $utils.used(_this.uid); // Create SVG group to hold everything in _this.group = _this.paper.addGroup("g"); // Set defaults // it is better to set defauls like this in order to save invaliation calls and events _this.setPropertyValue("scale", 1); _this.setPropertyValue("rotation", 0); _this.setPropertyValue("align", "none"); _this.setPropertyValue("valign", "none"); _this.setPropertyValue("pixelPerfect", false); _this.setPropertyValue("visible", true); _this.setPropertyValue("tooltipPosition", "fixed"); _this.setPropertyValue("verticalCenter", "none"); _this.setPropertyValue("horizontalCenter", "none"); _this.setPropertyValue("tooltipX", percent(50)); _this.setPropertyValue("tooltipX", percent(50)); _this.setPropertyValue("marginTop", 0); _this.setPropertyValue("marginBottom", 0); _this.setPropertyValue("marginLeft", 0); _this.setPropertyValue("marginRight", 0); _this.setPropertyValue("dx", 0); _this.setPropertyValue("dy", 0); _this.setPropertyValue("paddingTop", 0); _this.setPropertyValue("paddingBottom", 0); _this.setPropertyValue("paddingRight", 0); _this.setPropertyValue("paddingLeft", 0); _this.setPropertyValue("togglable", false); _this.setPropertyValue("hidden", false); _this.setPropertyValue("urlTarget", "_self"); _this.setPropertyValue("alwaysShowTooltip", false); _this.setPropertyValue("showTooltipOn", "hover"); _this._prevMeasuredWidth = 0; _this._prevMeasuredHeight = 0; _this._measuredWidth = 0; _this._measuredHeight = 0; _this._isMeasured = true; // Invalidate the Sprite so that renderer knows it needs to be drawn _this.invalidate(); //this.states.create("default").properties.opacity = 1; // Apply the theme _this.applyTheme(); //this._disposers.push(this._clickable); // Decorate adapter with events so that we can apply its settings whenever // it is modified // @todo Think what to do here. We can't just apply the adapter value to // property since not all of those are for properties. Commented out for // now. /*this.adapter.events.on("inserted", (ev: any) => { (<any>this)[ev.newValue.key] = (<any>this)[ev.newValue.key]; }); this.adapter.events.on("removed", (ev: any) => { (<any>this)[ev.newValue.key] = (<any>this)[ev.newValue.key]; });*/ // Add disposable dependencies to `_disposers` so they are automatically // disposed of when this object is disposed _this._disposers.push(_this.events); _this._disposers.push(_this.group); _this._disposers.push(_this._mask); _this._disposers.push(_this._language); _this._disposers.push(_this._exporting); //this._disposers.push(this._parent); //this._disposers.push(this._modal); _this._disposers.push(new Disposer(function () { $object.each(_this._bindings, function (key, value) { value.dispose(); }); })); _this.setPropertyValue("interactionsEnabled", true); return _this; } Object.defineProperty(Sprite.prototype, "events", { /** * Event dispatcher. * * @see {@link https://www.amcharts.com/docs/v4/concepts/event-listeners/} for more info about Events */ get: function () { return this._eventDispatcher; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "adapter", { /** * Holds Adapter. * * @see {@link https://www.amcharts.com/docs/v4/concepts/adapters/} for more info about Adapters */ get: function () { if (!this._adapterO) { this._adapterO = new Adapter(this); } return this._adapterO; }, enumerable: true, configurable: true }); /** * ========================================================================== * ELEMENT VALIDATION, INIT, AND DRAWING STUFF * ========================================================================== * @hidden */ /** * Applies properties from all assigned themes. * * We do this here so that we can apply class names as well. * * @ignore Exclude from docs */ Sprite.prototype.applyTheme = function () { _super.prototype.applyTheme.call(this); if (options.autoSetClassName) { this.setClassName(); } }; /** * Returns theme(s) used by this object either set explicitly on this * element, inherited from parent, or inherited from [[System]]. * * @return An array of theme references */ Sprite.prototype.getCurrentThemes = function () { var themes = this._themes; if (themes) { return themes; } else { var parent_1 = this._parent; if (parent_1) { return parent_1.getCurrentThemes(); } else { return registry.themes; } } }; /** * Called just before element's validation, this function allows setting * defaults. * * @ignore Exclude from docs */ Sprite.prototype.applyInternalDefaults = function () { // Nothing here, but extending classes might override this function // so that they can set their defaults this._internalDefaultsApplied = true; }; /** * Invalidates element. * * Object will be redrawn during the next update cycle. * * Please note that in most cases elements will auto-invalidate when needed. If * everything works, DO NOT use this method. Use it only if some changes do * not take otherwise. */ Sprite.prototype.invalidate = function () { if (this.disabled || this._isTemplate || this.__disabled) { return; } // We no longer reset this on each invalidate, so that they are applied // only once, and do not overwrite user-defined settings //this._internalDefaultsApplied = false; if (!this.invalid) { this.invalid = true; registry.addToInvalidSprites(this); system.requestFrame(); } }; /** * Validates element: * * Triggers events * * Redraws the element * * @ignore Exclude from docs */ Sprite.prototype.validate = function () { this.dispatchImmediately("beforevalidated"); // prevents from drawing if topparent is 0x0 /* let topParent = this.topParent; if (topParent) { if (!topParent.maxWidth || !topParent.maxHeight) { this._disposers.push(topParent.events.once("maxsizechanged", this.invalidate, this)); } }*/ // Set internal defaults if (!this._internalDefaultsApplied) { this.applyInternalDefaults(); } this.beforeDraw(); this.draw(); this.invalid = false; registry.removeFromInvalidSprites(this); this.afterDraw(); }; /** * Invalidates element's position. * * @ignore Exclude from docs */ Sprite.prototype.invalidatePosition = function () { if (this.disabled || this._isTemplate) { return; } if (!this.positionInvalid) { this.positionInvalid = true; registry.addToInvalidPositions(this); system.requestFrame(); } }; /** * Transforms the element. * * @todo Description (review) * @ignore Exclude from docs */ Sprite.prototype.validatePosition = function () { var pixelX = this.pixelX; var pixelY = this.pixelY; var dx = this.dx; var dy = this.dy; var x = pixelX + dx; var y = pixelY + dy; if (this._updateDisabled) { if (this._internalDisabled) { this.group.attr({ "display": "none" }); } else { if (!this.disabled) { this.removeSVGAttribute("display"); } } this._updateDisabled = false; } var sizeChanged = this.measure(); //if (!this.invalid) { var prevGroupTransform = this.group.transformString; this.group.moveTo({ x: x, y: y }); this.group.rotation = this.rotation; if (this.nonScaling) { this.group.scale = this.scale / this.globalScale; } else { this.group.scale = this.scale; } if (prevGroupTransform != this.group.transformString || sizeChanged) { // not yet sure, this is to avoid many transforms=>container layout invalidation on initial buid if (prevGroupTransform == null) { this.dispatch("transformed"); } else { this.dispatchImmediately("transformed"); } // TODO clear existing positionchanged dispatches ? this.dispatch("positionchanged"); if (this.showTooltipOn == "hit" || this.showTooltipOn == "always") { this.updateTooltipPosition(); } } //} // it might happen that x and y changed again, so we only remove if they didn't if (pixelX + dx == x && pixelY + dy == y) { registry.removeFromInvalidPositions(this); this.positionInvalid = false; } var maskRectangle = this._maskRectangle; // todo: verify this if (maskRectangle) { this._clipElement.moveTo({ x: maskRectangle.x - pixelX, y: maskRectangle.y - pixelY }); } }; /** * A placeholder method that is called **before** element begins to be drawn. * * @ignore Exclude from docs */ Sprite.prototype.beforeDraw = function () { }; /** * A placeholder method that draws the element. * * @ignore Exclude from docs */ Sprite.prototype.draw = function () { }; /** * A placeholder method that is called **after** element finishes drawing * itself. * * @ignore Exclude from docs */ Sprite.prototype.afterDraw = function () { var e_1, _a; if (this.isMeasured || this.horizontalCenter !== "none" || this.verticalCenter !== "none") { this.measureElement(); } //this.applyMask(); if (!this._inited) { if (this._adapterO) { try { // used to be applySVGAttrbutes here, this is more efficient for (var _b = __values(this._adapterO.keys()), _c = _b.next(); !_c.done; _c = _b.next()) { var key = _c.value; switch (key) { case "mask": case "fill": case "opacity": case "fillOpacity": case "stroke": case "strokeOpacity": case "strokeWidth": case "shapeRendering": case "strokeDasharray": case "strokeDashoffset": case "strokeLinecap": case "strokeLinejoin": case "textDecoration": case "fontSize": case "fontFamily": case "fontWeight": //case "focusable": //case "tabindex": //case "role": this[key] = this[key]; break; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_c && !_c.done && (_a = _b.return)) _a.call(_b); } finally { if (e_1) throw e_1.error; } } } this.applyFilters(); this.visible = this.visible; this.interactionsEnabled = this.getPropertyValue("interactionsEnabled"); // can't use .interactionsEnabled as it get's parent's this._inited = true; if (!this.showOnInit) { this.appeared = true; } if (this.hidden) { this.hide(0); } this.applyMask(); this.dispatch("validated"); this.dispatch("inited"); this.dispatchReady(); } else { this.dispatch("validated"); } if (this.showTooltipOn == "always") { if (this.visible && !this.disabled && !this.__disabled) { this.showTooltip(); } else { this.hideTooltip(0); } } }; /** * Dispatches `"ready"` event. Sprite dispatches it right after `"inited"` event. * * @ignore */ Sprite.prototype.dispatchReady = function () { if (!this.isReady()) { this._ready = true; this.dispatch("ready"); } }; /** * Triggers a re-initialization of this element. * * Will result in complete redrawing of the element. * * @ignore Exclude from docs */ Sprite.prototype.reinit = function () { this._inited = false; this.setState(this.defaultState); this.invalidate(); }; /** * Handles the situation where parent element is resized. * * @ignore Exclude from docs */ Sprite.prototype.handleGlobalScale = function () { this.dispatch("globalscalechanged"); if (this.nonScalingStroke) { this.strokeWidth = this.strokeWidth; } if (this.nonScaling) { this.validatePosition(); } this.updateFilterScale(); }; /** * Updates filter properties which might depend on scale * * @ignore Exclude from docs */ Sprite.prototype.updateFilterScale = function () { var _this = this; $iter.each(this.filters.iterator(), function (filter) { filter.scale = _this.globalScale; }); }; /** * Removes itself from system's invalid lists. * * @ignore Exclude from docs */ Sprite.prototype.removeFromInvalids = function () { registry.removeFromInvalidSprites(this); registry.removeFromInvalidPositions(this); }; /** * Copies all parameters from another [[Sprite]]. * * @param source Source Sprite */ Sprite.prototype.copyFrom = function (source) { var _this = this; _super.prototype.copyFrom.call(this, source); this.isMeasured = source.isMeasured; this.states.copyFrom(source.states); if (source.filters.length > 0) { source.filters.each(function (filter) { _this.filters.push(filter.clone()); }); } if (source._adapterO) { this.adapter.copyFrom(source._adapterO); } //helps to avoid calling getter which creates instance if (source["_interaction"]) { this.interactions.copyFrom(source.interactions); } if (source["_plugins"]) { this.plugins.copyFrom(source.plugins); } this.configField = source.configField; this.applyOnClones = source.applyOnClones; // this.numberFormatter = source.numberFormatter; // todo: this creates loose number formatter and copies it to all clones. somehow we need to know if source had numberFormatter explicitly created and not just because a getter was called. //this.mask = source.mask; need to think about this, generally this causes a lot of problems this.disabled = source.disabled; this.virtualParent = source.virtualParent; this.exportable = source.exportable; //@todo: create tooltip if it's on source but not on this? //const tooltip = this._tooltip; //if (tooltip) { // tooltip.copyFrom(source.tooltip); //} if (source._tooltip) { if (this._tooltip) { this._tooltip.copyFrom(source.tooltip); } else { if (source.cloneTooltip) { this.tooltip = source.tooltip.clone(); } else { this._tooltip = source.tooltip; } } } //if ((<any>source)["_tooltip"] && !this._tooltip) { // this._tooltip = (<any>source)["_tooltip"]; //} this._showSystemTooltip = source.showSystemTooltip; $utils.copyProperties(source.propertyFields, this.propertyFields); $utils.copyProperties(source.properties, this); if (source.fillModifier) { this.fillModifier = source.fillModifier.clone(); } if (source.strokeModifier) { this.strokeModifier = source.strokeModifier.clone(); } if (source.focusFilter) { this.focusFilter = source.focusFilter.clone(); } }; /** * Destroys this object and all related data. */ Sprite.prototype.dispose = function () { if (!this.isDisposed()) { if (this.showTooltipOn == "always" && this.tooltip) { this.tooltip.hide(); } this.dispatchImmediately("beforedisposed"); if (this.isBaseSprite) { if (this.htmlContainer) { while (this.htmlContainer.childNodes.length > 0) { this.htmlContainer.removeChild(this.htmlContainer.firstChild); } } this.isBaseSprite = false; } _super.prototype.dispose.call(this); // Clear adapters if (this._adapterO) { this._adapterO.clear(); } if (this.applyOnClones) { if (this._clones) { for (var i = this._clones.length - 1; i >= 0; i--) { var clone = this._clones.getIndex(i); clone.dispose(); } } } if (this._svgContainer) { this._svgContainer.dispose(); } if (this._interactionDisposer) { this._interactionDisposer.dispose(); this._interactionDisposer = undefined; } if (this._urlDisposer) { this._urlDisposer.dispose(); } this.removeFromInvalids(); if (this.element) { this.element.dispose(); } if (this.group) { this.group.dispose(); } if (this._numberFormatter) { this._numberFormatter.dispose(); } if (this._focusFilter) { this._focusFilter.dispose(); } var stroke = this.stroke; if (stroke && !(stroke instanceof Color) && stroke.dispose) { if (this.clonedFrom && this.clonedFrom.stroke == stroke) { // do nothing } else { stroke.dispose(); } } // TODO a bit hacky var fill = this.fill; if (fill && !(fill instanceof Color) && fill.dispose) { if (this.clonedFrom && this.clonedFrom.fill == fill) { // do nothing } else { fill.dispose(); } } // remove from map if ($type.hasValue(this.id)) { this.map.removeKey(this.id); } this.parent = undefined; if (this._filters) { while (this._filters.length > 0) { var filter = this._filters.getIndex(0); filter.dispose(); this._filters.removeValue(filter); } } this._alwaysShowDisposers = undefined; } }; Object.defineProperty(Sprite.prototype, "isTemplate", { /** * @ignore Exclude from docs * @return Is template? */ get: function () { return this._isTemplate; }, /** * Indicates if this element is a "template". * * Template Sprites act only as a holders for config for other "real" * elements to be cloned from. * * Templates are treated differently, as they are not validated, redrawn, or * otherwise are processed. * * @ignore Exclude from docs * @param value Is template? */ set: function (value) { value = $type.toBoolean(value); if (this._isTemplate != value) { this._isTemplate = value; if (this instanceof Container) { $iter.each(this.children.iterator(), function (child) { child.isTemplate = value; }); } if (value) { this.parent = this._parent; this.removeFromInvalids(); } else { this.invalidate(); } } }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "showSystemTooltip", { /** * @return Show system tooltip? */ get: function () { if (!$type.hasValue(this._showSystemTooltip)) { if (this.virtualParent) { return this.virtualParent.showSystemTooltip; } else if (this._parent) { return this._parent.showSystemTooltip; } else { return false; } } return this._showSystemTooltip; }, /** * Indicates whether the element should attempt to construct itself in a way * so that system tooltip is shown if its `readerTitle` is set. * * @param value Show system tooltip? */ set: function (value) { value = $type.toBoolean(value); if (this._showSystemTooltip != value) { this._showSystemTooltip = value; this.applyAccessibility(); } }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "topParent", { /** * ========================================================================== * HIERARCHY AND STRUCTURE RELATED STUFF * ========================================================================== * @hidden */ /** * Sprites's top-level [[Container]]. * * Please note that in most cases it won't be the chart element. * * To access base chart element, use `baseSprite` instead. * * @return Top-level ascendant */ get: function () { if (this._topParent) { return this._topParent; } else { if (this._parent) { return this._parent.topParent; } } }, /** * @ignore * @param value {Container} top parent of a sprite */ set: function (value) { this._topParent = value; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "parent", { /** * @return Parent container */ get: function () { return this._parent; }, /** * Elements' parent [[Container]]. * * @param parent Parent container */ set: function (parent) { if (this._isTemplate) { return; } // TODO is this needed ? $utils.used(this.paper); var oldParent = this._parent; if (oldParent != parent) { if (oldParent) { oldParent.children.removeValue(this); } this._parent = parent; if (parent) { this.topParent = parent.topParent; if (parent.isTemplate) { this.isTemplate = true; } this.baseId = parent.baseId; parent.children.push(this); // insert handler at Container invalidates + invalidatesLayout + adds to group if (this._tooltip && !this._tooltipContainer) { this._tooltip.parent = parent.tooltipContainer; } if (!this._dataItem) { this.dataItem = parent.dataItem; } this.handleAlwaysShowTooltip(); if (this.dataItem) { // No need to apply accessibility if there's no data item // The whole reason of applying it here is to populate data // placesholders, and if tehre's no data item, it won't work anyway this.applyAccessibility(); } this.dispatchImmediately("parentset"); } else { this.topParent = undefined; } } }, enumerable: true, configurable: true }); /** * @ignore */ Sprite.prototype.handleAlwaysShow = function () { this.showTooltip(); }; /** * @ignore */ Sprite.prototype.handleAlwaysShowTooltip = function () { var sprite = this; var oldDisposers = this._alwaysShowDisposers; if (oldDisposers) { $array.each(oldDisposers, function (oldDisposer) { oldDisposer.dispose(); }); } this._alwaysShowDisposers = []; if (this.showTooltipOn == "always") { while (sprite != undefined) { var disposer = sprite.events.on("visibilitychanged", this.handleAlwaysShow, this, false); this.addDisposer(disposer); this._alwaysShowDisposers.push(disposer); sprite = sprite.parent; } } }; Object.defineProperty(Sprite.prototype, "virtualParent", { /** * @return Virtual parent */ get: function () { return this._virtualParent; }, /** * Element's "virtual" parent. * * This is required in ordere to maintain proper inheritance (like * formatters). * * Sometimes an element is a "logical" parent, even though it's not a direct * ascendant. * * Example: a bullet is not a child of the axis, but it would make sense * for it to inherit series' formatters. * * @ignore Exclude from docs * @param value Virtual parent */ set: function (value) { this._virtualParent = value; if (this.dataItem) { // No need to apply accessibility if there's no data item // The whole reason of applying it here is to populate data // placesholders, and if tehre's no data item, it won't work anyway this.applyAccessibility(); } }, enumerable: true, configurable: true }); /** * Moves `<defs>` to correct place in DOM. * * Some elements are initially created in "ghost" container. When moving * those into proper place in DOM, their respective `<defs>` need to be moved * as well. * * @ignore Exclude from docs */ Sprite.prototype.appendDefs = function () { if (this.filterElement) { this.paper.appendDef(this.filterElement); } var fill = this.fill; if (fill && fill.element) { this.paper.appendDef(fill.element); } var stroke = this.stroke; if (stroke && stroke.element) { this.paper.appendDef(stroke.element); } if (this.fillModifier && this.fill instanceof Color) { var fill_1 = this.fillModifier.modify(this.fill); if (fill_1 && fill_1.element) { this.paper.appendDef(fill_1.element); } } if (this.strokeModifier && this.stroke instanceof Color) { var stroke_1 = this.strokeModifier.modify(this.stroke); if (stroke_1 && stroke_1.element) { this.paper.appendDef(stroke_1.element); } } if (this._clipPath) { this.paper.appendDef(this._clipPath); } if (this._exportable === false) { this.exportable = false; } }; Object.defineProperty(Sprite.prototype, "map", { /** * Returns a [[Dictionary]] which maps object ids with their respective * objects. * * Can be used to retrieve any object by id, e.g.: * * ```TypeScript * console.log(mySprite.map.getKey("myid")); * ``` * ```JavaScript * console.log(mySprite.map.getKey("myid")); * ``` * * @return Map collection */ get: function () { var top = this.topParent; if (top) { return top.map; } else if (!this._map) { this._map = new Dictionary(); } return this._map; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "delayedMap", { /** * @ignore * @return Map collection */ get: function () { var top = this.topParent; if (top) { return top.delayedMap; } else if (!this._delayedMap) { this._delayedMap = new Dictionary(); } return this._delayedMap; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "id", { /** * @return ID */ get: function () { return this._id; }, /** * Element's user-defined ID. * * Will throw an Error if there already is an object with the same ID. * * Please note that above check will be performed withing the scope of the * current chart instance. It will not do checks across other chart instances * or in globally in DOM. * * Make sure the IDs are unique. * * @param value ID */ set: function (value) { if (this._id != value) { this._id = value; if (this.map.hasKey(value)) { throw Error("Duplicate id (" + value + ") used on multiple objects."); } else { this.map.setKey(value, this); } if (options.autoSetClassName) { this.setClassName(); } } }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "dom", { /** * ========================================================================== * ELEMENT AND DOM TREE MANIPULATION AND MEASURING * ========================================================================== * @hidden */ /** * Returns DOM element reference associated with this element. * * @readonly * @return DOM element */ get: function () { return this.group.node; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "paper", { /** * @ignore Exclude from docs * @return Paper */ get: function () { if (this._paper) { return this._paper; } else { var parent_2 = this._parent; if (parent_2) { return parent_2.paper; } } return getGhostPaper(); }, /** * A [[Paper]] instance to place elements on. * * If there's no Paper set for this element, it goes up the ascendant tree * until it finds one. * * This method is used by important `addChild()` method, so it's essential * to have a [[Paper]] instance. * * If this element has a separate `htmlContainer` set, it will have a * [[Paper]] instance itself. * * @ignore Exclude from docs * @param paper Paper */ set: function (paper) { this.setPaper(paper); }, enumerable: true, configurable: true }); /** * Sets [[Paper]] instance to use to draw elements. * @ignore * @param paper Paper * @return true if paper was changed, false, if it's the same */ Sprite.prototype.setPaper = function (paper) { var oldPaper = this._paper; if (oldPaper != paper) { this._paper = paper; this.appendDefs(); return true; } return false; }; Object.defineProperty(Sprite.prototype, "htmlContainer", { /** * @return HTML element */ get: function () { if (this._htmlContainer) { return this._htmlContainer; } else { var parent_3 = this._parent; if (parent_3) { return parent_3.htmlContainer; } } }, /** * An HTML element to be used when placing wrapper element (`<div>`) * for the whole chart. * * This is the same for **all** elements within the same chart. * * @param htmlContainer HTML element */ set: function (htmlContainer) { this._htmlContainer = htmlContainer; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "titleElement", { /** * Creates (if not yet created) and returns element's `<title>` element. * * @ignore Exclude from docs * @return Title element */ get: function () { if (!this._titleElement) { this._titleElement = this.paper.add("title"); this.group.add(this._titleElement); } return this._titleElement; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "descriptionElement", { /** * Creates (if not yet created) and returns element's `<desc>` element. * * @ignore Exclude from docs * @return Desc element */ get: function () { if (!this._descriptionElement) { this._descriptionElement = this.paper.add("desc"); this.group.add(this._descriptionElement); } return this._descriptionElement; }, enumerable: true, configurable: true }); Object.defineProperty(Sprite.prototype, "filters", { /** * Returns list of SVG filters (effects) applied to element. If the filter * list is not yet initilized, creates and returns an empty one.