highcharts-js-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,734 @@
1
+ /**
2
+ * @license Highcharts JS v2.2.0 (2012-02-16)
3
+ * Exporting module
4
+ *
5
+ * (c) 2010-2011 Torstein Hønsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ // JSLint options:
11
+ /*global Highcharts, document, window, Math, setTimeout */
12
+
13
+ (function () { // encapsulate
14
+
15
+ // create shortcuts
16
+ var HC = Highcharts,
17
+ Chart = HC.Chart,
18
+ addEvent = HC.addEvent,
19
+ removeEvent = HC.removeEvent,
20
+ createElement = HC.createElement,
21
+ discardElement = HC.discardElement,
22
+ css = HC.css,
23
+ merge = HC.merge,
24
+ each = HC.each,
25
+ extend = HC.extend,
26
+ math = Math,
27
+ mathMax = math.max,
28
+ doc = document,
29
+ win = window,
30
+ hasTouch = doc.documentElement.ontouchstart !== undefined,
31
+ M = 'M',
32
+ L = 'L',
33
+ DIV = 'div',
34
+ HIDDEN = 'hidden',
35
+ NONE = 'none',
36
+ PREFIX = 'highcharts-',
37
+ ABSOLUTE = 'absolute',
38
+ PX = 'px',
39
+ UNDEFINED,
40
+ defaultOptions = HC.getOptions();
41
+
42
+ // Add language
43
+ extend(defaultOptions.lang, {
44
+ downloadPNG: 'Download PNG image',
45
+ downloadJPEG: 'Download JPEG image',
46
+ downloadPDF: 'Download PDF document',
47
+ downloadSVG: 'Download SVG vector image',
48
+ exportButtonTitle: 'Export to raster or vector image',
49
+ printButtonTitle: 'Print the chart'
50
+ });
51
+
52
+ // Buttons and menus are collected in a separate config option set called 'navigation'.
53
+ // This can be extended later to add control buttons like zoom and pan right click menus.
54
+ defaultOptions.navigation = {
55
+ menuStyle: {
56
+ border: '1px solid #A0A0A0',
57
+ background: '#FFFFFF'
58
+ },
59
+ menuItemStyle: {
60
+ padding: '0 5px',
61
+ background: NONE,
62
+ color: '#303030',
63
+ fontSize: hasTouch ? '14px' : '11px'
64
+ },
65
+ menuItemHoverStyle: {
66
+ background: '#4572A5',
67
+ color: '#FFFFFF'
68
+ },
69
+
70
+ buttonOptions: {
71
+ align: 'right',
72
+ backgroundColor: {
73
+ linearGradient: [0, 0, 0, 20],
74
+ stops: [
75
+ [0.4, '#F7F7F7'],
76
+ [0.6, '#E3E3E3']
77
+ ]
78
+ },
79
+ borderColor: '#B0B0B0',
80
+ borderRadius: 3,
81
+ borderWidth: 1,
82
+ //enabled: true,
83
+ height: 20,
84
+ hoverBorderColor: '#909090',
85
+ hoverSymbolFill: '#81A7CF',
86
+ hoverSymbolStroke: '#4572A5',
87
+ symbolFill: '#E0E0E0',
88
+ //symbolSize: 12,
89
+ symbolStroke: '#A0A0A0',
90
+ //symbolStrokeWidth: 1,
91
+ symbolX: 11.5,
92
+ symbolY: 10.5,
93
+ verticalAlign: 'top',
94
+ width: 24,
95
+ y: 10
96
+ }
97
+ };
98
+
99
+
100
+
101
+ // Add the export related options
102
+ defaultOptions.exporting = {
103
+ //enabled: true,
104
+ //filename: 'chart',
105
+ type: 'image/png',
106
+ url: 'http://export.highcharts.com/',
107
+ width: 800,
108
+ buttons: {
109
+ exportButton: {
110
+ //enabled: true,
111
+ symbol: 'exportIcon',
112
+ x: -10,
113
+ symbolFill: '#A8BF77',
114
+ hoverSymbolFill: '#768F3E',
115
+ _id: 'exportButton',
116
+ _titleKey: 'exportButtonTitle',
117
+ menuItems: [{
118
+ textKey: 'downloadPNG',
119
+ onclick: function () {
120
+ this.exportChart();
121
+ }
122
+ }, {
123
+ textKey: 'downloadJPEG',
124
+ onclick: function () {
125
+ this.exportChart({
126
+ type: 'image/jpeg'
127
+ });
128
+ }
129
+ }, {
130
+ textKey: 'downloadPDF',
131
+ onclick: function () {
132
+ this.exportChart({
133
+ type: 'application/pdf'
134
+ });
135
+ }
136
+ }, {
137
+ textKey: 'downloadSVG',
138
+ onclick: function () {
139
+ this.exportChart({
140
+ type: 'image/svg+xml'
141
+ });
142
+ }
143
+ }
144
+ // Enable this block to add "View SVG" to the dropdown menu
145
+ /*
146
+ ,{
147
+
148
+ text: 'View SVG',
149
+ onclick: function () {
150
+ var svg = this.getSVG()
151
+ .replace(/</g, '\n&lt;')
152
+ .replace(/>/g, '&gt;');
153
+
154
+ doc.body.innerHTML = '<pre>' + svg + '</pre>';
155
+ }
156
+ } // */
157
+ ]
158
+
159
+ },
160
+ printButton: {
161
+ //enabled: true,
162
+ symbol: 'printIcon',
163
+ x: -36,
164
+ symbolFill: '#B5C9DF',
165
+ hoverSymbolFill: '#779ABF',
166
+ _id: 'printButton',
167
+ _titleKey: 'printButtonTitle',
168
+ onclick: function () {
169
+ this.print();
170
+ }
171
+ }
172
+ }
173
+ };
174
+
175
+
176
+
177
+ extend(Chart.prototype, {
178
+ /**
179
+ * Return an SVG representation of the chart
180
+ *
181
+ * @param additionalOptions {Object} Additional chart options for the generated SVG representation
182
+ */
183
+ getSVG: function (additionalOptions) {
184
+ var chart = this,
185
+ chartCopy,
186
+ sandbox,
187
+ svg,
188
+ seriesOptions,
189
+ options = merge(chart.options, additionalOptions); // copy the options and add extra options
190
+
191
+ // IE compatibility hack for generating SVG content that it doesn't really understand
192
+ if (!doc.createElementNS) {
193
+ /*jslint unparam: true*//* allow unused parameter ns in function below */
194
+ doc.createElementNS = function (ns, tagName) {
195
+ var elem = doc.createElement(tagName);
196
+ elem.getBBox = function () {
197
+ return HC.Renderer.prototype.Element.prototype.getBBox.apply({ element: elem });
198
+ };
199
+ return elem;
200
+ };
201
+ /*jslint unparam: false*/
202
+ }
203
+
204
+ // create a sandbox where a new chart will be generated
205
+ sandbox = createElement(DIV, null, {
206
+ position: ABSOLUTE,
207
+ top: '-9999em',
208
+ width: chart.chartWidth + PX,
209
+ height: chart.chartHeight + PX
210
+ }, doc.body);
211
+
212
+ // override some options
213
+ extend(options.chart, {
214
+ renderTo: sandbox,
215
+ forExport: true
216
+ });
217
+ options.exporting.enabled = false; // hide buttons in print
218
+ options.chart.plotBackgroundImage = null; // the converter doesn't handle images
219
+
220
+ // prepare for replicating the chart
221
+ options.series = [];
222
+ each(chart.series, function (serie) {
223
+ seriesOptions = merge(serie.options, {
224
+ animation: false, // turn off animation
225
+ showCheckbox: false,
226
+ visible: serie.visible
227
+ });
228
+
229
+ if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
230
+
231
+ // remove image markers
232
+ if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
233
+ seriesOptions.marker.symbol = 'circle';
234
+ }
235
+
236
+ options.series.push(seriesOptions);
237
+ }
238
+ });
239
+
240
+ // generate the chart copy
241
+ chartCopy = new Highcharts.Chart(options);
242
+
243
+ // reflect axis extremes in the export
244
+ each(['xAxis', 'yAxis'], function (axisType) {
245
+ each(chart[axisType], function (axis, i) {
246
+ var axisCopy = chartCopy[axisType][i],
247
+ extremes = axis.getExtremes(),
248
+ userMin = extremes.userMin,
249
+ userMax = extremes.userMax;
250
+
251
+ if (userMin !== UNDEFINED || userMax !== UNDEFINED) {
252
+ axisCopy.setExtremes(userMin, userMax, true, false);
253
+ }
254
+ });
255
+ });
256
+
257
+ // get the SVG from the container's innerHTML
258
+ svg = chartCopy.container.innerHTML;
259
+
260
+ // free up memory
261
+ options = null;
262
+ chartCopy.destroy();
263
+ discardElement(sandbox);
264
+
265
+ // sanitize
266
+ svg = svg
267
+ .replace(/zIndex="[^"]+"/g, '')
268
+ .replace(/isShadow="[^"]+"/g, '')
269
+ .replace(/symbolName="[^"]+"/g, '')
270
+ .replace(/jQuery[0-9]+="[^"]+"/g, '')
271
+ .replace(/isTracker="[^"]+"/g, '')
272
+ .replace(/url\([^#]+#/g, 'url(#')
273
+ /*.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
274
+ .replace(/ href=/, ' xlink:href=')
275
+ .replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
276
+ /* This fails in IE < 8
277
+ .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
278
+ return s2 +'.'+ s3[0];
279
+ })*/
280
+
281
+ // Replace HTML entities, issue #347
282
+ .replace(/&nbsp;/g, '\u00A0') // no-break space
283
+ .replace(/&shy;/g, '\u00AD') // soft hyphen
284
+
285
+ // IE specific
286
+ .replace(/id=([^" >]+)/g, 'id="$1"')
287
+ .replace(/class=([^" ]+)/g, 'class="$1"')
288
+ .replace(/ transform /g, ' ')
289
+ .replace(/:(path|rect)/g, '$1')
290
+ .replace(/style="([^"]+)"/g, function (s) {
291
+ return s.toLowerCase();
292
+ });
293
+
294
+ // IE9 beta bugs with innerHTML. Test again with final IE9.
295
+ svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
296
+ .replace(/&quot;/g, "'");
297
+ if (svg.match(/ xmlns="/g).length === 2) {
298
+ svg = svg.replace(/xmlns="[^"]+"/, '');
299
+ }
300
+
301
+ return svg;
302
+ },
303
+
304
+ /**
305
+ * Submit the SVG representation of the chart to the server
306
+ * @param {Object} options Exporting options. Possible members are url, type and width.
307
+ * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
308
+ */
309
+ exportChart: function (options, chartOptions) {
310
+ var form,
311
+ chart = this,
312
+ svg = chart.getSVG(merge(chart.options.exporting.chartOptions, chartOptions)); // docs
313
+
314
+ // merge the options
315
+ options = merge(chart.options.exporting, options);
316
+
317
+ // create the form
318
+ form = createElement('form', {
319
+ method: 'post',
320
+ action: options.url
321
+ }, {
322
+ display: NONE
323
+ }, doc.body);
324
+
325
+ // add the values
326
+ each(['filename', 'type', 'width', 'svg'], function (name) {
327
+ createElement('input', {
328
+ type: HIDDEN,
329
+ name: name,
330
+ value: {
331
+ filename: options.filename || 'chart',
332
+ type: options.type,
333
+ width: options.width,
334
+ svg: svg
335
+ }[name]
336
+ }, null, form);
337
+ });
338
+
339
+ // submit
340
+ form.submit();
341
+
342
+ // clean up
343
+ discardElement(form);
344
+ },
345
+
346
+ /**
347
+ * Print the chart
348
+ */
349
+ print: function () {
350
+
351
+ var chart = this,
352
+ container = chart.container,
353
+ origDisplay = [],
354
+ origParent = container.parentNode,
355
+ body = doc.body,
356
+ childNodes = body.childNodes;
357
+
358
+ if (chart.isPrinting) { // block the button while in printing mode
359
+ return;
360
+ }
361
+
362
+ chart.isPrinting = true;
363
+
364
+ // hide all body content
365
+ each(childNodes, function (node, i) {
366
+ if (node.nodeType === 1) {
367
+ origDisplay[i] = node.style.display;
368
+ node.style.display = NONE;
369
+ }
370
+ });
371
+
372
+ // pull out the chart
373
+ body.appendChild(container);
374
+
375
+ // print
376
+ win.print();
377
+
378
+ // allow the browser to prepare before reverting
379
+ setTimeout(function () {
380
+
381
+ // put the chart back in
382
+ origParent.appendChild(container);
383
+
384
+ // restore all body content
385
+ each(childNodes, function (node, i) {
386
+ if (node.nodeType === 1) {
387
+ node.style.display = origDisplay[i];
388
+ }
389
+ });
390
+
391
+ chart.isPrinting = false;
392
+
393
+ }, 1000);
394
+
395
+ },
396
+
397
+ /**
398
+ * Display a popup menu for choosing the export type
399
+ *
400
+ * @param {String} name An identifier for the menu
401
+ * @param {Array} items A collection with text and onclicks for the items
402
+ * @param {Number} x The x position of the opener button
403
+ * @param {Number} y The y position of the opener button
404
+ * @param {Number} width The width of the opener button
405
+ * @param {Number} height The height of the opener button
406
+ */
407
+ contextMenu: function (name, items, x, y, width, height) {
408
+ var chart = this,
409
+ navOptions = chart.options.navigation,
410
+ menuItemStyle = navOptions.menuItemStyle,
411
+ chartWidth = chart.chartWidth,
412
+ chartHeight = chart.chartHeight,
413
+ cacheName = 'cache-' + name,
414
+ menu = chart[cacheName],
415
+ menuPadding = mathMax(width, height), // for mouse leave detection
416
+ boxShadow = '3px 3px 10px #888',
417
+ innerMenu,
418
+ hide,
419
+ menuStyle;
420
+
421
+ // create the menu only the first time
422
+ if (!menu) {
423
+
424
+ // create a HTML element above the SVG
425
+ chart[cacheName] = menu = createElement(DIV, {
426
+ className: PREFIX + name
427
+ }, {
428
+ position: ABSOLUTE,
429
+ zIndex: 1000,
430
+ padding: menuPadding + PX
431
+ }, chart.container);
432
+
433
+ innerMenu = createElement(DIV, null,
434
+ extend({
435
+ MozBoxShadow: boxShadow,
436
+ WebkitBoxShadow: boxShadow,
437
+ boxShadow: boxShadow
438
+ }, navOptions.menuStyle), menu);
439
+
440
+ // hide on mouse out
441
+ hide = function () {
442
+ css(menu, { display: NONE });
443
+ };
444
+
445
+ addEvent(menu, 'mouseleave', hide);
446
+
447
+
448
+ // create the items
449
+ each(items, function (item) {
450
+ if (item) {
451
+ var div = createElement(DIV, {
452
+ onmouseover: function () {
453
+ css(this, navOptions.menuItemHoverStyle);
454
+ },
455
+ onmouseout: function () {
456
+ css(this, menuItemStyle);
457
+ },
458
+ innerHTML: item.text || chart.options.lang[item.textKey]
459
+ }, extend({
460
+ cursor: 'pointer'
461
+ }, menuItemStyle), innerMenu);
462
+
463
+ div[hasTouch ? 'ontouchstart' : 'onclick'] = function () {
464
+ hide();
465
+ item.onclick.apply(chart, arguments);
466
+ };
467
+
468
+ // Keep references to menu divs to be able to destroy them
469
+ chart.exportDivElements.push(div);
470
+ }
471
+ });
472
+
473
+ // Keep references to menu and innerMenu div to be able to destroy them
474
+ chart.exportDivElements.push(innerMenu, menu);
475
+
476
+ chart.exportMenuWidth = menu.offsetWidth;
477
+ chart.exportMenuHeight = menu.offsetHeight;
478
+ }
479
+
480
+ menuStyle = { display: 'block' };
481
+
482
+ // if outside right, right align it
483
+ if (x + chart.exportMenuWidth > chartWidth) {
484
+ menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
485
+ } else {
486
+ menuStyle.left = (x - menuPadding) + PX;
487
+ }
488
+ // if outside bottom, bottom align it
489
+ if (y + height + chart.exportMenuHeight > chartHeight) {
490
+ menuStyle.bottom = (chartHeight - y - menuPadding) + PX;
491
+ } else {
492
+ menuStyle.top = (y + height - menuPadding) + PX;
493
+ }
494
+
495
+ css(menu, menuStyle);
496
+ },
497
+
498
+ /**
499
+ * Add the export button to the chart
500
+ */
501
+ addButton: function (options) {
502
+ var chart = this,
503
+ renderer = chart.renderer,
504
+ btnOptions = merge(chart.options.navigation.buttonOptions, options),
505
+ onclick = btnOptions.onclick,
506
+ menuItems = btnOptions.menuItems,
507
+ buttonWidth = btnOptions.width,
508
+ buttonHeight = btnOptions.height,
509
+ box,
510
+ symbol,
511
+ button,
512
+ borderWidth = btnOptions.borderWidth,
513
+ boxAttr = {
514
+ stroke: btnOptions.borderColor
515
+
516
+ },
517
+ symbolAttr = {
518
+ stroke: btnOptions.symbolStroke,
519
+ fill: btnOptions.symbolFill
520
+ },
521
+ symbolSize = btnOptions.symbolSize || 12;
522
+
523
+ // Keeps references to the button elements
524
+ if (!chart.exportDivElements) {
525
+ chart.exportDivElements = [];
526
+ chart.exportSVGElements = [];
527
+ }
528
+
529
+ if (btnOptions.enabled === false) {
530
+ return;
531
+ }
532
+
533
+ // element to capture the click
534
+ function revert() {
535
+ symbol.attr(symbolAttr);
536
+ box.attr(boxAttr);
537
+ }
538
+
539
+ // the box border
540
+ box = renderer.rect(
541
+ 0,
542
+ 0,
543
+ buttonWidth,
544
+ buttonHeight,
545
+ btnOptions.borderRadius,
546
+ borderWidth
547
+ )
548
+ //.translate(buttonLeft, buttonTop) // to allow gradients
549
+ .align(btnOptions, true)
550
+ .attr(extend({
551
+ fill: btnOptions.backgroundColor,
552
+ 'stroke-width': borderWidth,
553
+ zIndex: 19
554
+ }, boxAttr)).add();
555
+
556
+ // the invisible element to track the clicks
557
+ button = renderer.rect(
558
+ 0,
559
+ 0,
560
+ buttonWidth,
561
+ buttonHeight,
562
+ 0
563
+ )
564
+ .align(btnOptions)
565
+ .attr({
566
+ id: btnOptions._id,
567
+ fill: 'rgba(255, 255, 255, 0.001)',
568
+ title: chart.options.lang[btnOptions._titleKey],
569
+ zIndex: 21
570
+ }).css({
571
+ cursor: 'pointer'
572
+ })
573
+ .on('mouseover', function () {
574
+ symbol.attr({
575
+ stroke: btnOptions.hoverSymbolStroke,
576
+ fill: btnOptions.hoverSymbolFill
577
+ });
578
+ box.attr({
579
+ stroke: btnOptions.hoverBorderColor
580
+ });
581
+ })
582
+ .on('mouseout', revert)
583
+ .on('click', revert)
584
+ .add();
585
+
586
+ // add the click event
587
+ if (menuItems) {
588
+ onclick = function () {
589
+ revert();
590
+ var bBox = button.getBBox();
591
+ chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight);
592
+ };
593
+ }
594
+ /*addEvent(button.element, 'click', function() {
595
+ onclick.apply(chart, arguments);
596
+ });*/
597
+ button.on('click', function () {
598
+ onclick.apply(chart, arguments);
599
+ });
600
+
601
+ // the icon
602
+ symbol = renderer.symbol(
603
+ btnOptions.symbol,
604
+ btnOptions.symbolX - (symbolSize / 2),
605
+ btnOptions.symbolY - (symbolSize / 2),
606
+ symbolSize,
607
+ symbolSize
608
+ )
609
+ .align(btnOptions, true)
610
+ .attr(extend(symbolAttr, {
611
+ 'stroke-width': btnOptions.symbolStrokeWidth || 1,
612
+ zIndex: 20
613
+ })).add();
614
+
615
+ // Keep references to the renderer element so to be able to destroy them later.
616
+ chart.exportSVGElements.push(box, button, symbol);
617
+ },
618
+
619
+ /**
620
+ * Destroy the buttons.
621
+ */
622
+ destroyExport: function () {
623
+ var i,
624
+ chart = this,
625
+ elem;
626
+
627
+ // Destroy the extra buttons added
628
+ for (i = 0; i < chart.exportSVGElements.length; i++) {
629
+ elem = chart.exportSVGElements[i];
630
+ // Destroy and null the svg/vml elements
631
+ elem.onclick = elem.ontouchstart = null;
632
+ chart.exportSVGElements[i] = elem.destroy();
633
+ }
634
+
635
+ // Destroy the divs for the menu
636
+ for (i = 0; i < chart.exportDivElements.length; i++) {
637
+ elem = chart.exportDivElements[i];
638
+
639
+ // Remove the event handler
640
+ removeEvent(elem, 'mouseleave');
641
+
642
+ // Remove inline events
643
+ chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
644
+
645
+ // Destroy the div by moving to garbage bin
646
+ discardElement(elem);
647
+ }
648
+ }
649
+ });
650
+
651
+ /**
652
+ * Crisp for 1px stroke width, which is default. In the future, consider a smarter,
653
+ * global function.
654
+ */
655
+ function crisp(arr) {
656
+ var i = arr.length;
657
+ while (i--) {
658
+ if (typeof arr[i] === 'number') {
659
+ arr[i] = Math.round(arr[i]) - 0.5;
660
+ }
661
+ }
662
+ return arr;
663
+ }
664
+
665
+ // Create the export icon
666
+ HC.Renderer.prototype.symbols.exportIcon = function (x, y, width, height) {
667
+ return crisp([
668
+ M, // the disk
669
+ x, y + width,
670
+ L,
671
+ x + width, y + height,
672
+ x + width, y + height * 0.8,
673
+ x, y + height * 0.8,
674
+ 'Z',
675
+ M, // the arrow
676
+ x + width * 0.5, y + height * 0.8,
677
+ L,
678
+ x + width * 0.8, y + height * 0.4,
679
+ x + width * 0.4, y + height * 0.4,
680
+ x + width * 0.4, y,
681
+ x + width * 0.6, y,
682
+ x + width * 0.6, y + height * 0.4,
683
+ x + width * 0.2, y + height * 0.4,
684
+ 'Z'
685
+ ]);
686
+ };
687
+ // Create the print icon
688
+ HC.Renderer.prototype.symbols.printIcon = function (x, y, width, height) {
689
+ return crisp([
690
+ M, // the printer
691
+ x, y + height * 0.7,
692
+ L,
693
+ x + width, y + height * 0.7,
694
+ x + width, y + height * 0.4,
695
+ x, y + height * 0.4,
696
+ 'Z',
697
+ M, // the upper sheet
698
+ x + width * 0.2, y + height * 0.4,
699
+ L,
700
+ x + width * 0.2, y,
701
+ x + width * 0.8, y,
702
+ x + width * 0.8, y + height * 0.4,
703
+ 'Z',
704
+ M, // the lower sheet
705
+ x + width * 0.2, y + height * 0.7,
706
+ L,
707
+ x, y + height,
708
+ x + width, y + height,
709
+ x + width * 0.8, y + height * 0.7,
710
+ 'Z'
711
+ ]);
712
+ };
713
+
714
+
715
+ // Add the buttons on chart load
716
+ Chart.prototype.callbacks.push(function (chart) {
717
+ var n,
718
+ exportingOptions = chart.options.exporting,
719
+ buttons = exportingOptions.buttons;
720
+
721
+ if (exportingOptions.enabled !== false) {
722
+
723
+ for (n in buttons) {
724
+ chart.addButton(buttons[n]);
725
+ }
726
+
727
+ // Destroy the export elements at chart destroy
728
+ addEvent(chart, 'destroy', chart.destroyExport);
729
+ }
730
+
731
+ });
732
+
733
+
734
+ }());