highcharts-rails 4.2.7 → 5.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.
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
  }));