@amcharts/amcharts4
Version:
amCharts 4
1,404 lines (1,402 loc) • 311 kB
JavaScript
/**
* 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.