Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 lars 1
/**
2
 * @license Highcharts JS v4.1.8 (2015-08-20)
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: !allowHTML,
297
			width: sourceWidth,
298
			height: sourceHeight
299
		});
300
		options.exporting.enabled = false; // hide buttons in print
301
		delete options.data; // #3004
302
 
303
		// prepare for replicating the chart
304
		options.series = [];
305
		each(chart.series, function (serie) {
306
			seriesOptions = merge(serie.options, {
307
				animation: false, // turn off animation
308
				enableMouseTracking: false,
309
				showCheckbox: false,
310
				visible: serie.visible
311
			});
312
 
313
			if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
314
				options.series.push(seriesOptions);
315
			}
316
		});
317
 
318
		// Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
319
		if (additionalOptions) {
320
			each(['xAxis', 'yAxis'], function (axisType) {
321
				each(splat(additionalOptions[axisType]), function (axisOptions, i) {
322
					options[axisType][i] = merge(options[axisType][i], axisOptions);
323
				});
324
			});
325
		}
326
 
327
		// generate the chart copy
328
		chartCopy = new Highcharts.Chart(options, chart.callback);
329
 
330
		// reflect axis extremes in the export
331
		each(['xAxis', 'yAxis'], function (axisType) {
332
			each(chart[axisType], function (axis, i) {
333
				var axisCopy = chartCopy[axisType][i],
334
					extremes = axis.getExtremes(),
335
					userMin = extremes.userMin,
336
					userMax = extremes.userMax;
337
 
338
				if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) {
339
					axisCopy.setExtremes(userMin, userMax, true, false);
340
				}
341
			});
342
		});
343
 
344
		// get the SVG from the container's innerHTML
345
		svg = chartCopy.getChartHTML();
346
 
347
		// free up memory
348
		options = null;
349
		chartCopy.destroy();
350
		discardElement(sandbox);
351
 
352
		// Move HTML into a foreignObject
353
		if (allowHTML) {
354
			html = svg.match(/<\/svg>(.*?$)/);
355
			if (html) {
356
				html = '<foreignObject x="0" y="0 width="200" height="200">' +
357
					'<body xmlns="http://www.w3.org/1999/xhtml">' +
358
					html[1] +
359
					'</body>' +
360
					'</foreignObject>';
361
				svg = svg.replace('</svg>', html + '</svg>');
362
			}
363
		}
364
 
365
		// sanitize
366
		svg = this.sanitizeSVG(svg);
367
 
368
		// IE9 beta bugs with innerHTML. Test again with final IE9.
369
		svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
370
			.replace(/&quot;/g, "'");
371
 
372
		return svg;
373
	},
374
 
375
	getSVGForExport: function (options, chartOptions) {
376
		var chartExportingOptions = this.options.exporting;
377
 
378
		return this.getSVG(merge(
379
			{ chart: { borderRadius: 0 } },
380
			chartExportingOptions.chartOptions,
381
			chartOptions,
382
			{
383
				exporting: {
384
					sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
385
					sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
386
				}
387
			}
388
		));
389
	},
390
 
391
	/**
392
	 * Submit the SVG representation of the chart to the server
393
	 * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
394
	 * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
395
	 */
396
	exportChart: function (options, chartOptions) {
397
 
398
		var svg = this.getSVGForExport(options, chartOptions);
399
 
400
		// merge the options
401
		options = merge(this.options.exporting, options);
402
 
403
		// do the post
404
		Highcharts.post(options.url, {
405
			filename: options.filename || 'chart',
406
			type: options.type,
407
			width: options.width || 0, // IE8 fails to post undefined correctly, so use 0
408
			scale: options.scale || 2,
409
			svg: svg
410
		}, options.formAttributes);
411
 
412
	},
413
 
414
	/**
415
	 * Print the chart
416
	 */
417
	print: function () {
418
 
419
		var chart = this,
420
			container = chart.container,
421
			origDisplay = [],
422
			origParent = container.parentNode,
423
			body = doc.body,
424
			childNodes = body.childNodes;
425
 
426
		if (chart.isPrinting) { // block the button while in printing mode
427
			return;
428
		}
429
 
430
		chart.isPrinting = true;
431
 
432
		fireEvent(chart, 'beforePrint');
433
 
434
		// hide all body content
435
		each(childNodes, function (node, i) {
436
			if (node.nodeType === 1) {
437
				origDisplay[i] = node.style.display;
438
				node.style.display = NONE;
439
			}
440
		});
441
 
442
		// pull out the chart
443
		body.appendChild(container);
444
 
445
		// print
446
		win.focus(); // #1510
447
		win.print();
448
 
449
		// allow the browser to prepare before reverting
450
		setTimeout(function () {
451
 
452
			// put the chart back in
453
			origParent.appendChild(container);
454
 
455
			// restore all body content
456
			each(childNodes, function (node, i) {
457
				if (node.nodeType === 1) {
458
					node.style.display = origDisplay[i];
459
				}
460
			});
461
 
462
			chart.isPrinting = false;
463
 
464
			fireEvent(chart, 'afterPrint');
465
 
466
		}, 1000);
467
 
468
	},
469
 
470
	/**
471
	 * Display a popup menu for choosing the export type
472
	 *
473
	 * @param {String} className An identifier for the menu
474
	 * @param {Array} items A collection with text and onclicks for the items
475
	 * @param {Number} x The x position of the opener button
476
	 * @param {Number} y The y position of the opener button
477
	 * @param {Number} width The width of the opener button
478
	 * @param {Number} height The height of the opener button
479
	 */
480
	contextMenu: function (className, items, x, y, width, height, button) {
481
		var chart = this,
482
			navOptions = chart.options.navigation,
483
			menuItemStyle = navOptions.menuItemStyle,
484
			chartWidth = chart.chartWidth,
485
			chartHeight = chart.chartHeight,
486
			cacheName = 'cache-' + className,
487
			menu = chart[cacheName],
488
			menuPadding = mathMax(width, height), // for mouse leave detection
489
			boxShadow = '3px 3px 10px #888',
490
			innerMenu,
491
			hide,
492
			hideTimer,
493
			menuStyle,
494
			docMouseUpHandler = function (e) {
495
				if (!chart.pointer.inClass(e.target, className)) {
496
					hide();
497
				}
498
			};
499
 
500
		// create the menu only the first time
501
		if (!menu) {
502
 
503
			// create a HTML element above the SVG
504
			chart[cacheName] = menu = createElement(DIV, {
505
				className: className
506
			}, {
507
				position: ABSOLUTE,
508
				zIndex: 1000,
509
				padding: menuPadding + PX
510
			}, chart.container);
511
 
512
			innerMenu = createElement(DIV, null,
513
				extend({
514
					MozBoxShadow: boxShadow,
515
					WebkitBoxShadow: boxShadow,
516
					boxShadow: boxShadow
517
				}, navOptions.menuStyle), menu);
518
 
519
			// hide on mouse out
520
			hide = function () {
521
				css(menu, { display: NONE });
522
				if (button) {
523
					button.setState(0);
524
				}
525
				chart.openMenu = false;
526
			};
527
 
528
			// Hide the menu some time after mouse leave (#1357)
529
			addEvent(menu, 'mouseleave', function () {
530
				hideTimer = setTimeout(hide, 500);
531
			});
532
			addEvent(menu, 'mouseenter', function () {
533
				clearTimeout(hideTimer);
534
			});
535
 
536
 
537
			// Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
538
			addEvent(document, 'mouseup', docMouseUpHandler);
539
			addEvent(chart, 'destroy', function () {
540
				removeEvent(document, 'mouseup', docMouseUpHandler);
541
			});
542
 
543
 
544
			// create the items
545
			each(items, function (item) {
546
				if (item) {
547
					var element = item.separator ?
548
						createElement('hr', null, null, innerMenu) :
549
						createElement(DIV, {
550
							onmouseover: function () {
551
								css(this, navOptions.menuItemHoverStyle);
552
							},
553
							onmouseout: function () {
554
								css(this, menuItemStyle);
555
							},
556
							onclick: function (e) {
557
								e.stopPropagation();
558
								hide();
559
								if (item.onclick) {
560
									item.onclick.apply(chart, arguments);
561
								}
562
							},
563
							innerHTML: item.text || chart.options.lang[item.textKey]
564
						}, extend({
565
							cursor: 'pointer'
566
						}, menuItemStyle), innerMenu);
567
 
568
 
569
					// Keep references to menu divs to be able to destroy them
570
					chart.exportDivElements.push(element);
571
				}
572
			});
573
 
574
			// Keep references to menu and innerMenu div to be able to destroy them
575
			chart.exportDivElements.push(innerMenu, menu);
576
 
577
			chart.exportMenuWidth = menu.offsetWidth;
578
			chart.exportMenuHeight = menu.offsetHeight;
579
		}
580
 
581
		menuStyle = { display: 'block' };
582
 
583
		// if outside right, right align it
584
		if (x + chart.exportMenuWidth > chartWidth) {
585
			menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
586
		} else {
587
			menuStyle.left = (x - menuPadding) + PX;
588
		}
589
		// if outside bottom, bottom align it
590
		if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {
591
			menuStyle.bottom = (chartHeight - y - menuPadding)  + PX;
592
		} else {
593
			menuStyle.top = (y + height - menuPadding) + PX;
594
		}
595
 
596
		css(menu, menuStyle);
597
		chart.openMenu = true;
598
	},
599
 
600
	/**
601
	 * Add the export button to the chart
602
	 */
603
	addButton: function (options) {
604
		var chart = this,
605
			renderer = chart.renderer,
606
			btnOptions = merge(chart.options.navigation.buttonOptions, options),
607
			onclick = btnOptions.onclick,
608
			menuItems = btnOptions.menuItems,
609
			symbol,
610
			button,
611
			symbolAttr = {
612
				stroke: btnOptions.symbolStroke,
613
				fill: btnOptions.symbolFill
614
			},
615
			symbolSize = btnOptions.symbolSize || 12;
616
		if (!chart.btnCount) {
617
			chart.btnCount = 0;
618
		}
619
 
620
		// Keeps references to the button elements
621
		if (!chart.exportDivElements) {
622
			chart.exportDivElements = [];
623
			chart.exportSVGElements = [];
624
		}
625
 
626
		if (btnOptions.enabled === false) {
627
			return;
628
		}
629
 
630
 
631
		var attr = btnOptions.theme,
632
			states = attr.states,
633
			hover = states && states.hover,
634
			select = states && states.select,
635
			callback;
636
 
637
		delete attr.states;
638
 
639
		if (onclick) {
640
			callback = function (e) {
641
				e.stopPropagation();
642
				onclick.call(chart, e);
643
			};
644
 
645
		} else if (menuItems) {
646
			callback = function () {
647
				chart.contextMenu(
648
					button.menuClassName,
649
					menuItems,
650
					button.translateX,
651
					button.translateY,
652
					button.width,
653
					button.height,
654
					button
655
				);
656
				button.setState(2);
657
			};
658
		}
659
 
660
 
661
		if (btnOptions.text && btnOptions.symbol) {
662
			attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);
663
 
664
		} else if (!btnOptions.text) {
665
			extend(attr, {
666
				width: btnOptions.width,
667
				height: btnOptions.height,
668
				padding: 0
669
			});
670
		}
671
 
672
		button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
673
			.attr({
674
				title: chart.options.lang[btnOptions._titleKey],
675
				'stroke-linecap': 'round'
676
			});
677
		button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++;
678
 
679
		if (btnOptions.symbol) {
680
			symbol = renderer.symbol(
681
					btnOptions.symbol,
682
					btnOptions.symbolX - (symbolSize / 2),
683
					btnOptions.symbolY - (symbolSize / 2),
684
					symbolSize,
685
					symbolSize
686
				)
687
				.attr(extend(symbolAttr, {
688
					'stroke-width': btnOptions.symbolStrokeWidth || 1,
689
					zIndex: 1
690
				})).add(button);
691
		}
692
 
693
		button.add()
694
			.align(extend(btnOptions, {
695
				width: button.width,
696
				x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654
697
			}), true, 'spacingBox');
698
 
699
		buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
700
 
701
		chart.exportSVGElements.push(button, symbol);
702
 
703
	},
704
 
705
	/**
706
	 * Destroy the buttons.
707
	 */
708
	destroyExport: function (e) {
709
		var chart = e.target,
710
			i,
711
			elem;
712
 
713
		// Destroy the extra buttons added
714
		for (i = 0; i < chart.exportSVGElements.length; i++) {
715
			elem = chart.exportSVGElements[i];
716
 
717
			// Destroy and null the svg/vml elements
718
			if (elem) { // #1822
719
				elem.onclick = elem.ontouchstart = null;
720
				chart.exportSVGElements[i] = elem.destroy();
721
			}
722
		}
723
 
724
		// Destroy the divs for the menu
725
		for (i = 0; i < chart.exportDivElements.length; i++) {
726
			elem = chart.exportDivElements[i];
727
 
728
			// Remove the event handler
729
			removeEvent(elem, 'mouseleave');
730
 
731
			// Remove inline events
732
			chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
733
 
734
			// Destroy the div by moving to garbage bin
735
			discardElement(elem);
736
		}
737
	}
738
});
739
 
740
 
741
symbols.menu = function (x, y, width, height) {
742
	var arr = [
743
		M, x, y + 2.5,
744
		L, x + width, y + 2.5,
745
		M, x, y + height / 2 + 0.5,
746
		L, x + width, y + height / 2 + 0.5,
747
		M, x, y + height - 1.5,
748
		L, x + width, y + height - 1.5
749
	];
750
	return arr;
751
};
752
 
753
// Add the buttons on chart load
754
Chart.prototype.callbacks.push(function (chart) {
755
	var n,
756
		exportingOptions = chart.options.exporting,
757
		buttons = exportingOptions.buttons;
758
 
759
	buttonOffset = 0;
760
 
761
	if (exportingOptions.enabled !== false) {
762
 
763
		for (n in buttons) {
764
			chart.addButton(buttons[n]);
765
		}
766
 
767
		// Destroy the export elements at chart destroy
768
		addEvent(chart, 'destroy', chart.destroyExport);
769
	}
770
 
771
});
772
 
773
 
774
}(Highcharts));