highcharts-rails 4.2.7 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +34 -0
  3. data/Gemfile +4 -0
  4. data/Rakefile +53 -32
  5. data/app/assets/javascripts/highcharts.js +18775 -17176
  6. data/app/assets/javascripts/highcharts/highcharts-3d.js +1849 -1563
  7. data/app/assets/javascripts/highcharts/highcharts-more.js +2162 -1988
  8. data/app/assets/javascripts/highcharts/modules/accessibility.js +1005 -0
  9. data/app/assets/javascripts/highcharts/modules/annotations.js +408 -401
  10. data/app/assets/javascripts/highcharts/modules/boost.js +561 -546
  11. data/app/assets/javascripts/highcharts/modules/broken-axis.js +330 -324
  12. data/app/assets/javascripts/highcharts/modules/data.js +973 -965
  13. data/app/assets/javascripts/highcharts/modules/drilldown.js +783 -723
  14. data/app/assets/javascripts/highcharts/modules/exporting.js +864 -785
  15. data/app/assets/javascripts/highcharts/modules/funnel.js +290 -306
  16. data/app/assets/javascripts/highcharts/modules/heatmap.js +701 -645
  17. data/app/assets/javascripts/highcharts/modules/no-data-to-display.js +150 -132
  18. data/app/assets/javascripts/highcharts/modules/offline-exporting.js +414 -355
  19. data/app/assets/javascripts/highcharts/modules/overlapping-datalabels.js +164 -0
  20. data/app/assets/javascripts/highcharts/modules/series-label.js +473 -448
  21. data/app/assets/javascripts/highcharts/modules/solid-gauge.js +279 -271
  22. data/app/assets/javascripts/highcharts/modules/treemap.js +921 -886
  23. data/app/assets/javascripts/highcharts/themes/dark-blue.js +307 -244
  24. data/app/assets/javascripts/highcharts/themes/dark-green.js +303 -244
  25. data/app/assets/javascripts/highcharts/themes/dark-unica.js +231 -201
  26. data/app/assets/javascripts/highcharts/themes/gray.js +314 -245
  27. data/app/assets/javascripts/highcharts/themes/grid-light.js +91 -66
  28. data/app/assets/javascripts/highcharts/themes/grid.js +124 -96
  29. data/app/assets/javascripts/highcharts/themes/sand-signika.js +119 -94
  30. data/app/assets/javascripts/highcharts/themes/skies.js +108 -85
  31. data/lib/highcharts/version.rb +1 -1
  32. metadata +13 -14
  33. data/app/assets/javascripts/highcharts/adapters/standalone-framework.js +0 -1
  34. data/app/assets/javascripts/highcharts/modules/canvas-tools.js +0 -3115
  35. data/app/assets/javascripts/highcharts/modules/map.js +0 -2117
@@ -1,798 +1,877 @@
1
1
  /**
2
- * @license Highcharts JS v4.2.7 (2016-09-21)
2
+ * @license Highcharts JS v5.0.0 (2016-09-29)
3
3
  * Exporting module
4
4
  *
5
5
  * (c) 2010-2016 Torstein Honsi
6
6
  *
7
7
  * License: www.highcharts.com/license
8
8
  */
9
-
10
- /* eslint indent:0 */
11
- (function (factory) {
9
+ (function(factory) {
12
10
  if (typeof module === 'object' && module.exports) {
13
11
  module.exports = factory;
14
12
  } else {
15
13
  factory(Highcharts);
16
14
  }
17
- }(function (Highcharts) {
18
-
19
- // create shortcuts
20
- var win = Highcharts.win,
21
- doc = win.document,
22
- Chart = Highcharts.Chart,
23
- addEvent = Highcharts.addEvent,
24
- removeEvent = Highcharts.removeEvent,
25
- fireEvent = Highcharts.fireEvent,
26
- createElement = Highcharts.createElement,
27
- discardElement = Highcharts.discardElement,
28
- css = Highcharts.css,
29
- merge = Highcharts.merge,
30
- each = Highcharts.each,
31
- extend = Highcharts.extend,
32
- splat = Highcharts.splat,
33
- math = Math,
34
- mathMax = math.max,
35
- isTouchDevice = Highcharts.isTouchDevice,
36
- M = 'M',
37
- L = 'L',
38
- DIV = 'div',
39
- HIDDEN = 'hidden',
40
- NONE = 'none',
41
- PREFIX = 'highcharts-',
42
- ABSOLUTE = 'absolute',
43
- PX = 'px',
44
- UNDEFINED,
45
- symbols = Highcharts.Renderer.prototype.symbols,
46
- defaultOptions = Highcharts.getOptions(),
47
- buttonOffset;
48
-
49
- // Add language
50
- extend(defaultOptions.lang, {
51
- printChart: 'Print chart',
52
- downloadPNG: 'Download PNG image',
53
- downloadJPEG: 'Download JPEG image',
54
- downloadPDF: 'Download PDF document',
55
- downloadSVG: 'Download SVG vector image',
56
- contextButtonTitle: 'Chart context menu'
57
- });
58
-
59
- // Buttons and menus are collected in a separate config option set called 'navigation'.
60
- // This can be extended later to add control buttons like zoom and pan right click menus.
61
- defaultOptions.navigation = {
62
- menuStyle: {
63
- border: '1px solid #A0A0A0',
64
- background: '#FFFFFF',
65
- padding: '5px 0'
66
- },
67
- menuItemStyle: {
68
- padding: '0 10px',
69
- background: NONE,
70
- color: '#303030',
71
- fontSize: isTouchDevice ? '14px' : '11px'
72
- },
73
- menuItemHoverStyle: {
74
- background: '#4572A5',
75
- color: '#FFFFFF'
76
- },
77
-
78
- buttonOptions: {
79
- symbolFill: '#E0E0E0',
80
- symbolSize: 14,
81
- symbolStroke: '#666',
82
- symbolStrokeWidth: 3,
83
- symbolX: 12.5,
84
- symbolY: 10.5,
85
- align: 'right',
86
- buttonSpacing: 3,
87
- height: 22,
88
- // text: null,
89
- theme: {
90
- fill: 'white', // capture hover
91
- stroke: 'none'
92
- },
93
- verticalAlign: 'top',
94
- width: 24
95
- }
96
- };
97
-
98
-
99
-
100
- // Add the export related options
101
- defaultOptions.exporting = {
102
- //enabled: true,
103
- //filename: 'chart',
104
- type: 'image/png',
105
- url: 'https://export.highcharts.com/',
106
- //width: undefined,
107
- printMaxWidth: 780,
108
- scale: 2,
109
- buttons: {
110
- contextButton: {
111
- menuClassName: PREFIX + 'contextmenu',
112
- //x: -10,
113
- symbol: 'menu',
114
- _titleKey: 'contextButtonTitle',
115
- menuItems: [{
116
- textKey: 'printChart',
117
- onclick: function () {
118
- this.print();
119
- }
120
- }, {
121
- separator: true
122
- }, {
123
- textKey: 'downloadPNG',
124
- onclick: function () {
125
- this.exportChart();
126
- }
127
- }, {
128
- textKey: 'downloadJPEG',
129
- onclick: function () {
130
- this.exportChart({
131
- type: 'image/jpeg'
132
- });
133
- }
134
- }, {
135
- textKey: 'downloadPDF',
136
- onclick: function () {
137
- this.exportChart({
138
- type: 'application/pdf'
139
- });
140
- }
141
- }, {
142
- textKey: 'downloadSVG',
143
- onclick: function () {
144
- this.exportChart({
145
- type: 'image/svg+xml'
146
- });
147
- }
148
- }
149
- // Enable this block to add "View SVG" to the dropdown menu
150
- /*
151
- ,{
152
-
153
- text: 'View SVG',
154
- onclick: function () {
155
- var svg = this.getSVG()
156
- .replace(/</g, '\n&lt;')
157
- .replace(/>/g, '&gt;');
158
-
159
- doc.body.innerHTML = '<pre>' + svg + '</pre>';
160
- }
161
- } // */
162
- ]
163
- }
164
- }
165
- };
166
-
167
- // Add the Highcharts.post utility
168
- Highcharts.post = function (url, data, formAttributes) {
169
- var name,
170
- form;
171
-
172
- // create the form
173
- form = createElement('form', merge({
174
- method: 'post',
175
- action: url,
176
- enctype: 'multipart/form-data'
177
- }, formAttributes), {
178
- display: NONE
179
- }, doc.body);
180
-
181
- // add the data
182
- for (name in data) {
183
- createElement('input', {
184
- type: HIDDEN,
185
- name: name,
186
- value: data[name]
187
- }, null, form);
188
- }
189
-
190
- // submit
191
- form.submit();
192
-
193
- // clean up
194
- discardElement(form);
195
- };
196
-
197
- extend(Chart.prototype, {
198
-
199
- /**
200
- * A collection of regex fixes on the produces SVG to account for expando properties,
201
- * browser bugs, VML problems and other. Returns a cleaned SVG.
202
- */
203
- sanitizeSVG: function (svg) {
204
- return svg
205
- .replace(/zIndex="[^"]+"/g, '')
206
- .replace(/isShadow="[^"]+"/g, '')
207
- .replace(/symbolName="[^"]+"/g, '')
208
- .replace(/jQuery[0-9]+="[^"]+"/g, '')
209
- .replace(/url\(("|&quot;)(\S+)("|&quot;)\)/g, 'url($2)')
210
- .replace(/url\([^#]+#/g, 'url(#')
211
- .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
212
- .replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
213
- .replace(/\n/, ' ')
214
- // Any HTML added to the container after the SVG (#894)
215
- .replace(/<\/svg>.*?$/, '</svg>')
216
- // Batik doesn't support rgba fills and strokes (#3095)
217
- .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
218
- /* This fails in IE < 8
219
- .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
220
- return s2 +'.'+ s3[0];
221
- })*/
222
-
223
- // Replace HTML entities, issue #347
224
- .replace(/&nbsp;/g, '\u00A0') // no-break space
225
- .replace(/&shy;/g, '\u00AD') // soft hyphen
226
-
227
- // IE specific
228
- .replace(/<IMG /g, '<image ')
229
- .replace(/<(\/?)TITLE>/g, '<$1title>')
230
- .replace(/height=([^" ]+)/g, 'height="$1"')
231
- .replace(/width=([^" ]+)/g, 'width="$1"')
232
- .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
233
- .replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
234
- .replace(/class=([^" >]+)/g, 'class="$1"')
235
- .replace(/ transform /g, ' ')
236
- .replace(/:(path|rect)/g, '$1')
237
- .replace(/style="([^"]+)"/g, function (s) {
238
- return s.toLowerCase();
239
- });
240
- },
241
-
242
- /**
243
- * Return innerHTML of chart. Used as hook for plugins.
244
- */
245
- getChartHTML: function () {
246
- return this.container.innerHTML;
247
- },
248
-
249
- /**
250
- * Return an SVG representation of the chart
251
- *
252
- * @param additionalOptions {Object} Additional chart options for the generated SVG representation
253
- */
254
- getSVG: function (additionalOptions) {
255
- var chart = this,
256
- chartCopy,
257
- sandbox,
258
- svg,
259
- seriesOptions,
260
- sourceWidth,
261
- sourceHeight,
262
- cssWidth,
263
- cssHeight,
264
- html,
265
- options = merge(chart.options, additionalOptions), // copy the options and add extra options
266
- allowHTML = options.exporting.allowHTML;
267
-
268
-
269
- // IE compatibility hack for generating SVG content that it doesn't really understand
270
- if (!doc.createElementNS) {
271
- doc.createElementNS = function (ns, tagName) {
272
- return doc.createElement(tagName);
273
- };
274
- }
275
-
276
- // create a sandbox where a new chart will be generated
277
- sandbox = createElement(DIV, null, {
278
- position: ABSOLUTE,
279
- top: '-9999em',
280
- width: chart.chartWidth + PX,
281
- height: chart.chartHeight + PX
282
- }, doc.body);
283
-
284
- // get the source size
285
- cssWidth = chart.renderTo.style.width;
286
- cssHeight = chart.renderTo.style.height;
287
- sourceWidth = options.exporting.sourceWidth ||
288
- options.chart.width ||
289
- (/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||
290
- 600;
291
- sourceHeight = options.exporting.sourceHeight ||
292
- options.chart.height ||
293
- (/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||
294
- 400;
295
-
296
- // override some options
297
- extend(options.chart, {
298
- animation: false,
299
- renderTo: sandbox,
300
- forExport: true,
301
- renderer: 'SVGRenderer',
302
- width: sourceWidth,
303
- height: sourceHeight
304
- });
305
- options.exporting.enabled = false; // hide buttons in print
306
- delete options.data; // #3004
307
-
308
- // prepare for replicating the chart
309
- options.series = [];
310
- each(chart.series, function (serie) {
311
- seriesOptions = merge(serie.userOptions, { // #4912
312
- animation: false, // turn off animation
313
- enableMouseTracking: false,
314
- showCheckbox: false,
315
- visible: serie.visible
316
- });
317
-
318
- if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
319
- options.series.push(seriesOptions);
320
- }
321
- });
322
-
323
- // Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
324
- if (additionalOptions) {
325
- each(['xAxis', 'yAxis'], function (axisType) {
326
- each(splat(additionalOptions[axisType]), function (axisOptions, i) {
327
- options[axisType][i] = merge(options[axisType][i], axisOptions);
328
- });
329
- });
330
- }
331
-
332
- // generate the chart copy
333
- chartCopy = new Highcharts.Chart(options, chart.callback);
334
-
335
- // reflect axis extremes in the export
336
- each(['xAxis', 'yAxis'], function (axisType) {
337
- each(chart[axisType], function (axis, i) {
338
- var axisCopy = chartCopy[axisType][i],
339
- extremes = axis.getExtremes(),
340
- userMin = extremes.userMin,
341
- userMax = extremes.userMax;
342
-
343
- if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) {
344
- axisCopy.setExtremes(userMin, userMax, true, false);
345
- }
346
- });
347
- });
348
-
349
- // get the SVG from the container's innerHTML
350
- svg = chartCopy.getChartHTML();
351
-
352
- // free up memory
353
- options = null;
354
- chartCopy.destroy();
355
- discardElement(sandbox);
356
-
357
- // Move HTML into a foreignObject
358
- if (allowHTML) {
359
- html = svg.match(/<\/svg>(.*?$)/);
360
- if (html) {
361
- html = '<foreignObject x="0" y="0" width="200" height="200">' +
362
- '<body xmlns="http://www.w3.org/1999/xhtml">' +
363
- html[1] +
364
- '</body>' +
365
- '</foreignObject>';
366
- svg = svg.replace('</svg>', html + '</svg>');
367
- }
368
- }
369
-
370
- // sanitize
371
- svg = this.sanitizeSVG(svg);
372
-
373
- // IE9 beta bugs with innerHTML. Test again with final IE9.
374
- svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
375
- .replace(/&quot;/g, '\'');
376
-
377
- return svg;
378
- },
379
-
380
- getSVGForExport: function (options, chartOptions) {
381
- var chartExportingOptions = this.options.exporting;
382
-
383
- return this.getSVG(merge(
384
- { chart: { borderRadius: 0 } },
385
- chartExportingOptions.chartOptions,
386
- chartOptions,
387
- {
388
- exporting: {
389
- sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
390
- sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
391
- }
392
- }
393
- ));
394
- },
395
-
396
- /**
397
- * Submit the SVG representation of the chart to the server
398
- * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
399
- * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
400
- */
401
- exportChart: function (options, chartOptions) {
402
-
403
- var svg = this.getSVGForExport(options, chartOptions);
404
-
405
- // merge the options
406
- options = merge(this.options.exporting, options);
407
-
408
- // do the post
409
- Highcharts.post(options.url, {
410
- filename: options.filename || 'chart',
411
- type: options.type,
412
- width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
413
- scale: options.scale,
414
- svg: svg
415
- }, options.formAttributes);
416
-
417
- },
418
-
419
- /**
420
- * Print the chart
421
- */
422
- print: function () {
423
-
424
- var chart = this,
425
- container = chart.container,
426
- origDisplay = [],
427
- origParent = container.parentNode,
428
- body = doc.body,
429
- childNodes = body.childNodes,
430
- printMaxWidth = chart.options.exporting.printMaxWidth,
431
- resetParams,
432
- handleMaxWidth;
433
-
434
- if (chart.isPrinting) { // block the button while in printing mode
435
- return;
436
- }
437
-
438
- chart.isPrinting = true;
439
- chart.pointer.reset(null, 0);
440
-
441
- fireEvent(chart, 'beforePrint');
442
-
443
- // Handle printMaxWidth
444
- handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
445
- if (handleMaxWidth) {
446
- resetParams = [chart.options.chart.width, undefined, false];
447
- chart.setSize(printMaxWidth, undefined, false);
448
- }
449
-
450
- // hide all body content
451
- each(childNodes, function (node, i) {
452
- if (node.nodeType === 1) {
453
- origDisplay[i] = node.style.display;
454
- node.style.display = NONE;
455
- }
456
- });
457
-
458
- // pull out the chart
459
- body.appendChild(container);
460
-
461
- // print
462
- win.focus(); // #1510
463
- win.print();
464
-
465
- // allow the browser to prepare before reverting
466
- setTimeout(function () {
467
-
468
- // put the chart back in
469
- origParent.appendChild(container);
470
-
471
- // restore all body content
472
- each(childNodes, function (node, i) {
473
- if (node.nodeType === 1) {
474
- node.style.display = origDisplay[i];
475
- }
476
- });
477
-
478
- chart.isPrinting = false;
479
-
480
- // Reset printMaxWidth
481
- if (handleMaxWidth) {
482
- chart.setSize.apply(chart, resetParams);
483
- }
484
-
485
- fireEvent(chart, 'afterPrint');
486
-
487
- }, 1000);
488
-
489
- },
490
-
491
- /**
492
- * Display a popup menu for choosing the export type
493
- *
494
- * @param {String} className An identifier for the menu
495
- * @param {Array} items A collection with text and onclicks for the items
496
- * @param {Number} x The x position of the opener button
497
- * @param {Number} y The y position of the opener button
498
- * @param {Number} width The width of the opener button
499
- * @param {Number} height The height of the opener button
500
- */
501
- contextMenu: function (className, items, x, y, width, height, button) {
502
- var chart = this,
503
- navOptions = chart.options.navigation,
504
- menuItemStyle = navOptions.menuItemStyle,
505
- chartWidth = chart.chartWidth,
506
- chartHeight = chart.chartHeight,
507
- cacheName = 'cache-' + className,
508
- menu = chart[cacheName],
509
- menuPadding = mathMax(width, height), // for mouse leave detection
510
- boxShadow = '3px 3px 10px #888',
511
- innerMenu,
512
- hide,
513
- hideTimer,
514
- menuStyle,
515
- docMouseUpHandler = function (e) {
516
- if (!chart.pointer.inClass(e.target, className)) {
517
- hide();
518
- }
519
- };
520
-
521
- // create the menu only the first time
522
- if (!menu) {
523
-
524
- // create a HTML element above the SVG
525
- chart[cacheName] = menu = createElement(DIV, {
526
- className: className
527
- }, {
528
- position: ABSOLUTE,
529
- zIndex: 1000,
530
- padding: menuPadding + PX
531
- }, chart.container);
532
-
533
- innerMenu = createElement(DIV, null,
534
- extend({
535
- MozBoxShadow: boxShadow,
536
- WebkitBoxShadow: boxShadow,
537
- boxShadow: boxShadow
538
- }, navOptions.menuStyle), menu);
539
-
540
- // hide on mouse out
541
- hide = function () {
542
- css(menu, { display: NONE });
543
- if (button) {
544
- button.setState(0);
545
- }
546
- chart.openMenu = false;
547
- };
548
-
549
- // Hide the menu some time after mouse leave (#1357)
550
- addEvent(menu, 'mouseleave', function () {
551
- hideTimer = setTimeout(hide, 500);
552
- });
553
- addEvent(menu, 'mouseenter', function () {
554
- clearTimeout(hideTimer);
555
- });
556
-
557
-
558
- // Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
559
- addEvent(doc, 'mouseup', docMouseUpHandler);
560
- addEvent(chart, 'destroy', function () {
561
- removeEvent(doc, 'mouseup', docMouseUpHandler);
562
- });
563
-
564
-
565
- // create the items
566
- each(items, function (item) {
567
- if (item) {
568
- var element = item.separator ?
569
- createElement('hr', null, null, innerMenu) :
570
- createElement(DIV, {
571
- onmouseover: function () {
572
- css(this, navOptions.menuItemHoverStyle);
573
- },
574
- onmouseout: function () {
575
- css(this, menuItemStyle);
576
- },
577
- onclick: function (e) {
578
- if (e) { // IE7
579
- e.stopPropagation();
580
- }
581
- hide();
582
- if (item.onclick) {
583
- item.onclick.apply(chart, arguments);
584
- }
585
- },
586
- innerHTML: item.text || chart.options.lang[item.textKey]
587
- }, extend({
588
- cursor: 'pointer'
589
- }, menuItemStyle), innerMenu);
590
-
591
-
592
- // Keep references to menu divs to be able to destroy them
593
- chart.exportDivElements.push(element);
594
- }
595
- });
596
-
597
- // Keep references to menu and innerMenu div to be able to destroy them
598
- chart.exportDivElements.push(innerMenu, menu);
599
-
600
- chart.exportMenuWidth = menu.offsetWidth;
601
- chart.exportMenuHeight = menu.offsetHeight;
602
- }
603
-
604
- menuStyle = { display: 'block' };
605
-
606
- // if outside right, right align it
607
- if (x + chart.exportMenuWidth > chartWidth) {
608
- menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
609
- } else {
610
- menuStyle.left = (x - menuPadding) + PX;
611
- }
612
- // if outside bottom, bottom align it
613
- if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {
614
- menuStyle.bottom = (chartHeight - y - menuPadding) + PX;
615
- } else {
616
- menuStyle.top = (y + height - menuPadding) + PX;
617
- }
618
-
619
- css(menu, menuStyle);
620
- chart.openMenu = true;
621
- },
622
-
623
- /**
624
- * Add the export button to the chart
625
- */
626
- addButton: function (options) {
627
- var chart = this,
628
- renderer = chart.renderer,
629
- btnOptions = merge(chart.options.navigation.buttonOptions, options),
630
- onclick = btnOptions.onclick,
631
- menuItems = btnOptions.menuItems,
632
- symbol,
633
- button,
634
- symbolAttr = {
635
- stroke: btnOptions.symbolStroke,
636
- fill: btnOptions.symbolFill
637
- },
638
- symbolSize = btnOptions.symbolSize || 12;
639
- if (!chart.btnCount) {
640
- chart.btnCount = 0;
641
- }
642
-
643
- // Keeps references to the button elements
644
- if (!chart.exportDivElements) {
645
- chart.exportDivElements = [];
646
- chart.exportSVGElements = [];
647
- }
648
-
649
- if (btnOptions.enabled === false) {
650
- return;
651
- }
652
-
653
-
654
- var attr = btnOptions.theme,
655
- states = attr.states,
656
- hover = states && states.hover,
657
- select = states && states.select,
658
- callback;
659
-
660
- delete attr.states;
661
-
662
- if (onclick) {
663
- callback = function (e) {
664
- e.stopPropagation();
665
- onclick.call(chart, e);
666
- };
667
-
668
- } else if (menuItems) {
669
- callback = function () {
670
- chart.contextMenu(
671
- button.menuClassName,
672
- menuItems,
673
- button.translateX,
674
- button.translateY,
675
- button.width,
676
- button.height,
677
- button
678
- );
679
- button.setState(2);
680
- };
681
- }
682
-
683
-
684
- if (btnOptions.text && btnOptions.symbol) {
685
- attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);
686
-
687
- } else if (!btnOptions.text) {
688
- extend(attr, {
689
- width: btnOptions.width,
690
- height: btnOptions.height,
691
- padding: 0
692
- });
693
- }
694
-
695
- button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
696
- .attr({
697
- title: chart.options.lang[btnOptions._titleKey],
698
- 'stroke-linecap': 'round',
699
- zIndex: 3 // #4955
700
- });
701
- button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++;
702
-
703
- if (btnOptions.symbol) {
704
- symbol = renderer.symbol(
705
- btnOptions.symbol,
706
- btnOptions.symbolX - (symbolSize / 2),
707
- btnOptions.symbolY - (symbolSize / 2),
708
- symbolSize,
709
- symbolSize
710
- )
711
- .attr(extend(symbolAttr, {
712
- 'stroke-width': btnOptions.symbolStrokeWidth || 1,
713
- zIndex: 1
714
- })).add(button);
715
- }
716
-
717
- button.add()
718
- .align(extend(btnOptions, {
719
- width: button.width,
720
- x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654
721
- }), true, 'spacingBox');
722
-
723
- buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
724
-
725
- chart.exportSVGElements.push(button, symbol);
726
-
727
- },
728
-
729
- /**
730
- * Destroy the buttons.
731
- */
732
- destroyExport: function (e) {
733
- var chart = e.target,
734
- i,
735
- elem;
736
-
737
- // Destroy the extra buttons added
738
- for (i = 0; i < chart.exportSVGElements.length; i++) {
739
- elem = chart.exportSVGElements[i];
740
-
741
- // Destroy and null the svg/vml elements
742
- if (elem) { // #1822
743
- elem.onclick = elem.ontouchstart = null;
744
- chart.exportSVGElements[i] = elem.destroy();
745
- }
746
- }
747
-
748
- // Destroy the divs for the menu
749
- for (i = 0; i < chart.exportDivElements.length; i++) {
750
- elem = chart.exportDivElements[i];
751
-
752
- // Remove the event handler
753
- removeEvent(elem, 'mouseleave');
754
-
755
- // Remove inline events
756
- chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
757
-
758
- // Destroy the div by moving to garbage bin
759
- discardElement(elem);
760
- }
761
- }
762
- });
763
-
764
-
765
- symbols.menu = function (x, y, width, height) {
766
- var arr = [
767
- M, x, y + 2.5,
768
- L, x + width, y + 2.5,
769
- M, x, y + height / 2 + 0.5,
770
- L, x + width, y + height / 2 + 0.5,
771
- M, x, y + height - 1.5,
772
- L, x + width, y + height - 1.5
773
- ];
774
- return arr;
775
- };
776
-
777
- // Add the buttons on chart load
778
- Chart.prototype.callbacks.push(function (chart) {
779
- var n,
780
- exportingOptions = chart.options.exporting,
781
- buttons = exportingOptions.buttons;
782
-
783
- buttonOffset = 0;
784
-
785
- if (exportingOptions.enabled !== false) {
786
-
787
- for (n in buttons) {
788
- chart.addButton(buttons[n]);
789
- }
790
-
791
- // Destroy the export elements at chart destroy
792
- addEvent(chart, 'destroy', chart.destroyExport);
793
- }
794
-
795
- });
796
-
797
-
15
+ }(function(Highcharts) {
16
+ (function(H) {
17
+ /**
18
+ * Exporting module
19
+ *
20
+ * (c) 2010-2016 Torstein Honsi
21
+ *
22
+ * License: www.highcharts.com/license
23
+ */
24
+
25
+ /* eslint indent:0 */
26
+ 'use strict';
27
+
28
+ // create shortcuts
29
+ var defaultOptions = H.defaultOptions,
30
+ doc = H.doc,
31
+ Chart = H.Chart,
32
+ addEvent = H.addEvent,
33
+ removeEvent = H.removeEvent,
34
+ fireEvent = H.fireEvent,
35
+ createElement = H.createElement,
36
+ discardElement = H.discardElement,
37
+ css = H.css,
38
+ merge = H.merge,
39
+ pick = H.pick,
40
+ each = H.each,
41
+ extend = H.extend,
42
+ splat = H.splat,
43
+ isTouchDevice = H.isTouchDevice,
44
+ win = H.win,
45
+ SVGRenderer = H.SVGRenderer;
46
+
47
+ var symbols = H.Renderer.prototype.symbols;
48
+
49
+ // Add language
50
+ extend(defaultOptions.lang, {
51
+ printChart: 'Print chart',
52
+ downloadPNG: 'Download PNG image',
53
+ downloadJPEG: 'Download JPEG image',
54
+ downloadPDF: 'Download PDF document',
55
+ downloadSVG: 'Download SVG vector image',
56
+ contextButtonTitle: 'Chart context menu'
57
+ });
58
+
59
+ // Buttons and menus are collected in a separate config option set called 'navigation'.
60
+ // This can be extended later to add control buttons like zoom and pan right click menus.
61
+ defaultOptions.navigation = {
62
+ buttonOptions: {
63
+ theme: {},
64
+ symbolSize: 14,
65
+ symbolX: 12.5,
66
+ symbolY: 10.5,
67
+ align: 'right',
68
+ buttonSpacing: 3,
69
+ height: 22,
70
+ // text: null,
71
+ verticalAlign: 'top',
72
+ width: 24
73
+ }
74
+ };
75
+
76
+
77
+ // Presentational attributes
78
+ merge(true, defaultOptions.navigation, {
79
+ menuStyle: {
80
+ border: '1px solid #999999',
81
+ background: '#ffffff',
82
+ padding: '5px 0'
83
+ },
84
+ menuItemStyle: {
85
+ padding: '0.5em 1em',
86
+ background: 'none',
87
+ color: '#333333',
88
+ fontSize: isTouchDevice ? '14px' : '11px',
89
+ transition: 'background 250ms, color 250ms'
90
+ },
91
+ menuItemHoverStyle: {
92
+ background: '#335cad',
93
+ color: '#ffffff'
94
+ },
95
+ buttonOptions: {
96
+ symbolFill: '#666666',
97
+ symbolStroke: '#666666',
98
+ symbolStrokeWidth: 3,
99
+ theme: {
100
+ fill: '#ffffff', // capture hover
101
+ stroke: 'none',
102
+ padding: 5
103
+ }
104
+ }
105
+ });
106
+
107
+
108
+
109
+ // Add the export related options
110
+ defaultOptions.exporting = {
111
+ //enabled: true,
112
+ //filename: 'chart',
113
+ type: 'image/png',
114
+ url: 'https://export.highcharts.com/',
115
+ //width: undefined,
116
+ printMaxWidth: 780,
117
+ scale: 2,
118
+ buttons: {
119
+ contextButton: {
120
+ className: 'highcharts-contextbutton',
121
+ menuClassName: 'highcharts-contextmenu',
122
+ //x: -10,
123
+ symbol: 'menu',
124
+ _titleKey: 'contextButtonTitle',
125
+ menuItems: [{
126
+ textKey: 'printChart',
127
+ onclick: function() {
128
+ this.print();
129
+ }
130
+ }, {
131
+ separator: true
132
+ }, {
133
+ textKey: 'downloadPNG',
134
+ onclick: function() {
135
+ this.exportChart();
136
+ }
137
+ }, {
138
+ textKey: 'downloadJPEG',
139
+ onclick: function() {
140
+ this.exportChart({
141
+ type: 'image/jpeg'
142
+ });
143
+ }
144
+ }, {
145
+ textKey: 'downloadPDF',
146
+ onclick: function() {
147
+ this.exportChart({
148
+ type: 'application/pdf'
149
+ });
150
+ }
151
+ }, {
152
+ textKey: 'downloadSVG',
153
+ onclick: function() {
154
+ this.exportChart({
155
+ type: 'image/svg+xml'
156
+ });
157
+ }
158
+ }
159
+ // Enable this block to add "View SVG" to the dropdown menu
160
+ /*
161
+ ,{
162
+
163
+ text: 'View SVG',
164
+ onclick: function () {
165
+ var svg = this.getSVG()
166
+ .replace(/</g, '\n&lt;')
167
+ .replace(/>/g, '&gt;');
168
+
169
+ doc.body.innerHTML = '<pre>' + svg + '</pre>';
170
+ }
171
+ } // */
172
+ ]
173
+ }
174
+ }
175
+ };
176
+
177
+ // Add the H.post utility
178
+ H.post = function(url, data, formAttributes) {
179
+ var name,
180
+ form;
181
+
182
+ // create the form
183
+ form = createElement('form', merge({
184
+ method: 'post',
185
+ action: url,
186
+ enctype: 'multipart/form-data'
187
+ }, formAttributes), {
188
+ display: 'none'
189
+ }, doc.body);
190
+
191
+ // add the data
192
+ for (name in data) {
193
+ createElement('input', {
194
+ type: 'hidden',
195
+ name: name,
196
+ value: data[name]
197
+ }, null, form);
198
+ }
199
+
200
+ // submit
201
+ form.submit();
202
+
203
+ // clean up
204
+ discardElement(form);
205
+ };
206
+
207
+ extend(Chart.prototype, {
208
+
209
+ /**
210
+ * A collection of regex fixes on the produces SVG to account for expando properties,
211
+ * browser bugs, VML problems and other. Returns a cleaned SVG.
212
+ */
213
+ sanitizeSVG: function(svg) {
214
+ svg = svg
215
+ .replace(/zIndex="[^"]+"/g, '')
216
+ .replace(/isShadow="[^"]+"/g, '')
217
+ .replace(/symbolName="[^"]+"/g, '')
218
+ .replace(/jQuery[0-9]+="[^"]+"/g, '')
219
+ .replace(/url\(("|&quot;)(\S+)("|&quot;)\)/g, 'url($2)')
220
+ .replace(/url\([^#]+#/g, 'url(#')
221
+ .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
222
+ .replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
223
+ .replace(/\n/, ' ')
224
+ // Any HTML added to the container after the SVG (#894)
225
+ .replace(/<\/svg>.*?$/, '</svg>')
226
+ // Batik doesn't support rgba fills and strokes (#3095)
227
+ .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
228
+ /* This fails in IE < 8
229
+ .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
230
+ return s2 +'.'+ s3[0];
231
+ })*/
232
+
233
+ // Replace HTML entities, issue #347
234
+ .replace(/&nbsp;/g, '\u00A0') // no-break space
235
+ .replace(/&shy;/g, '\u00AD'); // soft hyphen
236
+
237
+
238
+ // IE specific
239
+ svg = svg
240
+ .replace(/<IMG /g, '<image ')
241
+ .replace(/<(\/?)TITLE>/g, '<$1title>')
242
+ .replace(/height=([^" ]+)/g, 'height="$1"')
243
+ .replace(/width=([^" ]+)/g, 'width="$1"')
244
+ .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
245
+ .replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
246
+ .replace(/class=([^" >]+)/g, 'class="$1"')
247
+ .replace(/ transform /g, ' ')
248
+ .replace(/:(path|rect)/g, '$1')
249
+ .replace(/style="([^"]+)"/g, function(s) {
250
+ return s.toLowerCase();
251
+ });
252
+
253
+
254
+ return svg;
255
+ },
256
+
257
+ /**
258
+ * Return innerHTML of chart. Used as hook for plugins.
259
+ */
260
+ getChartHTML: function() {
261
+
262
+ return this.container.innerHTML;
263
+ },
264
+
265
+ /**
266
+ * Return an SVG representation of the chart
267
+ *
268
+ * @param additionalOptions {Object} Additional chart options for the generated SVG representation
269
+ */
270
+ getSVG: function(additionalOptions) {
271
+ var chart = this,
272
+ chartCopy,
273
+ sandbox,
274
+ svg,
275
+ seriesOptions,
276
+ sourceWidth,
277
+ sourceHeight,
278
+ cssWidth,
279
+ cssHeight,
280
+ html,
281
+ options = merge(chart.options, additionalOptions), // copy the options and add extra options
282
+ allowHTML = options.exporting.allowHTML;
283
+
284
+
285
+ // IE compatibility hack for generating SVG content that it doesn't really understand
286
+ if (!doc.createElementNS) {
287
+ doc.createElementNS = function(ns, tagName) {
288
+ return doc.createElement(tagName);
289
+ };
290
+ }
291
+
292
+ // create a sandbox where a new chart will be generated
293
+ sandbox = createElement('div', null, {
294
+ position: 'absolute',
295
+ top: '-9999em',
296
+ width: chart.chartWidth + 'px',
297
+ height: chart.chartHeight + 'px'
298
+ }, doc.body);
299
+
300
+ // get the source size
301
+ cssWidth = chart.renderTo.style.width;
302
+ cssHeight = chart.renderTo.style.height;
303
+ sourceWidth = options.exporting.sourceWidth ||
304
+ options.chart.width ||
305
+ (/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||
306
+ 600;
307
+ sourceHeight = options.exporting.sourceHeight ||
308
+ options.chart.height ||
309
+ (/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||
310
+ 400;
311
+
312
+ // override some options
313
+ extend(options.chart, {
314
+ animation: false,
315
+ renderTo: sandbox,
316
+ forExport: true,
317
+ renderer: 'SVGRenderer',
318
+ width: sourceWidth,
319
+ height: sourceHeight
320
+ });
321
+ options.exporting.enabled = false; // hide buttons in print
322
+ delete options.data; // #3004
323
+
324
+ // prepare for replicating the chart
325
+ options.series = [];
326
+ each(chart.series, function(serie) {
327
+ seriesOptions = merge(serie.userOptions, { // #4912
328
+ animation: false, // turn off animation
329
+ enableMouseTracking: false,
330
+ showCheckbox: false,
331
+ visible: serie.visible
332
+ });
333
+
334
+ if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
335
+ options.series.push(seriesOptions);
336
+ }
337
+ });
338
+
339
+ // Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
340
+ if (additionalOptions) {
341
+ each(['xAxis', 'yAxis'], function(axisType) {
342
+ each(splat(additionalOptions[axisType]), function(axisOptions, i) {
343
+ options[axisType][i] = merge(options[axisType][i], axisOptions);
344
+ });
345
+ });
346
+ }
347
+
348
+ // generate the chart copy
349
+ chartCopy = new H.Chart(options, chart.callback);
350
+
351
+ // reflect axis extremes in the export
352
+ each(['xAxis', 'yAxis'], function(axisType) {
353
+ each(chart[axisType], function(axis, i) {
354
+ var axisCopy = chartCopy[axisType][i],
355
+ extremes = axis.getExtremes(),
356
+ userMin = extremes.userMin,
357
+ userMax = extremes.userMax;
358
+
359
+ if (axisCopy && (userMin !== undefined || userMax !== undefined)) {
360
+ axisCopy.setExtremes(userMin, userMax, true, false);
361
+ }
362
+ });
363
+ });
364
+
365
+ // get the SVG from the container's innerHTML
366
+ svg = chartCopy.getChartHTML();
367
+
368
+ // free up memory
369
+ options = null;
370
+ chartCopy.destroy();
371
+ discardElement(sandbox);
372
+
373
+ // Move HTML into a foreignObject
374
+ if (allowHTML) {
375
+ html = svg.match(/<\/svg>(.*?$)/);
376
+ if (html) {
377
+ html = '<foreignObject x="0" y="0" width="200" height="200">' +
378
+ '<body xmlns="http://www.w3.org/1999/xhtml">' +
379
+ html[1] +
380
+ '</body>' +
381
+ '</foreignObject>';
382
+ svg = svg.replace('</svg>', html + '</svg>');
383
+ }
384
+ }
385
+
386
+ // sanitize
387
+ svg = this.sanitizeSVG(svg);
388
+
389
+ // IE9 beta bugs with innerHTML. Test again with final IE9.
390
+ svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
391
+ .replace(/&quot;/g, '\'');
392
+
393
+ return svg;
394
+ },
395
+
396
+ getSVGForExport: function(options, chartOptions) {
397
+ var chartExportingOptions = this.options.exporting;
398
+
399
+ return this.getSVG(merge({
400
+ chart: {
401
+ borderRadius: 0
402
+ }
403
+ },
404
+ chartExportingOptions.chartOptions,
405
+ chartOptions, {
406
+ exporting: {
407
+ sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
408
+ sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
409
+ }
410
+ }
411
+ ));
412
+ },
413
+
414
+ /**
415
+ * Submit the SVG representation of the chart to the server
416
+ * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
417
+ * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
418
+ */
419
+ exportChart: function(options, chartOptions) {
420
+
421
+ var svg = this.getSVGForExport(options, chartOptions);
422
+
423
+ // merge the options
424
+ options = merge(this.options.exporting, options);
425
+
426
+ // do the post
427
+ H.post(options.url, {
428
+ filename: options.filename || 'chart',
429
+ type: options.type,
430
+ width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
431
+ scale: options.scale,
432
+ svg: svg
433
+ }, options.formAttributes);
434
+
435
+ },
436
+
437
+ /**
438
+ * Print the chart
439
+ */
440
+ print: function() {
441
+
442
+ var chart = this,
443
+ container = chart.container,
444
+ origDisplay = [],
445
+ origParent = container.parentNode,
446
+ body = doc.body,
447
+ childNodes = body.childNodes,
448
+ printMaxWidth = chart.options.exporting.printMaxWidth,
449
+ resetParams,
450
+ handleMaxWidth;
451
+
452
+ if (chart.isPrinting) { // block the button while in printing mode
453
+ return;
454
+ }
455
+
456
+ chart.isPrinting = true;
457
+ chart.pointer.reset(null, 0);
458
+
459
+ fireEvent(chart, 'beforePrint');
460
+
461
+ // Handle printMaxWidth
462
+ handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
463
+ if (handleMaxWidth) {
464
+ resetParams = [chart.options.chart.width, undefined, false];
465
+ chart.setSize(printMaxWidth, undefined, false);
466
+ }
467
+
468
+ // hide all body content
469
+ each(childNodes, function(node, i) {
470
+ if (node.nodeType === 1) {
471
+ origDisplay[i] = node.style.display;
472
+ node.style.display = 'none';
473
+ }
474
+ });
475
+
476
+ // pull out the chart
477
+ body.appendChild(container);
478
+
479
+ // print
480
+ win.focus(); // #1510
481
+ win.print();
482
+
483
+ // allow the browser to prepare before reverting
484
+ setTimeout(function() {
485
+
486
+ // put the chart back in
487
+ origParent.appendChild(container);
488
+
489
+ // restore all body content
490
+ each(childNodes, function(node, i) {
491
+ if (node.nodeType === 1) {
492
+ node.style.display = origDisplay[i];
493
+ }
494
+ });
495
+
496
+ chart.isPrinting = false;
497
+
498
+ // Reset printMaxWidth
499
+ if (handleMaxWidth) {
500
+ chart.setSize.apply(chart, resetParams);
501
+ }
502
+
503
+ fireEvent(chart, 'afterPrint');
504
+
505
+ }, 1000);
506
+
507
+ },
508
+
509
+ /**
510
+ * Display a popup menu for choosing the export type
511
+ *
512
+ * @param {String} className An identifier for the menu
513
+ * @param {Array} items A collection with text and onclicks for the items
514
+ * @param {Number} x The x position of the opener button
515
+ * @param {Number} y The y position of the opener button
516
+ * @param {Number} width The width of the opener button
517
+ * @param {Number} height The height of the opener button
518
+ */
519
+ contextMenu: function(className, items, x, y, width, height, button) {
520
+ var chart = this,
521
+ navOptions = chart.options.navigation,
522
+ chartWidth = chart.chartWidth,
523
+ chartHeight = chart.chartHeight,
524
+ cacheName = 'cache-' + className,
525
+ menu = chart[cacheName],
526
+ menuPadding = Math.max(width, height), // for mouse leave detection
527
+ innerMenu,
528
+ hide,
529
+ hideTimer,
530
+ menuStyle,
531
+ docMouseUpHandler = function(e) {
532
+ if (!chart.pointer.inClass(e.target, className)) {
533
+ hide();
534
+ }
535
+ };
536
+
537
+ // create the menu only the first time
538
+ if (!menu) {
539
+
540
+ // create a HTML element above the SVG
541
+ chart[cacheName] = menu = createElement('div', {
542
+ className: className
543
+ }, {
544
+ position: 'absolute',
545
+ zIndex: 1000,
546
+ padding: menuPadding + 'px'
547
+ }, chart.container);
548
+
549
+ innerMenu = createElement('div', {
550
+ className: 'highcharts-menu'
551
+ }, null, menu);
552
+
553
+
554
+ // Presentational CSS
555
+ css(innerMenu, extend({
556
+ MozBoxShadow: '3px 3px 10px #888',
557
+ WebkitBoxShadow: '3px 3px 10px #888',
558
+ boxShadow: '3px 3px 10px #888'
559
+ }, navOptions.menuStyle));
560
+
561
+
562
+ // hide on mouse out
563
+ hide = function() {
564
+ css(menu, {
565
+ display: 'none'
566
+ });
567
+ if (button) {
568
+ button.setState(0);
569
+ }
570
+ chart.openMenu = false;
571
+ };
572
+
573
+ // Hide the menu some time after mouse leave (#1357)
574
+ addEvent(menu, 'mouseleave', function() {
575
+ hideTimer = setTimeout(hide, 500);
576
+ });
577
+ addEvent(menu, 'mouseenter', function() {
578
+ clearTimeout(hideTimer);
579
+ });
580
+
581
+
582
+ // Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
583
+ addEvent(doc, 'mouseup', docMouseUpHandler);
584
+ addEvent(chart, 'destroy', function() {
585
+ removeEvent(doc, 'mouseup', docMouseUpHandler);
586
+ });
587
+
588
+
589
+ // create the items
590
+ each(items, function(item) {
591
+ if (item) {
592
+ var element;
593
+
594
+ if (item.separator) {
595
+ element = createElement('hr', null, null, innerMenu);
596
+
597
+ } else {
598
+ element = createElement('div', {
599
+ className: 'highcharts-menu-item',
600
+ onclick: function(e) {
601
+ if (e) { // IE7
602
+ e.stopPropagation();
603
+ }
604
+ hide();
605
+ if (item.onclick) {
606
+ item.onclick.apply(chart, arguments);
607
+ }
608
+ },
609
+ innerHTML: item.text || chart.options.lang[item.textKey]
610
+ }, null, innerMenu);
611
+
612
+
613
+ element.onmouseover = function() {
614
+ css(this, navOptions.menuItemHoverStyle);
615
+ };
616
+ element.onmouseout = function() {
617
+ css(this, navOptions.menuItemStyle);
618
+ };
619
+ css(element, extend({
620
+ cursor: 'pointer'
621
+ }, navOptions.menuItemStyle));
622
+
623
+ }
624
+
625
+ // Keep references to menu divs to be able to destroy them
626
+ chart.exportDivElements.push(element);
627
+ }
628
+ });
629
+
630
+ // Keep references to menu and innerMenu div to be able to destroy them
631
+ chart.exportDivElements.push(innerMenu, menu);
632
+
633
+ chart.exportMenuWidth = menu.offsetWidth;
634
+ chart.exportMenuHeight = menu.offsetHeight;
635
+ }
636
+
637
+ menuStyle = {
638
+ display: 'block'
639
+ };
640
+
641
+ // if outside right, right align it
642
+ if (x + chart.exportMenuWidth > chartWidth) {
643
+ menuStyle.right = (chartWidth - x - width - menuPadding) + 'px';
644
+ } else {
645
+ menuStyle.left = (x - menuPadding) + 'px';
646
+ }
647
+ // if outside bottom, bottom align it
648
+ if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {
649
+ menuStyle.bottom = (chartHeight - y - menuPadding) + 'px';
650
+ } else {
651
+ menuStyle.top = (y + height - menuPadding) + 'px';
652
+ }
653
+
654
+ css(menu, menuStyle);
655
+ chart.openMenu = true;
656
+ },
657
+
658
+ /**
659
+ * Add the export button to the chart
660
+ */
661
+ addButton: function(options) {
662
+ var chart = this,
663
+ renderer = chart.renderer,
664
+ btnOptions = merge(chart.options.navigation.buttonOptions, options),
665
+ onclick = btnOptions.onclick,
666
+ menuItems = btnOptions.menuItems,
667
+ symbol,
668
+ button,
669
+ symbolSize = btnOptions.symbolSize || 12;
670
+ if (!chart.btnCount) {
671
+ chart.btnCount = 0;
672
+ }
673
+
674
+ // Keeps references to the button elements
675
+ if (!chart.exportDivElements) {
676
+ chart.exportDivElements = [];
677
+ chart.exportSVGElements = [];
678
+ }
679
+
680
+ if (btnOptions.enabled === false) {
681
+ return;
682
+ }
683
+
684
+
685
+ var attr = btnOptions.theme,
686
+ states = attr.states,
687
+ hover = states && states.hover,
688
+ select = states && states.select,
689
+ callback;
690
+
691
+ delete attr.states;
692
+
693
+ if (onclick) {
694
+ callback = function(e) {
695
+ e.stopPropagation();
696
+ onclick.call(chart, e);
697
+ };
698
+
699
+ } else if (menuItems) {
700
+ callback = function() {
701
+ chart.contextMenu(
702
+ button.menuClassName,
703
+ menuItems,
704
+ button.translateX,
705
+ button.translateY,
706
+ button.width,
707
+ button.height,
708
+ button
709
+ );
710
+ button.setState(2);
711
+ };
712
+ }
713
+
714
+
715
+ if (btnOptions.text && btnOptions.symbol) {
716
+ attr.paddingLeft = pick(attr.paddingLeft, 25);
717
+
718
+ } else if (!btnOptions.text) {
719
+ extend(attr, {
720
+ width: btnOptions.width,
721
+ height: btnOptions.height,
722
+ padding: 0
723
+ });
724
+ }
725
+
726
+ button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
727
+ .addClass(options.className)
728
+ .attr({
729
+
730
+ 'stroke-linecap': 'round',
731
+
732
+ title: chart.options.lang[btnOptions._titleKey],
733
+ zIndex: 3 // #4955
734
+ });
735
+ button.menuClassName = options.menuClassName || 'highcharts-menu-' + chart.btnCount++;
736
+
737
+ if (btnOptions.symbol) {
738
+ symbol = renderer.symbol(
739
+ btnOptions.symbol,
740
+ btnOptions.symbolX - (symbolSize / 2),
741
+ btnOptions.symbolY - (symbolSize / 2),
742
+ symbolSize,
743
+ symbolSize
744
+ )
745
+ .addClass('highcharts-button-symbol')
746
+ .attr({
747
+ zIndex: 1
748
+ }).add(button);
749
+
750
+
751
+ symbol.attr({
752
+ stroke: btnOptions.symbolStroke,
753
+ fill: btnOptions.symbolFill,
754
+ 'stroke-width': btnOptions.symbolStrokeWidth || 1
755
+ });
756
+
757
+ }
758
+
759
+ button.add()
760
+ .align(extend(btnOptions, {
761
+ width: button.width,
762
+ x: pick(btnOptions.x, chart.buttonOffset) // #1654
763
+ }), true, 'spacingBox');
764
+
765
+ chart.buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
766
+
767
+ chart.exportSVGElements.push(button, symbol);
768
+
769
+ },
770
+
771
+ /**
772
+ * Destroy the buttons.
773
+ */
774
+ destroyExport: function(e) {
775
+ var chart = e ? e.target : this,
776
+ exportSVGElements = chart.exportSVGElements,
777
+ exportDivElements = chart.exportDivElements;
778
+
779
+ // Destroy the extra buttons added
780
+ if (exportSVGElements) {
781
+ each(exportSVGElements, function(elem, i) {
782
+
783
+ // Destroy and null the svg/vml elements
784
+ if (elem) { // #1822
785
+ elem.onclick = elem.ontouchstart = null;
786
+ chart.exportSVGElements[i] = elem.destroy();
787
+ }
788
+ });
789
+ exportSVGElements.length = 0;
790
+ }
791
+
792
+ // Destroy the divs for the menu
793
+ if (exportDivElements) {
794
+ each(exportDivElements, function(elem, i) {
795
+
796
+ // Remove the event handler
797
+ removeEvent(elem, 'mouseleave');
798
+
799
+ // Remove inline events
800
+ chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
801
+
802
+ // Destroy the div by moving to garbage bin
803
+ discardElement(elem);
804
+ });
805
+ exportDivElements.length = 0;
806
+ }
807
+ }
808
+ });
809
+
810
+
811
+
812
+
813
+ symbols.menu = function(x, y, width, height) {
814
+ var arr = [
815
+ 'M', x, y + 2.5,
816
+ 'L', x + width, y + 2.5,
817
+ 'M', x, y + height / 2 + 0.5,
818
+ 'L', x + width, y + height / 2 + 0.5,
819
+ 'M', x, y + height - 1.5,
820
+ 'L', x + width, y + height - 1.5
821
+ ];
822
+ return arr;
823
+ };
824
+
825
+ // Add the buttons on chart load
826
+ Chart.prototype.renderExporting = function() {
827
+ var n,
828
+ exportingOptions = this.options.exporting,
829
+ buttons = exportingOptions.buttons,
830
+ isDirty = this.isDirtyExporting || !this.exportSVGElements;
831
+
832
+ this.buttonOffset = 0;
833
+ if (this.isDirtyExporting) {
834
+ this.destroyExport();
835
+ }
836
+
837
+ if (isDirty && exportingOptions.enabled !== false) {
838
+
839
+ for (n in buttons) {
840
+ this.addButton(buttons[n]);
841
+ }
842
+
843
+ this.isDirtyExporting = false;
844
+ }
845
+
846
+ // Destroy the export elements at chart destroy
847
+ addEvent(this, 'destroy', this.destroyExport);
848
+ };
849
+
850
+ Chart.prototype.callbacks.push(function(chart) {
851
+
852
+ function update(prop, options, redraw) {
853
+ chart.isDirtyExporting = true;
854
+ merge(true, chart.options[prop], options);
855
+ if (pick(redraw, true)) {
856
+ chart.redraw();
857
+ }
858
+
859
+ }
860
+
861
+ chart.renderExporting();
862
+
863
+ addEvent(chart, 'redraw', chart.renderExporting);
864
+
865
+ // Add update methods to handle chart.update and chart.exporting.update
866
+ // and chart.navigation.update.
867
+ each(['exporting', 'navigation'], function(prop) {
868
+ chart[prop] = {
869
+ update: function(options, redraw) {
870
+ update(prop, options, redraw);
871
+ }
872
+ };
873
+ });
874
+ });
875
+
876
+ }(Highcharts));
798
877
  }));