123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622 |
- /**
- * @license Highcharts JS v9.1.1 (2021-06-04)
- *
- * Module for adding patterns and images as point fills.
- *
- * (c) 2010-2021 Highsoft AS
- * Author: Torstein Hønsi, Øystein Moseng
- *
- * License: www.highcharts.com/license
- */
- 'use strict';
- (function (factory) {
- if (typeof module === 'object' && module.exports) {
- factory['default'] = factory;
- module.exports = factory;
- } else if (typeof define === 'function' && define.amd) {
- define('highcharts/modules/pattern-fill', ['highcharts'], function (Highcharts) {
- factory(Highcharts);
- factory.Highcharts = Highcharts;
- return factory;
- });
- } else {
- factory(typeof Highcharts !== 'undefined' ? Highcharts : undefined);
- }
- }(function (Highcharts) {
- var _modules = Highcharts ? Highcharts._modules : {};
- function _registerModule(obj, path, args, fn) {
- if (!obj.hasOwnProperty(path)) {
- obj[path] = fn.apply(null, args);
- }
- }
- _registerModule(_modules, 'Extensions/PatternFill.js', [_modules['Core/Animation/AnimationUtilities.js'], _modules['Core/Chart/Chart.js'], _modules['Core/Globals.js'], _modules['Core/DefaultOptions.js'], _modules['Core/Series/Point.js'], _modules['Core/Series/Series.js'], _modules['Core/Renderer/SVG/SVGRenderer.js'], _modules['Core/Utilities.js']], function (A, Chart, H, D, Point, Series, SVGRenderer, U) {
- /* *
- *
- * Module for using patterns or images as point fills.
- *
- * (c) 2010-2021 Highsoft AS
- * Author: Torstein Hønsi, Øystein Moseng
- *
- * License: www.highcharts.com/license
- *
- * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
- *
- * */
- var animObject = A.animObject;
- var getOptions = D.getOptions;
- var addEvent = U.addEvent,
- erase = U.erase,
- merge = U.merge,
- pick = U.pick,
- removeEvent = U.removeEvent,
- wrap = U.wrap;
- // Add the predefined patterns
- var patterns = H.patterns = (function () {
- var patterns = [],
- colors = getOptions().colors;
- [
- 'M 0 0 L 10 10 M 9 -1 L 11 1 M -1 9 L 1 11',
- 'M 0 10 L 10 0 M -1 1 L 1 -1 M 9 11 L 11 9',
- 'M 3 0 L 3 10 M 8 0 L 8 10',
- 'M 0 3 L 10 3 M 0 8 L 10 8',
- 'M 0 3 L 5 3 L 5 0 M 5 10 L 5 7 L 10 7',
- 'M 3 3 L 8 3 L 8 8 L 3 8 Z',
- 'M 5 5 m -4 0 a 4 4 0 1 1 8 0 a 4 4 0 1 1 -8 0',
- 'M 10 3 L 5 3 L 5 0 M 5 10 L 5 7 L 0 7',
- 'M 2 5 L 5 2 L 8 5 L 5 8 Z',
- 'M 0 0 L 5 10 L 10 0'
- ].forEach(function (pattern, i) {
- patterns.push({
- path: pattern,
- color: colors[i],
- width: 10,
- height: 10
- });
- });
- return patterns;
- })();
- /**
- * Utility function to compute a hash value from an object. Modified Java
- * String.hashCode implementation in JS. Use the preSeed parameter to add an
- * additional seeding step.
- *
- * @private
- * @function hashFromObject
- *
- * @param {object} obj
- * The javascript object to compute the hash from.
- *
- * @param {boolean} [preSeed=false]
- * Add an optional preSeed stage.
- *
- * @return {string}
- * The computed hash.
- */
- function hashFromObject(obj, preSeed) {
- var str = JSON.stringify(obj),
- strLen = str.length || 0,
- hash = 0,
- i = 0,
- char,
- seedStep;
- if (preSeed) {
- seedStep = Math.max(Math.floor(strLen / 500), 1);
- for (var a = 0; a < strLen; a += seedStep) {
- hash += str.charCodeAt(a);
- }
- hash = hash & hash;
- }
- for (; i < strLen; ++i) {
- char = str.charCodeAt(i);
- hash = ((hash << 5) - hash) + char;
- hash = hash & hash;
- }
- return hash.toString(16).replace('-', '1');
- }
- /**
- * Set dimensions on pattern from point. This function will set internal
- * pattern._width/_height properties if width and height are not both already
- * set. We only do this on image patterns. The _width/_height properties are set
- * to the size of the bounding box of the point, optionally taking aspect ratio
- * into account. If only one of width or height are supplied as options, the
- * undefined option is calculated as above.
- *
- * @private
- * @function Highcharts.Point#calculatePatternDimensions
- *
- * @param {Highcharts.PatternOptionsObject} pattern
- * The pattern to set dimensions on.
- *
- * @return {void}
- *
- * @requires modules/pattern-fill
- */
- Point.prototype.calculatePatternDimensions = function (pattern) {
- if (pattern.width && pattern.height) {
- return;
- }
- var bBox = this.graphic && (this.graphic.getBBox &&
- this.graphic.getBBox(true) ||
- this.graphic.element &&
- this.graphic.element.getBBox()) || {},
- shapeArgs = this.shapeArgs;
- // Prefer using shapeArgs, as it is animation agnostic
- if (shapeArgs) {
- bBox.width = shapeArgs.width || bBox.width;
- bBox.height = shapeArgs.height || bBox.height;
- bBox.x = shapeArgs.x || bBox.x;
- bBox.y = shapeArgs.y || bBox.y;
- }
- // For images we stretch to bounding box
- if (pattern.image) {
- // If we do not have a bounding box at this point, simply add a defer
- // key and pick this up in the fillSetter handler, where the bounding
- // box should exist.
- if (!bBox.width || !bBox.height) {
- pattern._width = 'defer';
- pattern._height = 'defer';
- return;
- }
- // Handle aspect ratio filling
- if (pattern.aspectRatio) {
- bBox.aspectRatio = bBox.width / bBox.height;
- if (pattern.aspectRatio > bBox.aspectRatio) {
- // Height of bBox will determine width
- bBox.aspectWidth = bBox.height * pattern.aspectRatio;
- }
- else {
- // Width of bBox will determine height
- bBox.aspectHeight = bBox.width / pattern.aspectRatio;
- }
- }
- // We set the width/height on internal properties to differentiate
- // between the options set by a user and by this function.
- pattern._width = pattern.width ||
- Math.ceil(bBox.aspectWidth || bBox.width);
- pattern._height = pattern.height ||
- Math.ceil(bBox.aspectHeight || bBox.height);
- }
- // Set x/y accordingly, centering if using aspect ratio, otherwise adjusting
- // so bounding box corner is 0,0 of pattern.
- if (!pattern.width) {
- pattern._x = pattern.x || 0;
- pattern._x += bBox.x - Math.round(bBox.aspectWidth ?
- Math.abs(bBox.aspectWidth - bBox.width) / 2 :
- 0);
- }
- if (!pattern.height) {
- pattern._y = pattern.y || 0;
- pattern._y += bBox.y - Math.round(bBox.aspectHeight ?
- Math.abs(bBox.aspectHeight - bBox.height) / 2 :
- 0);
- }
- };
- /* eslint-disable no-invalid-this */
- /**
- * Add a pattern to the renderer.
- *
- * @private
- * @function Highcharts.SVGRenderer#addPattern
- *
- * @param {Highcharts.PatternObject} options
- * The pattern options.
- *
- * @param {boolean|Partial<Highcharts.AnimationOptionsObject>} [animation]
- * The animation options.
- *
- * @return {Highcharts.SVGElement|undefined}
- * The added pattern. Undefined if the pattern already exists.
- *
- * @requires modules/pattern-fill
- */
- SVGRenderer.prototype.addPattern = function (options, animation) {
- var pattern,
- animate = pick(animation,
- true),
- animationOptions = animObject(animate),
- path,
- defaultSize = 32,
- width = options.width || options._width || defaultSize,
- height = (options.height || options._height || defaultSize),
- color = options.color || '#343434',
- id = options.id,
- ren = this,
- rect = function (fill) {
- ren.rect(0, 0,
- width,
- height)
- .attr({ fill: fill })
- .add(pattern);
- }, attribs;
- if (!id) {
- this.idCounter = this.idCounter || 0;
- id = 'highcharts-pattern-' + this.idCounter + '-' + (this.chartIndex || 0);
- ++this.idCounter;
- }
- if (this.forExport) {
- id += '-export';
- }
- // Do nothing if ID already exists
- this.defIds = this.defIds || [];
- if (this.defIds.indexOf(id) > -1) {
- return;
- }
- // Store ID in list to avoid duplicates
- this.defIds.push(id);
- // Calculate pattern element attributes
- var attrs = {
- id: id,
- patternUnits: 'userSpaceOnUse',
- patternContentUnits: options.patternContentUnits || 'userSpaceOnUse',
- width: width,
- height: height,
- x: options._x || options.x || 0,
- y: options._y || options.y || 0
- };
- if (options.patternTransform) {
- attrs.patternTransform = options.patternTransform;
- }
- pattern = this.createElement('pattern').attr(attrs).add(this.defs);
- // Set id on the SVGRenderer object
- pattern.id = id;
- // Use an SVG path for the pattern
- if (options.path) {
- path = U.isObject(options.path) ?
- options.path :
- { d: options.path };
- // The background
- if (options.backgroundColor) {
- rect(options.backgroundColor);
- }
- // The pattern
- attribs = {
- 'd': path.d
- };
- if (!this.styledMode) {
- attribs.stroke = path.stroke || color;
- attribs['stroke-width'] = pick(path.strokeWidth, 2);
- attribs.fill = path.fill || 'none';
- }
- if (path.transform) {
- attribs.transform = path.transform;
- }
- this.createElement('path').attr(attribs).add(pattern);
- pattern.color = color;
- // Image pattern
- }
- else if (options.image) {
- if (animate) {
- this.image(options.image, 0, 0, width, height, function () {
- // Onload
- this.animate({
- opacity: pick(options.opacity, 1)
- }, animationOptions);
- removeEvent(this.element, 'load');
- }).attr({ opacity: 0 }).add(pattern);
- }
- else {
- this.image(options.image, 0, 0, width, height).add(pattern);
- }
- }
- // For non-animated patterns, set opacity now
- if (!(options.image && animate) && typeof options.opacity !== 'undefined') {
- [].forEach.call(pattern.element.childNodes, function (child) {
- child.setAttribute('opacity', options.opacity);
- });
- }
- // Store for future reference
- this.patternElements = this.patternElements || {};
- this.patternElements[id] = pattern;
- return pattern;
- };
- // Make sure we have a series color
- wrap(Series.prototype, 'getColor', function (proceed) {
- var oldColor = this.options.color;
- // Temporarely remove color options to get defaults
- if (oldColor &&
- oldColor.pattern &&
- !oldColor.pattern.color) {
- delete this.options.color;
- // Get default
- proceed.apply(this, Array.prototype.slice.call(arguments, 1));
- // Replace with old, but add default color
- oldColor.pattern.color =
- this.color;
- this.color = this.options.color = oldColor;
- }
- else {
- // We have a color, no need to do anything special
- proceed.apply(this, Array.prototype.slice.call(arguments, 1));
- }
- });
- // Calculate pattern dimensions on points that have their own pattern.
- addEvent(Series, 'render', function () {
- var isResizing = this.chart.isResizing;
- if (this.isDirtyData || isResizing || !this.chart.hasRendered) {
- (this.points || []).forEach(function (point) {
- var colorOptions = point.options && point.options.color;
- if (colorOptions &&
- colorOptions.pattern) {
- // For most points we want to recalculate the dimensions on
- // render, where we have the shape args and bbox. But if we
- // are resizing and don't have the shape args, defer it, since
- // the bounding box is still not resized.
- if (isResizing &&
- !(point.shapeArgs &&
- point.shapeArgs.width &&
- point.shapeArgs.height)) {
- colorOptions.pattern._width =
- 'defer';
- colorOptions.pattern._height =
- 'defer';
- }
- else {
- point.calculatePatternDimensions(colorOptions.pattern);
- }
- }
- });
- }
- });
- // Merge series color options to points
- addEvent(Point, 'afterInit', function () {
- var point = this,
- colorOptions = point.options.color;
- // Only do this if we have defined a specific color on this point. Otherwise
- // we will end up trying to re-add the series color for each point.
- if (colorOptions && colorOptions.pattern) {
- // Move path definition to object, allows for merge with series path
- // definition
- if (typeof colorOptions.pattern.path === 'string') {
- colorOptions.pattern.path = {
- d: colorOptions.pattern.path
- };
- }
- // Merge with series options
- point.color = point.options.color = merge(point.series.options.color, colorOptions);
- }
- });
- // Add functionality to SVG renderer to handle patterns as complex colors
- addEvent(SVGRenderer, 'complexColor', function (args) {
- var color = args.args[0],
- prop = args.args[1],
- element = args.args[2],
- chartIndex = (this.chartIndex || 0);
- var pattern = color.pattern,
- value = '#343434';
- // Handle patternIndex
- if (typeof color.patternIndex !== 'undefined' && patterns) {
- pattern = patterns[color.patternIndex];
- }
- // Skip and call default if there is no pattern
- if (!pattern) {
- return true;
- }
- // We have a pattern.
- if (pattern.image ||
- typeof pattern.path === 'string' ||
- pattern.path && pattern.path.d) {
- // Real pattern. Add it and set the color value to be a reference.
- // Force Hash-based IDs for legend items, as they are drawn before
- // point render, meaning they are drawn before autocalculated image
- // width/heights. We don't want them to highjack the width/height for
- // this ID if it is defined by users.
- var forceHashId = element.parentNode &&
- element.parentNode.getAttribute('class');
- forceHashId = forceHashId &&
- forceHashId.indexOf('highcharts-legend') > -1;
- // If we don't have a width/height yet, handle it. Try faking a point
- // and running the algorithm again.
- if (pattern._width === 'defer' || pattern._height === 'defer') {
- Point.prototype.calculatePatternDimensions.call({ graphic: { element: element } }, pattern);
- }
- // If we don't have an explicit ID, compute a hash from the
- // definition and use that as the ID. This ensures that points with
- // the same pattern definition reuse existing pattern elements by
- // default. We combine two hashes, the second with an additional
- // preSeed algorithm, to minimize collision probability.
- if (forceHashId || !pattern.id) {
- // Make a copy so we don't accidentally edit options when setting ID
- pattern = merge({}, pattern);
- pattern.id = 'highcharts-pattern-' + chartIndex + '-' +
- hashFromObject(pattern) + hashFromObject(pattern, true);
- }
- // Add it. This function does nothing if an element with this ID
- // already exists.
- this.addPattern(pattern, !this.forExport && pick(pattern.animation, this.globalAnimation, { duration: 100 }));
- value = "url(" + this.url + "#" + (pattern.id + (this.forExport ? '-export' : '')) + ")";
- }
- else {
- // Not a full pattern definition, just add color
- value = pattern.color || value;
- }
- // Set the fill/stroke prop on the element
- element.setAttribute(prop, value);
- // Allow the color to be concatenated into tooltips formatters etc.
- color.toString = function () {
- return value;
- };
- // Skip default handler
- return false;
- });
- // When animation is used, we have to recalculate pattern dimensions after
- // resize, as the bounding boxes are not available until then.
- addEvent(Chart, 'endResize', function () {
- if ((this.renderer && this.renderer.defIds || []).filter(function (id) {
- return (id &&
- id.indexOf &&
- id.indexOf('highcharts-pattern-') === 0);
- }).length) {
- // We have non-default patterns to fix. Find them by looping through
- // all points.
- this.series.forEach(function (series) {
- series.points.forEach(function (point) {
- var colorOptions = point.options && point.options.color;
- if (colorOptions &&
- colorOptions.pattern) {
- colorOptions.pattern._width =
- 'defer';
- colorOptions.pattern._height =
- 'defer';
- }
- });
- });
- // Redraw without animation
- this.redraw(false);
- }
- });
- // Add a garbage collector to delete old patterns with autogenerated hashes that
- // are no longer being referenced.
- addEvent(Chart, 'redraw', function () {
- var usedIds = {},
- renderer = this.renderer,
- // Get the autocomputed patterns - these are the ones we might delete
- patterns = (renderer.defIds || []).filter(function (pattern) {
- return (pattern.indexOf &&
- pattern.indexOf('highcharts-pattern-') === 0);
- });
- if (patterns.length) {
- // Look through the DOM for usage of the patterns. This can be points,
- // series, tooltips etc.
- [].forEach.call(this.renderTo.querySelectorAll('[color^="url("], [fill^="url("], [stroke^="url("]'), function (node) {
- var id = node.getAttribute('fill') ||
- node.getAttribute('color') ||
- node.getAttribute('stroke');
- if (id) {
- var sanitizedId = id.replace(renderer.url, '').replace('url(#', '').replace(')', '');
- usedIds[sanitizedId] = true;
- }
- });
- // Loop through the patterns that exist and see if they are used
- patterns.forEach(function (id) {
- if (!usedIds[id]) {
- // Remove id from used id list
- erase(renderer.defIds, id);
- // Remove pattern element
- if (renderer.patternElements[id]) {
- renderer.patternElements[id].destroy();
- delete renderer.patternElements[id];
- }
- }
- });
- }
- });
- /* *
- *
- * API Declarations
- *
- * */
- /**
- * Pattern options
- *
- * @interface Highcharts.PatternOptionsObject
- */ /**
- * Background color for the pattern if a `path` is set (not images).
- * @name Highcharts.PatternOptionsObject#backgroundColor
- * @type {Highcharts.ColorString}
- */ /**
- * URL to an image to use as the pattern.
- * @name Highcharts.PatternOptionsObject#image
- * @type {string}
- */ /**
- * Width of the pattern. For images this is automatically set to the width of
- * the element bounding box if not supplied. For non-image patterns the default
- * is 32px. Note that automatic resizing of image patterns to fill a bounding
- * box dynamically is only supported for patterns with an automatically
- * calculated ID.
- * @name Highcharts.PatternOptionsObject#width
- * @type {number}
- */ /**
- * Analogous to pattern.width.
- * @name Highcharts.PatternOptionsObject#height
- * @type {number}
- */ /**
- * For automatically calculated width and height on images, it is possible to
- * set an aspect ratio. The image will be zoomed to fill the bounding box,
- * maintaining the aspect ratio defined.
- * @name Highcharts.PatternOptionsObject#aspectRatio
- * @type {number}
- */ /**
- * Horizontal offset of the pattern. Defaults to 0.
- * @name Highcharts.PatternOptionsObject#x
- * @type {number|undefined}
- */ /**
- * Vertical offset of the pattern. Defaults to 0.
- * @name Highcharts.PatternOptionsObject#y
- * @type {number|undefined}
- */ /**
- * Either an SVG path as string, or an object. As an object, supply the path
- * string in the `path.d` property. Other supported properties are standard SVG
- * attributes like `path.stroke` and `path.fill`. If a path is supplied for the
- * pattern, the `image` property is ignored.
- * @name Highcharts.PatternOptionsObject#path
- * @type {string|Highcharts.SVGAttributes}
- */ /**
- * SVG `patternTransform` to apply to the entire pattern.
- * @name Highcharts.PatternOptionsObject#patternTransform
- * @type {string}
- * @see [patternTransform demo](https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/series/pattern-fill-transform)
- */ /**
- * Pattern color, used as default path stroke.
- * @name Highcharts.PatternOptionsObject#color
- * @type {Highcharts.ColorString}
- */ /**
- * Opacity of the pattern as a float value from 0 to 1.
- * @name Highcharts.PatternOptionsObject#opacity
- * @type {number}
- */ /**
- * ID to assign to the pattern. This is automatically computed if not added, and
- * identical patterns are reused. To refer to an existing pattern for a
- * Highcharts color, use `color: "url(#pattern-id)"`.
- * @name Highcharts.PatternOptionsObject#id
- * @type {string|undefined}
- */
- /**
- * Holds a pattern definition.
- *
- * @sample highcharts/series/pattern-fill-area/
- * Define a custom path pattern
- * @sample highcharts/series/pattern-fill-pie/
- * Default patterns and a custom image pattern
- * @sample maps/demo/pattern-fill-map/
- * Custom images on map
- *
- * @example
- * // Pattern used as a color option
- * color: {
- * pattern: {
- * path: {
- * d: 'M 3 3 L 8 3 L 8 8 Z',
- * fill: '#102045'
- * },
- * width: 12,
- * height: 12,
- * color: '#907000',
- * opacity: 0.5
- * }
- * }
- *
- * @interface Highcharts.PatternObject
- */ /**
- * Pattern options
- * @name Highcharts.PatternObject#pattern
- * @type {Highcharts.PatternOptionsObject}
- */ /**
- * Animation options for the image pattern loading.
- * @name Highcharts.PatternObject#animation
- * @type {boolean|Partial<Highcharts.AnimationOptionsObject>|undefined}
- */ /**
- * Optionally an index referencing which pattern to use. Highcharts adds
- * 10 default patterns to the `Highcharts.patterns` array. Additional
- * pattern definitions can be pushed to this array if desired. This option
- * is an index into this array.
- * @name Highcharts.PatternObject#patternIndex
- * @type {number|undefined}
- */
- ''; // keeps doclets above in transpiled file
- });
- _registerModule(_modules, 'masters/modules/pattern-fill.src.js', [], function () {
- });
- }));
|