Skip to content

Commit e9bb900

Browse files
committed
v12.1.0
1 parent 06333ab commit e9bb900

File tree

331 files changed

+4644
-3429
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

331 files changed

+4644
-3429
lines changed

bower.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "highcharts",
3-
"version": "12.0.2",
3+
"version": "12.1.0",
44
"main": "highcharts.js",
55
"license": "https://www.highcharts.com/license",
66
"types": "highcharts.d.ts"

es-modules/Accessibility/Options/A11yDefaults.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,8 @@ const Options = {
266266
/**
267267
* When a series contains more points than this, we no longer expose
268268
* information about individual points to screen readers.
269+
* Note that the keyboard navigation remains functional, but points
270+
* won't have accessible descriptions unless handled separately.
269271
*
270272
* Set to `false` to disable.
271273
*

es-modules/Core/Color/Color.js

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -192,18 +192,11 @@ class Color {
192192
}
193193
// Check for has alpha, because rgba colors perform worse due to
194194
// lack of support in WebKit.
195-
const hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1);
196-
return (hasAlpha ? 'rgba(' : 'rgb(') +
197-
Math.round(toRgba[0] + (fromRgba[0] - toRgba[0]) * (1 - pos)) +
198-
',' +
199-
Math.round(toRgba[1] + (fromRgba[1] - toRgba[1]) * (1 - pos)) +
200-
',' +
201-
Math.round(toRgba[2] + (fromRgba[2] - toRgba[2]) * (1 - pos)) +
202-
(hasAlpha ?
203-
(',' +
204-
(toRgba[3] + (fromRgba[3] - toRgba[3]) * (1 - pos))) :
205-
'') +
206-
')';
195+
const hasAlpha = (toRgba[3] !== 1 || fromRgba[3] !== 1), channel = (to, i) => to + (fromRgba[i] - to) * (1 - pos), rgba = toRgba.slice(0, 3).map(channel).map(Math.round);
196+
if (hasAlpha) {
197+
rgba.push(channel(toRgba[3], 3));
198+
}
199+
return (hasAlpha ? 'rgba(' : 'rgb(') + rgba.join(',') + ')';
207200
}
208201
}
209202
/* *

es-modules/Core/Renderer/HTML/HTMLElement.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class HTMLElement extends SVGElement {
251251
textWidth,
252252
this.textAlign
253253
].join(','), parentPadding = (this.parentGroup?.padding * -1) || 0;
254-
let baseline, hasBoxWidthChanged = false;
254+
let baseline;
255255
// Update textWidth. Use the memoized textPxLength if possible, to
256256
// avoid the getTextPxLength function using elem.offsetWidth.
257257
// Calling offsetWidth affects rendering time as it forces layout
@@ -274,10 +274,8 @@ class HTMLElement extends SVGElement {
274274
whiteSpace: whiteSpace || 'normal' // #3331
275275
});
276276
this.oldTextWidth = textWidth;
277-
hasBoxWidthChanged = true; // #8159
278277
}
279278
}
280-
this.hasBoxWidthChanged = hasBoxWidthChanged; // #8159
281279
// Do the calculations and DOM access only if properties changed
282280
if (currentTextTransform !== this.cTT) {
283281
baseline = renderer.fontMetrics(element).b;

es-modules/Core/Renderer/SVG/SVGElement.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,9 @@ class SVGElement {
213213
* @return {Highcharts.SVGElement} Returns the SVGElement for chaining.
214214
*/
215215
align(alignOptions, alignByTranslate, alignTo, redraw = true) {
216-
const attribs = {}, renderer = this.renderer, alignedObjects = renderer.alignedObjects, initialAlignment = Boolean(alignOptions);
216+
const attribs = {
217+
'text-align': alignOptions?.align
218+
}, renderer = this.renderer, alignedObjects = renderer.alignedObjects, initialAlignment = Boolean(alignOptions);
217219
// First call on instanciate
218220
if (alignOptions) {
219221
this.alignOptions = alignOptions;

es-modules/Core/Renderer/SVG/SVGLabel.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class SVGLabel extends SVGElement {
7171
* */
7272
alignSetter(value) {
7373
const alignFactor = getAlignFactor(value);
74+
this.textAlign = value;
7475
if (alignFactor !== this.alignFactor) {
7576
this.alignFactor = alignFactor;
7677
// Bounding box exists, means we're dynamically changing
@@ -246,6 +247,7 @@ class SVGLabel extends SVGElement {
246247
}
247248
'text-alignSetter'(value) {
248249
this.textAlign = value;
250+
this.updateTextPadding();
249251
}
250252
textSetter(text) {
251253
if (typeof text !== 'undefined') {
@@ -315,24 +317,19 @@ class SVGLabel extends SVGElement {
315317
* is changed.
316318
*/
317319
updateTextPadding() {
318-
const text = this.text;
320+
const text = this.text, textAlign = text.styles.textAlign || this.textAlign;
319321
if (!text.textPath) {
320322
this.updateBoxSize();
321323
// Determine y based on the baseline
322324
const textY = this.baseline ? 0 : this.baselineOffset, textX = (this.paddingLeft ?? this.padding) +
323325
// Compensate for alignment
324-
((defined(this.widthSetting) && this.bBox) ?
325-
getAlignFactor(this.textAlign) *
326-
(this.widthSetting - this.bBox.width) :
327-
0);
326+
getAlignFactor(textAlign) * (this.widthSetting ?? this.bBox.width);
328327
// Update if anything changed
329328
if (textX !== text.x || textY !== text.y) {
330-
text.attr('x', textX);
331-
// #8159 - prevent misplaced data labels in treemap
332-
// (useHTML: true)
333-
if (text.hasBoxWidthChanged) {
334-
this.bBox = text.getBBox(true);
335-
}
329+
text.attr({
330+
align: textAlign,
331+
x: textX
332+
});
336333
if (typeof textY !== 'undefined') {
337334
text.attr('y', textY);
338335
}

es-modules/Core/Series/DataLabel.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ var DataLabel;
110110
(unrotatedbBox.width - bBox.width);
111111
dataLabel.alignAttr.y += getAlignFactor(options.verticalAlign) *
112112
(unrotatedbBox.height - bBox.height);
113-
dataLabel[dataLabel.placed ? 'animate' : 'attr']({
113+
dataLabel.attr({
114+
'text-align': dataLabel.alignAttr['text-align'] || 'center'
115+
})[dataLabel.placed ? 'animate' : 'attr']({
114116
x: dataLabel.alignAttr.x +
115117
(bBox.width - unrotatedbBox.width) / 2,
116118
y: dataLabel.alignAttr.y +

es-modules/Core/Series/Series.js

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,9 +2394,14 @@ class Series {
23942394
* @private
23952395
* @function Highcharts.Series#searchKDTree
23962396
*/
2397-
searchKDTree(point, compareX, e) {
2397+
searchKDTree(point, compareX, e, suppliedPointEvaluator, suppliedBSideCheckEvaluator) {
23982398
const series = this, [kdX, kdY] = this.kdAxisArray, kdComparer = compareX ? 'distX' : 'dist', kdDimensions = (series.options.findNearestPointBy || '')
2399-
.indexOf('y') > -1 ? 2 : 1, useRadius = !!series.isBubble;
2399+
.indexOf('y') > -1 ? 2 : 1, useRadius = !!series.isBubble, pointEvaluator = suppliedPointEvaluator || ((p1, p2, comparisonProp) => [
2400+
(p1[comparisonProp] || 0) < (p2[comparisonProp] || 0) ?
2401+
p1 :
2402+
p2,
2403+
false
2404+
]), bSideCheckEvaluator = suppliedBSideCheckEvaluator || ((a, b) => a < b);
24002405
/**
24012406
* Set the one and two dimensional distance on the point object.
24022407
* @private
@@ -2411,28 +2416,21 @@ class Series {
24112416
*/
24122417
function doSearch(search, tree, depth, dimensions) {
24132418
const point = tree.point, axis = series.kdAxisArray[depth % dimensions];
2414-
let nPoint1, nPoint2, ret = point;
2419+
let ret = point, flip = false;
24152420
setDistance(search, point);
24162421
// Pick side based on distance to splitting point
24172422
const tdist = (search[axis] || 0) - (point[axis] || 0) +
24182423
(useRadius ? (point.marker?.radius || 0) : 0), sideA = tdist < 0 ? 'left' : 'right', sideB = tdist < 0 ? 'right' : 'left';
24192424
// End of tree
24202425
if (tree[sideA]) {
2421-
nPoint1 = doSearch(search, tree[sideA], depth + 1, dimensions);
2422-
ret = (nPoint1[kdComparer] <
2423-
ret[kdComparer] ?
2424-
nPoint1 :
2425-
point);
2426+
[ret, flip] = pointEvaluator(point, doSearch(search, tree[sideA], depth + 1, dimensions), kdComparer);
24262427
}
24272428
if (tree[sideB]) {
2429+
const sqrtTDist = Math.sqrt(tdist * tdist), retDist = ret[kdComparer];
24282430
// Compare distance to current best to splitting point to decide
2429-
// whether to check side B or not
2430-
if (Math.sqrt(tdist * tdist) < ret[kdComparer]) {
2431-
nPoint2 = doSearch(search, tree[sideB], depth + 1, dimensions);
2432-
ret = (nPoint2[kdComparer] <
2433-
ret[kdComparer] ?
2434-
nPoint2 :
2435-
ret);
2431+
// whether to check side B or no
2432+
if (bSideCheckEvaluator(sqrtTDist, retDist, flip)) {
2433+
ret = pointEvaluator(ret, doSearch(search, tree[sideB], depth + 1, dimensions), kdComparer)[0];
24362434
}
24372435
}
24382436
return ret;

es-modules/Core/Templating.js

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const { defaultOptions, defaultTime } = D;
1313
import G from './Globals.js';
1414
const { doc } = G;
1515
import U from './Utilities.js';
16-
const { extend, getNestedProperty, isArray, isNumber, isObject, pick, ucfirst } = U;
16+
const { extend, getNestedProperty, isArray, isNumber, isObject, isString, pick, ucfirst } = U;
1717
const helpers = {
1818
// Built-in helpers
1919
add: (a, b) => a + b,
@@ -48,6 +48,8 @@ const numberFormatCache = {};
4848
* Functions
4949
*
5050
* */
51+
// Internal convenience function
52+
const isQuotedString = (str) => /^["'].+["']$/.test(str);
5153
/**
5254
* Formats a JavaScript date timestamp (milliseconds since Jan 1st 1970) into a
5355
* human readable date string. The format is a subset of the formats for PHP's
@@ -125,11 +127,11 @@ function dateFormat(format, timestamp, upperCaseFirst) {
125127
* The formatted string.
126128
*/
127129
function format(str = '', ctx, chart) {
128-
const regex = /\{([\p{L}\d:\.,;\-\/<>\[\]%_@"'= #\(\)]+)\}/gu,
130+
const regex = /\{([\p{L}\d:\.,;\-\/<>\[\]%_@+"'= #\(\)]+)\}/gu,
129131
// The sub expression regex is the same as the top expression regex,
130132
// but except parens and block helpers (#), and surrounded by parens
131133
// instead of curly brackets.
132-
subRegex = /\(([\p{L}\d:\.,;\-\/<>\[\]%_@"'= ]+)\)/gu, matches = [], floatRegex = /f$/, decRegex = /\.(\d)/, lang = chart?.options.lang || defaultOptions.lang, time = chart && chart.time || defaultTime, numberFormatter = chart && chart.numberFormatter || numberFormat;
134+
subRegex = /\(([\p{L}\d:\.,;\-\/<>\[\]%_@+"'= ]+)\)/gu, matches = [], floatRegex = /f$/, decRegex = /\.(\d)/, lang = chart?.options.lang || defaultOptions.lang, time = chart && chart.time || defaultTime, numberFormatter = chart && chart.numberFormatter || numberFormat;
133135
/*
134136
* Get a literal or variable value inside a template expression. May be
135137
* extended with other types like string or null if needed, but keep it
@@ -147,7 +149,7 @@ function format(str = '', ctx, chart) {
147149
if ((n = Number(key)).toString() === key) {
148150
return n;
149151
}
150-
if (/^["'].+["']$/.test(key)) {
152+
if (isQuotedString(key)) {
151153
return key.slice(1, -1);
152154
}
153155
// Variables and constants
@@ -260,7 +262,8 @@ function format(str = '', ctx, chart) {
260262
// Simple variable replacement
261263
}
262264
else {
263-
const valueAndFormat = expression.split(':');
265+
const valueAndFormat = isQuotedString(expression) ?
266+
[expression] : expression.split(':');
264267
replacement = resolveProperty(valueAndFormat.shift() || '');
265268
// Format the replacement
266269
if (valueAndFormat.length && typeof replacement === 'number') {
@@ -273,13 +276,14 @@ function format(str = '', ctx, chart) {
273276
}
274277
else {
275278
replacement = time.dateFormat(segment, replacement);
276-
// Use string literal in order to be preserved in the outer
277-
// expression
278-
if (hasSub) {
279-
replacement = `"${replacement}"`;
280-
}
281279
}
282280
}
281+
// Use string literal in order to be preserved in the outer
282+
// expression
283+
subRegex.lastIndex = 0;
284+
if (subRegex.test(match.find) && isString(replacement)) {
285+
replacement = `"${replacement}"`;
286+
}
283287
}
284288
str = str.replace(match.find, pick(replacement, ''));
285289
});

es-modules/Core/Time.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ class Time {
9191
* Properties
9292
*
9393
* */
94-
this.options = {};
94+
this.options = {
95+
timezone: 'UTC'
96+
};
9597
this.variableTimezone = false;
9698
this.Date = win.Date;
9799
this.update(options);
@@ -113,12 +115,13 @@ class Time {
113115
*
114116
*/
115117
update(options = {}) {
116-
let timezone = options.timezone ?? 'UTC';
117118
this.dTLCache = {};
118119
this.options = options = merge(true, this.options, options);
119120
const { timezoneOffset, useUTC } = options;
120121
// Allow using a different Date class
121122
this.Date = options.Date || win.Date || Date;
123+
// Assign the time zone. Handle the legacy, deprecated `useUTC` option.
124+
let timezone = options.timezone;
122125
if (defined(useUTC)) {
123126
timezone = useUTC ? 'UTC' : void 0;
124127
}
@@ -561,7 +564,7 @@ class Time {
561564
}
562565
else if (isObject(format)) {
563566
const tzHours = (this.getTimezoneOffset(timestamp) || 0) /
564-
(60000 * 60), timeZone = this.options.timezone || ('Etc/GMT' + (tzHours >= 0 ? '+' : '') + tzHours), { prefix = '', suffix = '' } = format;
567+
(60000 * 60), timeZone = this.timezone || ('Etc/GMT' + (tzHours >= 0 ? '+' : '') + tzHours), { prefix = '', suffix = '' } = format;
565568
format = prefix + this.dateTimeFormat(extend({ timeZone }, format), timestamp) + suffix;
566569
}
567570
// Optionally sentence-case the string and return

0 commit comments

Comments
 (0)