PageSourceSearch

https://www.pyrenees-bearnaises.com/app/dist/pyreneesbearnaises/ad…/es-modules/Core/Renderer/SVG/SVGElement.js

js pyrenees-bearnaises.com collected 2026-10-02 15:34:52 UTC 69,329 bytes, 1,882 lines download raw bytes

1/* *
2 *
3 *  (c) 2010-2024 Torstein Honsi
4 *
5 *  License: www.highcharts.com/license
6 *
7 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
8 *
9 * */
10'use strict';
11import A from '../../Animation/AnimationUtilities.js';
12const { animate, animObject, stop } = A;
13import Color from '../../Color/Color.js';
14import H from '../../Globals.js';
15const { deg2rad, doc, svg, SVG_NS, win, isFirefox } = H;
16import U from '../../Utilities.js';
17const { addEvent, attr, createElement, crisp, css, defined, erase, extend, fireEvent, getAlignFactor, isArray, isFunction, isNumber, isObject, isString, merge, objectEach, pick, pInt, pushUnique, replaceNested, syncTimeout, uniqueKey } = U;
18/* *
19 *
20 *  Class
21 *
22 * */
23/* eslint-disable no-invalid-this, valid-jsdoc */
24/**
25 * The SVGElement prototype is a JavaScript wrapper for SVG elements used in the
26 * rendering layer of Highcharts. Combined with the
27 * {@link Highcharts.SVGRenderer}
28 * object, these prototypes allow freeform annotation in the charts or even in
29 * HTML pages without instanciating a chart. The SVGElement can also wrap HTML
30 * labels, when `text` or `label` elements are created with the `useHTML`
31 * parameter.
32 *
33 * The SVGElement instances are created through factory functions on the
34 * {@link Highcharts.SVGRenderer}
35 * object, like
36 * {@link Highcharts.SVGRenderer#rect|rect},
37 * {@link Highcharts.SVGRenderer#path|path},
38 * {@link Highcharts.SVGRenderer#text|text},
39 * {@link Highcharts.SVGRenderer#label|label},
40 * {@link Highcharts.SVGRenderer#g|g}
41 * and more.
42 *
43 * See [How to use the SVG Renderer](
44 * https://www.highcharts.com/docs/advanced-chart-features/renderer) for a
45 * comprehensive tutorial on how to draw SVG elements on a chart.
46 *
47 * @class
48 * @name Highcharts.SVGElement
49 */
50class SVGElement {
51    // @todo public zIndex?: number;
52    /* *
53     *
54     *  Functions
55     *
56     * */
57    /**
58     * Get the current value of an attribute or pseudo attribute,
59     * used mainly for animation. Called internally from
60     * the {@link Highcharts.SVGRenderer#attr} function.
61     *
62     * @private
63     * @function Highcharts.SVGElement#_defaultGetter
64     *
65     * @param {string} key
66     *        Property key.
67     *
68     * @return {number|string}
69     *         Property value.
70     */
71    _defaultGetter(key) {
72        let ret = pick(this[key + 'Value'], // Align getter
73        this[key], this.element ? this.element.getAttribute(key) : null, 0);
74        if (/^-?[\d\.]+$/.test(ret)) { // Is numerical
75            ret = parseFloat(ret);
76        }
77        return ret;
78    }
79    /**
80     * @private
81     * @function Highcharts.SVGElement#_defaultSetter
82     *
83     * @param {string} value
84     *
85     * @param {string} key
86     *
87     * @param {Highcharts.SVGDOMElement} element
88     *
89     */
90    _defaultSetter(value, key, element) {
91        element.setAttribute(key, value);
92    }
93    /**
94     * Add the element to the DOM. All elements must be added this way.
95     *
96     * @sample highcharts/members/renderer-g
97     *         Elements added to a group
98     *
99     * @function Highcharts.SVGElement#add
100     *
101     * @param {Highcharts.SVGElement} [parent]
102     *        The parent item to add it to. If undefined, the element is added
103     *        to the {@link Highcharts.SVGRenderer.box}.
104     *
105     * @return {Highcharts.SVGElement}
106     *         Returns the SVGElement for chaining.
107     */
108    add(parent) {
109        const renderer = this.renderer, element = this.element;
110        let inserted;
111        if (parent) {
112            this.parentGroup = parent;
113        }
114        // Build formatted text
115        if (typeof this.textStr !== 'undefined' &&
116            this.element.nodeName === 'text' // Not for SVGLabel instances
117        ) {
118            renderer.buildText(this);
119        }
120        // Mark as added
121        this.added = true;
122        // If we're adding to renderer root, or other elements in the group
123        // have a z index, we need to handle it
124        if (!parent || parent.handleZ || this.zIndex) {
125            inserted = this.zIndexSetter();
126        }
127        // If zIndex is not handled, append at the end
128        if (!inserted) {
129            (parent ?
130                parent.element :
131                renderer.box).appendChild(element);
132        }
133        // Fire an event for internal hooks
134        if (this.onAdd) {
135            this.onAdd();
136        }
137        return this;
138    }
139    /**
140     * Add a class name to an element.
141     *
142     * @function Highcharts.SVGElement#addClass
143     *
144     * @param {string} className
145     * The new class name to add.
146     *
147     * @param {boolean} [replace=false]
148     * When true, the existing class name(s) will be overwritten with the new
149     * one. When false, the new one is added.
150     *
151     * @return {Highcharts.SVGElement}
152     * Return the SVG element for chainability.
153     */
154    addClass(className, replace) {
155        const currentClassName = replace ? '' : (this.attr('class') || '');
156        // Trim the string and remove duplicates
157        className = (className || '')
158            .split(/ /g)
159            .reduce(function (newClassName, name) {
160            if (currentClassName.indexOf(name) === -1) {
161                newClassName.push(name);
162            }
163            return newClassName;
164        }, (currentClassName ?
165            [currentClassName] :
166            []))
167            .join(' ');
168        if (className !== currentClassName) {
169            this.attr('class', className);
170        }
171        return this;
172    }
173    /**
174     * This method is executed in the end of `attr()`, after setting all
175     * attributes in the hash. In can be used to efficiently consolidate
176     * multiple attributes in one SVG property -- e.g., translate, rotate and
177     * scale are merged in one "transform" attribute in the SVG node.
178     *
179     * @private
180     * @function Highcharts.SVGElement#afterSetters
181     */
182    afterSetters() {
183        // Update transform. Do this outside the loop to prevent redundant
184        // updating for batch setting of attributes.
185        if (this.doTransform) {
186            this.updateTransform();
187            this.doTransform = false;
188        }
189    }
190    /**
191     * Align the element relative to the chart or another box.
192     *
193     * @function Highcharts.SVGElement#align
194     *
195     * @param {Highcharts.AlignObject} [alignOptions]
196     *        The alignment options. The function can be called without this
197     *        parameter in order to re-align an element after the box has been
198     *        updated.
199     *
200     * @param {boolean} [alignByTranslate]
201     *        Align element by translation.
202     *
203     * @param {string|Highcharts.BBoxObject} [alignTo]
204     *        The box to align to, needs a width and height. When the box is a
205     *        string, it refers to an object in the Renderer. For example, when
206     *        box is `spacingBox`, it refers to `Renderer.spacingBox` which
207     *        holds `width`, `height`, `x` and `y` properties.
208     *
209     * @param {boolean} [redraw]
210     *        Decide if SVGElement should be redrawn with new alignment or
211     *        just change its attributes.
212     *
213     * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
214     */
215    align(alignOptions, alignByTranslate, alignTo, redraw = true) {
216        const renderer = this.renderer, alignedObjects = renderer.alignedObjects, initialAlignment = Boolean(alignOptions);
217        // First call on instanciate
218        if (alignOptions) {
219            this.alignOptions = alignOptions;
220            this.alignByTranslate = alignByTranslate;
221            this.alignTo = alignTo;
222            // When called on resize, no arguments are supplied
223        }
224        else {
225            alignOptions = this.alignOptions || {};
226            alignByTranslate = this.alignByTranslate;
227            alignTo = this.alignTo;
228        }
229        const alignToKey = !alignTo || isString(alignTo) ?
230            alignTo || 'renderer' :
231            void 0;
232        // When aligned to a key, automatically re-align on redraws
233        if (alignToKey) {
234            // Prevent duplicates, like legendGroup after resize
235            if (initialAlignment) {
236                pushUnique(alignedObjects, this);
237            }
238            alignTo = void 0; // Do not use the box
239        }
240        const alignToBox = pick(alignTo, renderer[alignToKey], renderer), 
241        // Default: left align
242        x = (alignToBox.x || 0) + (alignOptions.x || 0) +
243            ((alignToBox.width || 0) - (alignOptions.width || 0)) *
244                getAlignFactor(alignOptions.align), 
245        // Default: top align
246        y = (alignToBox.y || 0) + (alignOptions.y || 0) +
vendor: 9,351 bytes, lines 247-485
247            ((alignToBox.height || 0) - (alignOptions.height || 0)) *
248                getAlignFactor(alignOptions.verticalAlign), attribs = {
249            'text-align': alignOptions?.align
250        };
251        attribs[alignByTranslate ? 'translateX' : 'x'] = Math.round(x);
252        attribs[alignByTranslate ? 'translateY' : 'y'] = Math.round(y);
253        // Animate only if already placed
254        if (redraw) {
255            this[this.placed ? 'animate' : 'attr'](attribs);
256            this.placed = true;
257        }
258        this.alignAttr = attribs;
259        return this;
260    }
261    /**
262     * @private
263     * @function Highcharts.SVGElement#alignSetter
264     * @param {"left"|"center"|"right"} value
265     */
266    alignSetter(value) {
267        const convert = {
268            left: 'start',
269            center: 'middle',
270            right: 'end'
271        };
272        if (convert[value]) {
273            this.alignValue = value;
274            this.element.setAttribute('text-anchor', convert[value]);
275        }
276    }
277    /**
278     * Animate to given attributes or CSS properties.
279     *
280     * @sample highcharts/members/element-on/
281     *         Setting some attributes by animation
282     *
283     * @function Highcharts.SVGElement#animate
284     *
285     * @param {Highcharts.SVGAttributes} params
286     *        SVG attributes or CSS to animate.
287     *
288     * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [options]
289     *        Animation options.
290     *
291     * @param {Function} [complete]
292     *        Function to perform at the end of animation.
293     *
294     * @return {Highcharts.SVGElement}
295     *         Returns the SVGElement for chaining.
296     */
297    animate(params, options, complete) {
298        const animOptions = animObject(pick(options, this.renderer.globalAnimation, true)), deferTime = animOptions.defer;
299        // When the page is hidden save resources in the background by not
300        // running animation at all (#9749).
301        if (doc.hidden) {
302            animOptions.duration = 0;
303        }
304        if (animOptions.duration !== 0) {
305            // Allows using a callback with the global animation without
306            // overwriting it
307            if (complete) {
308                animOptions.complete = complete;
309            }
310            // If defer option is defined delay the animation #12901
311            syncTimeout(() => {
312                if (this.element) {
313                    animate(this, params, animOptions);
314                }
315            }, deferTime);
316        }
317        else {
318            this.attr(params, void 0, complete || animOptions.complete);
319            // Call the end step synchronously
320            objectEach(params, function (val, prop) {
321                if (animOptions.step) {
322                    animOptions.step.call(this, val, { prop: prop, pos: 1, elem: this });
323                }
324            }, this);
325        }
326        return this;
327    }
328    /**
329     * Apply a text outline through a custom CSS property, by copying the text
330     * element and apply stroke to the copy. Used internally. Contrast checks at
331     * [example](https://jsfiddle.net/highcharts/43soe9m1/2/).
332     *
333     * @example
334     * // Specific color
335     * text.css({
336     *    textOutline: '1px black'
337     * });
338     * // Automatic contrast
339     * text.css({
340     *    color: '#000000', // black text
341     *    textOutline: '1px contrast' // => white outline
342     * });
343     *
344     * @private
345     * @function Highcharts.SVGElement#applyTextOutline
346     *
347     * @param {string} textOutline
348     *        A custom CSS `text-outline` setting, defined by `width color`.
349     */
350    applyTextOutline(textOutline) {
351        const elem = this.element, hasContrast = textOutline.indexOf('contrast') !== -1, styles = {};
352        // When the text shadow is set to contrast, use dark stroke for light
353        // text and vice versa.
354        if (hasContrast) {
355            styles.textOutline = textOutline = textOutline.replace(/contrast/g, this.renderer.getContrast(elem.style.fill));
356        }
357        // Extract the stroke width and color
358        const parts = textOutline.split(' ');
359        const color = parts[parts.length - 1];
360        let strokeWidth = parts[0];
361        if (strokeWidth && strokeWidth !== 'none' && H.svg) {
362            this.fakeTS = true; // Fake text shadow
363            // Since the stroke is applied on center of the actual outline, we
364            // need to double it to get the correct stroke-width outside the
365            // glyphs.
366            strokeWidth = strokeWidth.replace(/(^[\d\.]+)(.*?)$/g, function (match, digit, unit) {
367                return (2 * Number(digit)) + unit;
368            });
369            // Remove shadows from previous runs.
370            this.removeTextOutline();
371            const outline = doc.createElementNS(SVG_NS, 'tspan');
372            attr(outline, {
373                'class': 'highcharts-text-outline',
374                fill: color,
375                stroke: color,
376                'stroke-width': strokeWidth,
377                'stroke-linejoin': 'round'
378            });
379            // For each of the tspans and text nodes, create a copy in the
380            // outline.
381            const parentElem = elem.querySelector('textPath') || elem;
382            [].forEach.call(parentElem.childNodes, (childNode) => {
383                const clone = childNode.cloneNode(true);
384                if (clone.removeAttribute) {
385                    ['fill', 'stroke', 'stroke-width', 'stroke'].forEach((prop) => clone
386                        .removeAttribute(prop));
387                }
388                outline.appendChild(clone);
389            });
390            // Collect the sum of dy from all children, included nested ones
391            let totalHeight = 0;
392            [].forEach.call(parentElem.querySelectorAll('text tspan'), (element) => {
393                totalHeight += Number(element.getAttribute('dy'));
394            });
395            // Insert an absolutely positioned break before the original text
396            // to keep it in place
397            const br = doc.createElementNS(SVG_NS, 'tspan');
398            br.textContent = '\u200B';
399            // Reset the position for the following text
400            attr(br, {
401                x: Number(elem.getAttribute('x')),
402                dy: -totalHeight
403            });
404            // Insert the outline
405            outline.appendChild(br);
406            parentElem.insertBefore(outline, parentElem.firstChild);
407        }
408    }
409    /**
410     * @function Highcharts.SVGElement#attr
411     * @param {string} key
412     * @return {number|string}
413     */ /**
414    * Apply native and custom attributes to the SVG elements.
415    *
416    * In order to set the rotation center for rotation, set x and y to 0 and
417    * use `translateX` and `translateY` attributes to position the element
418    * instead.
419    *
420    * Attributes frequently used in Highcharts are `fill`, `stroke`,
421    * `stroke-width`.
422    *
423    * @sample highcharts/members/renderer-rect/
424    *         Setting some attributes
425    *
426    * @example
427    * // Set multiple attributes
428    * element.attr({
429    *     stroke: 'red',
430    *     fill: 'blue',
431    *     x: 10,
432    *     y: 10
433    * });
434    *
435    * // Set a single attribute
436    * element.attr('stroke', 'red');
437    *
438    * // Get an attribute
439    * element.attr('stroke'); // => 'red'
440    *
441    * @function Highcharts.SVGElement#attr
442    *
443    * @param {string|Highcharts.SVGAttributes} [hash]
444    *        The native and custom SVG attributes.
445    *
446    * @param {number|string|Highcharts.SVGPathArray} [val]
447    *        If the type of the first argument is `string`, the second can be a
448    *        value, which will serve as a single attribute setter. If the first
449    *        argument is a string and the second is undefined, the function
450    *        serves as a getter and the current value of the property is
451    *        returned.
452    *
453    * @param {Function} [complete]
454    *        A callback function to execute after setting the attributes. This
455    *        makes the function compliant and interchangeable with the
456    *        {@link SVGElement#animate} function.
457    *
458    * @param {boolean} [continueAnimation=true]
459    *        Used internally when `.attr` is called as part of an animation
460    *        step. Otherwise, calling `.attr` for an attribute will stop
461    *        animation for that attribute.
462    *
463    * @return {Highcharts.SVGElement}
464    *         If used as a setter, it returns the current
465    *         {@link Highcharts.SVGElement} so the calls can be chained. If
466    *         used as a getter, the current value of the attribute is returned.
467    */
468    attr(hash, val, complete, continueAnimation) {
469        const { element } = this, symbolCustomAttribs = SVGElement.symbolCustomAttribs;
470        let key, hasSetSymbolSize, ret = this, skipAttr, setter;
471        // Single key-value pair
472        if (typeof hash === 'string' && typeof val !== 'undefined') {
473            key = hash;
474            hash = {};
475            hash[key] = val;
476        }
477        // Used as a getter: first argument is a string, second is undefined
478        if (typeof hash === 'string') {
479            ret = (this[hash + 'Getter'] ||
480                this._defaultGetter).call(this, hash, element);
481            // Setter
482        }
483        else {
484            objectEach(hash, function eachAttribute(val, key) {
485                skipAttr = false;
vendor: 14,071 bytes, lines 486-822
486                // Unless .attr is from the animator update, stop current
487                // running animation of this property
488                if (!continueAnimation) {
489                    stop(this, key);
490                }
491                // Special handling of symbol attributes
492                if (this.symbolName &&
493                    symbolCustomAttribs.indexOf(key) !== -1) {
494                    if (!hasSetSymbolSize) {
495                        this.symbolAttr(hash);
496                        hasSetSymbolSize = true;
497                    }
498                    skipAttr = true;
499                }
500                if (this.rotation && (key === 'x' || key === 'y')) {
501                    this.doTransform = true;
502                }
503                if (!skipAttr) {
504                    setter = (this[key + 'Setter'] ||
505                        this._defaultSetter);
506                    setter.call(this, val, key, element);
507                }
508            }, this);
509            this.afterSetters();
510        }
511        // In accordance with animate, run a complete callback
512        if (complete) {
513            complete.call(this);
514        }
515        return ret;
516    }
517    /**
518     * Apply a clipping shape to this element.
519     *
520     * @function Highcharts.SVGElement#clip
521     *
522     * @param {SVGElement} [clipElem]
523     *        The clipping shape. If skipped, the current clip is removed.
524     *
525     * @return {Highcharts.SVGElement}
526     *         Returns the SVG element to allow chaining.
527     */
528    clip(clipElem) {
529        if (clipElem && !clipElem.clipPath) {
530            // Add a hyphen at the end to avoid confusion in testing indexes
531            // -1 and -10, -11 etc (#6550)
532            const id = uniqueKey() + '-', clipPath = this.renderer.createElement('clipPath')
533                .attr({ id })
534                .add(this.renderer.defs);
535            extend(clipElem, { clipPath, id, count: 0 });
536            clipElem.add(clipPath);
537        }
538        return this.attr('clip-path', clipElem ?
539            `url(${this.renderer.url}#${clipElem.id})` :
540            'none');
541    }
542    /**
543     * Calculate the coordinates needed for drawing a rectangle crisply and
544     * return the calculated attributes.
545     *
546     * @function Highcharts.SVGElement#crisp
547     *
548     * @param {Highcharts.RectangleObject} rect
549     * Rectangle to crisp.
550     *
551     * @param {number} [strokeWidth]
552     * The stroke width to consider when computing crisp positioning. It can
553     * also be set directly on the rect parameter.
554     *
555     * @return {Highcharts.RectangleObject}
556     * The modified rectangle arguments.
557     */
558    crisp(rect, strokeWidth) {
559        // Math.round because strokeWidth can sometimes have roundoff errors
560        strokeWidth = Math.round(strokeWidth || rect.strokeWidth || 0);
561        const x1 = rect.x || this.x || 0, y1 = rect.y || this.y || 0, x2 = (rect.width || this.width || 0) + x1, y2 = (rect.height || this.height || 0) + y1, 
562        // Find all the rounded coordinates for corners
563        x = crisp(x1, strokeWidth), y = crisp(y1, strokeWidth), x2Crisp = crisp(x2, strokeWidth), y2Crisp = crisp(y2, strokeWidth);
564        extend(rect, {
565            x,
566            y,
567            width: x2Crisp - x,
568            height: y2Crisp - y
569        });
570        if (defined(rect.strokeWidth)) {
571            rect.strokeWidth = strokeWidth;
572        }
573        return rect;
574    }
575    /**
576     * Build and apply an SVG gradient out of a common JavaScript configuration
577     * object. This function is called from the attribute setters. An event
578     * hook is added for supporting other complex color types.
579     *
580     * @private
581     * @function Highcharts.SVGElement#complexColor
582     *
583     * @param {Highcharts.GradientColorObject|Highcharts.PatternObject} colorOptions
584     * The gradient or pattern options structure.
585     *
586     * @param {string} prop
587     * The property to apply, can either be `fill` or `stroke`.
588     *
589     * @param {Highcharts.SVGDOMElement} elem
590     * SVG element to apply the gradient on.
591     */
592    complexColor(colorOptions, prop, elem) {
593        const renderer = this.renderer;
594        let colorObject, gradName, gradAttr, radAttr, gradients, stops, stopColor, stopOpacity, radialReference, id, key = [], value;
595        fireEvent(this.renderer, 'complexColor', {
596            args: arguments
597        }, function () {
598            // Apply linear or radial gradients
599            if (colorOptions.radialGradient) {
600                gradName = 'radialGradient';
601            }
602            else if (colorOptions.linearGradient) {
603                gradName = 'linearGradient';
604            }
605            if (gradName) {
606                gradAttr = colorOptions[gradName];
607                gradients = renderer.gradients;
608                stops = colorOptions.stops;
609                radialReference = elem.radialReference;
610                // Keep < 2.2 compatibility
611                if (isArray(gradAttr)) {
612                    colorOptions[gradName] = gradAttr = {
613                        x1: gradAttr[0],
614                        y1: gradAttr[1],
615                        x2: gradAttr[2],
616                        y2: gradAttr[3],
617                        gradientUnits: 'userSpaceOnUse'
618                    };
619                }
620                // Correct the radial gradient for the radial reference system
621                if (gradName === 'radialGradient' &&
622                    radialReference &&
623                    !defined(gradAttr.gradientUnits)) {
624                    // Save the radial attributes for updating
625                    radAttr = gradAttr;
626                    gradAttr = merge(gradAttr, renderer.getRadialAttr(radialReference, radAttr), { gradientUnits: 'userSpaceOnUse' });
627                }
628                // Build the unique key to detect whether we need to create a
629                // new element (#1282)
630                objectEach(gradAttr, function (value, n) {
631                    if (n !== 'id') {
632                        key.push(n, value);
633                    }
634                });
635                objectEach(stops, function (val) {
636                    key.push(val);
637                });
638                key = key.join(',');
639                // Check if a gradient object with the same config object is
640                // created within this renderer
641                if (gradients[key]) {
642                    id = gradients[key].attr('id');
643                }
644                else {
645                    // Set the id and create the element
646                    gradAttr.id = id = uniqueKey();
647                    const gradientObject = gradients[key] =
648                        renderer.createElement(gradName)
649                            .attr(gradAttr)
650                            .add(renderer.defs);
651                    gradientObject.radAttr = radAttr;
652                    // The gradient needs to keep a list of stops to be able to
653                    // destroy them
654                    gradientObject.stops = [];
655                    stops.forEach(function (stop) {
656                        if (stop[1].indexOf('rgba') === 0) {
657                            colorObject = Color.parse(stop[1]);
658                            stopColor = colorObject.get('rgb');
659                            stopOpacity = colorObject.get('a');
660                        }
661                        else {
662                            stopColor = stop[1];
663                            stopOpacity = 1;
664                        }
665                        const stopObject = renderer.createElement('stop').attr({
666                            offset: stop[0],
667                            'stop-color': stopColor,
668                            'stop-opacity': stopOpacity
669                        }).add(gradientObject);
670                        // Add the stop element to the gradient
671                        gradientObject.stops.push(stopObject);
672                    });
673                }
674                // Set the reference to the gradient object
675                value = 'url(' + renderer.url + '#' + id + ')';
676                elem.setAttribute(prop, value);
677                elem.gradient = key;
678                // Allow the color to be concatenated into tooltips formatters
679                // etc. (#2995)
680                colorOptions.toString = function () {
681                    return value;
682                };
683            }
684        });
685    }
686    /**
687     * Set styles for the element. In addition to CSS styles supported by
688     * native SVG and HTML elements, there are also some custom made for
689     * Highcharts, like `width`, `ellipsis` and `textOverflow` for SVG text
690     * elements.
691     *
692     * @sample highcharts/members/renderer-text-on-chart/
693     *         Styled text
694     *
695     * @function Highcharts.SVGElement#css
696     *
697     * @param {Highcharts.CSSObject} styles
698     *        The new CSS styles.
699     *
700     * @return {Highcharts.SVGElement}
701     *         Return the SVG element for chaining.
702     */
703    css(styles) {
704        const oldStyles = this.styles, newStyles = {}, elem = this.element;
705        let textWidth, hasNew = !oldStyles;
706        // Filter out existing styles to increase performance (#2640)
707        if (oldStyles) {
708            objectEach(styles, function (value, n) {
709                if (oldStyles && oldStyles[n] !== value) {
710                    newStyles[n] = value;
711                    hasNew = true;
712                }
713            });
714        }
715        if (hasNew) {
716            // Merge the new styles with the old ones
717            if (oldStyles) {
718                styles = extend(oldStyles, newStyles);
719            }
720            // Get the text width from style
721            // Previously set, unset it (#8234)
722            if (styles.width === null || styles.width === 'auto') {
723                delete this.textWidth;
724                // Apply new
725            }
726            else if (elem.nodeName.toLowerCase() === 'text' &&
727                styles.width) {
728                textWidth = this.textWidth = pInt(styles.width);
729            }
730            // Store object
731            extend(this.styles, styles);
732            if (textWidth && (!svg && this.renderer.forExport)) {
733                delete styles.width;
734            }
735            const fontSize = isFirefox && styles.fontSize || null;
736            // Necessary in firefox to be able to set font-size, #22124
737            if (fontSize && (isNumber(fontSize) ||
738                /^\d+$/.test(fontSize))) {
739                styles.fontSize += 'px';
740            }
741            const stylesToApply = merge(styles);
742            if (elem.namespaceURI === this.SVG_NS) {
743                // These CSS properties are interpreted internally by the SVG
744                // renderer, but are not supported by SVG and should not be
745                // added to the DOM. In styled mode, no CSS should find its way
746                // to the DOM whatsoever (#6173, #6474).
747                ['textOutline', 'textOverflow', 'whiteSpace', 'width'].forEach((key) => (stylesToApply &&
748                    delete stylesToApply[key]));
749                // SVG requires fill for text
750                if (stylesToApply.color) {
751                    stylesToApply.fill = stylesToApply.color;
752                }
753            }
754            css(elem, stylesToApply);
755        }
756        if (this.added) {
757            // Rebuild text after added. Cache mechanisms in the buildText will
758            // prevent building if there are no significant changes.
759            if (this.element.nodeName === 'text') {
760                this.renderer.buildText(this);
761            }
762            // Apply text outline after added
763            if (styles.textOutline) {
764                this.applyTextOutline(styles.textOutline);
765            }
766        }
767        return this;
768    }
769    /**
770     * @private
771     * @function Highcharts.SVGElement#dashstyleSetter
772     * @param {string} value
773     */
774    dashstyleSetter(value) {
775        let i, strokeWidth = this['stroke-width'];
776        // If "inherit", like maps in IE, assume 1 (#4981). With HC5 and the new
777        // strokeWidth function, we should be able to use that instead.
778        if (strokeWidth === 'inherit') {
779            strokeWidth = 1;
780        }
781        value = value && value.toLowerCase();
782        if (value) {
783            const v = value
784                .replace('shortdashdotdot', '3,1,1,1,1,1,')
785                .replace('shortdashdot', '3,1,1,1')
786                .replace('shortdot', '1,1,')
787                .replace('shortdash', '3,1,')
788                .replace('longdash', '8,3,')
789                .replace(/dot/g, '1,3,')
790                .replace('dash', '4,3,')
791                .replace(/,$/, '')
792                .split(','); // Ending comma
793            i = v.length;
794            while (i--) {
795                v[i] = '' + (pInt(v[i]) * pick(strokeWidth, NaN));
796            }
797            value = v.join(',').replace(/NaN/g, 'none'); // #3226
798            this.element.setAttribute('stroke-dasharray', value);
799        }
800    }
801    /**
802     * Destroy the element and element wrapper and clear up the DOM and event
803     * hooks.
804     *
805     * @function Highcharts.SVGElement#destroy
806     */
807    destroy() {
808        const wrapper = this, element = wrapper.element || {}, renderer = wrapper.renderer, ownerSVGElement = element.ownerSVGElement;
809        let parentToClean = (element.nodeName === 'SPAN' &&
810            wrapper.parentGroup ||
811            void 0), grandParent, i;
812        // Remove events
813        element.onclick = element.onmouseout = element.onmouseover =
814            element.onmousemove = element.point = null;
815        stop(wrapper); // Stop running animations
816        if (wrapper.clipPath && ownerSVGElement) {
817            const clipPath = wrapper.clipPath;
818            // Look for existing references to this clipPath and remove them
819            // before destroying the element (#6196).
820            // The upper case version is for Edge
821            [].forEach.call(ownerSVGElement.querySelectorAll('[clip-path],[CLIP-PATH]'), function (el) {
822                if (el.getAttribute('clip-path').indexOf(clipPath.element.i
822d) > -1) {
823                    el.removeAttribute('clip-path');
824                }
825            });
826            wrapper.clipPath = clipPath.destroy();
827        }
828        wrapper.connector = wrapper.connector?.destroy();
829        // Destroy stops in case this is a gradient object @todo old code?
830        if (wrapper.stops) {
831            for (i = 0; i < wrapper.stops.length; i++) {
832                wrapper.stops[i].destroy();
833            }
834            wrapper.stops.length = 0;
835            wrapper.stops = void 0;
836        }
837        // Remove element
838        wrapper.safeRemoveChild(element);
839        // In case of useHTML, clean up empty containers emulating SVG groups
840        // (#1960, #2393, #2697).
841        while (parentToClean &&
842            parentToClean.div &&
843            parentToClean.div.childNodes.length === 0) {
844            grandParent = parentToClean.parentGroup;
845            wrapper.safeRemoveChild(parentToClean.div);
846            delete parentToClean.div;
847            parentToClean = grandParent;
848        }
849        // Remove from alignObjects
850        if (wrapper.alignOptions) {
851            erase(renderer.alignedObjects, wrapper);
852        }
853        objectEach(wrapper, function (val, key) {
854            // Destroy child elements of a group
855            if (wrapper[key] &&
856                wrapper[key].parentGroup === wrapper &&
857                wrapper[key].destroy) {
858                wrapper[key].destroy();
859            }
860            // Delete all properties
861            delete wrapper[key];
862        });
863        return;
864    }
865    /**
866     * @private
867     * @function Highcharts.SVGElement#dSettter
868     * @param {number|string|Highcharts.SVGPathArray} value
869     * @param {string} key
870     * @param {Highcharts.SVGDOMElement} element
871     */
872    dSetter(value, key, element) {
873        if (isArray(value)) {
874            // Backwards compatibility, convert one-dimensional array into an
875            // array of segments
876            if (typeof value[0] === 'string') {
877                value = this.renderer.pathToSegments(value);
878            }
879            this.pathArray = value;
880            value = value.reduce((acc, seg, i) => {
881                if (!seg || !seg.join) {
882                    return (seg || '').toString();
883                }
884                return (i ? acc + ' ' : '') + seg.join(' ');
885            }, '');
886        }
887        if (/(NaN| {2}|^$)/.test(value)) {
888            value = 'M 0 0';
889        }
890        // Check for cache before resetting. Resetting causes disturbance in the
891        // DOM, causing flickering in some cases in Edge/IE (#6747). Also
892        // possible performance gain.
893        if (this[key] !== value) {
894            element.setAttribute(key, value);
895            this[key] = value;
896        }
897    }
898    /**
899     * @private
900     * @function Highcharts.SVGElement#fillSetter
901     * @param {Highcharts.ColorType} value
902     * @param {string} key
903     * @param {Highcharts.SVGDOMElement} element
904     */
905    fillSetter(value, key, element) {
906        if (typeof value === 'string') {
907            element.setAttribute(key, value);
908        }
909        else if (value) {
910            this.complexColor(value, key, element);
911        }
912    }
913    /**
914     * @private
915     * @function Highcharts.SVGElement#hrefSetter
916     * @param {Highcharts.ColorType} value
917     * @param {string} key
918     * @param {Highcharts.SVGDOMElement} element
919     */
920    hrefSetter(value, key, element) {
921        // Namespace is needed for offline export, #19106
922        element.setAttributeNS('http://www.w3.org/1999/xlink', key, value);
923    }
924    /**
925     * Get the bounding box (width, height, x and y) for the element. Generally
926     * used to get rendered text size. Since this is called a lot in charts,
927     * the results are cached based on text properties, in order to save DOM
928     * traffic. The returned bounding box includes the rotation, so for example
929     * a single text line of rotation 90 will report a greater height, and a
930     * width corresponding to the line-height.
931     *
932     * @sample highcharts/members/renderer-on-chart/
933     *         Draw a rectangle based on a text's bounding box
934     *
935     * @function Highcharts.SVGElement#getBBox
936     *
937     * @param {boolean} [reload]
938     *        Skip the cache and get the updated DOM bounding box.
939     *
940     * @param {number} [rot]
941     *        Override the element's rotation. This is internally used on axis
942     *        labels with a value of 0 to find out what the bounding box would
943     *        be have been if it were not rotated.
944     *
945     * @return {Highcharts.BBoxObject}
946     *         The bounding box with `x`, `y`, `width` and `height` properties.
947     */
948    getBBox(reload, rot) {
949        const wrapper = this, { alignValue, element, renderer, styles, textStr } = wrapper, { cache, cacheKeys } = renderer, isSVG = element.namespaceURI === wrapper.SVG_NS, rotation = pick(rot, wrapper.rotation, 0), fontSize = renderer.styledMode ? (element &&
950            SVGElement.prototype.getStyle.call(element, 'font-size')) : (styles.fontSize);
951        let bBox, height, toggleTextShadowShim, cacheKey;
952        // Avoid undefined and null (#7316)
953        if (defined(textStr)) {
954            cacheKey = textStr.toString();
955            // Since numbers are monospaced, and numerical labels appear a lot
956            // in a chart, we assume that a label of n characters has the same
957            // bounding box as others of the same length. Unless there is inner
958            // HTML in the label. In that case, leave the numbers as is (#5899).
959            if (cacheKey.indexOf('<') === -1) {
960                cacheKey = cacheKey.replace(/\d/g, '0');
961            }
962            // Properties that affect bounding box
963            cacheKey += [
964                '',
965                renderer.rootFontSize,
966                fontSize,
967                rotation,
968                wrapper.textWidth, // #7874, also useHTML
969                alignValue,
970                styles.lineClamp,
971                styles.textOverflow, // #5968
972                styles.fontWeight // #12163
973            ].join(',');
974        }
vendor: 16,317 bytes, lines 975-1415
975        if (cacheKey && !reload) {
976            bBox = cache[cacheKey];
977        }
978        // No cache found
979        if (!bBox || bBox.polygon) {
980            // SVG elements
981            if (isSVG || renderer.forExport) {
982                try { // Fails in Firefox if the container has display: none.
983                    // When the text shadow shim is used, we need to hide the
984                    // fake shadows to get the correct bounding box (#3872)
985                    toggleTextShadowShim = this.fakeTS && function (display) {
986                        const outline = element.querySelector('.highcharts-text-outline');
987                        if (outline) {
988                            css(outline, { display });
989                        }
990                    };
991                    // Workaround for #3842, Firefox reporting wrong bounding
992                    // box for shadows
993                    if (isFunction(toggleTextShadowShim)) {
994                        toggleTextShadowShim('none');
995                    }
996                    bBox = element.getBBox ?
997                        // SVG: use extend because IE9 is not allowed to change
998                        // width and height in case of rotation (below)
999                        extend({}, element.getBBox()) : {
1000                        // HTML elements with `exporting.allowHTML` and
1001                        // legacy IE in export mode
1002                        width: element.offsetWidth,
1003                        height: element.offsetHeight,
1004                        x: 0,
1005                        y: 0
1006                    };
1007                    // #3842
1008                    if (isFunction(toggleTextShadowShim)) {
1009                        toggleTextShadowShim('');
1010                    }
1011                }
1012                catch (e) {
1013                    '';
1014                }
1015                // If the bBox is not set, the try-catch block above failed. The
1016                // other condition is for Opera that returns a width of
1017                // -Infinity on hidden elements.
1018                if (!bBox || bBox.width < 0) {
1019                    bBox = { x: 0, y: 0, width: 0, height: 0 };
1020                }
1021                // Use HTML within SVG
1022            }
1023            else {
1024                bBox = wrapper.htmlGetBBox();
1025            }
1026            // True SVG elements as well as HTML elements in modern browsers
1027            // using the .useHTML option need to compensated for rotation
1028            height = bBox.height;
1029            // Workaround for wrong bounding box in IE, Edge and Chrome on
1030            // Windows. With Highcharts' default font, IE and Edge report
1031            // a box height of 16.899 and Chrome rounds it to 17. If this
1032            // stands uncorrected, it results in more padding added below
1033            // the text than above when adding a label border or background.
1034            // Also vertical positioning is affected.
1035            // https://jsfiddle.net/highcharts/em37nvuj/
1036            // (#1101, #1505, #1669, #2568, #6213).
1037            if (isSVG) {
1038                bBox.height = height = ({
1039                    '11px,17': 14,
1040                    '13px,20': 16
1041                }[`${fontSize || ''},${Math.round(height)}`] ||
1042                    height);
1043            }
1044            // Adjust for rotated text
1045            if (rotation) {
1046                bBox = this.getRotatedBox(bBox, rotation);
1047            }
1048            // Create a reference to catch changes to bBox
1049            const e = { bBox };
1050            fireEvent(this, 'afterGetBBox', e);
1051            // Pick up any changes after the fired event
1052            bBox = e.bBox;
1053        }
1054        // Cache it. When loading a chart in a hidden iframe in Firefox and
1055        // IE/Edge, the bounding box height is 0, so don't cache it (#5620).
1056        if (cacheKey && (textStr === '' || bBox.height > 0)) {
1057            // Rotate (#4681)
1058            while (cacheKeys.length > 250) {
1059                delete cache[cacheKeys.shift()];
1060            }
1061            if (!cache[cacheKey]) {
1062                cacheKeys.push(cacheKey);
1063            }
1064            cache[cacheKey] = bBox;
1065        }
1066        return bBox;
1067    }
1068    /**
1069     * Get the rotated box.
1070     * @private
1071     */
1072    getRotatedBox(box, rotation) {
1073        const { x: boxX, y: boxY, width, height } = box, { alignValue, translateY, rotationOriginX = 0, rotationOriginY = 0 } = this, alignFactor = getAlignFactor(alignValue), baseline = Number(this.element.getAttribute('y') || 0) -
1074            (translateY ? 0 : boxY), rad = rotation * deg2rad, rad90 = (rotation - 90) * deg2rad, cosRad = Math.cos(rad), sinRad = Math.sin(rad), wCosRad = width * cosRad, wSinRad = width * sinRad, cosRad90 = Math.cos(rad90), sinRad90 = Math.sin(rad90), [[xOriginCosRad, xOriginSinRad], [yOriginCosRad, yOriginSinRad]] = [
1075            rotationOriginX,
1076            rotationOriginY
1077        ].map((rotOrigin) => [
1078            rotOrigin - (rotOrigin * cosRad),
1079            rotOrigin * sinRad
1080        ]), 
1081        // Find the starting point on the left side baseline of
1082        // the text
1083        pX = ((boxX + alignFactor * (width - wCosRad)) +
1084            xOriginCosRad + yOriginSinRad), pY = ((boxY + baseline - alignFactor * wSinRad) -
1085            xOriginSinRad + yOriginCosRad), 
1086        // Find all corners
1087        aX = pX + baseline * cosRad90, bX = aX + wCosRad, cX = bX - height * cosRad90, dX = cX - wCosRad, aY = pY + baseline * sinRad90, bY = aY + wSinRad, cY = bY - height * sinRad90, dY = cY - wSinRad;
1088        // Deduct the bounding box from the corners
1089        const x = Math.min(aX, bX, cX, dX), y = Math.min(aY, bY, cY, dY), boxWidth = Math.max(aX, bX, cX, dX) - x, boxHeight = Math.max(aY, bY, cY, dY) - y;
1090        /* Uncomment to debug boxes
1091        this.renderer.path([
1092            ['M', aX, aY],
1093            ['L', bX, bY],
1094            ['L', cX, cY],
1095            ['L', dX, dY],
1096            ['Z']
1097        ])
1098            .attr({
1099                stroke: 'red',
1100                'stroke-width': 1
1101            })
1102            .add();
1103        // */
1104        return {
1105            x,
1106            y,
1107            width: boxWidth,
1108            height: boxHeight,
1109            polygon: [
1110                [aX, aY],
1111                [bX, bY],
1112                [cX, cY],
1113                [dX, dY]
1114            ]
1115        };
1116    }
1117    /**
1118     * Get the computed style. Only in styled mode.
1119     *
1120     * @example
1121     * chart.series[0].points[0].graphic.getStyle('stroke-width'); // => '1px'
1122     *
1123     * @function Highcharts.SVGElement#getStyle
1124     *
1125     * @param {string} prop
1126     *        The property name to check for.
1127     *
1128     * @return {string}
1129     *         The current computed value.
1130     */
1131    getStyle(prop) {
1132        return win
1133            .getComputedStyle(this.element || this, '')
1134            .getPropertyValue(prop);
1135    }
1136    /**
1137     * Check if an element has the given class name.
1138     *
1139     * @function Highcharts.SVGElement#hasClass
1140     *
1141     * @param {string} className
1142     * The class name to check for.
1143     *
1144     * @return {boolean}
1145     * Whether the class name is found.
1146     */
1147    hasClass(className) {
1148        return ('' + this.attr('class'))
1149            .split(' ')
1150            .indexOf(className) !== -1;
1151    }
1152    /**
1153     * Hide the element, similar to setting the `visibility` attribute to
1154     * `hidden`.
1155     *
1156     * @function Highcharts.SVGElement#hide
1157     *
1158     * @return {Highcharts.SVGElement}
1159     *         Returns the SVGElement for chaining.
1160     */
1161    hide() {
1162        return this.attr({ visibility: 'hidden' });
1163    }
1164    /**
1165     * @private
1166     */
1167    htmlGetBBox() {
1168        return { height: 0, width: 0, x: 0, y: 0 };
1169    }
1170    /**
1171     * Initialize the SVG element. This function only exists to make the
1172     * initialization process overridable. It should not be called directly.
1173     *
1174     * @function Highcharts.SVGElement#init
1175     *
1176     * @param {Highcharts.SVGRenderer} renderer
1177     * The SVGRenderer instance to initialize to.
1178     *
1179     * @param {string} nodeName
1180     * The SVG node name.
1181     */
1182    constructor(renderer, nodeName) {
1183        this.onEvents = {};
1184        this.opacity = 1; // Default base for animation
1185        this.SVG_NS = SVG_NS;
1186        /**
1187         * The primary DOM node. Each `SVGElement` instance wraps a main DOM
1188         * node, but may also represent more nodes.
1189         *
1190         * @name Highcharts.SVGElement#element
1191         * @type {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement}
1192         */
1193        this.element = nodeName === 'span' || nodeName === 'body' ?
1194            createElement(nodeName) :
1195            doc.createElementNS(this.SVG_NS, nodeName);
1196        /**
1197         * The renderer that the SVGElement belongs to.
1198         *
1199         * @name Highcharts.SVGElement#renderer
1200         * @type {Highcharts.SVGRenderer}
1201         */
1202        this.renderer = renderer;
1203        this.styles = {};
1204        fireEvent(this, 'afterInit');
1205    }
1206    /**
1207     * Add an event listener. This is a simple setter that replaces the
1208     * previous event of the same type added by this function, as opposed to
1209     * the {@link Highcharts#addEvent} function.
1210     *
1211     * @sample highcharts/members/element-on/
1212     *         A clickable rectangle
1213     *
1214     * @function Highcharts.SVGElement#on
1215     *
1216     * @param {string} eventType
1217     * The event type.
1218     *
1219     * @param {Function} handler
1220     * The handler callback.
1221     *
1222     * @return {Highcharts.SVGElement}
1223     * The SVGElement for chaining.
1224     */
1225    on(eventType, handler) {
1226        const { onEvents } = this;
1227        if (onEvents[eventType]) {
1228            // Unbind existing event
1229            onEvents[eventType]();
1230        }
1231        onEvents[eventType] = addEvent(this.element, eventType, handler);
1232        return this;
1233    }
1234    /**
1235     * @private
1236     * @function Highcharts.SVGElement#opacitySetter
1237     * @param {string} value
1238     * @param {string} key
1239     * @param {Highcharts.SVGDOMElement} element
1240     */
1241    opacitySetter(value, key, element) {
1242        // Round off to avoid float errors, like tests where opacity lands on
1243        // 9.86957e-06 instead of 0
1244        const opacity = Number(Number(value).toFixed(3));
1245        this.opacity = opacity;
1246        element.setAttribute(key, opacity);
1247    }
1248    /**
1249     * Re-align an aligned text or label after setting the text.
1250     *
1251     * @private
1252     * @function Highcharts.SVGElement#reAlign
1253     *
1254     */
1255    reAlign() {
1256        if (this.alignOptions?.width && this.alignOptions.align !== 'left') {
1257            this.alignOptions.width = this.getBBox().width;
1258            this.placed = false; // Block animation
1259            this.align();
1260        }
1261    }
1262    /**
1263     * Remove a class name from the element.
1264     *
1265     * @function Highcharts.SVGElement#removeClass
1266     *
1267     * @param {string|RegExp} className
1268     *        The class name to remove.
1269     *
1270     * @return {Highcharts.SVGElement} Returns the SVG element for chainability.
1271     */
1272    removeClass(className) {
1273        return this.attr('class', ('' + this.attr('class'))
1274            .replace(isString(className) ?
1275            new RegExp(`(^| )${className}( |$)`) : // #12064, #13590
1276            className, ' ')
1277            .replace(/ +/g, ' ')
1278            .trim());
1279    }
1280    /**
1281     *
1282     * @private
1283     */
1284    removeTextOutline() {
1285        const outline = this.element
1286            .querySelector('tspan.highcharts-text-outline');
1287        if (outline) {
1288            this.safeRemoveChild(outline);
1289        }
1290    }
1291    /**
1292     * Removes an element from the DOM.
1293     *
1294     * @private
1295     * @function Highcharts.SVGElement#safeRemoveChild
1296     *
1297     * @param {Highcharts.SVGDOMElement|Highcharts.HTMLDOMElement} element
1298     * The DOM node to remove.
1299     */
1300    safeRemoveChild(element) {
1301        const parentNode = element.parentNode;
1302        if (parentNode) {
1303            parentNode.removeChild(element);
1304        }
1305    }
1306    /**
1307     * Set the coordinates needed to draw a consistent radial gradient across
1308     * a shape regardless of positioning inside the chart. Used on pie slices
1309     * to make all the slices have the same radial reference point.
1310     *
1311     * @function Highcharts.SVGElement#setRadialReference
1312     *
1313     * @param {Array<number>} coordinates
1314     * The center reference. The format is `[centerX, centerY, diameter]` in
1315     * pixels.
1316     *
1317     * @return {Highcharts.SVGElement}
1318     * Returns the SVGElement for chaining.
1319     */
1320    setRadialReference(coordinates) {
1321        const existingGradient = (this.element.gradient &&
1322            this.renderer.gradients[this.element.gradient]);
1323        this.element.radialReference = coordinates;
1324        // On redrawing objects with an existing gradient, the gradient needs
1325        // to be repositioned (#3801)
1326        if (existingGradient && existingGradient.radAttr) {
1327            existingGradient.animate(this.renderer.getRadialAttr(coordinates, existingGradient.radAttr));
1328        }
1329        return this;
1330    }
1331    /**
1332     * Add a shadow to the element. In styled mode, this method is not used,
1333     * instead use `defs` and filters.
1334     *
1335     * @example
1336     * renderer.rect(10, 100, 100, 100)
1337     *     .attr({ fill: 'red' })
1338     *     .shadow(true);
1339     *
1340     * @function Highcharts.SVGElement#shadow
1341     *
1342     * @param {boolean|Highcharts.ShadowOptionsObject} [shadowOptions] The
1343     *        shadow options. If `true`, the default options are applied. If
1344     *        `false`, the current shadow will be removed.
1345     *
1346     * @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
1347     */
1348    shadow(shadowOptions) {
1349        const { renderer } = this, options = merge(this.parentGroup?.rotation === 90 ? {
1350            offsetX: -1,
1351            offsetY: -1
1352        } : {}, isObject(shadowOptions) ? shadowOptions : {}), id = renderer.shadowDefinition(options);
1353        return this.attr({
1354            filter: shadowOptions ?
1355                `url(${renderer.url}#${id})` :
1356                'none'
1357        });
1358    }
1359    /**
1360     * Show the element after it has been hidden.
1361     *
1362     * @function Highcharts.SVGElement#show
1363     *
1364     * @param {boolean} [inherit=true]
1365     *        Set the visibility attribute to `inherit` rather than `visible`.
1366     *        The difference is that an element with `visibility="visible"`
1367     *        will be visible even if the parent is hidden.
1368     *
1369     * @return {Highcharts.SVGElement}
1370     *         Returns the SVGElement for chaining.
1371     */
1372    show(inherit = true) {
1373        return this.attr({ visibility: inherit ? 'inherit' : 'visible' });
1374    }
1375    /**
1376     * Set the stroke-width and record it on the SVGElement
1377     *
1378     * @private
1379     * @function Highcharts.SVGElement#strokeSetter
1380     * @param {number|string|ColorType} value
1381     * @param {string} key
1382     * @param {Highcharts.SVGDOMElement} element
1383     */
1384    'stroke-widthSetter'(value, key, element) {
1385        // Record it for quick access in getter
1386        this[key] = value;
1387        element.setAttribute(key, value);
1388    }
1389    /**
1390     * Get the computed stroke width in pixel values. This is used extensively
1391     * when drawing shapes to ensure the shapes are rendered crisp and
1392     * positioned correctly relative to each other. Using
1393     * `shape-rendering: crispEdges` leaves us less control over positioning,
1394     * for example when we want to stack columns next to each other, or position
1395     * things pixel-perfectly within the plot box.
1396     *
1397     * The common pattern when placing a shape is:
1398     * - Create the SVGElement and add it to the DOM. In styled mode, it will
1399     *   now receive a stroke width from the style sheet. In classic mode we
1400     *   will add the `stroke-width` attribute.
1401     * - Read the computed `elem.strokeWidth()`.
1402     * - Place it based on the stroke width.
1403     *
1404     * @function Highcharts.SVGElement#strokeWidth
1405     *
1406     * @return {number}
1407     * The stroke width in pixels. Even if the given stroke width (in CSS or by
1408     * attributes) is based on `em` or other units, the pixel size is returned.
1409     */
1410    strokeWidth() {
1411        // In non-styled mode, read the stroke width as set by .attr
1412        if (!this.renderer.styledMode) {
1413            return this['stroke-width'] || 0;
1414        }
1415        // In styled mode, read computed stroke width
1416        const val = this.getStyle('stroke-width');
1417        let ret = 0, tempElement;
1418        // Read pixel values directly
1419        if (/px$/.test(val)) {
1420            ret = pInt(val);
1421            // Other values like em, pt etc need to be measured
1422        }
1423        else if (val !== '') {
1424            tempElement = doc.createElementNS(SVG_NS, 'rect');
1425            attr(tempElement, {
1426                width: val,
1427                'stroke-width': 0
1428            });
1429            this.element.parentNode.appendChild(tempElement);
1430            ret = tempElement.getBBox().width;
1431            tempElement.parentNode.removeChild(tempElement);
1432        }
1433        return ret;
1434    }
1435    /**
1436     * If one of the symbol size affecting parameters are changed,
1437     * check all the others only once for each call to an element's
1438     * .attr() method
1439     *
1440     * @private
1441     * @function Highcharts.SVGElement#symbolAttr
1442     *
1443     * @param {Highcharts.SVGAttributes} hash
1444     * The attributes to set.
1445     */
1446    symbolAttr(hash) {
1447        const wrapper = this;
1448        SVGElement.symbolCustomAttribs.forEach(function (key) {
1449            wrapper[key] = pick(hash[key], wrapper[key]);
1450        });
1451        wrapper.attr({
1452            d: wrapper.renderer.symbols[wrapper.symbolName](wrapper.x, wrapper.y, wrapper.width, wrapper.height, wrapper)
1453        });
1454    }
1455    /**
1456     * @private
1457     * @function Highcharts.SVGElement#textSetter
1458     * @param {string} value
1459     */
1460    textSetter(value) {
1461        if (value !== this.textStr) {
1462            // Delete size caches when the text changes
1463            // delete this.bBox; // old code in series-label
1464            delete this.textPxLength;
1465            this.textStr = value;
1466            if (this.added) {
1467                this.renderer.buildText(this);
1468            }
1469            this.reAlign();
1470        }
1471    }
1472    /**
1473     * @private
1474     * @function Highcharts.SVGElement#titleSetter
1475     * @param {string} value
1476     */
1477    titleSetter(value) {
1478        const el = this.element;
1479        const titleNode = el.getElementsByTagName('title')[0] ||
1480            doc.createElementNS(this.SVG_NS, 'title');
1481        // Move to first child
1482        if (el.insertBefore) {
1483            el.insertBefore(titleNode, el.firstChild);
1484        }
1485        else {
1486            el.appendChild(titleNode);
1487        }
1488        // Replace text content and escape markup
1489        titleNode.textContent = replaceNested(// Scan #[73]
1490        pick(value, ''), // #3276, #3895
1491        [/<[^>]*>/g, '']).replace(/&lt;/g, '<').replace(/&gt;/g, '>');
1492    }
1493    /**
1494     * Bring the element to the front. Alternatively, a new zIndex can be set.
1495     *
1496     * @sample highcharts/members/element-tofront/
1497     *         Click an element to bring it to front
1498     *
1499     * @function Highcharts.SVGElement#toFront
1500     *
1501     * @return {Highcharts.SVGElement}
1502     * Returns the SVGElement for chaining.
1503     */
1504    toFront() {
1505        const element = this.element;
1506        element.parentNode.appendChild(element);
1507        return this;
1508    }
1509    /**
1510     * Move an object and its children by x and y values.
1511     *
1512     * @function Highcharts.SVGElement#translate
1513     *
1514     * @param {number} x
1515     * The x value.
1516     *
1517     * @param {number} y
1518     * The y value.
1519     *
1520     * @return {Highcharts.SVGElement}
1521     * Translated element.
1522     */
1523    translate(x, y) {
1524        return this.attr({
1525            translateX: x,
1526            translateY: y
1527        });
1528    }
1529    /**
1530     * Update the transform attribute based on internal properties. Deals with
1531     * the custom `translateX`, `translateY`, `rotation`, `scaleX` and `scaleY`
1532     * attributes and updates the SVG `transform` attribute.
1533     *
1534     * @private
1535     * @function Highcharts.SVGElement#updateTransform
1536     */
1537    updateTransform(attrib = 'transform') {
1538        const { element, matrix, rotation = 0, rotationOriginX, rotationOriginY, scaleX, scaleY, translateX = 0, translateY = 0 } = this;
1539        // Apply translate. Nearly all transformed elements have translation,
1540        // so instead of checking for translate = 0, do it always (#1767,
1541        // #1846).
1542        const transform = ['translate(' + translateX + ',' + translateY + ')'];
1543        // Apply matrix
1544        if (defined(matrix)) {
1545            transform.push('matrix(' + matrix.join(',') + ')');
1546        }
1547        // Apply rotation
1548        if (rotation) {
1549            transform.push('rotate(' + rotation + ' ' +
1550                pick(rotationOriginX, element.getAttribute('x'), 0) +
1551                ' ' +
1552                pick(rotationOriginY, element.getAttribute('y') || 0) + ')');
1553            // HTML labels rotation (#20685)
1554            if (this.text?.element.tagName === 'SPAN') {
1555                this.text.attr({
1556                    rotation,
1557                    rotationOriginX: (rotationOriginX || 0) - this.padding,
1558                    rotationOriginY: (rotationOriginY || 0) - this.padding
1559                });
1560            }
1561        }
1562        // Apply scale
1563        if (defined(scaleX) || defined(scaleY)) {
1564            transform.push('scale(' + pick(scaleX, 1) + ' ' + pick(scaleY, 1) + ')');
1565        }
1566        if (transform.length && !(this.text || this).textPath) {
1567            element.setAttribute(attrib, transform.join(' '));
1568        }
1569    }
1570    /**
1571     * @private
1572     * @function Highcharts.SVGElement#visibilitySetter
1573     *
1574     * @param {string} value
1575     *
1576     * @param {string} key
1577     *
1578     * @param {Highcharts.SVGDOMElement} element
1579     *
1580     */
1581    visibilitySetter(value, key, element) {
1582        // IE9-11 doesn't handle visibility:inherit well, so we remove the
1583        // attribute instead (#2881, #3909)
1584        if (value === 'inherit') {
1585            element.removeAttribute(key);
1586        }
1587        else if (this[key] !== value) { // #6747
1588            element.setAttribute(key, value);
1589        }
1590        this[key] = value;
1591    }
1592    /**
1593     * @private
1594     * @function Highcharts.SVGElement#xGetter
1595     */
1596    xGetter(key) {
1597        if (this.element.nodeName === 'circle') {
1598            if (key === 'x') {
1599                key = 'cx';
1600            }
1601            else if (key === 'y') {
1602                key = 'cy';
1603            }
1604        }
1605        return this._defaultGetter(key);
1606    }
1607    /**
1608     * @private
1609     * @function Highcharts.SVGElement#zIndexSetter
1610     */
1611    zIndexSetter(value, key) {
1612        const renderer = this.renderer, parentGroup = this.parentGroup, parentWrapper = parentGroup || renderer, parentNode = parentWrapper.element || renderer.box, element = this.element, svgParent = parentNode === renderer.box;
1613        let childNodes, otherElement, otherZIndex, inserted = false, undefinedOtherZIndex, run = this.added, i;
1614        if (defined(value)) {
1615            // So we can read it for other elements in the group
1616            element.setAttribute('data-z-index', value);
1617            value = +value;
1618            if (this[key] === value) {
1619                // Only update when needed (#3865)
1620                run = false;
vendor: 5,011 bytes, lines 1621-1784
1621            }
1622        }
1623        else if (defined(this[key])) {
1624            element.removeAttribute('data-z-index');
1625        }
1626        this[key] = value;
1627        // Insert according to this and other elements' zIndex. Before .add() is
1628        // called, nothing is done. Then on add, or by later calls to
1629        // zIndexSetter, the node is placed on the right place in the DOM.
1630        if (run) {
1631            value = this.zIndex;
1632            if (value && parentGroup) {
1633                parentGroup.handleZ = true;
1634            }
1635            childNodes = parentNode.childNodes;
1636            for (i = childNodes.length - 1; i >= 0 && !inserted; i--) {
1637                otherElement = childNodes[i];
1638                otherZIndex = otherElement.getAttribute('data-z-index');
1639                undefinedOtherZIndex = !defined(otherZIndex);
1640                if (otherElement !== element) {
1641                    if (
1642                    // Negative zIndex versus no zIndex:
1643                    // On all levels except the highest. If the parent is
1644                    // <svg>, then we don't want to put items before <desc>
1645                    // or <defs>
1646                    value < 0 &&
1647                        undefinedOtherZIndex &&
1648                        !svgParent &&
1649                        !i) {
1650                        parentNode.insertBefore(element, childNodes[i]);
1651                        inserted = true;
1652                    }
1653                    else if (
1654                    // Insert after the first element with a lower zIndex
1655                    pInt(otherZIndex) <= value ||
1656                        // If negative zIndex, add this before first undefined
1657                        // zIndex element
1658                        (undefinedOtherZIndex &&
1659                            (!defined(value) || value >= 0))) {
1660                        parentNode.insertBefore(element, childNodes[i + 1]);
1661                        inserted = true;
1662                    }
1663                }
1664            }
1665            if (!inserted) {
1666                parentNode.insertBefore(element, childNodes[svgParent ? 3 : 0]);
1667                inserted = true;
1668            }
1669        }
1670        return inserted;
1671    }
1672}
1673/* *
1674 *
1675 *  Properties
1676 *
1677 * */
1678// Custom attributes used for symbols, these should be filtered out when
1679// setting SVGElement attributes (#9375).
1680SVGElement.symbolCustomAttribs = [
1681    'anchorX',
1682    'anchorY',
1683    'clockwise',
1684    'end',
1685    'height',
1686    'innerR',
1687    'r',
1688    'start',
1689    'width',
1690    'x',
1691    'y'
1692];
1693// Some shared setters and getters
1694SVGElement.prototype.strokeSetter = SVGElement.prototype.fillSetter;
1695SVGElement.prototype.yGetter = SVGElement.prototype.xGetter;
1696SVGElement.prototype.matrixSetter =
1697    SVGElement.prototype.rotationOriginXSetter =
1698        SVGElement.prototype.rotationOriginYSetter =
1699            SVGElement.prototype.rotationSetter =
1700                SVGElement.prototype.scaleXSetter =
1701                    SVGElement.prototype.scaleYSetter =
1702                        SVGElement.prototype.translateXSetter =
1703                            SVGElement.prototype.translateYSetter =
1704                                SVGElement.prototype.verticalAlignSetter = function (value, key) {
1705                                    this[key] = value;
1706                                    this.doTransform = true;
1707                                };
1708/* *
1709 *
1710 *  Default Export
1711 *
1712 * */
1713export default SVGElement;
1714/* *
1715 *
1716 *  API Declarations
1717 *
1718 * */
1719/**
1720 * Reference to the global SVGElement class as a workaround for a name conflict
1721 * in the Highcharts namespace.
1722 *
1723 * @global
1724 * @typedef {global.SVGElement} GlobalSVGElement
1725 *
1726 * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
1727 */
1728/**
1729 * The horizontal alignment of an element.
1730 *
1731 * @typedef {"center"|"left"|"right"} Highcharts.AlignValue
1732 */
1733/**
1734 * Options to align the element relative to the chart or another box.
1735 *
1736 * @interface Highcharts.AlignObject
1737 */ /**
1738* Horizontal alignment. Can be one of `left`, `center` and `right`.
1739*
1740* @name Highcharts.AlignObject#align
1741* @type {Highcharts.AlignValue|undefined}
1742*
1743* @default left
1744*/ /**
1745* Vertical alignment. Can be one of `top`, `middle` and `bottom`.
1746*
1747* @name Highcharts.AlignObject#verticalAlign
1748* @type {Highcharts.VerticalAlignValue|undefined}
1749*
1750* @default top
1751*/ /**
1752* Horizontal pixel offset from alignment.
1753*
1754* @name Highcharts.AlignObject#x
1755* @type {number|undefined}
1756*
1757* @default 0
1758*/ /**
1759* Vertical pixel offset from alignment.
1760*
1761* @name Highcharts.AlignObject#y
1762* @type {number|undefined}
1763*
1764* @default 0
1765*/ /**
1766* Use the `transform` attribute with translateX and translateY custom
1767* attributes to align this elements rather than `x` and `y` attributes.
1768*
1769* @name Highcharts.AlignObject#alignByTranslate
1770* @type {boolean|undefined}
1771*
1772* @default false
1773*/
1774/**
1775 * Bounding box of an element.
1776 *
1777 * @interface Highcharts.BBoxObject
1778 * @extends Highcharts.PositionObject
1779 */ /**
1780* Height of the bounding box.
1781*
1782* @name Highcharts.BBoxObject#height
1783* @type {number}
1784*/ /**
1785* Width of the bounding box.
1786*
1787* @name Highcharts.BBoxObject#width
1788* @type {number}
1789*/ /**
1790* Horizontal position of the bounding box.
1791*
1792* @name Highcharts.BBoxObject#x
1793* @type {number}
1794*/ /**
1795* Vertical position of the bounding box.
1796*
1797* @name Highcharts.BBoxObject#y
1798* @type {number}
1799*/
1800/**
1801 * An object of key-value pairs for SVG attributes. Attributes in Highcharts
1802 * elements for the most parts correspond to SVG, but some are specific to
1803 * Highcharts, like `zIndex`, `rotation`, `rotationOriginX`,
1804 * `rotationOriginY`, `translateX`, `translateY`, `scaleX` and `scaleY`. SVG
1805 * attributes containing a hyphen are _not_ camel-cased, they should be
1806 * quoted to preserve the hyphen.
1807 *
1808 * @example
1809 * {
1810 *     'stroke': '#ff0000', // basic
1811 *     'stroke-width': 2, // hyphenated
1812 *     'rotation': 45 // custom
1813 *     'd': ['M', 10, 10, 'L', 30, 30, 'z'] // path definition, note format
1814 * }
1815 *
1816 * @interface Highcharts.SVGAttributes
1817 */ /**
1818* @name Highcharts.SVGAttributes#[key:string]
1819* @type {*}
1820*/ /**
1821* @name Highcharts.SVGAttributes#d
1822* @type {string|Highcharts.SVGPathArray|undefined}
1823*/ /**
1824* @name Highcharts.SVGAttributes#dx
1825* @type {number|undefined}
1826*/ /**
1827* @name Highcharts.SVGAttributes#dy
1828* @type {number|undefined}
1829*/ /**
1830* @name Highcharts.SVGAttributes#fill
1831* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
1832*/ /**
1833* @name Highcharts.SVGAttributes#inverted
1834* @type {boolean|undefined}
1835*/ /**
1836* @name Highcharts.SVGAttributes#matrix
1837* @type {Array<number>|undefined}
1838*/ /**
1839* @name Highcharts.SVGAttributes#rotation
1840* @type {number|undefined}
1841*/ /**
1842* @name Highcharts.SVGAttributes#rotationOriginX
1843* @type {number|undefined}
1844*/ /**
1845* @name Highcharts.SVGAttributes#rotationOriginY
1846* @type {number|undefined}
1847*/ /**
1848* @name Highcharts.SVGAttributes#scaleX
1849* @type {number|undefined}
1850*/ /**
1851* @name Highcharts.SVGAttributes#scaleY
1852* @type {number|undefined}
1853*/ /**
1854* @name Highcharts.SVGAttributes#stroke
1855* @type {Highcharts.ColorString|Highcharts.GradientColorObject|Highcharts.PatternObject|undefined}
1856*/ /**
1857* @name Highcharts.SVGAttributes#style
1858* @type {string|Highcharts.CSSObject|undefined}
1859*/ /**
1860* @name Highcharts.SVGAttributes#translateX
1861* @type {number|undefined}
1862*/ /**
1863* @name Highcharts.SVGAttributes#translateY
1864* @type {number|undefined}
1865*/ /**
1866* @name Highcharts.SVGAttributes#zIndex
1867* @type {number|undefined}
1868*/
1869/**
1870 * An SVG DOM element. The type is a reference to the regular SVGElement in the
1871 * global scope.
1872 *
1873 * @typedef {globals.GlobalSVGElement} Highcharts.SVGDOMElement
1874 *
1875 * @see https://developer.mozilla.org/en-US/docs/Web/API/SVGElement
1876 */
1877/**
1878 * The vertical alignment of an element.
1879 *
1880 * @typedef {"bottom"|"middle"|"top"} Highcharts.VerticalAlignValue
1881 */
1882''; // Keeps doclets above in JS file

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.