app/assets/javascripts/highcharts/modules/exporting.js in highcharts-rails-4.2.7 vs app/assets/javascripts/highcharts/modules/exporting.js in highcharts-rails-5.0.0
- old
+ new
@@ -1,798 +1,877 @@
/**
- * @license Highcharts JS v4.2.7 (2016-09-21)
+ * @license Highcharts JS v5.0.0 (2016-09-29)
* Exporting module
*
* (c) 2010-2016 Torstein Honsi
*
* License: www.highcharts.com/license
*/
-
-/* eslint indent:0 */
-(function (factory) {
+(function(factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory;
} else {
factory(Highcharts);
}
-}(function (Highcharts) {
+}(function(Highcharts) {
+ (function(H) {
+ /**
+ * Exporting module
+ *
+ * (c) 2010-2016 Torstein Honsi
+ *
+ * License: www.highcharts.com/license
+ */
-// create shortcuts
-var win = Highcharts.win,
- doc = win.document,
- Chart = Highcharts.Chart,
- addEvent = Highcharts.addEvent,
- removeEvent = Highcharts.removeEvent,
- fireEvent = Highcharts.fireEvent,
- createElement = Highcharts.createElement,
- discardElement = Highcharts.discardElement,
- css = Highcharts.css,
- merge = Highcharts.merge,
- each = Highcharts.each,
- extend = Highcharts.extend,
- splat = Highcharts.splat,
- math = Math,
- mathMax = math.max,
- 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;
+ /* eslint indent:0 */
+ 'use strict';
- // 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'
- });
+ // create shortcuts
+ var defaultOptions = H.defaultOptions,
+ doc = H.doc,
+ Chart = H.Chart,
+ addEvent = H.addEvent,
+ removeEvent = H.removeEvent,
+ fireEvent = H.fireEvent,
+ createElement = H.createElement,
+ discardElement = H.discardElement,
+ css = H.css,
+ merge = H.merge,
+ pick = H.pick,
+ each = H.each,
+ extend = H.extend,
+ splat = H.splat,
+ isTouchDevice = H.isTouchDevice,
+ win = H.win,
+ SVGRenderer = H.SVGRenderer;
-// 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'
- },
+ var symbols = H.Renderer.prototype.symbols;
- 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 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 = {
+ buttonOptions: {
+ theme: {},
+ symbolSize: 14,
+ symbolX: 12.5,
+ symbolY: 10.5,
+ align: 'right',
+ buttonSpacing: 3,
+ height: 22,
+ // text: null,
+ verticalAlign: 'top',
+ width: 24
+ }
+ };
-// Add the export related options
-defaultOptions.exporting = {
- //enabled: true,
- //filename: 'chart',
- type: 'image/png',
- url: 'https://export.highcharts.com/',
- //width: undefined,
- printMaxWidth: 780,
- 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
- /*
- ,{
+ // Presentational attributes
+ merge(true, defaultOptions.navigation, {
+ menuStyle: {
+ border: '1px solid #999999',
+ background: '#ffffff',
+ padding: '5px 0'
+ },
+ menuItemStyle: {
+ padding: '0.5em 1em',
+ background: 'none',
+ color: '#333333',
+ fontSize: isTouchDevice ? '14px' : '11px',
+ transition: 'background 250ms, color 250ms'
+ },
+ menuItemHoverStyle: {
+ background: '#335cad',
+ color: '#ffffff'
+ },
+ buttonOptions: {
+ symbolFill: '#666666',
+ symbolStroke: '#666666',
+ symbolStrokeWidth: 3,
+ theme: {
+ fill: '#ffffff', // capture hover
+ stroke: 'none',
+ padding: 5
+ }
+ }
+ });
- text: 'View SVG',
- onclick: function () {
- var svg = this.getSVG()
- .replace(/</g, '\n<')
- .replace(/>/g, '>');
- doc.body.innerHTML = '<pre>' + svg + '</pre>';
- }
- } // */
- ]
- }
- }
-};
-// Add the Highcharts.post utility
-Highcharts.post = function (url, data, formAttributes) {
- var name,
- form;
+ // Add the export related options
+ defaultOptions.exporting = {
+ //enabled: true,
+ //filename: 'chart',
+ type: 'image/png',
+ url: 'https://export.highcharts.com/',
+ //width: undefined,
+ printMaxWidth: 780,
+ scale: 2,
+ buttons: {
+ contextButton: {
+ className: 'highcharts-contextbutton',
+ menuClassName: 'highcharts-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
+ /*
+ ,{
- // create the form
- form = createElement('form', merge({
- method: 'post',
- action: url,
- enctype: 'multipart/form-data'
- }, formAttributes), {
- display: NONE
- }, doc.body);
+ text: 'View SVG',
+ onclick: function () {
+ var svg = this.getSVG()
+ .replace(/</g, '\n<')
+ .replace(/>/g, '>');
- // add the data
- for (name in data) {
- createElement('input', {
- type: HIDDEN,
- name: name,
- value: data[name]
- }, null, form);
- }
+ doc.body.innerHTML = '<pre>' + svg + '</pre>';
+ }
+ } // */
+ ]
+ }
+ }
+ };
- // submit
- form.submit();
+ // Add the H.post utility
+ H.post = function(url, data, formAttributes) {
+ var name,
+ form;
- // clean up
- discardElement(form);
-};
+ // create the form
+ form = createElement('form', merge({
+ method: 'post',
+ action: url,
+ enctype: 'multipart/form-data'
+ }, formAttributes), {
+ display: 'none'
+ }, doc.body);
-extend(Chart.prototype, {
+ // add the data
+ for (name in data) {
+ createElement('input', {
+ type: 'hidden',
+ name: name,
+ value: data[name]
+ }, null, form);
+ }
- /**
- * A collection of regex fixes on the produces SVG to account for expando properties,
- * browser bugs, VML problems and other. Returns a cleaned SVG.
- */
- sanitizeSVG: function (svg) {
- return svg
- .replace(/zIndex="[^"]+"/g, '')
- .replace(/isShadow="[^"]+"/g, '')
- .replace(/symbolName="[^"]+"/g, '')
- .replace(/jQuery[0-9]+="[^"]+"/g, '')
- .replace(/url\(("|")(\S+)("|")\)/g, 'url($2)')
- .replace(/url\([^#]+#/g, 'url(#')
- .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
- .replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
- .replace(/\n/, ' ')
- // Any HTML added to the container after the SVG (#894)
- .replace(/<\/svg>.*?$/, '</svg>')
- // Batik doesn't support rgba fills and strokes (#3095)
- .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
- /* This fails in IE < 8
- .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
- return s2 +'.'+ s3[0];
- })*/
+ // submit
+ form.submit();
- // Replace HTML entities, issue #347
- .replace(/ /g, '\u00A0') // no-break space
- .replace(/­/g, '\u00AD') // soft hyphen
+ // clean up
+ discardElement(form);
+ };
- // IE specific
- .replace(/<IMG /g, '<image ')
- .replace(/<(\/?)TITLE>/g, '<$1title>')
- .replace(/height=([^" ]+)/g, 'height="$1"')
- .replace(/width=([^" ]+)/g, 'width="$1"')
- .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
- .replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
- .replace(/class=([^" >]+)/g, 'class="$1"')
- .replace(/ transform /g, ' ')
- .replace(/:(path|rect)/g, '$1')
- .replace(/style="([^"]+)"/g, function (s) {
- return s.toLowerCase();
- });
- },
+ extend(Chart.prototype, {
- /**
- * Return innerHTML of chart. Used as hook for plugins.
- */
- getChartHTML: function () {
- return this.container.innerHTML;
- },
+ /**
+ * A collection of regex fixes on the produces SVG to account for expando properties,
+ * browser bugs, VML problems and other. Returns a cleaned SVG.
+ */
+ sanitizeSVG: function(svg) {
+ svg = svg
+ .replace(/zIndex="[^"]+"/g, '')
+ .replace(/isShadow="[^"]+"/g, '')
+ .replace(/symbolName="[^"]+"/g, '')
+ .replace(/jQuery[0-9]+="[^"]+"/g, '')
+ .replace(/url\(("|")(\S+)("|")\)/g, 'url($2)')
+ .replace(/url\([^#]+#/g, 'url(#')
+ .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
+ .replace(/ (NS[0-9]+\:)?href=/g, ' xlink:href=') // #3567
+ .replace(/\n/, ' ')
+ // Any HTML added to the container after the SVG (#894)
+ .replace(/<\/svg>.*?$/, '</svg>')
+ // Batik doesn't support rgba fills and strokes (#3095)
+ .replace(/(fill|stroke)="rgba\(([ 0-9]+,[ 0-9]+,[ 0-9]+),([ 0-9\.]+)\)"/g, '$1="rgb($2)" $1-opacity="$3"')
+ /* This fails in IE < 8
+ .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
+ return s2 +'.'+ s3[0];
+ })*/
- /**
- * 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,
- html,
- options = merge(chart.options, additionalOptions), // copy the options and add extra options
- allowHTML = options.exporting.allowHTML;
+ // Replace HTML entities, issue #347
+ .replace(/ /g, '\u00A0') // no-break space
+ .replace(/­/g, '\u00AD'); // soft hyphen
- // IE compatibility hack for generating SVG content that it doesn't really understand
- if (!doc.createElementNS) {
- doc.createElementNS = function (ns, tagName) {
- return doc.createElement(tagName);
- };
- }
+ // IE specific
+ svg = svg
+ .replace(/<IMG /g, '<image ')
+ .replace(/<(\/?)TITLE>/g, '<$1title>')
+ .replace(/height=([^" ]+)/g, 'height="$1"')
+ .replace(/width=([^" ]+)/g, 'width="$1"')
+ .replace(/hc-svg-href="([^"]+)">/g, 'xlink:href="$1"/>')
+ .replace(/ id=([^" >]+)/g, ' id="$1"') // #4003
+ .replace(/class=([^" >]+)/g, 'class="$1"')
+ .replace(/ transform /g, ' ')
+ .replace(/:(path|rect)/g, '$1')
+ .replace(/style="([^"]+)"/g, function(s) {
+ return s.toLowerCase();
+ });
- // 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;
+ return svg;
+ },
- // override some options
- extend(options.chart, {
- animation: false,
- renderTo: sandbox,
- forExport: true,
- renderer: 'SVGRenderer',
- width: sourceWidth,
- height: sourceHeight
- });
- options.exporting.enabled = false; // hide buttons in print
- delete options.data; // #3004
+ /**
+ * Return innerHTML of chart. Used as hook for plugins.
+ */
+ getChartHTML: function() {
- // prepare for replicating the chart
- options.series = [];
- each(chart.series, function (serie) {
- seriesOptions = merge(serie.userOptions, { // #4912
- animation: false, // turn off animation
- enableMouseTracking: false,
- showCheckbox: false,
- visible: serie.visible
- });
+ return this.container.innerHTML;
+ },
- if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
- options.series.push(seriesOptions);
- }
- });
+ /**
+ * 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,
+ html,
+ options = merge(chart.options, additionalOptions), // copy the options and add extra options
+ allowHTML = options.exporting.allowHTML;
- // Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
- if (additionalOptions) {
- each(['xAxis', 'yAxis'], function (axisType) {
- each(splat(additionalOptions[axisType]), function (axisOptions, i) {
- options[axisType][i] = merge(options[axisType][i], axisOptions);
- });
- });
- }
- // generate the chart copy
- chartCopy = new Highcharts.Chart(options, chart.callback);
+ // IE compatibility hack for generating SVG content that it doesn't really understand
+ if (!doc.createElementNS) {
+ doc.createElementNS = function(ns, tagName) {
+ return doc.createElement(tagName);
+ };
+ }
- // 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;
+ // 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);
- if (axisCopy && (userMin !== UNDEFINED || userMax !== UNDEFINED)) {
- axisCopy.setExtremes(userMin, userMax, true, false);
- }
- });
- });
+ // 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;
- // get the SVG from the container's innerHTML
- svg = chartCopy.getChartHTML();
+ // override some options
+ extend(options.chart, {
+ animation: false,
+ renderTo: sandbox,
+ forExport: true,
+ renderer: 'SVGRenderer',
+ width: sourceWidth,
+ height: sourceHeight
+ });
+ options.exporting.enabled = false; // hide buttons in print
+ delete options.data; // #3004
- // free up memory
- options = null;
- chartCopy.destroy();
- discardElement(sandbox);
+ // prepare for replicating the chart
+ options.series = [];
+ each(chart.series, function(serie) {
+ seriesOptions = merge(serie.userOptions, { // #4912
+ animation: false, // turn off animation
+ enableMouseTracking: false,
+ showCheckbox: false,
+ visible: serie.visible
+ });
- // Move HTML into a foreignObject
- if (allowHTML) {
- html = svg.match(/<\/svg>(.*?$)/);
- if (html) {
- html = '<foreignObject x="0" y="0" width="200" height="200">' +
- '<body xmlns="http://www.w3.org/1999/xhtml">' +
- html[1] +
- '</body>' +
- '</foreignObject>';
- svg = svg.replace('</svg>', html + '</svg>');
- }
- }
+ if (!seriesOptions.isInternal) { // used for the navigator series that has its own option set
+ options.series.push(seriesOptions);
+ }
+ });
- // sanitize
- svg = this.sanitizeSVG(svg);
+ // Axis options must be merged in one by one, since it may be an array or an object (#2022, #3900)
+ if (additionalOptions) {
+ each(['xAxis', 'yAxis'], function(axisType) {
+ each(splat(additionalOptions[axisType]), function(axisOptions, i) {
+ options[axisType][i] = merge(options[axisType][i], axisOptions);
+ });
+ });
+ }
- // IE9 beta bugs with innerHTML. Test again with final IE9.
- svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
- .replace(/"/g, '\'');
+ // generate the chart copy
+ chartCopy = new H.Chart(options, chart.callback);
- return svg;
- },
+ // 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;
- getSVGForExport: function (options, chartOptions) {
- var chartExportingOptions = this.options.exporting;
+ if (axisCopy && (userMin !== undefined || userMax !== undefined)) {
+ axisCopy.setExtremes(userMin, userMax, true, false);
+ }
+ });
+ });
- return this.getSVG(merge(
- { chart: { borderRadius: 0 } },
- chartExportingOptions.chartOptions,
- chartOptions,
- {
- exporting: {
- sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
- sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
- }
- }
- ));
- },
+ // get the SVG from the container's innerHTML
+ svg = chartCopy.getChartHTML();
- /**
- * Submit the SVG representation of the chart to the server
- * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
- * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
- */
- exportChart: function (options, chartOptions) {
+ // free up memory
+ options = null;
+ chartCopy.destroy();
+ discardElement(sandbox);
- var svg = this.getSVGForExport(options, chartOptions);
+ // Move HTML into a foreignObject
+ if (allowHTML) {
+ html = svg.match(/<\/svg>(.*?$)/);
+ if (html) {
+ html = '<foreignObject x="0" y="0" width="200" height="200">' +
+ '<body xmlns="http://www.w3.org/1999/xhtml">' +
+ html[1] +
+ '</body>' +
+ '</foreignObject>';
+ svg = svg.replace('</svg>', html + '</svg>');
+ }
+ }
- // merge the options
- options = merge(this.options.exporting, options);
+ // sanitize
+ svg = this.sanitizeSVG(svg);
- // 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,
- svg: svg
- }, options.formAttributes);
+ // IE9 beta bugs with innerHTML. Test again with final IE9.
+ svg = svg.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
+ .replace(/"/g, '\'');
- },
+ return svg;
+ },
- /**
- * Print the chart
- */
- print: function () {
+ getSVGForExport: function(options, chartOptions) {
+ var chartExportingOptions = this.options.exporting;
- var chart = this,
- container = chart.container,
- origDisplay = [],
- origParent = container.parentNode,
- body = doc.body,
- childNodes = body.childNodes,
- printMaxWidth = chart.options.exporting.printMaxWidth,
- resetParams,
- handleMaxWidth;
+ return this.getSVG(merge({
+ chart: {
+ borderRadius: 0
+ }
+ },
+ chartExportingOptions.chartOptions,
+ chartOptions, {
+ exporting: {
+ sourceWidth: (options && options.sourceWidth) || chartExportingOptions.sourceWidth,
+ sourceHeight: (options && options.sourceHeight) || chartExportingOptions.sourceHeight
+ }
+ }
+ ));
+ },
- if (chart.isPrinting) { // block the button while in printing mode
- return;
- }
+ /**
+ * Submit the SVG representation of the chart to the server
+ * @param {Object} options Exporting options. Possible members are url, type, width and formAttributes.
+ * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
+ */
+ exportChart: function(options, chartOptions) {
- chart.isPrinting = true;
- chart.pointer.reset(null, 0);
+ var svg = this.getSVGForExport(options, chartOptions);
- fireEvent(chart, 'beforePrint');
+ // merge the options
+ options = merge(this.options.exporting, options);
- // Handle printMaxWidth
- handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
- if (handleMaxWidth) {
- resetParams = [chart.options.chart.width, undefined, false];
- chart.setSize(printMaxWidth, undefined, false);
- }
+ // do the post
+ H.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,
+ svg: svg
+ }, options.formAttributes);
- // 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 the chart
+ */
+ print: function() {
- // print
- win.focus(); // #1510
- win.print();
+ var chart = this,
+ container = chart.container,
+ origDisplay = [],
+ origParent = container.parentNode,
+ body = doc.body,
+ childNodes = body.childNodes,
+ printMaxWidth = chart.options.exporting.printMaxWidth,
+ resetParams,
+ handleMaxWidth;
- // allow the browser to prepare before reverting
- setTimeout(function () {
+ if (chart.isPrinting) { // block the button while in printing mode
+ return;
+ }
- // put the chart back in
- origParent.appendChild(container);
+ chart.isPrinting = true;
+ chart.pointer.reset(null, 0);
- // restore all body content
- each(childNodes, function (node, i) {
- if (node.nodeType === 1) {
- node.style.display = origDisplay[i];
- }
- });
+ fireEvent(chart, 'beforePrint');
- chart.isPrinting = false;
+ // Handle printMaxWidth
+ handleMaxWidth = printMaxWidth && chart.chartWidth > printMaxWidth;
+ if (handleMaxWidth) {
+ resetParams = [chart.options.chart.width, undefined, false];
+ chart.setSize(printMaxWidth, undefined, false);
+ }
- // Reset printMaxWidth
- if (handleMaxWidth) {
- chart.setSize.apply(chart, resetParams);
- }
+ // hide all body content
+ each(childNodes, function(node, i) {
+ if (node.nodeType === 1) {
+ origDisplay[i] = node.style.display;
+ node.style.display = 'none';
+ }
+ });
- fireEvent(chart, 'afterPrint');
+ // pull out the chart
+ body.appendChild(container);
- }, 1000);
+ // print
+ win.focus(); // #1510
+ win.print();
- },
+ // allow the browser to prepare before reverting
+ setTimeout(function() {
- /**
- * 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,
- docMouseUpHandler = function (e) {
- if (!chart.pointer.inClass(e.target, className)) {
- hide();
- }
- };
+ // put the chart back in
+ origParent.appendChild(container);
- // create the menu only the first time
- if (!menu) {
+ // restore all body content
+ each(childNodes, function(node, i) {
+ if (node.nodeType === 1) {
+ node.style.display = origDisplay[i];
+ }
+ });
- // create a HTML element above the SVG
- chart[cacheName] = menu = createElement(DIV, {
- className: className
- }, {
- position: ABSOLUTE,
- zIndex: 1000,
- padding: menuPadding + PX
- }, chart.container);
+ chart.isPrinting = false;
- innerMenu = createElement(DIV, null,
- extend({
- MozBoxShadow: boxShadow,
- WebkitBoxShadow: boxShadow,
- boxShadow: boxShadow
- }, navOptions.menuStyle), menu);
+ // Reset printMaxWidth
+ if (handleMaxWidth) {
+ chart.setSize.apply(chart, resetParams);
+ }
- // hide on mouse out
- hide = function () {
- css(menu, { display: NONE });
- if (button) {
- button.setState(0);
- }
- chart.openMenu = false;
- };
+ fireEvent(chart, 'afterPrint');
- // Hide the menu some time after mouse leave (#1357)
- addEvent(menu, 'mouseleave', function () {
- hideTimer = setTimeout(hide, 500);
- });
- addEvent(menu, 'mouseenter', function () {
- clearTimeout(hideTimer);
- });
+ }, 1000);
+ },
- // Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
- addEvent(doc, 'mouseup', docMouseUpHandler);
- addEvent(chart, 'destroy', function () {
- removeEvent(doc, 'mouseup', docMouseUpHandler);
- });
+ /**
+ * 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,
+ chartWidth = chart.chartWidth,
+ chartHeight = chart.chartHeight,
+ cacheName = 'cache-' + className,
+ menu = chart[cacheName],
+ menuPadding = Math.max(width, height), // for mouse leave detection
+ innerMenu,
+ hide,
+ hideTimer,
+ menuStyle,
+ docMouseUpHandler = function(e) {
+ if (!chart.pointer.inClass(e.target, className)) {
+ hide();
+ }
+ };
+ // create the menu only the first time
+ if (!menu) {
- // 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 (e) {
- if (e) { // IE7
- e.stopPropagation();
- }
- hide();
- if (item.onclick) {
- item.onclick.apply(chart, arguments);
- }
- },
- innerHTML: item.text || chart.options.lang[item.textKey]
- }, extend({
- cursor: 'pointer'
- }, menuItemStyle), innerMenu);
+ // 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', {
+ className: 'highcharts-menu'
+ }, null, menu);
- // 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);
+ // Presentational CSS
+ css(innerMenu, extend({
+ MozBoxShadow: '3px 3px 10px #888',
+ WebkitBoxShadow: '3px 3px 10px #888',
+ boxShadow: '3px 3px 10px #888'
+ }, navOptions.menuStyle));
- chart.exportMenuWidth = menu.offsetWidth;
- chart.exportMenuHeight = menu.offsetHeight;
- }
- menuStyle = { display: 'block' };
+ // hide on mouse out
+ hide = function() {
+ css(menu, {
+ display: 'none'
+ });
+ if (button) {
+ button.setState(0);
+ }
+ chart.openMenu = false;
+ };
- // 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;
- }
+ // Hide the menu some time after mouse leave (#1357)
+ addEvent(menu, 'mouseleave', function() {
+ hideTimer = setTimeout(hide, 500);
+ });
+ addEvent(menu, 'mouseenter', function() {
+ clearTimeout(hideTimer);
+ });
- 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;
- }
+ // Hide it on clicking or touching outside the menu (#2258, #2335, #2407)
+ addEvent(doc, 'mouseup', docMouseUpHandler);
+ addEvent(chart, 'destroy', function() {
+ removeEvent(doc, 'mouseup', docMouseUpHandler);
+ });
- // Keeps references to the button elements
- if (!chart.exportDivElements) {
- chart.exportDivElements = [];
- chart.exportSVGElements = [];
- }
- if (btnOptions.enabled === false) {
- return;
- }
+ // create the items
+ each(items, function(item) {
+ if (item) {
+ var element;
+ if (item.separator) {
+ element = createElement('hr', null, null, innerMenu);
- var attr = btnOptions.theme,
- states = attr.states,
- hover = states && states.hover,
- select = states && states.select,
- callback;
+ } else {
+ element = createElement('div', {
+ className: 'highcharts-menu-item',
+ onclick: function(e) {
+ if (e) { // IE7
+ e.stopPropagation();
+ }
+ hide();
+ if (item.onclick) {
+ item.onclick.apply(chart, arguments);
+ }
+ },
+ innerHTML: item.text || chart.options.lang[item.textKey]
+ }, null, innerMenu);
- delete attr.states;
- if (onclick) {
- callback = function (e) {
- e.stopPropagation();
- onclick.call(chart, e);
- };
+ element.onmouseover = function() {
+ css(this, navOptions.menuItemHoverStyle);
+ };
+ element.onmouseout = function() {
+ css(this, navOptions.menuItemStyle);
+ };
+ css(element, extend({
+ cursor: 'pointer'
+ }, navOptions.menuItemStyle));
- } else if (menuItems) {
- callback = function () {
- chart.contextMenu(
- button.menuClassName,
- menuItems,
- button.translateX,
- button.translateY,
- button.width,
- button.height,
- button
- );
- button.setState(2);
- };
- }
+ }
+ // Keep references to menu divs to be able to destroy them
+ chart.exportDivElements.push(element);
+ }
+ });
- if (btnOptions.text && btnOptions.symbol) {
- attr.paddingLeft = Highcharts.pick(attr.paddingLeft, 25);
+ // Keep references to menu and innerMenu div to be able to destroy them
+ chart.exportDivElements.push(innerMenu, menu);
- } else if (!btnOptions.text) {
- extend(attr, {
- width: btnOptions.width,
- height: btnOptions.height,
- padding: 0
- });
- }
+ chart.exportMenuWidth = menu.offsetWidth;
+ chart.exportMenuHeight = menu.offsetHeight;
+ }
- button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
- .attr({
- title: chart.options.lang[btnOptions._titleKey],
- 'stroke-linecap': 'round',
- zIndex: 3 // #4955
- });
- button.menuClassName = options.menuClassName || PREFIX + 'menu-' + chart.btnCount++;
+ menuStyle = {
+ display: 'block'
+ };
- 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);
- }
+ // 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';
+ }
- button.add()
- .align(extend(btnOptions, {
- width: button.width,
- x: Highcharts.pick(btnOptions.x, buttonOffset) // #1654
- }), true, 'spacingBox');
+ css(menu, menuStyle);
+ chart.openMenu = true;
+ },
- buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
+ /**
+ * 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,
+ symbolSize = btnOptions.symbolSize || 12;
+ if (!chart.btnCount) {
+ chart.btnCount = 0;
+ }
- chart.exportSVGElements.push(button, symbol);
+ // Keeps references to the button elements
+ if (!chart.exportDivElements) {
+ chart.exportDivElements = [];
+ chart.exportSVGElements = [];
+ }
- },
+ if (btnOptions.enabled === false) {
+ return;
+ }
- /**
- * 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];
+ var attr = btnOptions.theme,
+ states = attr.states,
+ hover = states && states.hover,
+ select = states && states.select,
+ callback;
- // Destroy and null the svg/vml elements
- if (elem) { // #1822
- elem.onclick = elem.ontouchstart = null;
- chart.exportSVGElements[i] = elem.destroy();
- }
- }
+ delete attr.states;
- // Destroy the divs for the menu
- for (i = 0; i < chart.exportDivElements.length; i++) {
- elem = chart.exportDivElements[i];
+ if (onclick) {
+ callback = function(e) {
+ e.stopPropagation();
+ onclick.call(chart, e);
+ };
- // Remove the event handler
- removeEvent(elem, 'mouseleave');
+ } else if (menuItems) {
+ callback = function() {
+ chart.contextMenu(
+ button.menuClassName,
+ menuItems,
+ button.translateX,
+ button.translateY,
+ button.width,
+ button.height,
+ button
+ );
+ button.setState(2);
+ };
+ }
- // 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);
- }
- }
-});
+ if (btnOptions.text && btnOptions.symbol) {
+ attr.paddingLeft = pick(attr.paddingLeft, 25);
+ } else if (!btnOptions.text) {
+ extend(attr, {
+ width: btnOptions.width,
+ height: btnOptions.height,
+ padding: 0
+ });
+ }
-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;
-};
+ button = renderer.button(btnOptions.text, 0, 0, callback, attr, hover, select)
+ .addClass(options.className)
+ .attr({
-// Add the buttons on chart load
-Chart.prototype.callbacks.push(function (chart) {
- var n,
- exportingOptions = chart.options.exporting,
- buttons = exportingOptions.buttons;
+ 'stroke-linecap': 'round',
- buttonOffset = 0;
+ title: chart.options.lang[btnOptions._titleKey],
+ zIndex: 3 // #4955
+ });
+ button.menuClassName = options.menuClassName || 'highcharts-menu-' + chart.btnCount++;
- if (exportingOptions.enabled !== false) {
+ if (btnOptions.symbol) {
+ symbol = renderer.symbol(
+ btnOptions.symbol,
+ btnOptions.symbolX - (symbolSize / 2),
+ btnOptions.symbolY - (symbolSize / 2),
+ symbolSize,
+ symbolSize
+ )
+ .addClass('highcharts-button-symbol')
+ .attr({
+ zIndex: 1
+ }).add(button);
- for (n in buttons) {
- chart.addButton(buttons[n]);
- }
- // Destroy the export elements at chart destroy
- addEvent(chart, 'destroy', chart.destroyExport);
- }
+ symbol.attr({
+ stroke: btnOptions.symbolStroke,
+ fill: btnOptions.symbolFill,
+ 'stroke-width': btnOptions.symbolStrokeWidth || 1
+ });
-});
+ }
+ button.add()
+ .align(extend(btnOptions, {
+ width: button.width,
+ x: pick(btnOptions.x, chart.buttonOffset) // #1654
+ }), true, 'spacingBox');
+ chart.buttonOffset += (button.width + btnOptions.buttonSpacing) * (btnOptions.align === 'right' ? -1 : 1);
+
+ chart.exportSVGElements.push(button, symbol);
+
+ },
+
+ /**
+ * Destroy the buttons.
+ */
+ destroyExport: function(e) {
+ var chart = e ? e.target : this,
+ exportSVGElements = chart.exportSVGElements,
+ exportDivElements = chart.exportDivElements;
+
+ // Destroy the extra buttons added
+ if (exportSVGElements) {
+ each(exportSVGElements, function(elem, i) {
+
+ // Destroy and null the svg/vml elements
+ if (elem) { // #1822
+ elem.onclick = elem.ontouchstart = null;
+ chart.exportSVGElements[i] = elem.destroy();
+ }
+ });
+ exportSVGElements.length = 0;
+ }
+
+ // Destroy the divs for the menu
+ if (exportDivElements) {
+ each(exportDivElements, function(elem, 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);
+ });
+ exportDivElements.length = 0;
+ }
+ }
+ });
+
+
+
+
+ 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 load
+ Chart.prototype.renderExporting = function() {
+ var n,
+ exportingOptions = this.options.exporting,
+ buttons = exportingOptions.buttons,
+ isDirty = this.isDirtyExporting || !this.exportSVGElements;
+
+ this.buttonOffset = 0;
+ if (this.isDirtyExporting) {
+ this.destroyExport();
+ }
+
+ if (isDirty && exportingOptions.enabled !== false) {
+
+ for (n in buttons) {
+ this.addButton(buttons[n]);
+ }
+
+ this.isDirtyExporting = false;
+ }
+
+ // Destroy the export elements at chart destroy
+ addEvent(this, 'destroy', this.destroyExport);
+ };
+
+ Chart.prototype.callbacks.push(function(chart) {
+
+ function update(prop, options, redraw) {
+ chart.isDirtyExporting = true;
+ merge(true, chart.options[prop], options);
+ if (pick(redraw, true)) {
+ chart.redraw();
+ }
+
+ }
+
+ chart.renderExporting();
+
+ addEvent(chart, 'redraw', chart.renderExporting);
+
+ // Add update methods to handle chart.update and chart.exporting.update
+ // and chart.navigation.update.
+ each(['exporting', 'navigation'], function(prop) {
+ chart[prop] = {
+ update: function(options, redraw) {
+ update(prop, options, redraw);
+ }
+ };
+ });
+ });
+
+ }(Highcharts));
}));