Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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