| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607 | 
							- /* *
 
-  * Module for using patterns or images as point fills.
 
-  *
 
-  * (c) 2010-2019 Highsoft AS
 
-  * Author: Torstein Hønsi, Øystein Moseng
 
-  *
 
-  * License: www.highcharts.com/license
 
-  */
 
- /**
 
-  * Pattern options
 
-  *
 
-  * @interface Highcharts.PatternOptionsObject
 
-  *//**
 
-  * 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#with
 
-  * @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}
 
-  *//**
 
-  * 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}
 
-  */
 
- /**
 
-  * 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|Highcharts.AnimationOptionsObject|undefined}
 
-  */
 
- 'use strict';
 
- import H from '../parts/Globals.js';
 
- import '../parts/Utilities.js';
 
- var addEvent = H.addEvent,
 
-     wrap = H.wrap,
 
-     merge = H.merge,
 
-     pick = H.pick;
 
- /**
 
-  * 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.PatternObject} pattern
 
-  *        The pattern to set dimensions on.
 
-  */
 
- H.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
 
-         );
 
-     }
 
- };
 
- /**
 
-  * Add a pattern to the renderer.
 
-  *
 
-  * @private
 
-  * @function Highcharts.SVGRenderer#addPattern
 
-  *
 
-  * @param {Highcharts.PatternObject} options
 
-  *        The pattern options.
 
-  *
 
-  * @return {Highcharts.SVGElement|undefined}
 
-  *         The added pattern. Undefined if the pattern already exists.
 
-  */
 
- H.SVGRenderer.prototype.addPattern = function (options, animation) {
 
-     var pattern,
 
-         animate = H.pick(animation, true),
 
-         animationOptions = H.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.idCounter;
 
-     }
 
-     // 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);
 
-     // Create pattern element
 
-     pattern = this.createElement('pattern').attr({
 
-         id: id,
 
-         patternUnits: 'userSpaceOnUse',
 
-         width: width,
 
-         height: height,
 
-         x: options._x || options.x || 0,
 
-         y: options._y || options.y || 0
 
-     }).add(this.defs);
 
-     // Set id on the SVGRenderer object
 
-     pattern.id = id;
 
-     // Use an SVG path for the pattern
 
-     if (options.path) {
 
-         path = options.path;
 
-         // The background
 
-         if (path.fill) {
 
-             rect(path.fill);
 
-         }
 
-         // The pattern
 
-         attribs = {
 
-             'd': path.d || path
 
-         };
 
-         if (!this.styledMode) {
 
-             attribs.stroke = path.stroke || color;
 
-             attribs['stroke-width'] = path.strokeWidth || 2;
 
-         }
 
-         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);
 
-                     H.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) && 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(H.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(H.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(H.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
 
- H.addEvent(H.SVGRenderer, 'complexColor', function (args) {
 
-     var color = args.args[0],
 
-         prop = args.args[1],
 
-         element = args.args[2],
 
-         pattern = color.pattern,
 
-         value = '#343434',
 
-         forceHashId;
 
-     // 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.
 
-         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') {
 
-             H.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-' + hashFromObject(pattern) +
 
-                 hashFromObject(pattern, true);
 
-         }
 
-         // Add it. This function does nothing if an element with this ID
 
-         // already exists.
 
-         this.addPattern(pattern, !this.forExport && H.pick(
 
-             pattern.animation,
 
-             this.globalAnimation,
 
-             { duration: 100 }
 
-         ));
 
-         value = 'url(' + this.url + '#' + pattern.id + ')';
 
-     } 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.
 
- H.addEvent(H.Chart, 'endResize', function () {
 
-     if (
 
-         (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.
 
- H.addEvent(H.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) {
 
-                     usedIds.push(
 
-                         id
 
-                             .substring(id.indexOf('url(#') + 5)
 
-                             .replace(')', '')
 
-                     );
 
-                 }
 
-             }
 
-         );
 
-         // Loop through the patterns that exist and see if they are used
 
-         patterns.forEach(function (id) {
 
-             if (usedIds.indexOf(id) === -1) {
 
-                 // Remove id from used id list
 
-                 H.erase(renderer.defIds, id);
 
-                 // Remove pattern element
 
-                 if (renderer.patternElements[id]) {
 
-                     renderer.patternElements[id].destroy();
 
-                     delete renderer.patternElements[id];
 
-                 }
 
-             }
 
-         });
 
-     }
 
- });
 
- // Add the predefined patterns
 
- H.Chart.prototype.callbacks.push(function (chart) {
 
-     var colors = H.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) {
 
-         chart.renderer.addPattern({
 
-             id: 'highcharts-default-pattern-' + i,
 
-             path: pattern,
 
-             color: colors[i],
 
-             width: 10,
 
-             height: 10
 
-         });
 
-     });
 
- });
 
 
  |