highcharts-rails 0.1.1 → 2.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,703 +1,758 @@
1
- /**
2
- * @license Highcharts JS v2.1.6 (2011-07-08)
3
- * Exporting module
4
- *
5
- * (c) 2010-2011 Torstein Hønsi
6
- *
7
- * License: www.highcharts.com/license
8
- */
9
-
10
- // JSLint options:
11
- /*global Highcharts, document, window, Math, setTimeout */
12
-
13
- (function() { // encapsulate
14
-
15
- // create shortcuts
16
- var HC = Highcharts,
17
- Chart = HC.Chart,
18
- addEvent = HC.addEvent,
19
- createElement = HC.createElement,
20
- discardElement = HC.discardElement,
21
- css = HC.css,
22
- merge = HC.merge,
23
- each = HC.each,
24
- extend = HC.extend,
25
- math = Math,
26
- mathMax = math.max,
27
- doc = document,
28
- win = window,
29
- hasTouch = 'ontouchstart' in doc.documentElement,
30
- M = 'M',
31
- L = 'L',
32
- DIV = 'div',
33
- HIDDEN = 'hidden',
34
- NONE = 'none',
35
- PREFIX = 'highcharts-',
36
- ABSOLUTE = 'absolute',
37
- PX = 'px',
38
- UNDEFINED = undefined,
39
-
40
- // Add language and get the defaultOptions
41
- defaultOptions = HC.setOptions({
42
- lang: {
43
- downloadPNG: 'Download PNG image',
44
- downloadJPEG: 'Download JPEG image',
45
- downloadPDF: 'Download PDF document',
46
- downloadSVG: 'Download SVG vector image',
47
- exportButtonTitle: 'Export to raster or vector image',
48
- printButtonTitle: 'Print the chart'
49
- }
50
- });
51
-
52
- // Buttons and menus are collected in a separate config option set called 'navigation'.
53
- // This can be extended later to add control buttons like zoom and pan right click menus.
54
- defaultOptions.navigation = {
55
- menuStyle: {
56
- border: '1px solid #A0A0A0',
57
- background: '#FFFFFF'
58
- },
59
- menuItemStyle: {
60
- padding: '0 5px',
61
- background: NONE,
62
- color: '#303030',
63
- fontSize: hasTouch ? '14px' : '11px'
64
- },
65
- menuItemHoverStyle: {
66
- background: '#4572A5',
67
- color: '#FFFFFF'
68
- },
69
-
70
- buttonOptions: {
71
- align: 'right',
72
- backgroundColor: {
73
- linearGradient: [0, 0, 0, 20],
74
- stops: [
75
- [0.4, '#F7F7F7'],
76
- [0.6, '#E3E3E3']
77
- ]
78
- },
79
- borderColor: '#B0B0B0',
80
- borderRadius: 3,
81
- borderWidth: 1,
82
- //enabled: true,
83
- height: 20,
84
- hoverBorderColor: '#909090',
85
- hoverSymbolFill: '#81A7CF',
86
- hoverSymbolStroke: '#4572A5',
87
- symbolFill: '#E0E0E0',
88
- //symbolSize: 12,
89
- symbolStroke: '#A0A0A0',
90
- //symbolStrokeWidth: 1,
91
- symbolX: 11.5,
92
- symbolY: 10.5,
93
- verticalAlign: 'top',
94
- width: 24,
95
- y: 10
96
- }
97
- };
98
-
99
-
100
-
101
- // Add the export related options
102
- defaultOptions.exporting = {
103
- //enabled: true,
104
- //filename: 'chart',
105
- type: 'image/png',
106
- url: 'http://export.highcharts.com/',
107
- width: 800,
108
- enableImages: false,
109
- buttons: {
110
- exportButton: {
111
- //enabled: true,
112
- symbol: 'exportIcon',
113
- x: -10,
114
- symbolFill: '#A8BF77',
115
- hoverSymbolFill: '#768F3E',
116
- _titleKey: 'exportButtonTitle',
117
- menuItems: [{
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
- text: 'View SVG',
145
- onclick: function() {
146
- var svg = this.getSVG()
147
- .replace(/</g, '\n&lt;')
148
- .replace(/>/g, '&gt;');
149
-
150
- doc.body.innerHTML = '<pre>'+ svg +'</pre>';
151
- }
152
- }*/]
153
-
154
- },
155
- printButton: {
156
- //enabled: true,
157
- symbol: 'printIcon',
158
- x: -36,
159
- symbolFill: '#B5C9DF',
160
- hoverSymbolFill: '#779ABF',
161
- _titleKey: 'printButtonTitle',
162
- onclick: function() {
163
- this.print();
164
- }
165
- }
166
- }
167
- };
168
-
169
-
170
-
171
- extend(Chart.prototype, {
172
- /**
173
- * Return an SVG representation of the chart
174
- *
175
- * @param additionalOptions {Object} Additional chart options for the generated SVG representation
176
- */
177
- getSVG: function(additionalOptions) {
178
- var chart = this,
179
- chartCopy,
180
- sandbox,
181
- svg,
182
- seriesOptions,
183
- config,
184
- pointOptions,
185
- pointMarker,
186
- options = merge(chart.options, additionalOptions); // copy the options and add extra options
187
-
188
- // IE compatibility hack for generating SVG content that it doesn't really understand
189
- if (!doc.createElementNS) {
190
- doc.createElementNS = function(ns, tagName) {
191
- var elem = doc.createElement(tagName);
192
- elem.getBBox = function() {
193
- return HC.Renderer.prototype.Element.prototype.getBBox.apply({ element: elem });
194
- };
195
- return elem;
196
- };
197
- }
198
-
199
- // create a sandbox where a new chart will be generated
200
- sandbox = createElement(DIV, null, {
201
- position: ABSOLUTE,
202
- top: '-9999em',
203
- width: chart.chartWidth + PX,
204
- height: chart.chartHeight + PX
205
- }, doc.body);
206
-
207
- // override some options
208
- extend(options.chart, {
209
- renderTo: sandbox,
210
- forExport: true
211
- });
212
- options.exporting.enabled = false; // hide buttons in print
213
-
214
- if (!options.exporting.enableImages) {
215
- options.chart.plotBackgroundImage = null; // the converter doesn't handle images
216
- }
217
-
218
- // prepare for replicating the chart
219
- options.series = [];
220
- each(chart.series, function(serie) {
221
- seriesOptions = serie.options;
222
-
223
- seriesOptions.animation = false; // turn off animation
224
- seriesOptions.showCheckbox = false;
225
- seriesOptions.visible = serie.visible;
226
-
227
- if (!options.exporting.enableImages) {
228
- // remove image markers
229
- if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
230
- seriesOptions.marker.symbol = 'circle';
231
- }
232
- }
233
-
234
- seriesOptions.data = [];
235
-
236
- each(serie.data, function(point) {
237
-
238
- // extend the options by those values that can be expressed in a number or array config
239
- config = point.config;
240
- pointOptions = {
241
- x: point.x,
242
- y: point.y,
243
- name: point.name
244
- };
245
-
246
- if (typeof config == 'object' && point.config && config.constructor != Array) {
247
- extend(pointOptions, config);
248
- }
249
-
250
- pointOptions.visible = point.visible;
251
- seriesOptions.data.push(pointOptions); // copy fresh updated data
252
-
253
- if (!options.exporting.enableImages) {
254
- // remove image markers
255
- pointMarker = point.config && point.config.marker;
256
- if (pointMarker && /^url\(/.test(pointMarker.symbol)) {
257
- delete pointMarker.symbol;
258
- }
259
- }
260
- });
261
-
262
- options.series.push(seriesOptions);
263
- });
264
-
265
- // generate the chart copy
266
- chartCopy = new Highcharts.Chart(options);
267
-
268
- // reflect axis extremes in the export
269
- each(['xAxis', 'yAxis'], function(axisType) {
270
- each (chart[axisType], function(axis, i) {
271
- var axisCopy = chartCopy[axisType][i],
272
- extremes = axis.getExtremes(),
273
- userMin = extremes.userMin,
274
- userMax = extremes.userMax;
275
-
276
- if (userMin !== UNDEFINED || userMax !== UNDEFINED) {
277
- axisCopy.setExtremes(userMin, userMax, true, false);
278
- }
279
- });
280
- });
281
-
282
- // get the SVG from the container's innerHTML
283
- svg = chartCopy.container.innerHTML;
284
-
285
- // free up memory
286
- options = null;
287
- chartCopy.destroy();
288
- discardElement(sandbox);
289
-
290
- // sanitize
291
- svg = svg
292
- .replace(/zIndex="[^"]+"/g, '')
293
- .replace(/isShadow="[^"]+"/g, '')
294
- .replace(/symbolName="[^"]+"/g, '')
295
- .replace(/jQuery[0-9]+="[^"]+"/g, '')
296
- .replace(/isTracker="[^"]+"/g, '')
297
- .replace(/url\([^#]+#/g, 'url(#')
298
- .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
299
- .replace(/ href=/g, ' xlink:href=')
300
- /*.replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
301
- /* This fails in IE < 8
302
- .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
303
- return s2 +'.'+ s3[0];
304
- })*/
305
-
306
- // IE specific
307
- .replace(/id=([^" >]+)/g, 'id="$1"')
308
- .replace(/class=([^" ]+)/g, 'class="$1"')
309
- .replace(/ transform /g, ' ')
310
- .replace(/:(path|rect)/g, '$1')
311
- .replace(/<img ([^>]*)>/gi, '<image $1 />')
312
- .replace(/<\/image>/g, '') // remove closing tags for images as they'll never have any content
313
- .replace(/<image ([^>]*)([^\/])>/gi, '<image $1$2 />') // closes image tags for firefox
314
- .replace(/width=(\d+)/g, 'width="$1"')
315
- .replace(/height=(\d+)/g, 'height="$1"')
316
- .replace(/hc-svg-href="/g, 'xlink:href="')
317
- .replace(/style="([^"]+)"/g, function(s) {
318
- return s.toLowerCase();
319
- });
320
-
321
- // IE9 beta bugs with innerHTML. Test again with final IE9.
322
- svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
323
- .replace(/&quot;/g, "'");
324
- if (svg.match(/ xmlns="/g).length == 2) {
325
- svg = svg.replace(/xmlns="[^"]+"/, '');
326
- }
327
-
328
- return svg;
329
- },
330
-
331
- /**
332
- * Submit the SVG representation of the chart to the server
333
- * @param {Object} options Exporting options. Possible members are url, type and width.
334
- * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
335
- */
336
- exportChart: function(options, chartOptions) {
337
- var form,
338
- chart = this,
339
- svg = chart.getSVG(chartOptions);
340
-
341
- // merge the options
342
- options = merge(chart.options.exporting, options);
343
-
344
- // create the form
345
- form = createElement('form', {
346
- method: 'post',
347
- action: options.url
348
- }, {
349
- display: NONE
350
- }, doc.body);
351
-
352
- // add the values
353
- each(['filename', 'type', 'width', 'svg'], function(name) {
354
- createElement('input', {
355
- type: HIDDEN,
356
- name: name,
357
- value: {
358
- filename: options.filename || 'chart',
359
- type: options.type,
360
- width: options.width,
361
- svg: svg
362
- }[name]
363
- }, null, form);
364
- });
365
-
366
- // submit
367
- form.submit();
368
-
369
- // clean up
370
- discardElement(form);
371
- },
372
-
373
- /**
374
- * Print the chart
375
- */
376
- print: function() {
377
-
378
- var chart = this,
379
- container = chart.container,
380
- origDisplay = [],
381
- origParent = container.parentNode,
382
- body = doc.body,
383
- childNodes = body.childNodes;
384
-
385
- if (chart.isPrinting) { // block the button while in printing mode
386
- return;
387
- }
388
-
389
- chart.isPrinting = true;
390
-
391
- // hide all body content
392
- each(childNodes, function(node, i) {
393
- if (node.nodeType == 1) {
394
- origDisplay[i] = node.style.display;
395
- node.style.display = NONE;
396
- }
397
- });
398
-
399
- // pull out the chart
400
- body.appendChild(container);
401
-
402
- // print
403
- win.print();
404
-
405
- // allow the browser to prepare before reverting
406
- setTimeout(function() {
407
-
408
- // put the chart back in
409
- origParent.appendChild(container);
410
-
411
- // restore all body content
412
- each(childNodes, function(node, i) {
413
- if (node.nodeType == 1) {
414
- node.style.display = origDisplay[i];
415
- }
416
- });
417
-
418
- chart.isPrinting = false;
419
-
420
- }, 1000);
421
-
422
- },
423
-
424
- /**
425
- * Display a popup menu for choosing the export type
426
- *
427
- * @param {String} name An identifier for the menu
428
- * @param {Array} items A collection with text and onclicks for the items
429
- * @param {Number} x The x position of the opener button
430
- * @param {Number} y The y position of the opener button
431
- * @param {Number} width The width of the opener button
432
- * @param {Number} height The height of the opener button
433
- */
434
- contextMenu: function(name, items, x, y, width, height) {
435
- var chart = this,
436
- navOptions = chart.options.navigation,
437
- menuItemStyle = navOptions.menuItemStyle,
438
- chartWidth = chart.chartWidth,
439
- chartHeight = chart.chartHeight,
440
- cacheName = 'cache-'+ name,
441
- menu = chart[cacheName],
442
- menuPadding = mathMax(width, height), // for mouse leave detection
443
- boxShadow = '3px 3px 10px #888',
444
- innerMenu,
445
- hide,
446
- menuStyle;
447
-
448
- // create the menu only the first time
449
- if (!menu) {
450
-
451
- // create a HTML element above the SVG
452
- chart[cacheName] = menu = createElement(DIV, {
453
- className: PREFIX + name
454
- }, {
455
- position: ABSOLUTE,
456
- zIndex: 1000,
457
- padding: menuPadding + PX
458
- }, chart.container);
459
-
460
- innerMenu = createElement(DIV, null,
461
- extend({
462
- MozBoxShadow: boxShadow,
463
- WebkitBoxShadow: boxShadow,
464
- boxShadow: boxShadow
465
- }, navOptions.menuStyle) , menu);
466
-
467
- // hide on mouse out
468
- hide = function() {
469
- css(menu, { display: NONE });
470
- };
471
-
472
- addEvent(menu, 'mouseleave', hide);
473
-
474
-
475
- // create the items
476
- each(items, function(item) {
477
- if (item) {
478
- var div = createElement(DIV, {
479
- onmouseover: function() {
480
- css(this, navOptions.menuItemHoverStyle);
481
- },
482
- onmouseout: function() {
483
- css(this, menuItemStyle);
484
- },
485
- innerHTML: item.text || HC.getOptions().lang[item.textKey]
486
- }, extend({
487
- cursor: 'pointer'
488
- }, menuItemStyle), innerMenu);
489
-
490
- div[hasTouch ? 'ontouchstart' : 'onclick'] = function() {
491
- hide();
492
- item.onclick.apply(chart, arguments);
493
- };
494
-
495
- }
496
- });
497
-
498
- chart.exportMenuWidth = menu.offsetWidth;
499
- chart.exportMenuHeight = menu.offsetHeight;
500
- }
501
-
502
- menuStyle = { display: 'block' };
503
-
504
- // if outside right, right align it
505
- if (x + chart.exportMenuWidth > chartWidth) {
506
- menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
507
- } else {
508
- menuStyle.left = (x - menuPadding) + PX;
509
- }
510
- // if outside bottom, bottom align it
511
- if (y + height + chart.exportMenuHeight > chartHeight) {
512
- menuStyle.bottom = (chartHeight - y - menuPadding) + PX;
513
- } else {
514
- menuStyle.top = (y + height - menuPadding) + PX;
515
- }
516
-
517
- css(menu, menuStyle);
518
- },
519
-
520
- /**
521
- * Add the export button to the chart
522
- */
523
- addButton: function(options) {
524
- var chart = this,
525
- renderer = chart.renderer,
526
- btnOptions = merge(chart.options.navigation.buttonOptions, options),
527
- onclick = btnOptions.onclick,
528
- menuItems = btnOptions.menuItems,
529
- /*position = chart.getAlignment(btnOptions),
530
- buttonLeft = position.x,
531
- buttonTop = position.y,*/
532
- buttonWidth = btnOptions.width,
533
- buttonHeight = btnOptions.height,
534
- box,
535
- symbol,
536
- button,
537
- borderWidth = btnOptions.borderWidth,
538
- boxAttr = {
539
- stroke: btnOptions.borderColor
540
-
541
- },
542
- symbolAttr = {
543
- stroke: btnOptions.symbolStroke,
544
- fill: btnOptions.symbolFill
545
- };
546
-
547
- if (btnOptions.enabled === false) {
548
- return;
549
- }
550
-
551
- // element to capture the click
552
- function revert() {
553
- symbol.attr(symbolAttr);
554
- box.attr(boxAttr);
555
- }
556
-
557
- // the box border
558
- box = renderer.rect(
559
- 0,
560
- 0,
561
- buttonWidth,
562
- buttonHeight,
563
- btnOptions.borderRadius,
564
- borderWidth
565
- )
566
- //.translate(buttonLeft, buttonTop) // to allow gradients
567
- .align(btnOptions, true)
568
- .attr(extend({
569
- fill: btnOptions.backgroundColor,
570
- 'stroke-width': borderWidth,
571
- zIndex: 19
572
- }, boxAttr)).add();
573
-
574
- // the invisible element to track the clicks
575
- button = renderer.rect(
576
- 0,
577
- 0,
578
- buttonWidth,
579
- buttonHeight,
580
- 0
581
- )
582
- .align(btnOptions)
583
- .attr({
584
- fill: 'rgba(255, 255, 255, 0.001)',
585
- title: HC.getOptions().lang[btnOptions._titleKey],
586
- zIndex: 21
587
- }).css({
588
- cursor: 'pointer'
589
- })
590
- .on('mouseover', function() {
591
- symbol.attr({
592
- stroke: btnOptions.hoverSymbolStroke,
593
- fill: btnOptions.hoverSymbolFill
594
- });
595
- box.attr({
596
- stroke: btnOptions.hoverBorderColor
597
- });
598
- })
599
- .on('mouseout', revert)
600
- .on('click', revert)
601
- .add();
602
-
603
- //addEvent(button.element, 'click', revert);
604
-
605
- // add the click event
606
- if (menuItems) {
607
- onclick = function(e) {
608
- revert();
609
- var bBox = button.getBBox();
610
- chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight);
611
- };
612
- }
613
- /*addEvent(button.element, 'click', function() {
614
- onclick.apply(chart, arguments);
615
- });*/
616
- button.on('click', function() {
617
- onclick.apply(chart, arguments);
618
- });
619
-
620
- // the icon
621
- symbol = renderer.symbol(
622
- btnOptions.symbol,
623
- btnOptions.symbolX,
624
- btnOptions.symbolY,
625
- (btnOptions.symbolSize || 12) / 2
626
- )
627
- .align(btnOptions, true)
628
- .attr(extend(symbolAttr, {
629
- 'stroke-width': btnOptions.symbolStrokeWidth || 1,
630
- zIndex: 20
631
- })).add();
632
-
633
-
634
-
635
- }
636
- });
637
-
638
- // Create the export icon
639
- HC.Renderer.prototype.symbols.exportIcon = function(x, y, radius) {
640
- return [
641
- M, // the disk
642
- x - radius, y + radius,
643
- L,
644
- x + radius, y + radius,
645
- x + radius, y + radius * 0.5,
646
- x - radius, y + radius * 0.5,
647
- 'Z',
648
- M, // the arrow
649
- x, y + radius * 0.5,
650
- L,
651
- x - radius * 0.5, y - radius / 3,
652
- x - radius / 6, y - radius / 3,
653
- x - radius / 6, y - radius,
654
- x + radius / 6, y - radius,
655
- x + radius / 6, y - radius / 3,
656
- x + radius * 0.5, y - radius / 3,
657
- 'Z'
658
- ];
659
- };
660
- // Create the print icon
661
- HC.Renderer.prototype.symbols.printIcon = function(x, y, radius) {
662
- return [
663
- M, // the printer
664
- x - radius, y + radius * 0.5,
665
- L,
666
- x + radius, y + radius * 0.5,
667
- x + radius, y - radius / 3,
668
- x - radius, y - radius / 3,
669
- 'Z',
670
- M, // the upper sheet
671
- x - radius * 0.5, y - radius / 3,
672
- L,
673
- x - radius * 0.5, y - radius,
674
- x + radius * 0.5, y - radius,
675
- x + radius * 0.5, y - radius / 3,
676
- 'Z',
677
- M, // the lower sheet
678
- x - radius * 0.5, y + radius * 0.5,
679
- L,
680
- x - radius * 0.75, y + radius,
681
- x + radius * 0.75, y + radius,
682
- x + radius * 0.5, y + radius * 0.5,
683
- 'Z'
684
- ];
685
- };
686
-
687
-
688
- // Add the buttons on chart load
689
- Chart.prototype.callbacks.push(function(chart) {
690
- var n,
691
- exportingOptions = chart.options.exporting,
692
- buttons = exportingOptions.buttons;
693
-
694
- if (exportingOptions.enabled !== false) {
695
-
696
- for (n in buttons) {
697
- chart.addButton(buttons[n]);
698
- }
699
- }
700
- });
701
-
702
-
703
- })();
1
+ /**
2
+ * @license Highcharts JS v2.1.9 (2011-11-11)
3
+ * Exporting module
4
+ *
5
+ * (c) 2010-2011 Torstein Hønsi
6
+ *
7
+ * License: www.highcharts.com/license
8
+ */
9
+
10
+ // JSLint options:
11
+ /*global Highcharts, document, window, Math, setTimeout */
12
+
13
+ (function () { // encapsulate
14
+
15
+ // create shortcuts
16
+ var HC = Highcharts,
17
+ Chart = HC.Chart,
18
+ addEvent = HC.addEvent,
19
+ removeEvent = HC.removeEvent,
20
+ createElement = HC.createElement,
21
+ discardElement = HC.discardElement,
22
+ css = HC.css,
23
+ merge = HC.merge,
24
+ each = HC.each,
25
+ extend = HC.extend,
26
+ math = Math,
27
+ mathMax = math.max,
28
+ doc = document,
29
+ win = window,
30
+ hasTouch = doc.documentElement.ontouchstart !== undefined,
31
+ M = 'M',
32
+ L = 'L',
33
+ DIV = 'div',
34
+ HIDDEN = 'hidden',
35
+ NONE = 'none',
36
+ PREFIX = 'highcharts-',
37
+ ABSOLUTE = 'absolute',
38
+ PX = 'px',
39
+ UNDEFINED,
40
+ defaultOptions = HC.getOptions();
41
+
42
+ // Add language
43
+ extend(defaultOptions.lang, {
44
+ downloadPNG: 'Download PNG image',
45
+ downloadJPEG: 'Download JPEG image',
46
+ downloadPDF: 'Download PDF document',
47
+ downloadSVG: 'Download SVG vector image',
48
+ exportButtonTitle: 'Export to raster or vector image',
49
+ printButtonTitle: 'Print the chart'
50
+ });
51
+
52
+ // Buttons and menus are collected in a separate config option set called 'navigation'.
53
+ // This can be extended later to add control buttons like zoom and pan right click menus.
54
+ defaultOptions.navigation = {
55
+ menuStyle: {
56
+ border: '1px solid #A0A0A0',
57
+ background: '#FFFFFF'
58
+ },
59
+ menuItemStyle: {
60
+ padding: '0 5px',
61
+ background: NONE,
62
+ color: '#303030',
63
+ fontSize: hasTouch ? '14px' : '11px'
64
+ },
65
+ menuItemHoverStyle: {
66
+ background: '#4572A5',
67
+ color: '#FFFFFF'
68
+ },
69
+
70
+ buttonOptions: {
71
+ align: 'right',
72
+ backgroundColor: {
73
+ linearGradient: [0, 0, 0, 20],
74
+ stops: [
75
+ [0.4, '#F7F7F7'],
76
+ [0.6, '#E3E3E3']
77
+ ]
78
+ },
79
+ borderColor: '#B0B0B0',
80
+ borderRadius: 3,
81
+ borderWidth: 1,
82
+ //enabled: true,
83
+ height: 20,
84
+ hoverBorderColor: '#909090',
85
+ hoverSymbolFill: '#81A7CF',
86
+ hoverSymbolStroke: '#4572A5',
87
+ symbolFill: '#E0E0E0',
88
+ //symbolSize: 12,
89
+ symbolStroke: '#A0A0A0',
90
+ //symbolStrokeWidth: 1,
91
+ symbolX: 11.5,
92
+ symbolY: 10.5,
93
+ verticalAlign: 'top',
94
+ width: 24,
95
+ y: 10
96
+ }
97
+ };
98
+
99
+
100
+
101
+ // Add the export related options
102
+ defaultOptions.exporting = {
103
+ //enabled: true,
104
+ //filename: 'chart',
105
+ type: 'image/png',
106
+ url: 'http://export.highcharts.com/',
107
+ width: 800,
108
+ enableImages: false,
109
+ buttons: {
110
+ exportButton: {
111
+ //enabled: true,
112
+ symbol: 'exportIcon',
113
+ x: -10,
114
+ symbolFill: '#A8BF77',
115
+ hoverSymbolFill: '#768F3E',
116
+ _id: 'exportButton',
117
+ _titleKey: 'exportButtonTitle',
118
+ menuItems: [{
119
+ textKey: 'downloadPNG',
120
+ onclick: function () {
121
+ this.exportChart();
122
+ }
123
+ }, {
124
+ textKey: 'downloadJPEG',
125
+ onclick: function () {
126
+ this.exportChart({
127
+ type: 'image/jpeg'
128
+ });
129
+ }
130
+ }, {
131
+ textKey: 'downloadPDF',
132
+ onclick: function () {
133
+ this.exportChart({
134
+ type: 'application/pdf'
135
+ });
136
+ }
137
+ }, {
138
+ textKey: 'downloadSVG',
139
+ onclick: function () {
140
+ this.exportChart({
141
+ type: 'image/svg+xml'
142
+ });
143
+ }
144
+ }/*, {
145
+ text: 'View SVG',
146
+ onclick: function() {
147
+ var svg = this.getSVG()
148
+ .replace(/</g, '\n&lt;')
149
+ .replace(/>/g, '&gt;');
150
+
151
+ doc.body.innerHTML = '<pre>'+ svg +'</pre>';
152
+ }
153
+ }*/]
154
+
155
+ },
156
+ printButton: {
157
+ //enabled: true,
158
+ symbol: 'printIcon',
159
+ x: -36,
160
+ symbolFill: '#B5C9DF',
161
+ hoverSymbolFill: '#779ABF',
162
+ _id: 'printButton',
163
+ _titleKey: 'printButtonTitle',
164
+ onclick: function () {
165
+ this.print();
166
+ }
167
+ }
168
+ }
169
+ };
170
+
171
+
172
+
173
+ extend(Chart.prototype, {
174
+ /**
175
+ * Return an SVG representation of the chart
176
+ *
177
+ * @param additionalOptions {Object} Additional chart options for the generated SVG representation
178
+ */
179
+ getSVG: function (additionalOptions) {
180
+ var chart = this,
181
+ chartCopy,
182
+ sandbox,
183
+ svg,
184
+ seriesOptions,
185
+ config,
186
+ pointOptions,
187
+ pointMarker,
188
+ options = merge(chart.options, additionalOptions); // copy the options and add extra options
189
+
190
+ // IE compatibility hack for generating SVG content that it doesn't really understand
191
+ if (!doc.createElementNS) {
192
+ /*jslint unparam: true*//* allow unused parameter ns in function below */
193
+ doc.createElementNS = function (ns, tagName) {
194
+ var elem = doc.createElement(tagName);
195
+ elem.getBBox = function () {
196
+ return HC.Renderer.prototype.Element.prototype.getBBox.apply({ element: elem });
197
+ };
198
+ return elem;
199
+ };
200
+ /*jslint unparam: false*/
201
+ }
202
+
203
+ // create a sandbox where a new chart will be generated
204
+ sandbox = createElement(DIV, null, {
205
+ position: ABSOLUTE,
206
+ top: '-9999em',
207
+ width: chart.chartWidth + PX,
208
+ height: chart.chartHeight + PX
209
+ }, doc.body);
210
+
211
+ // override some options
212
+ extend(options.chart, {
213
+ renderTo: sandbox,
214
+ forExport: true
215
+ });
216
+ options.exporting.enabled = false; // hide buttons in print
217
+
218
+ if (!options.exporting.enableImages) {
219
+ options.chart.plotBackgroundImage = null; // the converter doesn't handle images
220
+ }
221
+
222
+ // prepare for replicating the chart
223
+ options.series = [];
224
+ each(chart.series, function (serie) {
225
+ seriesOptions = serie.options;
226
+
227
+ seriesOptions.animation = false; // turn off animation
228
+ seriesOptions.showCheckbox = false;
229
+ seriesOptions.visible = serie.visible;
230
+
231
+ if (!options.exporting.enableImages) {
232
+ // remove image markers
233
+ if (seriesOptions && seriesOptions.marker && /^url\(/.test(seriesOptions.marker.symbol)) {
234
+ seriesOptions.marker.symbol = 'circle';
235
+ }
236
+ }
237
+
238
+ seriesOptions.data = [];
239
+
240
+ each(serie.data, function (point) {
241
+
242
+ // extend the options by those values that can be expressed in a number or array config
243
+ config = point.config;
244
+ pointOptions = {
245
+ x: point.x,
246
+ y: point.y,
247
+ name: point.name
248
+ };
249
+
250
+ if (typeof config === 'object' && point.config && config.constructor !== Array) {
251
+ extend(pointOptions, config);
252
+ }
253
+
254
+ pointOptions.visible = point.visible;
255
+ seriesOptions.data.push(pointOptions); // copy fresh updated data
256
+
257
+ if (!options.exporting.enableImages) {
258
+ // remove image markers
259
+ pointMarker = point.config && point.config.marker;
260
+ if (pointMarker && /^url\(/.test(pointMarker.symbol)) {
261
+ delete pointMarker.symbol;
262
+ }
263
+ }
264
+ });
265
+
266
+ options.series.push(seriesOptions);
267
+ });
268
+
269
+ // generate the chart copy
270
+ chartCopy = new Highcharts.Chart(options);
271
+
272
+ // reflect axis extremes in the export
273
+ each(['xAxis', 'yAxis'], function (axisType) {
274
+ each(chart[axisType], function (axis, i) {
275
+ var axisCopy = chartCopy[axisType][i],
276
+ extremes = axis.getExtremes(),
277
+ userMin = extremes.userMin,
278
+ userMax = extremes.userMax;
279
+
280
+ if (userMin !== UNDEFINED || userMax !== UNDEFINED) {
281
+ axisCopy.setExtremes(userMin, userMax, true, false);
282
+ }
283
+ });
284
+ });
285
+
286
+ // get the SVG from the container's innerHTML
287
+ svg = chartCopy.container.innerHTML;
288
+
289
+ // free up memory
290
+ options = null;
291
+ chartCopy.destroy();
292
+ discardElement(sandbox);
293
+
294
+ // sanitize
295
+ svg = svg
296
+ .replace(/zIndex="[^"]+"/g, '')
297
+ .replace(/isShadow="[^"]+"/g, '')
298
+ .replace(/symbolName="[^"]+"/g, '')
299
+ .replace(/jQuery[0-9]+="[^"]+"/g, '')
300
+ .replace(/isTracker="[^"]+"/g, '')
301
+ .replace(/url\([^#]+#/g, 'url(#')
302
+ .replace(/<svg /, '<svg xmlns:xlink="http://www.w3.org/1999/xlink" ')
303
+ .replace(/ href=/g, ' xlink:href=')
304
+ /*.replace(/preserveAspectRatio="none">/g, 'preserveAspectRatio="none"/>')*/
305
+ /* This fails in IE < 8
306
+ .replace(/([0-9]+)\.([0-9]+)/g, function(s1, s2, s3) { // round off to save weight
307
+ return s2 +'.'+ s3[0];
308
+ })*/
309
+
310
+ // Replace HTML entities, issue #347
311
+ .replace(/&nbsp;/g, '\u00A0') // no-break space
312
+ .replace(/&shy;/g, '\u00AD') // soft hyphen
313
+
314
+ // IE specific
315
+ .replace(/id=([^" >]+)/g, 'id="$1"')
316
+ .replace(/class=([^" ]+)/g, 'class="$1"')
317
+ .replace(/ transform /g, ' ')
318
+ .replace(/:(path|rect)/g, '$1')
319
+ .replace(/<img ([^>]*)>/gi, '<image $1 />')
320
+ .replace(/<\/image>/g, '') // remove closing tags for images as they'll never have any content
321
+ .replace(/<image ([^>]*)([^\/])>/gi, '<image $1$2 />') // closes image tags for firefox
322
+ .replace(/width=(\d+)/g, 'width="$1"')
323
+ .replace(/height=(\d+)/g, 'height="$1"')
324
+ .replace(/hc-svg-href="/g, 'xlink:href="')
325
+ .replace(/style="([^"]+)"/g, function (s) {
326
+ return s.toLowerCase();
327
+ });
328
+
329
+ // IE9 beta bugs with innerHTML. Test again with final IE9.
330
+ svg = svg.replace(/(url\(#highcharts-[0-9]+)&quot;/g, '$1')
331
+ .replace(/&quot;/g, "'");
332
+ if (svg.match(/ xmlns="/g).length === 2) {
333
+ svg = svg.replace(/xmlns="[^"]+"/, '');
334
+ }
335
+
336
+ return svg;
337
+ },
338
+
339
+ /**
340
+ * Submit the SVG representation of the chart to the server
341
+ * @param {Object} options Exporting options. Possible members are url, type and width.
342
+ * @param {Object} chartOptions Additional chart options for the SVG representation of the chart
343
+ */
344
+ exportChart: function (options, chartOptions) {
345
+ var form,
346
+ chart = this,
347
+ svg = chart.getSVG(chartOptions);
348
+
349
+ // merge the options
350
+ options = merge(chart.options.exporting, options);
351
+
352
+ // create the form
353
+ form = createElement('form', {
354
+ method: 'post',
355
+ action: options.url
356
+ }, {
357
+ display: NONE
358
+ }, doc.body);
359
+
360
+ // add the values
361
+ each(['filename', 'type', 'width', 'svg'], function (name) {
362
+ createElement('input', {
363
+ type: HIDDEN,
364
+ name: name,
365
+ value: {
366
+ filename: options.filename || 'chart',
367
+ type: options.type,
368
+ width: options.width,
369
+ svg: svg
370
+ }[name]
371
+ }, null, form);
372
+ });
373
+
374
+ // submit
375
+ form.submit();
376
+
377
+ // clean up
378
+ discardElement(form);
379
+ },
380
+
381
+ /**
382
+ * Print the chart
383
+ */
384
+ print: function () {
385
+
386
+ var chart = this,
387
+ container = chart.container,
388
+ origDisplay = [],
389
+ origParent = container.parentNode,
390
+ body = doc.body,
391
+ childNodes = body.childNodes;
392
+
393
+ if (chart.isPrinting) { // block the button while in printing mode
394
+ return;
395
+ }
396
+
397
+ chart.isPrinting = true;
398
+
399
+ // hide all body content
400
+ each(childNodes, function (node, i) {
401
+ if (node.nodeType === 1) {
402
+ origDisplay[i] = node.style.display;
403
+ node.style.display = NONE;
404
+ }
405
+ });
406
+
407
+ // pull out the chart
408
+ body.appendChild(container);
409
+
410
+ // print
411
+ win.print();
412
+
413
+ // allow the browser to prepare before reverting
414
+ setTimeout(function () {
415
+
416
+ // put the chart back in
417
+ origParent.appendChild(container);
418
+
419
+ // restore all body content
420
+ each(childNodes, function (node, i) {
421
+ if (node.nodeType === 1) {
422
+ node.style.display = origDisplay[i];
423
+ }
424
+ });
425
+
426
+ chart.isPrinting = false;
427
+
428
+ }, 1000);
429
+
430
+ },
431
+
432
+ /**
433
+ * Display a popup menu for choosing the export type
434
+ *
435
+ * @param {String} name An identifier for the menu
436
+ * @param {Array} items A collection with text and onclicks for the items
437
+ * @param {Number} x The x position of the opener button
438
+ * @param {Number} y The y position of the opener button
439
+ * @param {Number} width The width of the opener button
440
+ * @param {Number} height The height of the opener button
441
+ */
442
+ contextMenu: function (name, items, x, y, width, height) {
443
+ var chart = this,
444
+ navOptions = chart.options.navigation,
445
+ menuItemStyle = navOptions.menuItemStyle,
446
+ chartWidth = chart.chartWidth,
447
+ chartHeight = chart.chartHeight,
448
+ cacheName = 'cache-' + name,
449
+ menu = chart[cacheName],
450
+ menuPadding = mathMax(width, height), // for mouse leave detection
451
+ boxShadow = '3px 3px 10px #888',
452
+ innerMenu,
453
+ hide,
454
+ menuStyle;
455
+
456
+ // create the menu only the first time
457
+ if (!menu) {
458
+
459
+ // create a HTML element above the SVG
460
+ chart[cacheName] = menu = createElement(DIV, {
461
+ className: PREFIX + name
462
+ }, {
463
+ position: ABSOLUTE,
464
+ zIndex: 1000,
465
+ padding: menuPadding + PX
466
+ }, chart.container);
467
+
468
+ innerMenu = createElement(DIV, null,
469
+ extend({
470
+ MozBoxShadow: boxShadow,
471
+ WebkitBoxShadow: boxShadow,
472
+ boxShadow: boxShadow
473
+ }, navOptions.menuStyle), menu);
474
+
475
+ // hide on mouse out
476
+ hide = function () {
477
+ css(menu, { display: NONE });
478
+ };
479
+
480
+ addEvent(menu, 'mouseleave', hide);
481
+
482
+
483
+ // create the items
484
+ each(items, function (item) {
485
+ if (item) {
486
+ var div = createElement(DIV, {
487
+ onmouseover: function () {
488
+ css(this, navOptions.menuItemHoverStyle);
489
+ },
490
+ onmouseout: function () {
491
+ css(this, menuItemStyle);
492
+ },
493
+ innerHTML: item.text || chart.options.lang[item.textKey]
494
+ }, extend({
495
+ cursor: 'pointer'
496
+ }, menuItemStyle), innerMenu);
497
+
498
+ div[hasTouch ? 'ontouchstart' : 'onclick'] = function () {
499
+ hide();
500
+ item.onclick.apply(chart, arguments);
501
+ };
502
+
503
+ // Keep references to menu divs to be able to destroy them
504
+ chart.exportDivElements.push(div);
505
+ }
506
+ });
507
+
508
+ // Keep references to menu and innerMenu div to be able to destroy them
509
+ chart.exportDivElements.push(innerMenu, menu);
510
+
511
+ chart.exportMenuWidth = menu.offsetWidth;
512
+ chart.exportMenuHeight = menu.offsetHeight;
513
+ }
514
+
515
+ menuStyle = { display: 'block' };
516
+
517
+ // if outside right, right align it
518
+ if (x + chart.exportMenuWidth > chartWidth) {
519
+ menuStyle.right = (chartWidth - x - width - menuPadding) + PX;
520
+ } else {
521
+ menuStyle.left = (x - menuPadding) + PX;
522
+ }
523
+ // if outside bottom, bottom align it
524
+ if (y + height + chart.exportMenuHeight > chartHeight) {
525
+ menuStyle.bottom = (chartHeight - y - menuPadding) + PX;
526
+ } else {
527
+ menuStyle.top = (y + height - menuPadding) + PX;
528
+ }
529
+
530
+ css(menu, menuStyle);
531
+ },
532
+
533
+ /**
534
+ * Add the export button to the chart
535
+ */
536
+ addButton: function (options) {
537
+ var chart = this,
538
+ renderer = chart.renderer,
539
+ btnOptions = merge(chart.options.navigation.buttonOptions, options),
540
+ onclick = btnOptions.onclick,
541
+ menuItems = btnOptions.menuItems,
542
+ /*position = chart.getAlignment(btnOptions),
543
+ buttonLeft = position.x,
544
+ buttonTop = position.y,*/
545
+ buttonWidth = btnOptions.width,
546
+ buttonHeight = btnOptions.height,
547
+ box,
548
+ symbol,
549
+ button,
550
+ borderWidth = btnOptions.borderWidth,
551
+ boxAttr = {
552
+ stroke: btnOptions.borderColor
553
+
554
+ },
555
+ symbolAttr = {
556
+ stroke: btnOptions.symbolStroke,
557
+ fill: btnOptions.symbolFill
558
+ };
559
+
560
+ // Keeps references to the button elements
561
+ if (!chart.exportDivElements) {
562
+ chart.exportDivElements = [];
563
+ chart.exportSVGElements = [];
564
+ }
565
+
566
+ if (btnOptions.enabled === false) {
567
+ return;
568
+ }
569
+
570
+ // element to capture the click
571
+ function revert() {
572
+ symbol.attr(symbolAttr);
573
+ box.attr(boxAttr);
574
+ }
575
+
576
+ // the box border
577
+ box = renderer.rect(
578
+ 0,
579
+ 0,
580
+ buttonWidth,
581
+ buttonHeight,
582
+ btnOptions.borderRadius,
583
+ borderWidth
584
+ )
585
+ //.translate(buttonLeft, buttonTop) // to allow gradients
586
+ .align(btnOptions, true)
587
+ .attr(extend({
588
+ fill: btnOptions.backgroundColor,
589
+ 'stroke-width': borderWidth,
590
+ zIndex: 19
591
+ }, boxAttr)).add();
592
+
593
+ // the invisible element to track the clicks
594
+ button = renderer.rect(
595
+ 0,
596
+ 0,
597
+ buttonWidth,
598
+ buttonHeight,
599
+ 0
600
+ )
601
+ .align(btnOptions)
602
+ .attr({
603
+ id: btnOptions._id,
604
+ fill: 'rgba(255, 255, 255, 0.001)',
605
+ title: chart.options.lang[btnOptions._titleKey],
606
+ zIndex: 21
607
+ }).css({
608
+ cursor: 'pointer'
609
+ })
610
+ .on('mouseover', function () {
611
+ symbol.attr({
612
+ stroke: btnOptions.hoverSymbolStroke,
613
+ fill: btnOptions.hoverSymbolFill
614
+ });
615
+ box.attr({
616
+ stroke: btnOptions.hoverBorderColor
617
+ });
618
+ })
619
+ .on('mouseout', revert)
620
+ .on('click', revert)
621
+ .add();
622
+
623
+ //addEvent(button.element, 'click', revert);
624
+
625
+ // add the click event
626
+ if (menuItems) {
627
+ onclick = function () {
628
+ revert();
629
+ var bBox = button.getBBox();
630
+ chart.contextMenu('export-menu', menuItems, bBox.x, bBox.y, buttonWidth, buttonHeight);
631
+ };
632
+ }
633
+ /*addEvent(button.element, 'click', function() {
634
+ onclick.apply(chart, arguments);
635
+ });*/
636
+ button.on('click', function () {
637
+ onclick.apply(chart, arguments);
638
+ });
639
+
640
+ // the icon
641
+ symbol = renderer.symbol(
642
+ btnOptions.symbol,
643
+ btnOptions.symbolX,
644
+ btnOptions.symbolY,
645
+ (btnOptions.symbolSize || 12) / 2
646
+ )
647
+ .align(btnOptions, true)
648
+ .attr(extend(symbolAttr, {
649
+ 'stroke-width': btnOptions.symbolStrokeWidth || 1,
650
+ zIndex: 20
651
+ })).add();
652
+
653
+ // Keep references to the renderer element so to be able to destroy them later.
654
+ chart.exportSVGElements.push(box, button, symbol);
655
+ },
656
+
657
+ /**
658
+ * Destroy the buttons.
659
+ */
660
+ destroyExport: function () {
661
+ var i,
662
+ chart = this,
663
+ elem;
664
+
665
+ // Destroy the extra buttons added
666
+ for (i = 0; i < chart.exportSVGElements.length; i++) {
667
+ elem = chart.exportSVGElements[i];
668
+ // Destroy and null the svg/vml elements
669
+ elem.onclick = elem.ontouchstart = null;
670
+ chart.exportSVGElements[i] = elem.destroy();
671
+ }
672
+
673
+ // Destroy the divs for the menu
674
+ for (i = 0; i < chart.exportDivElements.length; i++) {
675
+ elem = chart.exportDivElements[i];
676
+
677
+ // Remove the event handler
678
+ removeEvent(elem, 'mouseleave');
679
+
680
+ // Remove inline events
681
+ chart.exportDivElements[i] = elem.onmouseout = elem.onmouseover = elem.ontouchstart = elem.onclick = null;
682
+
683
+ // Destroy the div by moving to garbage bin
684
+ discardElement(elem);
685
+ }
686
+ }
687
+ });
688
+
689
+ // Create the export icon
690
+ HC.Renderer.prototype.symbols.exportIcon = function (x, y, radius) {
691
+ return [
692
+ M, // the disk
693
+ x - radius, y + radius,
694
+ L,
695
+ x + radius, y + radius,
696
+ x + radius, y + radius * 0.5,
697
+ x - radius, y + radius * 0.5,
698
+ 'Z',
699
+ M, // the arrow
700
+ x, y + radius * 0.5,
701
+ L,
702
+ x - radius * 0.5, y - radius / 3,
703
+ x - radius / 6, y - radius / 3,
704
+ x - radius / 6, y - radius,
705
+ x + radius / 6, y - radius,
706
+ x + radius / 6, y - radius / 3,
707
+ x + radius * 0.5, y - radius / 3,
708
+ 'Z'
709
+ ];
710
+ };
711
+ // Create the print icon
712
+ HC.Renderer.prototype.symbols.printIcon = function (x, y, radius) {
713
+ return [
714
+ M, // the printer
715
+ x - radius, y + radius * 0.5,
716
+ L,
717
+ x + radius, y + radius * 0.5,
718
+ x + radius, y - radius / 3,
719
+ x - radius, y - radius / 3,
720
+ 'Z',
721
+ M, // the upper sheet
722
+ x - radius * 0.5, y - radius / 3,
723
+ L,
724
+ x - radius * 0.5, y - radius,
725
+ x + radius * 0.5, y - radius,
726
+ x + radius * 0.5, y - radius / 3,
727
+ 'Z',
728
+ M, // the lower sheet
729
+ x - radius * 0.5, y + radius * 0.5,
730
+ L,
731
+ x - radius * 0.75, y + radius,
732
+ x + radius * 0.75, y + radius,
733
+ x + radius * 0.5, y + radius * 0.5,
734
+ 'Z'
735
+ ];
736
+ };
737
+
738
+
739
+ // Add the buttons on chart load
740
+ Chart.prototype.callbacks.push(function (chart) {
741
+ var n,
742
+ exportingOptions = chart.options.exporting,
743
+ buttons = exportingOptions.buttons;
744
+
745
+ if (exportingOptions.enabled !== false) {
746
+
747
+ for (n in buttons) {
748
+ chart.addButton(buttons[n]);
749
+ }
750
+
751
+ // Destroy the export elements at chart destroy
752
+ addEvent(chart, 'destroy', chart.destroyExport);
753
+ }
754
+
755
+ });
756
+
757
+
758
+ }());