diff options
Diffstat (limited to 'html/includes/js/modules/exporting.src.js')
-rw-r--r-- | html/includes/js/modules/exporting.src.js | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/html/includes/js/modules/exporting.src.js b/html/includes/js/modules/exporting.src.js new file mode 100644 index 0000000..2111990 --- /dev/null +++ b/html/includes/js/modules/exporting.src.js @@ -0,0 +1,719 @@ +/** + * @license Highcharts JS v4.0.4 (2014-09-02) + * Exporting module + * + * (c) 2010-2014 Torstein Honsi + * + * License: www.highcharts.com/license + */ + +// JSLint options: +/*global Highcharts, document, window, Math, setTimeout */ + +(function (Highcharts) { // encapsulate + +// create shortcuts +var 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 options +defaultOptions.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 utility +Highcharts.post = function (url, data, formAttributes) { + var name, + form; + + // create the form + form = createElement('form', merge({ + method: 'post', + action: url, + enctype: 'multipart/form-data' + }, formAttributes), { + 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 + enableMouseTracking: false, + 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/, ' ') + // 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]; + })*/ + + // 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, width and formAttributes. + * @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 + }, options.formAttributes); + + }, + + /** + * 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, + docMouseUpHandler = function (e) { + if (!chart.pointer.inClass(e.target, className)) { + hide(); + } + }; + + // 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, #2335, #2407) + addEvent(document, 'mouseup', docMouseUpHandler); + addEvent(chart, 'destroy', function () { + removeEvent(document, 'mouseup', docMouseUpHandler); + }); + + + // 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 load +Chart.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)); |