| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709 | /** * @license Highcharts JS v3.0.6 (2013-10-04) * Exporting module * * (c) 2010-2013 Torstein Hønsi * * License: www.highcharts.com/license */// JSLint options:/*global Highcharts, document, window, Math, setTimeout */(function (Highcharts) { // encapsulate// create shortcutsvar Chart = Highcharts.Chart,	addEvent = Highcharts.addEvent,	removeEvent = Highcharts.removeEvent,	createElement = Highcharts.createElement,	discardElement = Highcharts.discardElement,	css = Highcharts.css,	merge = Highcharts.merge,	each = Highcharts.each,	extend = Highcharts.extend,	math = Math,	mathMax = math.max,	doc = document,	win = window,	isTouchDevice = Highcharts.isTouchDevice,	M = 'M',	L = 'L',	DIV = 'div',	HIDDEN = 'hidden',	NONE = 'none',	PREFIX = 'highcharts-',	ABSOLUTE = 'absolute',	PX = 'px',	UNDEFINED,	symbols = Highcharts.Renderer.prototype.symbols,	defaultOptions = Highcharts.getOptions(),	buttonOffset;	// Add language	extend(defaultOptions.lang, {		printChart: 'Print chart',		downloadPNG: 'Download PNG image',		downloadJPEG: 'Download JPEG image',		downloadPDF: 'Download PDF document',		downloadSVG: 'Download SVG vector image',		contextButtonTitle: 'Chart context menu'	});// Buttons and menus are collected in a separate config option set called 'navigation'.// This can be extended later to add control buttons like zoom and pan right click menus.defaultOptions.navigation = {	menuStyle: {		border: '1px solid #A0A0A0',		background: '#FFFFFF',		padding: '5px 0'	},	menuItemStyle: {		padding: '0 10px',		background: NONE,		color: '#303030',		fontSize: isTouchDevice ? '14px' : '11px'	},	menuItemHoverStyle: {		background: '#4572A5',		color: '#FFFFFF'	},	buttonOptions: {		symbolFill: '#E0E0E0',		symbolSize: 14,		symbolStroke: '#666',		symbolStrokeWidth: 3,		symbolX: 12.5,		symbolY: 10.5,		align: 'right',		buttonSpacing: 3, 		height: 22,		// text: null,		theme: {			fill: 'white', // capture hover			stroke: 'none'		},		verticalAlign: 'top',		width: 24	}};// Add the export related optionsdefaultOptions.exporting = {	//enabled: true,	//filename: 'chart',	type: 'image/png',	url: 'http://export.highcharts.com/',	//width: undefined,	//scale: 2	buttons: {		contextButton: {			menuClassName: PREFIX + 'contextmenu',			//x: -10,			symbol: 'menu',			_titleKey: 'contextButtonTitle',			menuItems: [{				textKey: 'printChart',				onclick: function () {					this.print();				}			}, {				separator: true			}, {				textKey: 'downloadPNG',				onclick: function () {					this.exportChart();				}			}, {				textKey: 'downloadJPEG',				onclick: function () {					this.exportChart({						type: 'image/jpeg'					});				}			}, {				textKey: 'downloadPDF',				onclick: function () {					this.exportChart({						type: 'application/pdf'					});				}			}, {				textKey: 'downloadSVG',				onclick: function () {					this.exportChart({						type: 'image/svg+xml'					});				}			}			// Enable this block to add "View SVG" to the dropdown menu			/*			,{				text: 'View SVG',				onclick: function () {					var svg = this.getSVG()						.replace(/</g, '\n<')						.replace(/>/g, '>');					doc.body.innerHTML = '<pre>' + svg + '</pre>';				}			} // */			]		}	}};// Add the Highcharts.post utilityHighcharts.post = function (url, data) {	var name,		form;		// create the form	form = createElement('form', {		method: 'post',		action: url,		enctype: 'multipart/form-data'	}, {		display: NONE	}, doc.body);	// add the data	for (name in data) {		createElement('input', {			type: HIDDEN,			name: name,			value: data[name]		}, null, form);	}	// submit	form.submit();	// clean up	discardElement(form);};extend(Chart.prototype, {	/**	 * Return an SVG representation of the chart	 *	 * @param additionalOptions {Object} Additional chart options for the generated SVG representation	 */	getSVG: function (additionalOptions) {		var chart = this,			chartCopy,			sandbox,			svg,			seriesOptions,			sourceWidth,			sourceHeight,			cssWidth,			cssHeight,			options = merge(chart.options, additionalOptions); // copy the options and add extra options		// IE compatibility hack for generating SVG content that it doesn't really understand		if (!doc.createElementNS) {			/*jslint unparam: true*//* allow unused parameter ns in function below */			doc.createElementNS = function (ns, tagName) {				return doc.createElement(tagName);			};			/*jslint unparam: false*/		}		// create a sandbox where a new chart will be generated		sandbox = createElement(DIV, null, {			position: ABSOLUTE,			top: '-9999em',			width: chart.chartWidth + PX,			height: chart.chartHeight + PX		}, doc.body);				// get the source size		cssWidth = chart.renderTo.style.width;		cssHeight = chart.renderTo.style.height;		sourceWidth = options.exporting.sourceWidth ||			options.chart.width ||			(/px$/.test(cssWidth) && parseInt(cssWidth, 10)) ||			600;		sourceHeight = options.exporting.sourceHeight ||			options.chart.height ||			(/px$/.test(cssHeight) && parseInt(cssHeight, 10)) ||			400;		// override some options		extend(options.chart, {			animation: false,			renderTo: sandbox,			forExport: true,			width: sourceWidth,			height: sourceHeight		});		options.exporting.enabled = false; // hide buttons in print				// prepare for replicating the chart		options.series = [];		each(chart.series, function (serie) {			seriesOptions = merge(serie.options, {				animation: false, // turn off animation				showCheckbox: false,				visible: serie.visible			});			if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set				options.series.push(seriesOptions);			}		});		// generate the chart copy		chartCopy = new Highcharts.Chart(options, chart.callback);		// reflect axis extremes in the export		each(['xAxis', 'yAxis'], function (axisType) {			each(chart[axisType], function (axis, i) {				var axisCopy = chartCopy[axisType][i],					extremes = axis.getExtremes(),					userMin = extremes.userMin,					userMax = extremes.userMax;				if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) {					axisCopy.setExtremes(userMin, userMax, true, false);				}			});		});		// get the SVG from the container's innerHTML		svg = chartCopy.container.innerHTML;		// free up memory		options = null;		chartCopy.destroy();		discardElement(sandbox);		// sanitize		svg = svg			.replace(/zIndex="[^"]+"/g, '')			.replace(/isShadow="[^"]+"/g, '')			.replace(/symbolName="[^"]+"/g, '')			.replace(/jQuery[0-9]+="[^"]+"/g, '')			.replace(/url\([^#]+#/g, 'url(#')			.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')			.replace(/ href=/g, ' xlink:href=')			.replace(/\n/, ' ')			.replace(/<\/svg>.*?$/, '</svg>') // any HTML added to the container after the SVG (#894)			/* This fails in IE < 8			.replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight				return s2 +'.'+ s3[0];			})*/			// Replace HTML entities, issue #347			.replace(/ /g, '\u00A0') // no-break space			.replace(/­/g,  '\u00AD') // soft hyphen			// IE specific			.replace(/<IMG /g, '<image ')			.replace(/height=([^" ]+)/g, 'height="$1"')			.replace(/width=([^" ]+)/g, 'width="$1"')			.replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')			.replace(/id=([^" >]+)/g, 'id="$1"')			.replace(/class=([^" >]+)/g, 'class="$1"')			.replace(/ transform /g, ' ')			.replace(/:(path|rect)/g, '$1')			.replace(/style="([^"]+)"/g, function (s) {				return s.toLowerCase();			});		// IE9 beta bugs with innerHTML. Test again with final IE9.		svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')			.replace(/"/g, "'");		return svg;	},	/**	 * Submit the SVG representation of the chart to the server	 * @param {Object} options Exporting options. Possible members are url, type and width.	 * @param {Object} chartOptions Additional chart options for the SVG representation of the chart	 */	exportChart: function (options, chartOptions) {		options = options || {};				var chart = this,			chartExportingOptions = chart.options.exporting,			svg = chart.getSVG(merge(				{ chart: { borderRadius: 0 } },				chartExportingOptions.chartOptions,				chartOptions, 				{					exporting: {						sourceWidth: options.sourceWidth || chartExportingOptions.sourceWidth,						sourceHeight: options.sourceHeight || chartExportingOptions.sourceHeight					}				}			));		// merge the options		options = merge(chart.options.exporting, options);				// do the post		Highcharts.post(options.url, {			filename: options.filename || 'chart',			type: options.type,			width: options.width || 0, // IE8 fails to post undefined correctly, so use 0			scale: options.scale || 2,			svg: svg		});	},		/**	 * Print the chart	 */	print: function () {		var chart = this,			container = chart.container,			origDisplay = [],			origParent = container.parentNode,			body = doc.body,			childNodes = body.childNodes;		if (chart.isPrinting) { // block the button while in printing mode			return;		}		chart.isPrinting = true;		// hide all body content		each(childNodes, function (node, i) {			if (node.nodeType === 1) {				origDisplay[i] = node.style.display;				node.style.display = NONE;			}		});		// pull out the chart		body.appendChild(container);		// print		win.focus(); // #1510		win.print();		// allow the browser to prepare before reverting		setTimeout(function () {			// put the chart back in			origParent.appendChild(container);			// restore all body content			each(childNodes, function (node, i) {				if (node.nodeType === 1) {					node.style.display = origDisplay[i];				}			});			chart.isPrinting = false;		}, 1000);	},	/**	 * Display a popup menu for choosing the export type	 *	 * @param {String} className An identifier for the menu	 * @param {Array} items A collection with text and onclicks for the items	 * @param {Number} x The x position of the opener button	 * @param {Number} y The y position of the opener button	 * @param {Number} width The width of the opener button	 * @param {Number} height The height of the opener button	 */	contextMenu: function (className, items, x, y, width, height, button) {		var chart = this,			navOptions = chart.options.navigation,			menuItemStyle = navOptions.menuItemStyle,			chartWidth = chart.chartWidth,			chartHeight = chart.chartHeight,			cacheName = 'cache-' + className,			menu = chart[cacheName],			menuPadding = mathMax(width, height), // for mouse leave detection			boxShadow = '3px 3px 10px #888',			innerMenu,			hide,			hideTimer,			menuStyle;		// create the menu only the first time		if (!menu) {			// create a HTML element above the SVG			chart[cacheName] = menu = createElement(DIV, {				className: className			}, {				position: ABSOLUTE,				zIndex: 1000,				padding: menuPadding + PX			}, chart.container);			innerMenu = createElement(DIV, null,				extend({					MozBoxShadow: boxShadow,					WebkitBoxShadow: boxShadow,					boxShadow: boxShadow				}, navOptions.menuStyle), menu);			// hide on mouse out			hide = function () {				css(menu, { display: NONE });				if (button) {					button.setState(0);				}				chart.openMenu = false;			};			// Hide the menu some time after mouse leave (#1357)			addEvent(menu, 'mouseleave', function () {				hideTimer = setTimeout(hide, 500);			});			addEvent(menu, 'mouseenter', function () {				clearTimeout(hideTimer);			});			// Hide it on clicking or touching outside the menu (#2258)			addEvent(document, 'mousedown', function (e) {				if (!chart.pointer.inClass(e.target, className)) {					hide();				}			});			// create the items			each(items, function (item) {				if (item) {					var element = item.separator ? 						createElement('hr', null, null, innerMenu) :						createElement(DIV, {							onmouseover: function () {								css(this, navOptions.menuItemHoverStyle);							},							onmouseout: function () {								css(this, menuItemStyle);							},							onclick: function () {								hide();								item.onclick.apply(chart, arguments);							},							innerHTML: item.text || chart.options.lang[item.textKey]						}, extend({							cursor: 'pointer'						}, menuItemStyle), innerMenu);					// Keep references to menu divs to be able to destroy them					chart.exportDivElements.push(element);				}			});			// Keep references to menu and innerMenu div to be able to destroy them			chart.exportDivElements.push(innerMenu, menu);			chart.exportMenuWidth = menu.offsetWidth;			chart.exportMenuHeight = menu.offsetHeight;		}		menuStyle = { display: 'block' };		// if outside right, right align it		if (x + chart.exportMenuWidth > chartWidth) {			menuStyle.right = (chartWidth - x - width - menuPadding) + PX;		} else {			menuStyle.left = (x - menuPadding) + PX;		}		// if outside bottom, bottom align it		if (y + height + chart.exportMenuHeight > chartHeight && button.alignOptions.verticalAlign !== 'top') {			menuStyle.bottom = (chartHeight - y - menuPadding)  + PX;		} else {			menuStyle.top = (y + height - menuPadding) + PX;		}		css(menu, menuStyle);		chart.openMenu = true;	},	/**	 * Add the export button to the chart	 */	addButton: function (options) {		var chart = this,			renderer = chart.renderer,			btnOptions = merge(chart.options.navigation.buttonOptions, options),			onclick = btnOptions.onclick,			menuItems = btnOptions.menuItems,			symbol,			button,			symbolAttr = {				stroke: btnOptions.symbolStroke,				fill: btnOptions.symbolFill			},			symbolSize = btnOptions.symbolSize || 12;		if (!chart.btnCount) {			chart.btnCount = 0;		}		// Keeps references to the button elements		if (!chart.exportDivElements) {			chart.exportDivElements = [];			chart.exportSVGElements = [];		}		if (btnOptions.enabled === false) {			return;		}		var attr = btnOptions.theme,			states = attr.states,			hover = states && states.hover,			select = states && states.select,			callback;		delete attr.states;		if (onclick) {			callback = function () {				onclick.apply(chart, arguments);			};		} else if (menuItems) {			callback = function () {				chart.contextMenu(					button.menuClassName, 					menuItems, 					button.translateX, 					button.translateY, 					button.width, 					button.height,					button				);				button.setState(2);			};		}		if (btnOptions.text && btnOptions.symbol) {			attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);				} else if (!btnOptions.text) {			extend(attr, {				width: btnOptions.width,				height: btnOptions.height,				padding: 0			});		}		button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)			.attr({				title: chart.options.lang[btnOptions._titleKey],				'stroke-linecap': 'round'			});		button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++;		if (btnOptions.symbol) {			symbol = renderer.symbol(					btnOptions.symbol,					btnOptions.symbolX - (symbolSize / 2),					btnOptions.symbolY - (symbolSize / 2),					symbolSize,									symbolSize				)				.attr(extend(symbolAttr, {					'stroke-width': btnOptions.symbolStrokeWidth || 1,					zIndex: 1				})).add(button);		}		button.add()			.align(extend(btnOptions, {				width: button.width,				x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654			}), true, 'spacingBox');		buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);		chart.exportSVGElements.push(button, symbol);	},	/**	 * Destroy the buttons.	 */	destroyExport: function (e) {		var chart = e.target,			i,			elem;		// Destroy the extra buttons added		for (i = 0; i < chart.exportSVGElements.length; i++) {			elem = chart.exportSVGElements[i];						// Destroy and null the svg/vml elements			if (elem) { // #1822				elem.onclick = elem.ontouchstart = null;				chart.exportSVGElements[i] = elem.destroy();			}		}		// Destroy the divs for the menu		for (i = 0; i < chart.exportDivElements.length; i++) {			elem = chart.exportDivElements[i];			// Remove the event handler			removeEvent(elem, 'mouseleave');			// Remove inline events			chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;			// Destroy the div by moving to garbage bin			discardElement(elem);		}	}});symbols.menu = function (x, y, width, height) {	var arr = [		M, x, y + 2.5,		L, x + width, y + 2.5,		M, x, y + height / 2 + 0.5,		L, x + width, y + height / 2 + 0.5,		M, x, y + height - 1.5,		L, x + width, y + height - 1.5	];	return arr;};// Add the buttons on chart loadChart.prototype.callbacks.push(function (chart) {	var n,		exportingOptions = chart.options.exporting,		buttons = exportingOptions.buttons;	buttonOffset = 0;	if (exportingOptions.enabled !== false) {		for (n in buttons) {			chart.addButton(buttons[n]);		}		// Destroy the export elements at chart destroy		addEvent(chart, 'destroy', chart.destroyExport);	}});}(Highcharts));
 |