highcharts-rails 5.0.14 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.markdown +60 -0
- data/Rakefile +54 -5
- data/app/assets/images/highcharts/earth.svg +432 -0
- data/app/assets/javascripts/highcharts.js +5103 -3147
- data/app/assets/javascripts/highcharts/highcharts-3d.js +930 -277
- data/app/assets/javascripts/highcharts/highcharts-more.js +1374 -249
- data/app/assets/javascripts/highcharts/lib/canvg.js +3073 -0
- data/app/assets/javascripts/highcharts/lib/jspdf.js +16624 -0
- data/app/assets/javascripts/highcharts/lib/rgbcolor.js +299 -0
- data/app/assets/javascripts/highcharts/lib/svg2pdf.js +3488 -0
- data/app/assets/javascripts/highcharts/modules/accessibility.js +654 -212
- data/app/assets/javascripts/highcharts/modules/annotations.js +1552 -274
- data/app/assets/javascripts/highcharts/modules/boost-canvas.js +773 -0
- data/app/assets/javascripts/highcharts/modules/boost.js +636 -210
- data/app/assets/javascripts/highcharts/modules/broken-axis.js +2 -2
- data/app/assets/javascripts/highcharts/modules/bullet.js +364 -0
- data/app/assets/javascripts/highcharts/modules/data.js +766 -38
- data/app/assets/javascripts/highcharts/modules/drag-panes.js +588 -0
- data/app/assets/javascripts/highcharts/modules/drilldown.js +106 -36
- data/app/assets/javascripts/highcharts/modules/export-data.js +597 -0
- data/app/assets/javascripts/highcharts/modules/exporting.js +424 -162
- data/app/assets/javascripts/highcharts/modules/funnel.js +144 -22
- data/app/assets/javascripts/highcharts/modules/gantt.js +1154 -0
- data/app/assets/javascripts/highcharts/modules/grid-axis.js +1 -1
- data/app/assets/javascripts/highcharts/modules/heatmap.js +406 -80
- data/app/assets/javascripts/highcharts/modules/histogram-bellcurve.js +513 -0
- data/app/assets/javascripts/highcharts/modules/item-series.js +126 -0
- data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +31 -13
- data/app/assets/javascripts/highcharts/modules/offline-exporting.js +179 -57
- data/app/assets/javascripts/highcharts/modules/oldie.js +1378 -0
- data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +8 -6
- data/app/assets/javascripts/highcharts/modules/parallel-coordinates.js +494 -0
- data/app/assets/javascripts/highcharts/modules/pareto.js +275 -0
- data/app/assets/javascripts/highcharts/modules/sankey.js +641 -0
- data/app/assets/javascripts/highcharts/modules/series-label.js +355 -145
- data/app/assets/javascripts/highcharts/modules/solid-gauge.js +122 -1
- data/app/assets/javascripts/highcharts/modules/static-scale.js +64 -0
- data/app/assets/javascripts/highcharts/modules/stock.js +1944 -676
- data/app/assets/javascripts/highcharts/modules/streamgraph.js +139 -0
- data/app/assets/javascripts/highcharts/modules/sunburst.js +2403 -0
- data/app/assets/javascripts/highcharts/modules/tilemap.js +1199 -0
- data/app/assets/javascripts/highcharts/modules/treemap.js +538 -134
- data/app/assets/javascripts/highcharts/modules/variable-pie.js +490 -0
- data/app/assets/javascripts/highcharts/modules/variwide.js +283 -0
- data/app/assets/javascripts/highcharts/modules/vector.js +294 -0
- data/app/assets/javascripts/highcharts/modules/windbarb.js +490 -0
- data/app/assets/javascripts/highcharts/modules/wordcloud.js +681 -0
- data/app/assets/javascripts/highcharts/modules/xrange.js +615 -0
- data/app/assets/javascripts/highcharts/themes/avocado.js +54 -0
- data/app/assets/javascripts/highcharts/themes/dark-blue.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-green.js +6 -6
- data/app/assets/javascripts/highcharts/themes/dark-unica.js +6 -6
- data/app/assets/javascripts/highcharts/themes/gray.js +14 -10
- data/app/assets/javascripts/highcharts/themes/grid-light.js +6 -6
- data/app/assets/javascripts/highcharts/themes/grid.js +7 -5
- data/app/assets/javascripts/highcharts/themes/sand-signika.js +8 -7
- data/app/assets/javascripts/highcharts/themes/skies.js +15 -9
- data/app/assets/javascripts/highcharts/themes/sunset.js +53 -0
- data/app/assets/stylesheets/highcharts/highcharts.css +802 -0
- data/app/assets/stylesheets/highcharts/highcharts.scss +665 -0
- data/lib/highcharts/version.rb +1 -1
- metadata +31 -1
@@ -1,5 +1,5 @@
|
|
1
1
|
/**
|
2
|
-
* @license Highcharts JS
|
2
|
+
* @license Highcharts JS v6.0.0 (2017-10-04)
|
3
3
|
* Accessibility module
|
4
4
|
*
|
5
5
|
* (c) 2010-2017 Highsoft AS
|
@@ -24,6 +24,7 @@
|
|
24
24
|
*
|
25
25
|
* License: www.highcharts.com/license
|
26
26
|
*/
|
27
|
+
/* eslint max-len: ["warn", 80, 4] */
|
27
28
|
|
28
29
|
var win = H.win,
|
29
30
|
doc = win.document,
|
@@ -34,7 +35,18 @@
|
|
34
35
|
fireEvent = H.fireEvent,
|
35
36
|
dateFormat = H.dateFormat,
|
36
37
|
merge = H.merge,
|
37
|
-
//
|
38
|
+
// CSS style to hide element from visual users while still exposing it to
|
39
|
+
// screen readers
|
40
|
+
hiddenStyle = {
|
41
|
+
position: 'absolute',
|
42
|
+
left: '-9999px',
|
43
|
+
top: 'auto',
|
44
|
+
width: '1px',
|
45
|
+
height: '1px',
|
46
|
+
overflow: 'hidden'
|
47
|
+
},
|
48
|
+
// Human readable description of series and each point in singular and
|
49
|
+
// plural
|
38
50
|
typeToSeriesMap = {
|
39
51
|
'default': ['series', 'data point', 'data points'],
|
40
52
|
'line': ['line', 'data point', 'data points'],
|
@@ -47,7 +59,11 @@
|
|
47
59
|
'scatter': ['scatter series', 'data point', 'data points'],
|
48
60
|
'boxplot': ['boxplot series', 'box', 'boxes'],
|
49
61
|
'arearange': ['arearange series', 'data point', 'data points'],
|
50
|
-
'areasplinerange': [
|
62
|
+
'areasplinerange': [
|
63
|
+
'areasplinerange series',
|
64
|
+
'data point',
|
65
|
+
'data points'
|
66
|
+
],
|
51
67
|
'bubble': ['bubble series', 'bubble', 'bubbles'],
|
52
68
|
'columnrange': ['columnrange series', 'column', 'columns'],
|
53
69
|
'errorbar': ['errorbar series', 'errorbar', 'errorbars'],
|
@@ -61,16 +77,26 @@
|
|
61
77
|
},
|
62
78
|
// Descriptions for exotic chart types
|
63
79
|
typeDescriptionMap = {
|
64
|
-
boxplot: ' Box plot charts are typically used to display groups of
|
65
|
-
'Each data point in the chart can have up to 5
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
80
|
+
boxplot: ' Box plot charts are typically used to display groups of ' +
|
81
|
+
'statistical data. Each data point in the chart can have up to 5 ' +
|
82
|
+
'values: minimum, lower quartile, median, upper quartile and ' +
|
83
|
+
'maximum. ',
|
84
|
+
arearange: ' Arearange charts are line charts displaying a range ' +
|
85
|
+
'between a lower and higher value for each point. ',
|
86
|
+
areasplinerange: ' These charts are line charts displaying a range ' +
|
87
|
+
'between a lower and higher value for each point. ',
|
88
|
+
bubble: ' Bubble charts are scatter charts where each data point ' +
|
89
|
+
'also has a size value. ',
|
90
|
+
columnrange: ' Columnrange charts are column charts displaying a ' +
|
91
|
+
'range between a lower and higher value for each point. ',
|
92
|
+
errorbar: ' Errorbar series are used to display the variability of ' +
|
93
|
+
'the data. ',
|
94
|
+
funnel: ' Funnel charts are used to display reduction of data in ' +
|
95
|
+
'stages. ',
|
96
|
+
pyramid: ' Pyramid charts consist of a single pyramid with item ' +
|
97
|
+
'heights corresponding to each point value. ',
|
98
|
+
waterfall: ' A waterfall chart is a column chart where each column ' +
|
99
|
+
'contributes towards a total end value. '
|
74
100
|
};
|
75
101
|
|
76
102
|
// If a point has one of the special keys defined, we expose all keys to the
|
@@ -85,6 +111,15 @@
|
|
85
111
|
H.seriesTypes.pie.prototype.specialKeys = [];
|
86
112
|
}
|
87
113
|
|
114
|
+
// Set for which series types it makes sense to move to the closest point with
|
115
|
+
// up/down arrows, and which series types should just move to next series.
|
116
|
+
H.Series.prototype.keyboardMoveVertical = true;
|
117
|
+
each(['column', 'pie'], function(type) {
|
118
|
+
if (H.seriesTypes[type]) {
|
119
|
+
H.seriesTypes[type].prototype.keyboardMoveVertical = false;
|
120
|
+
}
|
121
|
+
});
|
122
|
+
|
88
123
|
|
89
124
|
/**
|
90
125
|
* Accessibility options
|
@@ -101,7 +136,6 @@
|
|
101
136
|
* com/docs/chart-concepts/accessibility).
|
102
137
|
*
|
103
138
|
* @since 5.0.0
|
104
|
-
* @product highcharts highstock highmaps
|
105
139
|
*/
|
106
140
|
accessibility: {
|
107
141
|
|
@@ -111,7 +145,6 @@
|
|
111
145
|
* @type {Boolean}
|
112
146
|
* @default true
|
113
147
|
* @since 5.0.0
|
114
|
-
* @product highcharts highstock highmaps
|
115
148
|
*/
|
116
149
|
enabled: true,
|
117
150
|
|
@@ -124,7 +157,6 @@
|
|
124
157
|
* @type {Number|Boolean}
|
125
158
|
* @default 30
|
126
159
|
* @since 5.0.0
|
127
|
-
* @product highcharts highstock highmaps
|
128
160
|
*/
|
129
161
|
pointDescriptionThreshold: 30, // set to false to disable
|
130
162
|
|
@@ -133,7 +165,6 @@
|
|
133
165
|
*
|
134
166
|
* @type {Object}
|
135
167
|
* @since 5.0.0
|
136
|
-
* @product highcharts highstock highmaps
|
137
168
|
*/
|
138
169
|
keyboardNavigation: {
|
139
170
|
|
@@ -144,17 +175,7 @@
|
|
144
175
|
* @default true
|
145
176
|
* @since 5.0.0
|
146
177
|
*/
|
147
|
-
enabled: true
|
148
|
-
|
149
|
-
/**
|
150
|
-
* Enable tab navigation for points. Without this, only arrow keys
|
151
|
-
* can be used to navigate between points.
|
152
|
-
*
|
153
|
-
* @type {Boolean}
|
154
|
-
* @default {all} true
|
155
|
-
* @since next
|
156
|
-
*/
|
157
|
-
tabThroughPoints: true
|
178
|
+
enabled: true
|
158
179
|
|
159
180
|
/**
|
160
181
|
* Skip null points when navigating through points with the
|
@@ -174,7 +195,6 @@
|
|
174
195
|
* @type {Boolean}
|
175
196
|
* @default false
|
176
197
|
* @since 5.0.0
|
177
|
-
* @product highcharts highstock highmaps
|
178
198
|
* @apioption accessibility.describeSingleSeries
|
179
199
|
*/
|
180
200
|
|
@@ -196,8 +216,8 @@
|
|
196
216
|
*
|
197
217
|
* Defaults to the same format as in tooltip.
|
198
218
|
*
|
199
|
-
* For an overview of the replacement codes, see
|
200
|
-
* #Highcharts.dateFormat).
|
219
|
+
* For an overview of the replacement codes, see
|
220
|
+
* [dateFormat](#Highcharts.dateFormat).
|
201
221
|
*
|
202
222
|
* @type {String}
|
203
223
|
* @see [pointDateFormatter](#accessibility.pointDateFormatter)
|
@@ -206,11 +226,11 @@
|
|
206
226
|
*/
|
207
227
|
|
208
228
|
/**
|
209
|
-
* Formatter function to determine the date/time format used with
|
210
|
-
* on datetime axes when describing them to screen reader users.
|
211
|
-
* one argument, `point`, referring to the point to describe.
|
212
|
-
* return a date format string compatible with
|
213
|
-
* dateFormat).
|
229
|
+
* Formatter function to determine the date/time format used with
|
230
|
+
* points on datetime axes when describing them to screen reader users.
|
231
|
+
* Receives one argument, `point`, referring to the point to describe.
|
232
|
+
* Should return a date format string compatible with
|
233
|
+
* [dateFormat](#Highcharts.dateFormat).
|
214
234
|
*
|
215
235
|
* @type {Function}
|
216
236
|
* @see [pointDateFormat](#accessibility.pointDateFormat)
|
@@ -219,13 +239,14 @@
|
|
219
239
|
*/
|
220
240
|
|
221
241
|
/**
|
222
|
-
* Formatter function to use instead of the default for point
|
242
|
+
* Formatter function to use instead of the default for point
|
243
|
+
* descriptions.
|
223
244
|
* Receives one argument, `point`, referring to the point to describe.
|
224
245
|
* Should return a String with the description of the point for a screen
|
225
246
|
* reader user.
|
226
247
|
*
|
227
248
|
* @type {Function}
|
228
|
-
* @see [point.description](#series
|
249
|
+
* @see [point.description](#series.line.data.description)
|
229
250
|
* @since 5.0.0
|
230
251
|
* @apioption accessibility.pointDescriptionFormatter
|
231
252
|
*/
|
@@ -236,8 +257,8 @@
|
|
236
257
|
* to the chart object. Should return a String with the HTML content
|
237
258
|
* of the region.
|
238
259
|
*
|
239
|
-
* The link to view the chart as a data table will be added
|
240
|
-
* after the custom HTML content.
|
260
|
+
* The link to view the chart as a data table will be added
|
261
|
+
* automatically after the custom HTML content.
|
241
262
|
*
|
242
263
|
* @type {Function}
|
243
264
|
* @default undefined
|
@@ -246,10 +267,10 @@
|
|
246
267
|
*/
|
247
268
|
|
248
269
|
/**
|
249
|
-
* Formatter function to use instead of the default for series
|
250
|
-
* Receives one argument, `series`, referring to the
|
251
|
-
* Should return a String with the description of
|
252
|
-
* screen reader user.
|
270
|
+
* Formatter function to use instead of the default for series
|
271
|
+
* descriptions. Receives one argument, `series`, referring to the
|
272
|
+
* series to describe. Should return a String with the description of
|
273
|
+
* the series for a screen reader user.
|
253
274
|
*
|
254
275
|
* @type {Function}
|
255
276
|
* @see [series.description](#plotOptions.series.description)
|
@@ -290,6 +311,24 @@
|
|
290
311
|
* @apioption chart.typeDescription
|
291
312
|
*/
|
292
313
|
|
314
|
+
/**
|
315
|
+
* Keyboard navigation for the legend. Requires the Accessibility module.
|
316
|
+
* @since 5.0.14
|
317
|
+
* @apioption legend.keyboardNavigation
|
318
|
+
*/
|
319
|
+
|
320
|
+
/**
|
321
|
+
* Enable/disable keyboard navigation for the legend. Requires the Accessibility
|
322
|
+
* module.
|
323
|
+
*
|
324
|
+
* @type {Boolean}
|
325
|
+
* @see [accessibility.keyboardNavigation](#accessibility.keyboardNavigation.
|
326
|
+
* enabled)
|
327
|
+
* @default true
|
328
|
+
* @since 5.0.13
|
329
|
+
* @apioption legend.keyboardNavigation.enabled
|
330
|
+
*/
|
331
|
+
|
293
332
|
/**
|
294
333
|
* HTML encode some characters vulnerable for XSS.
|
295
334
|
* @param {string} html The input string
|
@@ -323,6 +362,43 @@
|
|
323
362
|
}
|
324
363
|
}
|
325
364
|
|
365
|
+
// Determine if a point should be skipped
|
366
|
+
function isSkipPoint(point) {
|
367
|
+
return point.isNull &&
|
368
|
+
point.series.chart.options.accessibility
|
369
|
+
.keyboardNavigation.skipNullPoints ||
|
370
|
+
point.series.options.skipKeyboardNavigation ||
|
371
|
+
!point.series.visible;
|
372
|
+
}
|
373
|
+
|
374
|
+
// Get the point in a series that is closest to a reference point
|
375
|
+
// Optionally supply weight factors for x and y directions
|
376
|
+
function getClosestPoint(point, series, xWeight, yWeight) {
|
377
|
+
var minDistance = Infinity,
|
378
|
+
dPoint,
|
379
|
+
minIx,
|
380
|
+
distance,
|
381
|
+
i = series.points.length;
|
382
|
+
if (point.plotX === undefined || point.plotY === undefined) {
|
383
|
+
return;
|
384
|
+
}
|
385
|
+
while (i--) {
|
386
|
+
dPoint = series.points[i];
|
387
|
+
if (dPoint.plotX === undefined || dPoint.plotY === undefined) {
|
388
|
+
return;
|
389
|
+
}
|
390
|
+
distance = (point.plotX - dPoint.plotX) *
|
391
|
+
(point.plotX - dPoint.plotX) * (xWeight || 1) +
|
392
|
+
(point.plotY - dPoint.plotY) *
|
393
|
+
(point.plotY - dPoint.plotY) * (yWeight || 1);
|
394
|
+
if (distance < minDistance) {
|
395
|
+
minDistance = distance;
|
396
|
+
minIx = i;
|
397
|
+
}
|
398
|
+
}
|
399
|
+
return series.points[minIx || 0];
|
400
|
+
}
|
401
|
+
|
326
402
|
// Whenever drawing series, put info on DOM elements
|
327
403
|
H.wrap(H.Series.prototype, 'render', function(proceed) {
|
328
404
|
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
|
@@ -334,17 +410,35 @@
|
|
334
410
|
// Put accessible info on series and points of a series
|
335
411
|
H.Series.prototype.setA11yDescription = function() {
|
336
412
|
var a11yOptions = this.chart.options.accessibility,
|
337
|
-
firstPointEl =
|
338
|
-
|
413
|
+
firstPointEl = (
|
414
|
+
this.points &&
|
415
|
+
this.points.length &&
|
416
|
+
this.points[0].graphic &&
|
417
|
+
this.points[0].graphic.element
|
418
|
+
),
|
419
|
+
seriesEl = (
|
420
|
+
firstPointEl &&
|
421
|
+
firstPointEl.parentNode || this.graph &&
|
422
|
+
this.graph.element || this.group &&
|
423
|
+
this.group.element
|
424
|
+
); // Could be tracker series depending on series type
|
339
425
|
|
340
426
|
if (seriesEl) {
|
341
|
-
// For some series types the order of elements do not match the order of
|
342
|
-
// In that case we have to reverse them in order for
|
427
|
+
// For some series types the order of elements do not match the order of
|
428
|
+
// points in series. In that case we have to reverse them in order for
|
429
|
+
// AT to read them out in an understandable order
|
343
430
|
if (seriesEl.lastChild === firstPointEl) {
|
344
431
|
reverseChildNodes(seriesEl);
|
345
432
|
}
|
346
|
-
// Make individual point elements accessible if possible. Note: If
|
347
|
-
|
433
|
+
// Make individual point elements accessible if possible. Note: If
|
434
|
+
// markers are disabled there might not be any elements there to make
|
435
|
+
// accessible.
|
436
|
+
if (
|
437
|
+
this.points && (
|
438
|
+
this.points.length < a11yOptions.pointDescriptionThreshold ||
|
439
|
+
a11yOptions.pointDescriptionThreshold === false
|
440
|
+
)
|
441
|
+
) {
|
348
442
|
each(this.points, function(point) {
|
349
443
|
if (point.graphic) {
|
350
444
|
point.graphic.element.setAttribute('role', 'img');
|
@@ -365,23 +459,46 @@
|
|
365
459
|
this.options.exposeElementToA11y ? 'img' : 'region'
|
366
460
|
);
|
367
461
|
seriesEl.setAttribute('tabindex', '-1');
|
368
|
-
seriesEl.setAttribute(
|
369
|
-
|
462
|
+
seriesEl.setAttribute(
|
463
|
+
'aria-label',
|
464
|
+
a11yOptions.seriesDescriptionFormatter &&
|
465
|
+
a11yOptions.seriesDescriptionFormatter(this) ||
|
466
|
+
this.buildSeriesInfoString()
|
467
|
+
);
|
370
468
|
}
|
371
469
|
}
|
372
470
|
};
|
373
471
|
|
374
472
|
// Return string with information about series
|
375
473
|
H.Series.prototype.buildSeriesInfoString = function() {
|
376
|
-
var typeInfo =
|
474
|
+
var typeInfo = (
|
475
|
+
typeToSeriesMap[this.type] ||
|
476
|
+
typeToSeriesMap['default'] // eslint-disable-line dot-notation
|
477
|
+
),
|
377
478
|
description = this.description || this.options.description;
|
378
479
|
return (this.name ? this.name + ', ' : '') +
|
379
|
-
(this.chart.types.length === 1 ? typeInfo[0] : 'series') +
|
380
|
-
|
381
|
-
(
|
480
|
+
(this.chart.types.length === 1 ? typeInfo[0] : 'series') +
|
481
|
+
' ' + (this.index + 1) + ' of ' + (this.chart.series.length) +
|
482
|
+
(
|
483
|
+
this.chart.types.length === 1 ?
|
484
|
+
' with ' :
|
485
|
+
'. ' + typeInfo[0] + ' with '
|
486
|
+
) +
|
487
|
+
(
|
488
|
+
this.points.length + ' ' +
|
489
|
+
(this.points.length === 1 ? typeInfo[1] : typeInfo[2])
|
490
|
+
) +
|
382
491
|
(description ? '. ' + description : '') +
|
383
|
-
(
|
384
|
-
|
492
|
+
(
|
493
|
+
this.chart.yAxis.length > 1 && this.yAxis ?
|
494
|
+
'. Y axis, ' + this.yAxis.getDescription() :
|
495
|
+
''
|
496
|
+
) +
|
497
|
+
(
|
498
|
+
this.chart.xAxis.length > 1 && this.xAxis ?
|
499
|
+
'. X axis, ' + this.xAxis.getDescription() :
|
500
|
+
''
|
501
|
+
);
|
385
502
|
};
|
386
503
|
|
387
504
|
// Return string with information about point
|
@@ -391,38 +508,66 @@
|
|
391
508
|
a11yOptions = series.chart.options.accessibility,
|
392
509
|
infoString = '',
|
393
510
|
dateTimePoint = series.xAxis && series.xAxis.isDatetimeAxis,
|
394
|
-
timeDesc =
|
395
|
-
|
511
|
+
timeDesc =
|
512
|
+
dateTimePoint &&
|
513
|
+
dateFormat(
|
514
|
+
a11yOptions.pointDateFormatter &&
|
515
|
+
a11yOptions.pointDateFormatter(point) ||
|
516
|
+
a11yOptions.pointDateFormat ||
|
517
|
+
H.Tooltip.prototype.getXDateFormat(
|
518
|
+
point,
|
519
|
+
series.chart.options.tooltip,
|
520
|
+
series.xAxis
|
521
|
+
),
|
522
|
+
point.x
|
523
|
+
),
|
396
524
|
hasSpecialKey = H.find(series.specialKeys, function(key) {
|
397
525
|
return point[key] !== undefined;
|
398
526
|
});
|
399
527
|
|
400
|
-
// If the point has one of the less common properties defined, display all
|
528
|
+
// If the point has one of the less common properties defined, display all
|
529
|
+
// that are defined
|
401
530
|
if (hasSpecialKey) {
|
402
531
|
if (dateTimePoint) {
|
403
532
|
infoString = timeDesc;
|
404
533
|
}
|
405
534
|
each(series.commonKeys.concat(series.specialKeys), function(key) {
|
406
535
|
if (point[key] !== undefined && !(dateTimePoint && key === 'x')) {
|
407
|
-
infoString += (infoString ? '. ' : '') +
|
536
|
+
infoString += (infoString ? '. ' : '') +
|
537
|
+
key + ', ' +
|
538
|
+
point[key];
|
408
539
|
}
|
409
540
|
});
|
410
541
|
} else {
|
411
542
|
// Pick and choose properties for a succint label
|
412
|
-
infoString =
|
543
|
+
infoString =
|
544
|
+
(
|
545
|
+
this.name ||
|
546
|
+
timeDesc ||
|
547
|
+
this.category ||
|
548
|
+
this.id ||
|
549
|
+
'x, ' + this.x
|
550
|
+
) + ', ' +
|
413
551
|
(this.value !== undefined ? this.value : this.y);
|
414
552
|
}
|
415
553
|
|
416
|
-
return (this.index + 1) + '. ' + infoString + '.' +
|
554
|
+
return (this.index + 1) + '. ' + infoString + '.' +
|
555
|
+
(this.description ? ' ' + this.description : '');
|
417
556
|
};
|
418
557
|
|
419
558
|
// Get descriptive label for axis
|
420
559
|
H.Axis.prototype.getDescription = function() {
|
421
|
-
return
|
422
|
-
this.
|
560
|
+
return (
|
561
|
+
this.userOptions && this.userOptions.description ||
|
562
|
+
this.axisTitle && this.axisTitle.textStr ||
|
563
|
+
this.options.id ||
|
564
|
+
this.categories && 'categories' ||
|
565
|
+
'values'
|
566
|
+
);
|
423
567
|
};
|
424
568
|
|
425
|
-
// Pan along axis in a direction (1 or -1), optionally with a defined
|
569
|
+
// Pan along axis in a direction (1 or -1), optionally with a defined
|
570
|
+
// granularity (number of steps it takes to walk across current view)
|
426
571
|
H.Axis.prototype.panStep = function(direction, granularity) {
|
427
572
|
var gran = granularity || 3,
|
428
573
|
extremes = this.getExtremes(),
|
@@ -456,9 +601,13 @@
|
|
456
601
|
var removedSeries = this,
|
457
602
|
hasType = false;
|
458
603
|
|
459
|
-
// Check if any of the other series have the same type as this one.
|
604
|
+
// Check if any of the other series have the same type as this one.
|
605
|
+
// Otherwise remove it from the list.
|
460
606
|
each(chart.series, function(s) {
|
461
|
-
if (
|
607
|
+
if (
|
608
|
+
s !== removedSeries &&
|
609
|
+
chart.types.indexOf(removedSeries.type) < 0
|
610
|
+
) {
|
462
611
|
hasType = true;
|
463
612
|
}
|
464
613
|
});
|
@@ -469,7 +618,8 @@
|
|
469
618
|
}
|
470
619
|
});
|
471
620
|
|
472
|
-
// Return simplified description of chart type. Some types will not be familiar
|
621
|
+
// Return simplified description of chart type. Some types will not be familiar
|
622
|
+
// to most screen reader users, but we try.
|
473
623
|
H.Chart.prototype.getTypeDescription = function() {
|
474
624
|
var firstType = this.types && this.types[0],
|
475
625
|
mapTitle = this.series[0] && this.series[0].mapTitle;
|
@@ -493,7 +643,8 @@
|
|
493
643
|
i;
|
494
644
|
|
495
645
|
if (numXAxes) {
|
496
|
-
desc.xAxis = 'The chart has ' + numXAxes +
|
646
|
+
desc.xAxis = 'The chart has ' + numXAxes +
|
647
|
+
(numXAxes > 1 ? ' X axes' : ' X axis') + ' displaying ';
|
497
648
|
if (numXAxes < 2) {
|
498
649
|
desc.xAxis += this.xAxis[0].getDescription() + '.';
|
499
650
|
} else {
|
@@ -505,7 +656,8 @@
|
|
505
656
|
}
|
506
657
|
|
507
658
|
if (numYAxes) {
|
508
|
-
desc.yAxis = 'The chart has ' + numYAxes +
|
659
|
+
desc.yAxis = 'The chart has ' + numYAxes +
|
660
|
+
(numYAxes > 1 ? ' Y axes' : ' Y axis') + ' displaying ';
|
509
661
|
if (numYAxes < 2) {
|
510
662
|
desc.yAxis += this.yAxis[0].getDescription() + '.';
|
511
663
|
} else {
|
@@ -538,7 +690,8 @@
|
|
538
690
|
}
|
539
691
|
};
|
540
692
|
|
541
|
-
// Highlight a point (show tooltip and display hover state). Returns the
|
693
|
+
// Highlight a point (show tooltip and display hover state). Returns the
|
694
|
+
// highlighted point.
|
542
695
|
H.Point.prototype.highlight = function() {
|
543
696
|
var chart = this.series.chart;
|
544
697
|
if (this.graphic && this.graphic.element.focus) {
|
@@ -574,12 +727,6 @@
|
|
574
727
|
lastSeries.points[lastSeries.points.length - 1],
|
575
728
|
newSeries,
|
576
729
|
newPoint,
|
577
|
-
isSkipPoint = function(point) {
|
578
|
-
return point.isNull &&
|
579
|
-
chart.options.accessibility.keyboardNavigation.skipNullPoints ||
|
580
|
-
point.series.options.skipKeyboardNavigation ||
|
581
|
-
!point.series.visible;
|
582
|
-
},
|
583
730
|
// Handle connecting ends - where the points array has an extra last
|
584
731
|
// point that is a reference to the first one. We skip this.
|
585
732
|
forwardSkipAmount = curPoint && curPoint.series.connectEnds &&
|
@@ -632,6 +779,123 @@
|
|
632
779
|
return newPoint.highlight();
|
633
780
|
};
|
634
781
|
|
782
|
+
// Highlight first valid point in a series. Returns the point if successfully
|
783
|
+
// highlighted, otherwise false. If there is a highlighted point in the series,
|
784
|
+
// use that as starting point.
|
785
|
+
H.Series.prototype.highlightFirstValidPoint = function() {
|
786
|
+
var curPoint = this.chart.highlightedPoint,
|
787
|
+
start = curPoint.series === this ? curPoint.index : 0,
|
788
|
+
points = this.points;
|
789
|
+
|
790
|
+
for (var i = start, len = points.length; i < len; ++i) {
|
791
|
+
if (!isSkipPoint(points[i])) {
|
792
|
+
return points[i].highlight();
|
793
|
+
}
|
794
|
+
}
|
795
|
+
for (var j = start; j >= 0; --j) {
|
796
|
+
if (!isSkipPoint(points[j])) {
|
797
|
+
return points[j].highlight();
|
798
|
+
}
|
799
|
+
}
|
800
|
+
return false;
|
801
|
+
};
|
802
|
+
|
803
|
+
// Highlight next/previous series in chart. Returns false if no adjacent series
|
804
|
+
// in the direction, otherwise returns new highlighted point.
|
805
|
+
H.Chart.prototype.highlightAdjacentSeries = function(down) {
|
806
|
+
var chart = this,
|
807
|
+
newSeries,
|
808
|
+
newPoint,
|
809
|
+
adjacentNewPoint,
|
810
|
+
curPoint = chart.highlightedPoint,
|
811
|
+
lastSeries = chart.series && chart.series[chart.series.length - 1],
|
812
|
+
lastPoint = lastSeries && lastSeries.points &&
|
813
|
+
lastSeries.points[lastSeries.points.length - 1];
|
814
|
+
|
815
|
+
// If no point is highlighted, highlight the first/last point
|
816
|
+
if (!chart.highlightedPoint) {
|
817
|
+
newSeries = down ? (chart.series && chart.series[0]) : lastSeries;
|
818
|
+
newPoint = down ?
|
819
|
+
(newSeries && newSeries.points && newSeries.points[0]) : lastPoint;
|
820
|
+
return newPoint ? newPoint.highlight() : false;
|
821
|
+
}
|
822
|
+
|
823
|
+
newSeries = chart.series[curPoint.series.index + (down ? -1 : 1)];
|
824
|
+
|
825
|
+
if (!newSeries) {
|
826
|
+
return false;
|
827
|
+
}
|
828
|
+
|
829
|
+
// We have a new series in this direction, find the right point
|
830
|
+
// Weigh xDistance as counting much higher than Y distance
|
831
|
+
newPoint = getClosestPoint(curPoint, newSeries, 4);
|
832
|
+
|
833
|
+
if (!newPoint) {
|
834
|
+
return false;
|
835
|
+
}
|
836
|
+
|
837
|
+
// New series and point exists, but we might want to skip it
|
838
|
+
if (!newSeries.visible) {
|
839
|
+
// Skip the series
|
840
|
+
newPoint.highlight();
|
841
|
+
adjacentNewPoint = chart.highlightAdjacentSeries(down); // Try recurse
|
842
|
+
if (!adjacentNewPoint) {
|
843
|
+
// Recurse failed
|
844
|
+
curPoint.highlight();
|
845
|
+
return false;
|
846
|
+
}
|
847
|
+
// Recurse succeeded
|
848
|
+
return adjacentNewPoint;
|
849
|
+
}
|
850
|
+
|
851
|
+
// Highlight the new point or any first valid point back or forwards from it
|
852
|
+
newPoint.highlight();
|
853
|
+
return newPoint.series.highlightFirstValidPoint();
|
854
|
+
};
|
855
|
+
|
856
|
+
// Highlight the closest point vertically
|
857
|
+
H.Chart.prototype.highlightAdjacentPointVertical = function(down) {
|
858
|
+
var curPoint = this.highlightedPoint,
|
859
|
+
minDistance = Infinity,
|
860
|
+
bestPoint;
|
861
|
+
|
862
|
+
if (curPoint.plotX === undefined || curPoint.plotY === undefined) {
|
863
|
+
return false;
|
864
|
+
}
|
865
|
+
each(this.series, function(series) {
|
866
|
+
each(series.points, function(point) {
|
867
|
+
if (point.plotY === undefined || point.plotX === undefined ||
|
868
|
+
point === curPoint) {
|
869
|
+
return;
|
870
|
+
}
|
871
|
+
var yDistance = point.plotY - curPoint.plotY,
|
872
|
+
width = Math.abs(point.plotX - curPoint.plotX),
|
873
|
+
distance = Math.abs(yDistance) * Math.abs(yDistance) +
|
874
|
+
width * width * 4; // Weigh horizontal distance highly
|
875
|
+
|
876
|
+
// Reverse distance number if axis is reversed
|
877
|
+
if (series.yAxis.reversed) {
|
878
|
+
yDistance *= -1;
|
879
|
+
}
|
880
|
+
|
881
|
+
if (
|
882
|
+
yDistance < 0 && down || yDistance > 0 && !down || // Wrong dir
|
883
|
+
distance < 5 || // Points in same spot => infinite loop
|
884
|
+
isSkipPoint(point)
|
885
|
+
) {
|
886
|
+
return;
|
887
|
+
}
|
888
|
+
|
889
|
+
if (distance < minDistance) {
|
890
|
+
minDistance = distance;
|
891
|
+
bestPoint = point;
|
892
|
+
}
|
893
|
+
});
|
894
|
+
});
|
895
|
+
|
896
|
+
return bestPoint ? bestPoint.highlight() : false;
|
897
|
+
};
|
898
|
+
|
635
899
|
// Show the export menu and focus the first item (if exists)
|
636
900
|
H.Chart.prototype.showExportMenu = function() {
|
637
901
|
if (this.exportSVGElements && this.exportSVGElements[0]) {
|
@@ -643,9 +907,15 @@
|
|
643
907
|
// Highlight export menu item by index
|
644
908
|
H.Chart.prototype.highlightExportItem = function(ix) {
|
645
909
|
var listItem = this.exportDivElements && this.exportDivElements[ix],
|
646
|
-
curHighlighted =
|
910
|
+
curHighlighted =
|
911
|
+
this.exportDivElements &&
|
912
|
+
this.exportDivElements[this.highlightedExportItem];
|
647
913
|
|
648
|
-
if (
|
914
|
+
if (
|
915
|
+
listItem &&
|
916
|
+
listItem.tagName === 'DIV' &&
|
917
|
+
!(listItem.children && listItem.children.length)
|
918
|
+
) {
|
649
919
|
if (listItem.focus) {
|
650
920
|
listItem.focus();
|
651
921
|
}
|
@@ -665,7 +935,9 @@
|
|
665
935
|
var buttons = this.rangeSelector.buttons;
|
666
936
|
// Deselect old
|
667
937
|
if (buttons[this.highlightedRangeSelectorItemIx]) {
|
668
|
-
buttons[this.highlightedRangeSelectorItemIx].setState(
|
938
|
+
buttons[this.highlightedRangeSelectorItemIx].setState(
|
939
|
+
this.oldRangeSelectorItemState || 0
|
940
|
+
);
|
669
941
|
}
|
670
942
|
// Select new
|
671
943
|
this.highlightedRangeSelectorItemIx = ix;
|
@@ -684,7 +956,10 @@
|
|
684
956
|
H.Chart.prototype.highlightLegendItem = function(ix) {
|
685
957
|
var items = this.legend.allItems;
|
686
958
|
if (items[this.highlightedLegendItemIx]) {
|
687
|
-
fireEvent(
|
959
|
+
fireEvent(
|
960
|
+
items[this.highlightedLegendItemIx].legendGroup.element,
|
961
|
+
'mouseout'
|
962
|
+
);
|
688
963
|
}
|
689
964
|
this.highlightedLegendItemIx = ix;
|
690
965
|
if (items[ix]) {
|
@@ -704,7 +979,10 @@
|
|
704
979
|
each(exportList, function(el) {
|
705
980
|
fireEvent(el, 'mouseleave');
|
706
981
|
});
|
707
|
-
if (
|
982
|
+
if (
|
983
|
+
exportList[this.highlightedExportItem] &&
|
984
|
+
exportList[this.highlightedExportItem].onmouseout
|
985
|
+
) {
|
708
986
|
exportList[this.highlightedExportItem].onmouseout();
|
709
987
|
}
|
710
988
|
this.highlightedExportItem = 0;
|
@@ -716,12 +994,13 @@
|
|
716
994
|
H.Chart.prototype.addKeyboardNavEvents = function() {
|
717
995
|
var chart = this;
|
718
996
|
|
719
|
-
// Abstraction layer for keyboard navigation. Keep a map of keyCodes to
|
720
|
-
//
|
721
|
-
//
|
722
|
-
//
|
723
|
-
//
|
724
|
-
//
|
997
|
+
// Abstraction layer for keyboard navigation. Keep a map of keyCodes to
|
998
|
+
// handler functions, and a next/prev move handler for tab order. The
|
999
|
+
// module's keyCode handlers determine when to move to another module.
|
1000
|
+
// Validate holds a function to determine if there are prerequisites for
|
1001
|
+
// this module to run that are not met. Init holds a function to run once
|
1002
|
+
// before any keyCodes are interpreted. Terminate holds a function to run
|
1003
|
+
// once before moving to next/prev module.
|
725
1004
|
function KeyboardNavigationModule(options) {
|
726
1005
|
this.id = options.id;
|
727
1006
|
this.keyCodeMap = options.keyCodeMap;
|
@@ -729,36 +1008,47 @@
|
|
729
1008
|
this.validate = options.validate;
|
730
1009
|
this.init = options.init;
|
731
1010
|
this.terminate = options.terminate;
|
732
|
-
this.transformTabs = options.transformTabs !== false;
|
733
1011
|
}
|
734
1012
|
KeyboardNavigationModule.prototype = {
|
735
1013
|
// Find handler function(s) for key code in the keyCodeMap and run it.
|
736
1014
|
run: function(e) {
|
737
1015
|
var navModule = this,
|
738
1016
|
keyCode = e.which || e.keyCode,
|
1017
|
+
found = false,
|
739
1018
|
handled = false;
|
740
|
-
keyCode = this.transformTabs && keyCode === 9 ? (e.shiftKey ? 37 : 39) : keyCode; // Transform tabs
|
741
1019
|
each(this.keyCodeMap, function(codeSet) {
|
742
1020
|
if (codeSet[0].indexOf(keyCode) > -1) {
|
743
|
-
|
1021
|
+
found = true;
|
1022
|
+
handled = codeSet[1].call(navModule, keyCode, e) === false ?
|
1023
|
+
// If explicitly returning false, we haven't handled it
|
1024
|
+
false :
|
1025
|
+
true;
|
744
1026
|
}
|
745
1027
|
});
|
1028
|
+
// Default tab handler, move to next/prev module
|
1029
|
+
if (!found && keyCode === 9) {
|
1030
|
+
handled = this.move(e.shiftKey ? -1 : 1);
|
1031
|
+
}
|
746
1032
|
return handled;
|
747
1033
|
}
|
748
1034
|
};
|
749
|
-
// Maintain abstraction between KeyboardNavigationModule and Highcharts
|
750
|
-
// The chart object keeps track of a list of KeyboardNavigationModules that
|
1035
|
+
// Maintain abstraction between KeyboardNavigationModule and Highcharts.
|
1036
|
+
// The chart object keeps track of a list of KeyboardNavigationModules that
|
1037
|
+
// we move through.
|
751
1038
|
function navModuleFactory(id, keyMap, options) {
|
752
1039
|
return new KeyboardNavigationModule(merge({
|
753
1040
|
keyCodeMap: keyMap,
|
754
|
-
// Move to next/prev valid module, or undefined if none, and init
|
755
|
-
// Returns true on success and false if there is no valid module
|
1041
|
+
// Move to next/prev valid module, or undefined if none, and init
|
1042
|
+
// it. Returns true on success and false if there is no valid module
|
1043
|
+
// to move to.
|
756
1044
|
move: function(direction) {
|
757
1045
|
if (this.terminate) {
|
758
1046
|
this.terminate(direction);
|
759
1047
|
}
|
760
1048
|
chart.keyboardNavigationModuleIndex += direction;
|
761
|
-
var newModule = chart.keyboardNavigationModules[
|
1049
|
+
var newModule = chart.keyboardNavigationModules[
|
1050
|
+
chart.keyboardNavigationModuleIndex
|
1051
|
+
];
|
762
1052
|
if (newModule) {
|
763
1053
|
if (newModule.validate && !newModule.validate()) {
|
764
1054
|
return this.move(direction); // Invalid module
|
@@ -770,7 +1060,14 @@
|
|
770
1060
|
}
|
771
1061
|
// No module
|
772
1062
|
chart.keyboardNavigationModuleIndex = 0; // Reset counter
|
773
|
-
|
1063
|
+
|
1064
|
+
// Set focus to chart or exit anchor depending on direction
|
1065
|
+
if (direction > 0) {
|
1066
|
+
chart.tabExitAnchor.focus();
|
1067
|
+
} else {
|
1068
|
+
chart.renderTo.focus();
|
1069
|
+
}
|
1070
|
+
|
774
1071
|
return false;
|
775
1072
|
}
|
776
1073
|
}, {
|
@@ -781,56 +1078,49 @@
|
|
781
1078
|
// Route keydown events
|
782
1079
|
function keydownHandler(ev) {
|
783
1080
|
var e = ev || win.event,
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
// Handle tabbing
|
788
|
-
if (keyCode === 9) {
|
789
|
-
// If we reached end of chart, we need to let this tab slip through to allow users to tab further
|
790
|
-
if (chart.slipNextTab) {
|
791
|
-
chart.slipNextTab = false;
|
792
|
-
return;
|
793
|
-
}
|
794
|
-
}
|
795
|
-
// If key was not tab, don't slip the next tab
|
796
|
-
chart.slipNextTab = false;
|
1081
|
+
curNavModule = chart.keyboardNavigationModules[
|
1082
|
+
chart.keyboardNavigationModuleIndex
|
1083
|
+
];
|
797
1084
|
|
798
|
-
// If there is a navigation module for the current index, run it.
|
1085
|
+
// If there is a navigation module for the current index, run it.
|
1086
|
+
// Otherwise, we are outside of the chart in some direction.
|
799
1087
|
if (curNavModule) {
|
800
1088
|
if (curNavModule.run(e)) {
|
801
|
-
|
1089
|
+
// Successfully handled this key event, stop default handling
|
1090
|
+
e.preventDefault();
|
802
1091
|
}
|
803
1092
|
}
|
804
1093
|
}
|
805
1094
|
|
806
|
-
// List of the different keyboard handling modes we use depending on where
|
807
|
-
// Each mode has a set of handling functions mapped to
|
808
|
-
// Each mode determines when to move to the next/prev mode.
|
1095
|
+
// List of the different keyboard handling modes we use depending on where
|
1096
|
+
// we are in the chart. Each mode has a set of handling functions mapped to
|
1097
|
+
// key codes. Each mode determines when to move to the next/prev mode.
|
809
1098
|
chart.keyboardNavigationModules = [
|
1099
|
+
// Entry point catching the first tab, allowing users to tab into points
|
1100
|
+
// more intuitively.
|
1101
|
+
navModuleFactory('entry', []),
|
1102
|
+
|
810
1103
|
// Points
|
1104
|
+
// Prevents default and ignores failure regardless
|
811
1105
|
navModuleFactory('points', [
|
812
1106
|
// Left/Right
|
813
1107
|
[
|
814
1108
|
[37, 39],
|
815
1109
|
function(keyCode) {
|
816
|
-
|
817
|
-
|
818
|
-
}
|
1110
|
+
chart.highlightAdjacentPoint(keyCode === 39);
|
1111
|
+
return true;
|
819
1112
|
}
|
820
1113
|
],
|
821
1114
|
// Up/Down
|
822
1115
|
[
|
823
1116
|
[38, 40],
|
824
1117
|
function(keyCode) {
|
825
|
-
var
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
return this.move(keyCode === 40 ? 1 : -1); // Otherwise, attempt to move to next/prev module
|
832
|
-
}
|
833
|
-
}
|
1118
|
+
var highlightMethod = chart.highlightedPoint &&
|
1119
|
+
chart.highlightedPoint.series.keyboardMoveVertical ?
|
1120
|
+
'highlightAdjacentPointVertical' :
|
1121
|
+
'highlightAdjacentSeries';
|
1122
|
+
chart[highlightMethod](keyCode !== 38);
|
1123
|
+
return true;
|
834
1124
|
}
|
835
1125
|
],
|
836
1126
|
// Enter/Spacebar
|
@@ -846,6 +1136,15 @@
|
|
846
1136
|
// Always start highlighting from scratch when entering this module
|
847
1137
|
init: function() {
|
848
1138
|
delete chart.highlightedPoint;
|
1139
|
+
// Find first valid point to highlight
|
1140
|
+
for (var i = 0; i < chart.series.length; ++i) {
|
1141
|
+
for (var j = 0, len = chart.series[i].points &&
|
1142
|
+
chart.series[i].points.length; j < len; ++j) {
|
1143
|
+
if (!isSkipPoint(chart.series[i].points[j])) {
|
1144
|
+
return chart.series[i].points[j].highlight();
|
1145
|
+
}
|
1146
|
+
}
|
1147
|
+
}
|
849
1148
|
},
|
850
1149
|
// If leaving points, don't show tooltip anymore
|
851
1150
|
terminate: function() {
|
@@ -853,9 +1152,7 @@
|
|
853
1152
|
chart.tooltip.hide(0);
|
854
1153
|
}
|
855
1154
|
delete chart.highlightedPoint;
|
856
|
-
}
|
857
|
-
|
858
|
-
transformTabs: chart.options.accessibility.keyboardNavigation.tabThroughPoints
|
1155
|
+
}
|
859
1156
|
}),
|
860
1157
|
|
861
1158
|
// Exporting
|
@@ -866,7 +1163,8 @@
|
|
866
1163
|
function() {
|
867
1164
|
var i = chart.highlightedExportItem || 0,
|
868
1165
|
reachedEnd = true;
|
869
|
-
// Try to highlight prev item in list. Highlighting e.g.
|
1166
|
+
// Try to highlight prev item in list. Highlighting e.g.
|
1167
|
+
// separators will fail.
|
870
1168
|
while (i--) {
|
871
1169
|
if (chart.highlightExportItem(i)) {
|
872
1170
|
reachedEnd = false;
|
@@ -885,8 +1183,12 @@
|
|
885
1183
|
function() {
|
886
1184
|
var highlightedExportItem = chart.highlightedExportItem || 0,
|
887
1185
|
reachedEnd = true;
|
888
|
-
// Try to highlight next item in list. Highlighting e.g.
|
889
|
-
|
1186
|
+
// Try to highlight next item in list. Highlighting e.g.
|
1187
|
+
// separators will fail.
|
1188
|
+
for (
|
1189
|
+
var i = highlightedExportItem + 1; i < chart.exportDivElements.length;
|
1190
|
+
++i
|
1191
|
+
) {
|
890
1192
|
if (chart.highlightExportItem(i)) {
|
891
1193
|
reachedEnd = false;
|
892
1194
|
break;
|
@@ -902,19 +1204,29 @@
|
|
902
1204
|
[
|
903
1205
|
[13, 32],
|
904
1206
|
function() {
|
905
|
-
fakeClickEvent(
|
1207
|
+
fakeClickEvent(
|
1208
|
+
chart.exportDivElements[chart.highlightedExportItem]
|
1209
|
+
);
|
906
1210
|
}
|
907
1211
|
]
|
908
1212
|
], {
|
909
|
-
// Only run exporting navigation if exporting support exists and is
|
1213
|
+
// Only run exporting navigation if exporting support exists and is
|
1214
|
+
// enabled on chart
|
910
1215
|
validate: function() {
|
911
|
-
return
|
1216
|
+
return (
|
1217
|
+
chart.exportChart &&
|
1218
|
+
!(
|
1219
|
+
chart.options.exporting &&
|
1220
|
+
chart.options.exporting.enabled === false
|
1221
|
+
)
|
1222
|
+
);
|
912
1223
|
},
|
913
1224
|
// Show export menu
|
914
1225
|
init: function(direction) {
|
915
1226
|
chart.highlightedPoint = null;
|
916
1227
|
chart.showExportMenu();
|
917
|
-
// If coming back to export menu from other module, try to
|
1228
|
+
// If coming back to export menu from other module, try to
|
1229
|
+
// highlight last item in menu
|
918
1230
|
if (direction < 0 && chart.exportDivElements) {
|
919
1231
|
for (var i = chart.exportDivElements.length; i > -1; --i) {
|
920
1232
|
if (chart.highlightExportItem(i)) {
|
@@ -922,6 +1234,10 @@
|
|
922
1234
|
}
|
923
1235
|
}
|
924
1236
|
}
|
1237
|
+
},
|
1238
|
+
// Hide the menu
|
1239
|
+
terminate: function() {
|
1240
|
+
chart.hideExportMenu();
|
925
1241
|
}
|
926
1242
|
}),
|
927
1243
|
|
@@ -931,7 +1247,8 @@
|
|
931
1247
|
[
|
932
1248
|
[38, 40, 37, 39],
|
933
1249
|
function(keyCode) {
|
934
|
-
chart[keyCode === 38 || keyCode === 40 ? 'yAxis' : 'xAxis'][0]
|
1250
|
+
chart[keyCode === 38 || keyCode === 40 ? 'yAxis' : 'xAxis'][0]
|
1251
|
+
.panStep(keyCode < 39 ? -1 : 1);
|
935
1252
|
}
|
936
1253
|
],
|
937
1254
|
|
@@ -940,10 +1257,15 @@
|
|
940
1257
|
[9],
|
941
1258
|
function(keyCode, e) {
|
942
1259
|
var button;
|
943
|
-
|
944
|
-
|
1260
|
+
// Deselect old
|
1261
|
+
chart.mapNavButtons[chart.focusedMapNavButtonIx].setState(0);
|
1262
|
+
if (
|
1263
|
+
e.shiftKey && !chart.focusedMapNavButtonIx ||
|
1264
|
+
!e.shiftKey && chart.focusedMapNavButtonIx
|
1265
|
+
) { // trying to go somewhere we can't?
|
945
1266
|
chart.mapZoom(); // Reset zoom
|
946
|
-
|
1267
|
+
// Nowhere to go, go to prev/next module
|
1268
|
+
return this.move(e.shiftKey ? -1 : 1);
|
947
1269
|
}
|
948
1270
|
chart.focusedMapNavButtonIx += e.shiftKey ? -1 : 1;
|
949
1271
|
button = chart.mapNavButtons[chart.focusedMapNavButtonIx];
|
@@ -958,18 +1280,21 @@
|
|
958
1280
|
[
|
959
1281
|
[13, 32],
|
960
1282
|
function() {
|
961
|
-
fakeClickEvent(
|
1283
|
+
fakeClickEvent(
|
1284
|
+
chart.mapNavButtons[chart.focusedMapNavButtonIx].element
|
1285
|
+
);
|
962
1286
|
}
|
963
1287
|
]
|
964
1288
|
], {
|
965
1289
|
// Only run this module if we have map zoom on the chart
|
966
1290
|
validate: function() {
|
967
|
-
return
|
1291
|
+
return (
|
1292
|
+
chart.mapZoom &&
|
1293
|
+
chart.mapNavButtons &&
|
1294
|
+
chart.mapNavButtons.length === 2
|
1295
|
+
);
|
968
1296
|
},
|
969
1297
|
|
970
|
-
// Handle tabs separately
|
971
|
-
transformTabs: false,
|
972
|
-
|
973
1298
|
// Make zoom buttons do their magic
|
974
1299
|
init: function(direction) {
|
975
1300
|
var zoomIn = chart.mapNavButtons[0],
|
@@ -979,7 +1304,10 @@
|
|
979
1304
|
each(chart.mapNavButtons, function(button, i) {
|
980
1305
|
button.element.setAttribute('tabindex', -1);
|
981
1306
|
button.element.setAttribute('role', 'button');
|
982
|
-
button.element.setAttribute(
|
1307
|
+
button.element.setAttribute(
|
1308
|
+
'aria-label',
|
1309
|
+
'Zoom ' + (i ? 'out' : '') + 'chart'
|
1310
|
+
);
|
983
1311
|
});
|
984
1312
|
|
985
1313
|
if (initialButton.element.focus) {
|
@@ -998,7 +1326,9 @@
|
|
998
1326
|
function(keyCode) {
|
999
1327
|
var direction = (keyCode === 37 || keyCode === 38) ? -1 : 1;
|
1000
1328
|
// Try to highlight next/prev button
|
1001
|
-
if (!chart.highlightRangeSelectorButton(
|
1329
|
+
if (!chart.highlightRangeSelectorButton(
|
1330
|
+
chart.highlightedRangeSelectorItemIx + direction
|
1331
|
+
)) {
|
1002
1332
|
return this.move(direction);
|
1003
1333
|
}
|
1004
1334
|
}
|
@@ -1007,15 +1337,24 @@
|
|
1007
1337
|
[
|
1008
1338
|
[13, 32],
|
1009
1339
|
function() {
|
1010
|
-
|
1011
|
-
|
1340
|
+
// Don't allow click if button used to be disabled
|
1341
|
+
if (chart.oldRangeSelectorItemState !== 3) {
|
1342
|
+
fakeClickEvent(
|
1343
|
+
chart.rangeSelector.buttons[
|
1344
|
+
chart.highlightedRangeSelectorItemIx
|
1345
|
+
].element
|
1346
|
+
);
|
1012
1347
|
}
|
1013
1348
|
}
|
1014
1349
|
]
|
1015
1350
|
], {
|
1016
1351
|
// Only run this module if we have range selector
|
1017
1352
|
validate: function() {
|
1018
|
-
return
|
1353
|
+
return (
|
1354
|
+
chart.rangeSelector &&
|
1355
|
+
chart.rangeSelector.buttons &&
|
1356
|
+
chart.rangeSelector.buttons.length
|
1357
|
+
);
|
1019
1358
|
},
|
1020
1359
|
|
1021
1360
|
// Make elements focusable and accessible
|
@@ -1023,10 +1362,15 @@
|
|
1023
1362
|
each(chart.rangeSelector.buttons, function(button) {
|
1024
1363
|
button.element.setAttribute('tabindex', '-1');
|
1025
1364
|
button.element.setAttribute('role', 'button');
|
1026
|
-
button.element.setAttribute(
|
1365
|
+
button.element.setAttribute(
|
1366
|
+
'aria-label',
|
1367
|
+
'Select range ' + (button.text && button.text.textStr)
|
1368
|
+
);
|
1027
1369
|
});
|
1028
1370
|
// Focus first/last button
|
1029
|
-
chart.highlightRangeSelectorButton(
|
1371
|
+
chart.highlightRangeSelectorButton(
|
1372
|
+
direction > 0 ? 0 : chart.rangeSelector.buttons.length - 1
|
1373
|
+
);
|
1030
1374
|
}
|
1031
1375
|
}),
|
1032
1376
|
|
@@ -1036,29 +1380,42 @@
|
|
1036
1380
|
[
|
1037
1381
|
[9, 38, 40],
|
1038
1382
|
function(keyCode, e) {
|
1039
|
-
var direction =
|
1040
|
-
|
1383
|
+
var direction =
|
1384
|
+
(keyCode === 9 && e.shiftKey || keyCode === 38) ? -1 : 1,
|
1385
|
+
|
1386
|
+
newIx = chart.highlightedInputRangeIx =
|
1387
|
+
chart.highlightedInputRangeIx + direction;
|
1388
|
+
|
1041
1389
|
// Try to highlight next/prev item in list.
|
1042
1390
|
if (newIx > 1 || newIx < 0) { // Out of range
|
1043
1391
|
return this.move(direction);
|
1044
1392
|
}
|
1045
|
-
chart.rangeSelector[newIx ? 'maxInput' : 'minInput'].focus();
|
1393
|
+
chart.rangeSelector[newIx ? 'maxInput' : 'minInput'].focus();
|
1046
1394
|
}
|
1047
1395
|
]
|
1048
1396
|
], {
|
1049
1397
|
// Only run if we have range selector with input boxes
|
1050
1398
|
validate: function() {
|
1051
|
-
var inputVisible =
|
1052
|
-
|
1399
|
+
var inputVisible = (
|
1400
|
+
chart.rangeSelector &&
|
1401
|
+
chart.rangeSelector.inputGroup &&
|
1402
|
+
chart.rangeSelector.inputGroup.element
|
1403
|
+
.getAttribute('visibility') !== 'hidden'
|
1404
|
+
);
|
1405
|
+
return (
|
1406
|
+
inputVisible &&
|
1407
|
+
chart.options.rangeSelector.inputEnabled !== false &&
|
1408
|
+
chart.rangeSelector.minInput &&
|
1409
|
+
chart.rangeSelector.maxInput
|
1410
|
+
);
|
1053
1411
|
},
|
1054
1412
|
|
1055
|
-
// Handle tabs different from left/right (because we don't want to catch left/right in a text area)
|
1056
|
-
transformTabs: false,
|
1057
|
-
|
1058
1413
|
// Highlight first/last input box
|
1059
1414
|
init: function(direction) {
|
1060
1415
|
chart.highlightedInputRangeIx = direction > 0 ? 0 : 1;
|
1061
|
-
chart.rangeSelector[
|
1416
|
+
chart.rangeSelector[
|
1417
|
+
chart.highlightedInputRangeIx ? 'maxInput' : 'minInput'
|
1418
|
+
].focus();
|
1062
1419
|
}
|
1063
1420
|
}),
|
1064
1421
|
|
@@ -1070,7 +1427,9 @@
|
|
1070
1427
|
function(keyCode) {
|
1071
1428
|
var direction = (keyCode === 37 || keyCode === 38) ? -1 : 1;
|
1072
1429
|
// Try to highlight next/prev legend item
|
1073
|
-
if (!chart.highlightLegendItem(
|
1430
|
+
if (!chart.highlightLegendItem(
|
1431
|
+
chart.highlightedLegendItemIx + direction
|
1432
|
+
)) {
|
1074
1433
|
return this.move(direction);
|
1075
1434
|
}
|
1076
1435
|
}
|
@@ -1079,12 +1438,16 @@
|
|
1079
1438
|
[
|
1080
1439
|
[13, 32],
|
1081
1440
|
function() {
|
1082
|
-
fakeClickEvent(
|
1441
|
+
fakeClickEvent(
|
1442
|
+
chart.legend.allItems[
|
1443
|
+
chart.highlightedLegendItemIx
|
1444
|
+
].legendItem.element.parentNode
|
1445
|
+
);
|
1083
1446
|
}
|
1084
1447
|
]
|
1085
1448
|
], {
|
1086
|
-
// Only run this module if we have at least one legend - wait for
|
1087
|
-
// Don't run if the legend is populated by a colorAxis.
|
1449
|
+
// Only run this module if we have at least one legend - wait for
|
1450
|
+
// it - item. Don't run if the legend is populated by a colorAxis.
|
1088
1451
|
// Don't run if legend navigation is disabled.
|
1089
1452
|
validate: function() {
|
1090
1453
|
return chart.legend && chart.legend.allItems &&
|
@@ -1099,15 +1462,22 @@
|
|
1099
1462
|
each(chart.legend.allItems, function(item) {
|
1100
1463
|
item.legendGroup.element.setAttribute('tabindex', '-1');
|
1101
1464
|
item.legendGroup.element.setAttribute('role', 'button');
|
1102
|
-
item.legendGroup.element.setAttribute(
|
1465
|
+
item.legendGroup.element.setAttribute(
|
1466
|
+
'aria-label',
|
1467
|
+
'Toggle visibility of series ' + item.name
|
1468
|
+
);
|
1103
1469
|
});
|
1104
1470
|
// Focus first/last item
|
1105
|
-
chart.highlightLegendItem(
|
1471
|
+
chart.highlightLegendItem(
|
1472
|
+
direction > 0 ? 0 : chart.legend.allItems.length - 1
|
1473
|
+
);
|
1106
1474
|
}
|
1107
1475
|
})
|
1108
1476
|
];
|
1109
1477
|
|
1110
|
-
// Init nav module index. We start at the first module, and as the user
|
1478
|
+
// Init nav module index. We start at the first module, and as the user
|
1479
|
+
// navigates through the chart the index will increase to use different
|
1480
|
+
// handler modules.
|
1111
1481
|
chart.keyboardNavigationModuleIndex = 0;
|
1112
1482
|
|
1113
1483
|
// Make chart reachable by tab
|
@@ -1118,6 +1488,17 @@
|
|
1118
1488
|
chart.container.setAttribute('tabindex', '0');
|
1119
1489
|
}
|
1120
1490
|
|
1491
|
+
// Add tab exit anchor
|
1492
|
+
// We use this to move focus out of chart whenever we want, by setting focus
|
1493
|
+
// to this and not preventing the default tab action.
|
1494
|
+
if (!chart.tabExitAnchor) {
|
1495
|
+
chart.tabExitAnchor = doc.createElement('div');
|
1496
|
+
// Not reachable by user
|
1497
|
+
chart.tabExitAnchor.setAttribute('tabindex', '-1');
|
1498
|
+
merge(true, chart.tabExitAnchor.style, hiddenStyle);
|
1499
|
+
chart.renderTo.appendChild(chart.tabExitAnchor);
|
1500
|
+
}
|
1501
|
+
|
1121
1502
|
// Handle keyboard events
|
1122
1503
|
addEvent(chart.renderTo, 'keydown', keydownHandler);
|
1123
1504
|
addEvent(chart, 'destroy', function() {
|
@@ -1126,7 +1507,8 @@
|
|
1126
1507
|
};
|
1127
1508
|
|
1128
1509
|
// Add screen reader region to chart.
|
1129
|
-
// tableId is the HTML id of the table to focus when clicking the table anchor
|
1510
|
+
// tableId is the HTML id of the table to focus when clicking the table anchor
|
1511
|
+
// in the screen reader region.
|
1130
1512
|
H.Chart.prototype.addScreenReaderRegion = function(id, tableId) {
|
1131
1513
|
var chart = this,
|
1132
1514
|
series = chart.series,
|
@@ -1136,32 +1518,53 @@
|
|
1136
1518
|
tableShortcut = doc.createElement('h4'),
|
1137
1519
|
tableShortcutAnchor = doc.createElement('a'),
|
1138
1520
|
chartHeading = doc.createElement('h4'),
|
1139
|
-
hiddenStyle = { // CSS style to hide element from visual users while still exposing it to screen readers
|
1140
|
-
position: 'absolute',
|
1141
|
-
left: '-9999px',
|
1142
|
-
top: 'auto',
|
1143
|
-
width: '1px',
|
1144
|
-
height: '1px',
|
1145
|
-
overflow: 'hidden'
|
1146
|
-
},
|
1147
1521
|
chartTypes = chart.types || [],
|
1148
|
-
// Build axis info - but not for pies and maps. Consider not adding for
|
1149
|
-
|
1150
|
-
|
1522
|
+
// Build axis info - but not for pies and maps. Consider not adding for
|
1523
|
+
// certain other types as well (funnel, pyramid?)
|
1524
|
+
axesDesc = (
|
1525
|
+
chartTypes.length === 1 && chartTypes[0] === 'pie' ||
|
1526
|
+
chartTypes[0] === 'map'
|
1527
|
+
) && {} || chart.getAxesDescription(),
|
1528
|
+
chartTypeInfo = series[0] && typeToSeriesMap[series[0].type] ||
|
1529
|
+
typeToSeriesMap['default']; // eslint-disable-line dot-notation
|
1151
1530
|
|
1152
1531
|
hiddenSection.setAttribute('id', id);
|
1153
1532
|
hiddenSection.setAttribute('role', 'region');
|
1154
|
-
hiddenSection.setAttribute(
|
1155
|
-
|
1156
|
-
|
1533
|
+
hiddenSection.setAttribute(
|
1534
|
+
'aria-label',
|
1535
|
+
'Chart screen reader information.'
|
1536
|
+
);
|
1537
|
+
|
1538
|
+
hiddenSection.innerHTML =
|
1539
|
+
a11yOptions.screenReaderSectionFormatter &&
|
1540
|
+
a11yOptions.screenReaderSectionFormatter(chart) ||
|
1157
1541
|
'<div>Use regions/landmarks to skip ahead to chart' +
|
1158
1542
|
(series.length > 1 ? ' and navigate between data series' : '') +
|
1159
|
-
'.</div><h3>' +
|
1160
|
-
(options.
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1543
|
+
'.</div><h3>' +
|
1544
|
+
(options.title.text ? htmlencode(options.title.text) : 'Chart') +
|
1545
|
+
(
|
1546
|
+
options.subtitle && options.subtitle.text ?
|
1547
|
+
'. ' + htmlencode(options.subtitle.text) :
|
1548
|
+
''
|
1549
|
+
) +
|
1550
|
+
'</h3><h4>Long description.</h4><div>' +
|
1551
|
+
(options.chart.description || 'No description available.') +
|
1552
|
+
'</div><h4>Structure.</h4><div>Chart type: ' +
|
1553
|
+
(options.chart.typeDescription || chart.getTypeDescription()) +
|
1554
|
+
'</div>' +
|
1555
|
+
(
|
1556
|
+
series.length === 1 ?
|
1557
|
+
(
|
1558
|
+
'<div>' + chartTypeInfo[0] + ' with ' +
|
1559
|
+
series[0].points.length + ' ' +
|
1560
|
+
(
|
1561
|
+
series[0].points.length === 1 ?
|
1562
|
+
chartTypeInfo[1] :
|
1563
|
+
chartTypeInfo[2]
|
1564
|
+
) +
|
1565
|
+
'.</div>'
|
1566
|
+
) : ''
|
1567
|
+
) +
|
1165
1568
|
(axesDesc.xAxis ? ('<div>' + axesDesc.xAxis + '</div>') : '') +
|
1166
1569
|
(axesDesc.yAxis ? ('<div>' + axesDesc.yAxis + '</div>') : '');
|
1167
1570
|
|
@@ -1169,11 +1572,13 @@
|
|
1169
1572
|
if (chart.getCSV) {
|
1170
1573
|
tableShortcutAnchor.innerHTML = 'View as data table.';
|
1171
1574
|
tableShortcutAnchor.href = '#' + tableId;
|
1172
|
-
|
1173
|
-
tableShortcutAnchor.
|
1174
|
-
|
1175
|
-
|
1176
|
-
|
1575
|
+
// Make this unreachable by user tabbing
|
1576
|
+
tableShortcutAnchor.setAttribute('tabindex', '-1');
|
1577
|
+
tableShortcutAnchor.onclick =
|
1578
|
+
a11yOptions.onTableAnchorClick || function() {
|
1579
|
+
chart.viewData();
|
1580
|
+
doc.getElementById(tableId).focus();
|
1581
|
+
};
|
1177
1582
|
tableShortcut.appendChild(tableShortcutAnchor);
|
1178
1583
|
hiddenSection.appendChild(tableShortcut);
|
1179
1584
|
}
|
@@ -1199,15 +1604,25 @@
|
|
1199
1604
|
return;
|
1200
1605
|
}
|
1201
1606
|
|
1202
|
-
var titleElement = doc.createElementNS(
|
1203
|
-
|
1607
|
+
var titleElement = doc.createElementNS(
|
1608
|
+
'http://www.w3.org/2000/svg',
|
1609
|
+
'title'
|
1610
|
+
),
|
1611
|
+
exportGroupElement = doc.createElementNS(
|
1612
|
+
'http://www.w3.org/2000/svg',
|
1613
|
+
'g'
|
1614
|
+
),
|
1204
1615
|
descElement = chart.container.getElementsByTagName('desc')[0],
|
1205
1616
|
textElements = chart.container.getElementsByTagName('text'),
|
1206
1617
|
titleId = 'highcharts-title-' + chart.index,
|
1207
1618
|
tableId = 'highcharts-data-table-' + chart.index,
|
1208
1619
|
hiddenSectionId = 'highcharts-information-region-' + chart.index,
|
1209
1620
|
chartTitle = options.title.text || 'Chart',
|
1210
|
-
oldColumnHeaderFormatter =
|
1621
|
+
oldColumnHeaderFormatter = (
|
1622
|
+
options.exporting &&
|
1623
|
+
options.exporting.csv &&
|
1624
|
+
options.exporting.csv.columnHeaderFormatter
|
1625
|
+
),
|
1211
1626
|
topLevelColumns = [];
|
1212
1627
|
|
1213
1628
|
// Add SVG title/desc tags
|
@@ -1215,35 +1630,51 @@
|
|
1215
1630
|
titleElement.id = titleId;
|
1216
1631
|
descElement.parentNode.insertBefore(titleElement, descElement);
|
1217
1632
|
chart.renderTo.setAttribute('role', 'region');
|
1218
|
-
|
1219
|
-
|
1220
|
-
'.
|
1633
|
+
chart.renderTo.setAttribute(
|
1634
|
+
'aria-label',
|
1635
|
+
'Interactive chart. ' + chartTitle +
|
1636
|
+
'. Use up and down arrows to navigate with most screen readers.'
|
1637
|
+
);
|
1221
1638
|
|
1222
1639
|
// Set screen reader properties on export menu
|
1223
|
-
if (
|
1640
|
+
if (
|
1641
|
+
chart.exportSVGElements &&
|
1642
|
+
chart.exportSVGElements[0] &&
|
1643
|
+
chart.exportSVGElements[0].element
|
1644
|
+
) {
|
1224
1645
|
var oldExportCallback = chart.exportSVGElements[0].element.onclick,
|
1225
1646
|
parent = chart.exportSVGElements[0].element.parentNode;
|
1226
1647
|
chart.exportSVGElements[0].element.onclick = function() {
|
1227
|
-
oldExportCallback.apply(
|
1648
|
+
oldExportCallback.apply(
|
1649
|
+
this,
|
1650
|
+
Array.prototype.slice.call(arguments)
|
1651
|
+
);
|
1228
1652
|
chart.addAccessibleContextMenuAttribs();
|
1229
1653
|
chart.highlightExportItem(0);
|
1230
1654
|
};
|
1231
1655
|
chart.exportSVGElements[0].element.setAttribute('role', 'button');
|
1232
|
-
chart.exportSVGElements[0].element.setAttribute(
|
1656
|
+
chart.exportSVGElements[0].element.setAttribute(
|
1657
|
+
'aria-label',
|
1658
|
+
'View export menu'
|
1659
|
+
);
|
1233
1660
|
exportGroupElement.appendChild(chart.exportSVGElements[0].element);
|
1234
1661
|
exportGroupElement.setAttribute('role', 'region');
|
1235
1662
|
exportGroupElement.setAttribute('aria-label', 'Chart export menu');
|
1236
1663
|
parent.appendChild(exportGroupElement);
|
1237
1664
|
}
|
1238
1665
|
|
1239
|
-
// Set screen reader properties on input boxes for range selector. We need
|
1666
|
+
// Set screen reader properties on input boxes for range selector. We need
|
1667
|
+
// to do this regardless of whether or not these are visible, as they are
|
1240
1668
|
// by default part of the page's tabindex unless we set them to -1.
|
1241
1669
|
if (chart.rangeSelector) {
|
1242
1670
|
each(['minInput', 'maxInput'], function(key, i) {
|
1243
1671
|
if (chart.rangeSelector[key]) {
|
1244
1672
|
chart.rangeSelector[key].setAttribute('tabindex', '-1');
|
1245
1673
|
chart.rangeSelector[key].setAttribute('role', 'textbox');
|
1246
|
-
chart.rangeSelector[key].setAttribute(
|
1674
|
+
chart.rangeSelector[key].setAttribute(
|
1675
|
+
'aria-label',
|
1676
|
+
'Select ' + (i ? 'end' : 'start') + ' date.'
|
1677
|
+
);
|
1247
1678
|
}
|
1248
1679
|
});
|
1249
1680
|
}
|
@@ -1277,7 +1708,8 @@
|
|
1277
1708
|
var prevCol = topLevelColumns[topLevelColumns.length - 1];
|
1278
1709
|
if (keyLength > 1) {
|
1279
1710
|
// We need multiple levels of column headers
|
1280
|
-
// Populate a list of column headers to add in addition to
|
1711
|
+
// Populate a list of column headers to add in addition to
|
1712
|
+
// the ones added by export-data
|
1281
1713
|
if ((prevCol && prevCol.text) !== item.name) {
|
1282
1714
|
topLevelColumns.push({
|
1283
1715
|
text: item.name,
|
@@ -1286,7 +1718,12 @@
|
|
1286
1718
|
}
|
1287
1719
|
}
|
1288
1720
|
if (oldColumnHeaderFormatter) {
|
1289
|
-
return oldColumnHeaderFormatter.call(
|
1721
|
+
return oldColumnHeaderFormatter.call(
|
1722
|
+
this,
|
1723
|
+
item,
|
1724
|
+
key,
|
1725
|
+
keyLength
|
1726
|
+
);
|
1290
1727
|
}
|
1291
1728
|
return keyLength > 1 ? key : item.name;
|
1292
1729
|
}
|
@@ -1296,7 +1733,11 @@
|
|
1296
1733
|
// Add ID and title/caption to table HTML
|
1297
1734
|
H.wrap(chart, 'getTable', function(proceed) {
|
1298
1735
|
return proceed.apply(this, Array.prototype.slice.call(arguments, 1))
|
1299
|
-
.replace(
|
1736
|
+
.replace(
|
1737
|
+
'<table>',
|
1738
|
+
'<table id="' + tableId + '" summary="Table representation ' +
|
1739
|
+
'of chart"><caption>' + chartTitle + '</caption>'
|
1740
|
+
);
|
1300
1741
|
});
|
1301
1742
|
|
1302
1743
|
// Add accessibility attributes and top level columns
|
@@ -1334,7 +1775,8 @@
|
|
1334
1775
|
// Add top level columns
|
1335
1776
|
if (topLevelColumns.length) {
|
1336
1777
|
each(topLevelColumns, function(col) {
|
1337
|
-
columnHeaderRow += '<th scope="col" colspan="' + col.span +
|
1778
|
+
columnHeaderRow += '<th scope="col" colspan="' + col.span +
|
1779
|
+
'">' + col.text + '</th>';
|
1338
1780
|
});
|
1339
1781
|
head.insertAdjacentHTML('afterbegin', columnHeaderRow);
|
1340
1782
|
}
|