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