2 * @license Highcharts JS v2.1.4 (2011-03-02)
5 * (c) 2010 Torstein Hønsi
7 * License: www.highcharts.com/license
9 * Please Note: This file has been adjusted for use in phpMyAdmin,
10 * to allow chart exporting without the batik library
14 /*global Highcharts, document, window, Math, setTimeout */
16 (function() { // encapsulate
21 addEvent
= HC
.addEvent
,
22 createElement
= HC
.createElement
,
23 discardElement
= HC
.discardElement
,
32 hasTouch
= 'ontouchstart' in doc
.documentElement
,
38 PREFIX
= 'highcharts-',
39 ABSOLUTE
= 'absolute',
44 // Add language and get the defaultOptions
45 defaultOptions
= HC
.setOptions({
47 downloadPNG
: 'Download PNG image',
48 downloadJPEG
: 'Download JPEG image',
49 downloadPDF
: 'Download PDF document',
50 downloadSVG
: 'Download SVG vector image',
51 exportButtonTitle
: 'Export to raster or vector image',
52 printButton
: 'Print the chart'
56 // Buttons and menus are collected in a separate config option set called 'navigation'.
57 // This can be extended later to add control buttons like zoom and pan right click menus.
58 defaultOptions
.navigation
= {
60 border
: '1px solid #A0A0A0',
67 fontSize
: hasTouch
? '14px' : '11px'
70 background
: '#4572A5',
77 linearGradient
: [0, 0, 0, 20],
83 borderColor
: '#B0B0B0',
88 hoverBorderColor
: '#909090',
89 hoverSymbolFill
: '#81A7CF',
90 hoverSymbolStroke
: '#4572A5',
91 symbolFill
: '#E0E0E0',
93 symbolStroke
: '#A0A0A0',
94 //symbolStrokeWidth: 1,
105 // Add the export related options
106 defaultOptions
.exporting
= {
110 url
: 'file_echo.php',
115 symbol
: 'exportIcon',
117 symbolFill
: '#A8BF77',
118 hoverSymbolFill
: '#768F3E',
119 _titleKey
: 'exportButtonTitle',
122 textKey
: 'downloadPNG',
123 onclick: function() {
127 textKey
: 'downloadSVG',
128 onclick: function() {
130 type
: 'image/svg+xml'
134 textKey
: 'printButton',
135 onclick: function() {
146 extend(Chart
.prototype, {
148 * Return an SVG representation of the chart
150 * @param additionalOptions {Object} Additional chart options for the generated SVG representation
152 getSVG: function(additionalOptions
) {
161 options
= merge(chart
.options
, additionalOptions
); // copy the options and add extra options
163 // IE compatibility hack for generating SVG content that it doesn't really understand
164 if (!doc
.createElementNS
) {
165 doc
.createElementNS = function(ns
, tagName
) {
166 var elem
= doc
.createElement(tagName
);
167 elem
.getBBox = function() {
168 return chart
.renderer
.Element
.prototype.getBBox
.apply({ element
: elem
});
174 // create a sandbox where a new chart will be generated
175 sandbox
= createElement(DIV
, null, {
178 width
: chart
.chartWidth
+ PX
,
179 height
: chart
.chartHeight
+ PX
182 // override some options
183 extend(options
.chart
, {
187 options
.exporting
.enabled
= false; // hide buttons in print
188 options
.chart
.plotBackgroundImage
= null; // the converter doesn't handle images
189 // prepare for replicating the chart
191 each(chart
.series
, function(serie
) {
192 seriesOptions
= serie
.options
;
194 seriesOptions
.animation
= false; // turn off animation
195 seriesOptions
.showCheckbox
= false;
197 // remove image markers
198 if (seriesOptions
&& seriesOptions
.marker
&& /^url\(/.test(seriesOptions
.marker
.symbol
)) {
199 seriesOptions
.marker
.symbol
= 'circle';
202 seriesOptions
.data
= [];
204 each(serie
.data
, function(point
) {
206 // extend the options by those values that can be expressed in a number or array config
207 config
= point
.config
;
214 if (typeof config
== 'object' && point
.config
&& config
.constructor != Array
) {
215 extend(pointOptions
, config
);
218 seriesOptions
.data
.push(pointOptions
); // copy fresh updated data
220 // remove image markers
221 pointMarker
= point
.config
&& point
.config
.marker
;
222 if (pointMarker
&& /^url\(/.test(pointMarker
.symbol
)) {
223 delete pointMarker
.symbol
;
227 options
.series
.push(seriesOptions
);
230 // generate the chart copy
231 chartCopy
= new Highcharts
.Chart(options
);
233 // get the SVG from the container's innerHTML
234 svg
= chartCopy
.container
.innerHTML
;
239 discardElement(sandbox
);
243 .replace(/zIndex="[^"]+"/g, '')
244 .replace(/isShadow="[^"]+"/g, '')
245 .replace(/symbolName="[^"]+"/g, '')
246 .replace(/jQuery[0-9]+="[^"]+"/g, '')
247 .replace(/isTracker="[^"]+"/g, '')
248 .replace(/url\([^#]+#/g, 'url(#')
249 /*.replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
250 .replace(/ href=/, ' xlink:href=')
251 .replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
252 /* This fails in IE < 8
253 .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
254 return s2 +'.'+ s3[0];
258 .replace(/id=([^" >]+)/g, 'id="$1"')
259 .replace(/class=([^" ]+)/g, 'class="$1"')
260 .replace(/ transform
/g
, ' ')
261 .replace(/:(path|rect)/g, '$1')
262 .replace(/style="([^"]+)"/g, function(s
) {
263 return s
.toLowerCase();
266 // IE9 beta bugs with innerHTML. Test again with final IE9.
267 svg
= svg
.replace(/(url\(#highcharts-[0-9]+)"/g, '$1')
268 .replace(/"/g, "'");
269 if (svg
.match(/ xmlns
="/g).length == 2) {
270 svg = svg.replace(/xmlns="[^"]+"/, '');
277 * Submit the SVG representation of the chart to the server
278 * @param {Object} options Exporting options. Possible members are url, type and width.
279 * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
281 exportChart: function(options
, chartOptions
) {
284 canvas
=createElement('canvas');
286 $('body').append(canvas
);
287 $(canvas
).css('position','absolute');
288 $(canvas
).css('left','-10000px');
290 var submitData = function(chartData
) {
292 options
= merge(chart
.options
.exporting
, options
);
295 form
= createElement('form', {
303 each(['filename', 'type', 'width', 'image','token'], function(name
) {
304 createElement('input', {
308 filename
: options
.filename
|| 'chart',
310 width
: options
.width
,
321 discardElement(form
);
324 if(options
&& options
.type
=='image/svg+xml') {
325 submitData(chart
.getSVG(chartOptions
));
327 if (typeof FlashCanvas
!= "undefined") {
328 FlashCanvas
.initElement(canvas
);
331 // Generate data uri and submit once done
332 canvg(canvas
, chart
.getSVG(chartOptions
),{
333 ignoreAnimation
:true,
335 renderCallback:function() {
336 // IE8 fix: flashcanvas doesn't update the canvas immediately, thus requiring setTimeout.
337 // See also http://groups.google.com/group/flashcanvas/browse_thread/thread/e36ff7a03e1bfb0a
338 setTimeout(function() { submitData(canvas
.toDataURL()); }, 100);
350 container
= chart
.container
,
352 origParent
= container
.parentNode
,
354 childNodes
= body
.childNodes
;
356 if (chart
.isPrinting
) { // block the button while in printing mode
360 chart
.isPrinting
= true;
362 // hide all body content
363 each(childNodes
, function(node
, i
) {
364 if (node
.nodeType
== 1) {
365 origDisplay
[i
] = node
.style
.display
;
366 node
.style
.display
= NONE
;
370 // pull out the chart
371 body
.appendChild(container
);
376 // allow the browser to prepare before reverting
377 setTimeout(function() {
379 // put the chart back in
380 origParent
.appendChild(container
);
382 // restore all body content
383 each(childNodes
, function(node
, i
) {
384 if (node
.nodeType
== 1) {
385 node
.style
.display
= origDisplay
[i
];
389 chart
.isPrinting
= false;
396 * Display a popup menu for choosing the export type
398 * @param {String} name An identifier for the menu
399 * @param {Array} items A collection with text and onclicks for the items
400 * @param {Number} x The x position of the opener button
401 * @param {Number} y The y position of the opener button
402 * @param {Number} width The width of the opener button
403 * @param {Number} height The height of the opener button
405 contextMenu: function(name
, items
, x
, y
, width
, height
) {
407 navOptions
= chart
.options
.navigation
,
408 menuItemStyle
= navOptions
.menuItemStyle
,
409 chartWidth
= chart
.chartWidth
,
410 chartHeight
= chart
.chartHeight
,
411 cacheName
= 'cache-'+ name
,
412 menu
= chart
[cacheName
],
413 menuPadding
= mathMax(width
, height
), // for mouse leave detection
414 boxShadow
= '3px 3px 10px #888',
419 // create the menu only the first time
422 // create a HTML element above the SVG
423 chart
[cacheName
] = menu
= createElement(DIV
, {
424 className
: PREFIX
+ name
428 padding
: menuPadding
+ PX
431 innerMenu
= createElement(DIV
, null,
433 MozBoxShadow
: boxShadow
,
434 WebkitBoxShadow
: boxShadow
,
436 }, navOptions
.menuStyle
) , menu
);
440 css(menu
, { display
: NONE
});
443 addEvent(menu
, 'mouseleave', hide
);
447 each(items
, function(item
) {
449 var div
= createElement(DIV
, {
450 onmouseover: function() {
451 css(this, navOptions
.menuItemHoverStyle
);
453 onmouseout: function() {
454 css(this, menuItemStyle
);
456 innerHTML
: item
.text
|| HC
.getOptions().lang
[item
.textKey
]
459 }, menuItemStyle
), innerMenu
);
461 div
[hasTouch
? 'ontouchstart' : 'onclick'] = function() {
463 item
.onclick
.apply(chart
, arguments
);
469 chart
.exportMenuWidth
= menu
.offsetWidth
;
470 chart
.exportMenuHeight
= menu
.offsetHeight
;
473 menuStyle
= { display
: 'block' };
475 // if outside right, right align it
476 if (x
+ chart
.exportMenuWidth
> chartWidth
) {
477 menuStyle
.right
= (chartWidth
- x
- width
- menuPadding
) + PX
;
479 menuStyle
.left
= (x
- menuPadding
) + PX
;
481 // if outside bottom, bottom align it
482 if (y
+ height
+ chart
.exportMenuHeight
> chartHeight
) {
483 menuStyle
.bottom
= (chartHeight
- y
- menuPadding
) + PX
;
485 menuStyle
.top
= (y
+ height
- menuPadding
) + PX
;
488 css(menu
, menuStyle
);
492 * Add the export button to the chart
494 addButton: function(options
) {
496 renderer
= chart
.renderer
,
497 btnOptions
= merge(chart
.options
.navigation
.buttonOptions
, options
),
498 onclick
= btnOptions
.onclick
,
499 menuItems
= btnOptions
.menuItems
,
500 //position = chart.getAlignment(btnOptions),
501 /*buttonLeft = position.x,
502 buttonTop = position.y,*/
503 buttonWidth
= btnOptions
.width
,
504 buttonHeight
= btnOptions
.height
,
508 borderWidth
= btnOptions
.borderWidth
,
510 stroke
: btnOptions
.borderColor
514 stroke
: btnOptions
.symbolStroke
,
515 fill
: btnOptions
.symbolFill
518 if (btnOptions
.enabled
=== false) {
522 // element to capture the click
524 symbol
.attr(symbolAttr
);
534 btnOptions
.borderRadius
,
537 //.translate(buttonLeft, buttonTop) // to allow gradients
538 .align(btnOptions
, true)
540 fill
: btnOptions
.backgroundColor
,
541 'stroke-width': borderWidth
,
545 // the invisible element to track the clicks
546 button
= renderer
.rect(
555 fill
: 'rgba(255, 255, 255, 0.001)',
556 title
: HC
.getOptions().lang
[btnOptions
._titleKey
],
561 .on('mouseover', function() {
563 stroke
: btnOptions
.hoverSymbolStroke
,
564 fill
: btnOptions
.hoverSymbolFill
567 stroke
: btnOptions
.hoverBorderColor
570 .on('mouseout', revert
)
574 //addEvent(button.element, 'click', revert);
576 // add the click event
578 onclick = function(e
) {
580 var bBox
= button
.getBBox();
581 chart
.contextMenu(btnOptions
.menuName
, menuItems
, bBox
.x
, bBox
.y
, buttonWidth
, buttonHeight
);
584 /*addEvent(button.element, 'click', function() {
585 onclick.apply(chart, arguments);
587 button
.on('click', function() {
588 onclick
.apply(chart
, arguments
);
592 symbol
= renderer
.symbol(
596 (btnOptions
.symbolSize
|| 12) / 2
598 .align(btnOptions
, true)
599 .attr(extend(symbolAttr
, {
600 'stroke-width': btnOptions
.symbolStrokeWidth
|| 1,
609 // Create the export icon
610 HC
.Renderer
.prototype.symbols
.exportIcon = function(x
, y
, radius
) {
613 x
- radius
, y
+ radius
,
615 x
+ radius
, y
+ radius
,
616 x
+ radius
, y
+ radius
* 0.5,
617 x
- radius
, y
+ radius
* 0.5,
622 x
- radius
* 0.5, y
- radius
/ 3,
623 x
- radius
/ 6, y
- radius
/ 3,
624 x
- radius
/ 6, y
- radius
,
625 x
+ radius
/ 6, y
- radius
,
626 x
+ radius
/ 6, y
- radius
/ 3,
627 x
+ radius
* 0.5, y
- radius
/ 3,
632 // Add the buttons on chart load
633 Chart
.prototype.callbacks
.push(function(chart
) {
635 exportingOptions
= chart
.options
.exporting
,
636 buttons
= exportingOptions
.buttons
;
638 if (exportingOptions
.enabled
!== false) {
641 chart
.addButton(buttons
[n
]);
643 for (n
in chart
.options
.buttons
) {
644 chart
.addButton(chart
.options
.buttons
[n
]);