amcharts.rb 3.11.2.14 → 3.11.2.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +5 -13
  2. data/.travis.yml +12 -0
  3. data/README.md +1 -1
  4. data/amcharts.rb.gemspec +12 -5
  5. data/lib/amcharts/chart.rb +22 -2
  6. data/lib/amcharts/settings.rb +2 -2
  7. data/lib/amcharts/version.rb +1 -1
  8. data/spec/chart_spec.rb +96 -11
  9. data/vendor/assets/javascripts/amcharts/amcharts.js +380 -380
  10. data/vendor/assets/javascripts/amcharts/amstock.js +97 -97
  11. data/vendor/assets/javascripts/amcharts/changeLog.txt +1070 -1070
  12. data/vendor/assets/javascripts/amcharts/exporting/amexport.js +854 -854
  13. data/vendor/assets/javascripts/amcharts/exporting/canvg.js +2841 -2841
  14. data/vendor/assets/javascripts/amcharts/exporting/rgbcolor.js +288 -288
  15. data/vendor/assets/javascripts/amcharts/funnel.js +16 -16
  16. data/vendor/assets/javascripts/amcharts/gauge.js +19 -19
  17. data/vendor/assets/javascripts/amcharts/lang/de.js +12 -12
  18. data/vendor/assets/javascripts/amcharts/licence.txt +15 -15
  19. data/vendor/assets/javascripts/amcharts/pie.js +9 -9
  20. data/vendor/assets/javascripts/amcharts/radar.js +9 -9
  21. data/vendor/assets/javascripts/amcharts/serial.js +56 -56
  22. data/vendor/assets/javascripts/amcharts/themes/black.js +205 -205
  23. data/vendor/assets/javascripts/amcharts/themes/chalk.js +216 -216
  24. data/vendor/assets/javascripts/amcharts/themes/dark.js +204 -204
  25. data/vendor/assets/javascripts/amcharts/themes/light.js +198 -198
  26. data/vendor/assets/javascripts/amcharts/themes/patterns.js +258 -258
  27. data/vendor/assets/javascripts/amcharts/thirdPartySoftwareList.txt +33 -33
  28. data/vendor/assets/javascripts/amcharts/xy.js +16 -16
  29. metadata +69 -49
@@ -1,855 +1,855 @@
1
- AmCharts.AmExport = AmCharts.Class({
2
- construct: function(chart, cfg, init ) {
3
- var _this = this;
4
- _this.DEBUG = false;
5
- _this.chart = chart;
6
- _this.canvas = null;
7
- _this.svgs = [];
8
- _this.userCFG = cfg;
9
-
10
- _this.buttonIcon = 'export.png';
11
- _this.exportPNG = true;
12
- _this.exportPDF = false;
13
- _this.exportJPG = false;
14
- _this.exportSVG = false;
15
- //_this.left;
16
- _this.right = 0;
17
- //_this.bottom;
18
- _this.top = 0;
19
- //_this.color;
20
- _this.buttonRollOverColor = "#EFEFEF";
21
- //_this.buttonColor = "#FFFFFF";
22
- //_this.buttonRollOverAlpha = 0.5;
23
- _this.textRollOverColor = "#CC0000";
24
- _this.buttonTitle = "Save chart as an image";
25
- _this.buttonAlpha = 0.75;
26
- _this.imageFileName = "amChart";
27
- _this.imageBackgroundColor = "#FFFFFF";
28
-
29
- if (init) {
30
- _this.init();
31
- }
32
- },
33
-
34
- toCoordinate:function(value){
35
- if(value === undefined){
36
- return "auto";
37
- }
38
- if(String(value).indexOf("%") != -1){
39
- return value;
40
- }
41
- else{
42
- return value + "px";
43
- }
44
- },
45
-
46
- init: function(){
47
- var _this = this;
48
-
49
- var formats = [];
50
- if (_this.exportPNG) {
51
- formats.push("png");
52
- }
53
- if (_this.exportPDF) {
54
- formats.push("pdf");
55
- }
56
- if (_this.exportJPG) {
57
- formats.push("jpg");
58
- }
59
- if (_this.exportSVG) {
60
- formats.push("svg");
61
- }
62
-
63
- var menuItems = [];
64
- if(formats.length == 1){
65
- var format = formats[0];
66
- menuItems.push({format:format, iconTitle:_this.buttonTitle, icon:_this.chart.pathToImages + _this.buttonIcon})
67
- }
68
- else if(formats.length > 1){
69
- var subItems = [];
70
- for(var i = 0; i < formats.length; i++){
71
- subItems.push({format:formats[i], title:formats[i].toUpperCase()});
72
- }
73
- menuItems.push({onclick: function() {}, icon:_this.chart.pathToImages + _this.buttonIcon, items:subItems})
74
- }
75
-
76
-
77
- var color = _this.color;
78
- if(color === undefined){
79
- color = _this.chart.color;
80
- }
81
-
82
- var buttonColor = _this.buttonColor;
83
- if(buttonColor === undefined){
84
- buttonColor = "transparent";
85
- }
86
-
87
-
88
- _this.cfg = {
89
- menuTop : _this.toCoordinate(_this.top),
90
- menuLeft : _this.toCoordinate(_this.left),
91
- menuRight : _this.toCoordinate(_this.right),
92
- menuBottom : _this.toCoordinate(_this.bottom),
93
- menuItems : menuItems,
94
- menuItemStyle: {
95
- backgroundColor : buttonColor,
96
- opacity :_this.buttonAlpha,
97
- rollOverBackgroundColor : _this.buttonRollOverColor,
98
- color : color,
99
- rollOverColor : _this.textRollOverColor,
100
- paddingTop : '6px',
101
- paddingRight : '6px',
102
- paddingBottom : '6px',
103
- paddingLeft : '6px',
104
- marginTop : '0px',
105
- marginRight : '0px',
106
- marginBottom : '0px',
107
- marginLeft : '0px',
108
- textAlign : 'left',
109
- textDecoration : 'none',
110
- fontFamily : _this.chart.fontFamily,
111
- fontSize : _this.chart.fontSize + 'px'
112
- },
113
- menuItemOutput: {
114
- backgroundColor : _this.imageBackgroundColor,
115
- fileName : _this.imageFileName,
116
- format : 'png',
117
- output : 'dataurlnewwindow',
118
- render : 'browser',
119
- dpi : 90,
120
- onclick : function(instance, config, event) {
121
- event.preventDefault();
122
- instance.output(config);
123
- }
124
- },
125
- removeImagery: true
126
- };
127
-
128
- _this.processing = {
129
- buffer: [],
130
- drawn: 0,
131
- timer: 0
132
- };
133
-
134
- // Config dependency adaption
135
- if (typeof(window.canvg) != 'undefined' && typeof(window.RGBColor) != 'undefined') {
136
- _this.cfg.menuItemOutput.render = 'canvg';
137
- }
138
- if (typeof(window.saveAs) != 'undefined') {
139
- _this.cfg.menuItemOutput.output = 'save';
140
- }
141
- if (AmCharts.isIE && AmCharts.IEversion < 10) {
142
- _this.cfg.menuItemOutput.output = 'dataurlnewwindow';
143
- }
144
-
145
- // Merge given configs
146
- var cfg = _this.userCFG;
147
- if (cfg) {
148
- cfg.menuItemOutput = AmCharts.extend(_this.cfg.menuItemOutput, cfg.menuItemOutput || {});
149
- cfg.menuItemStyle = AmCharts.extend(_this.cfg.menuItemStyle, cfg.menuItemStyle || {});
150
- _this.cfg = AmCharts.extend(_this.cfg, cfg);
151
- }
152
-
153
- // Add reference to chart
154
- _this.chart.AmExport = _this;
155
-
156
- // Listen to the drawer
157
- _this.chart.addListener('rendered', function() {
158
- _this.setup();
159
- });
160
-
161
- // DEBUG; Public reference
162
- if (_this.DEBUG) {
163
- window.AmExport = _this;
164
- }
165
- },
166
-
167
-
168
- /*
169
- Simple log function for internal purpose
170
- @param **args
171
- */
172
- log: function() {
173
- console.log('AmExport: ', arguments);
174
- },
175
-
176
- /* PUBLIC
177
- Prepares everything to get exported
178
- @param none
179
- */
180
- setup: function() {
181
- var _this = this;
182
-
183
- if (_this.DEBUG == 10) {
184
- _this.log('SETUP START');
185
- } // DEBUG
186
-
187
-
188
- if (!AmCharts.isIE || (AmCharts.isIE && AmCharts.IEversion > 9)) {
189
- // Build Buttons
190
- _this.generateButtons();
191
- if (_this.DEBUG == 10) {
192
- _this.log('SETUP END');
193
- } // DEBUG
194
- } else {
195
- if (_this.DEBUG == 10) {
196
- _this.log('< IE10 NOT SUPPORTED');
197
- } // DEBUG
198
- }
199
- },
200
-
201
- /* PUBLIC
202
- Decodes base64 string to binary array
203
- @param base64_string
204
- @copyright Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
205
- */
206
- generateBinaryArray: function(base64_string) {
207
- var
208
- len = base64_string.length,
209
- buffer = new Uint8Array(len / 4 * 3 | 0),
210
- i = 0,
211
- outptr = 0,
212
- last = [0, 0],
213
- state = 0,
214
- save = 0,
215
- rank, code, undef, base64_ranks = new Uint8Array([
216
- 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
217
- ]);
218
- while (len--) {
219
- code = base64_string.charCodeAt(i++);
220
- rank = base64_ranks[code - 43];
221
- if (rank !== 255 && rank !== undef) {
222
- last[1] = last[0];
223
- last[0] = code;
224
- save = (save << 6) | rank;
225
- state++;
226
- if (state === 4) {
227
- buffer[outptr++] = save >>> 16;
228
- if (last[1] !== 61 /* padding character */ ) {
229
- buffer[outptr++] = save >>> 8;
230
- }
231
- if (last[0] !== 61 /* padding character */ ) {
232
- buffer[outptr++] = save;
233
- }
234
- state = 0;
235
- }
236
- }
237
- }
238
- // 2/3 chance there's going to be some null bytes at the end, but that
239
- // doesn't really matter with most image formats.
240
- // If it somehow matters for you, truncate the buffer up outptr.
241
- return buffer;
242
- },
243
-
244
- /*
245
- Creates blob object
246
- @param base64_datastring string
247
- @param type string
248
- */
249
- generateBlob: function(datastring, type) {
250
- var _this = this,
251
- header_end = type!='image/svg+xml'?datastring.indexOf(',') + 1:0,
252
- header = datastring.substring(0, header_end),
253
- data = datastring,
254
- blob = new Blob();
255
-
256
- if (header.indexOf('base64') != -1) {
257
- data = _this.generateBinaryArray(datastring.substring(header_end));
258
- }
259
-
260
- // Fake blob for IE
261
- if (AmCharts.isIE && AmCharts.IEversion < 10) {
262
- blob.data = data;
263
- blob.size = data.length;
264
- blob.type = type;
265
- blob.encoding = 'base64';
266
- } else {
267
- blob = new Blob([data], {
268
- type: type
269
- });
270
- }
271
- return blob;
272
- },
273
-
274
- /*
275
- Creates PDF object
276
- @param config object
277
- */
278
- generatePDF: function(cfg) {
279
- var _this = this,
280
- pdf = {
281
- output: function() {
282
- return '';
283
- }
284
- },
285
- data = _this.canvas.toDataURL('image/jpeg'), // JSPDF ONLY SUPPORTS JPG
286
- width = (_this.canvas.width * 25.4) / cfg.dpi,
287
- height = (_this.canvas.height * 25.4) / cfg.dpi;
288
-
289
- // Check
290
- if (window.jsPDF) {
291
- pdf = new jsPDF();
292
- if (pdf.addImage) {
293
- pdf.addImage(data, 'JPEG', 0, 0, width, height);
294
- } else {
295
- alert("Missing jsPDF plugin; Please add the 'addImage' plugin.");
296
- }
297
- } else {
298
- alert("Missing jsPDF lib; Don't forget to add the addImage plugin.");
299
- }
300
-
301
- return pdf;
302
- },
303
-
304
- /*
305
- Creates the CANVAS to receive the image data
306
- @param format void()
307
- @param callback; given callback function which returns the blob or datastring of the configured ouput type
308
- */
309
- output: function(cfg, externalCallback) {
310
- var _this = this;
311
- cfg = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), cfg || {});
312
-
313
- // Prepare chart
314
- if(_this.chart.prepareForExport){
315
- _this.chart.prepareForExport();
316
- }
317
-
318
- /* PRIVATE
319
- Callback function which gets called after the drawing process is done
320
- @param none
321
- */
322
- function internalCallback() {
323
- var data = null;
324
- var blob;
325
- if (_this.DEBUG == 10) {
326
- _this.log('OUTPUT', cfg.format);
327
- } // DEBUG
328
-
329
- // SVG
330
- if (cfg.format == 'image/svg+xml' || cfg.format == 'svg') {
331
- data = _this.generateSVG();
332
- blob = _this.generateBlob(data, 'image/svg+xml');
333
-
334
- if (cfg.output == 'save') {
335
- saveAs(blob, cfg.fileName + '.svg');
336
- } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
337
- blob = 'data:image/svg+xml;base64,' + btoa(data);
338
- } else if (cfg.output == 'dataurlnewwindow') {
339
- window.open('data:image/svg+xml;base64,' + btoa(data));
340
- } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
341
- location.href = 'data:image/svg+xml;base64,' + btoa(data);
342
- } else if (cfg.output == 'datastream') {
343
- location.href = 'data:image/octet-stream;base64,' + data;
344
- }
345
-
346
- if (externalCallback)
347
- externalCallback.apply(_this, [blob]);
348
-
349
- // PDF
350
- } else if (cfg.format == 'application/pdf' || cfg.format == 'pdf') {
351
- data = _this.generatePDF(cfg).output('dataurlstring');
352
- blob = _this.generateBlob(data, 'application/pdf');
353
-
354
- if (cfg.output == 'save') {
355
- saveAs(blob, cfg.fileName + '.pdf');
356
- } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
357
- blob = data;
358
- } else if (cfg.output == 'dataurlnewwindow') {
359
- window.open(data);
360
- } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
361
- location.href = data;
362
- } else if (cfg.output == 'datastream') {
363
- location.href = data.replace('application/pdf', 'application/octet-stream');
364
- }
365
-
366
- if (externalCallback)
367
- externalCallback.apply(_this, [blob]);
368
-
369
- // PNG
370
- } else if (cfg.format == 'image/png' || cfg.format == 'png') {
371
- data = _this.canvas.toDataURL('image/png');
372
- blob = _this.generateBlob(data, 'image/png');
373
-
374
- if (cfg.output == 'save') {
375
- saveAs(blob, cfg.fileName + '.png');
376
- } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
377
- blob = data;
378
- } else if (cfg.output == 'dataurlnewwindow') {
379
- window.open(data);
380
- } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
381
- location.href = data;
382
- } else if (cfg.output == 'datastream') {
383
- location.href = data.replace('image/png', 'image/octet-stream');
384
- }
385
-
386
- if (externalCallback)
387
- externalCallback.apply(_this, [blob]);
388
-
389
- // JPG
390
- } else if (cfg.format == 'image/jpeg' || cfg.format == 'jpeg' || cfg.format == 'jpg') {
391
- data = _this.canvas.toDataURL('image/jpeg');
392
- blob = _this.generateBlob(data, 'image/jpeg');
393
-
394
- if (cfg.output == 'save') {
395
- saveAs(blob, cfg.fileName + '.jpg');
396
- } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
397
- blob = data;
398
- } else if (cfg.output == 'dataurlnewwindow') {
399
- window.open(data);
400
- } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
401
- location.href = data;
402
- } else if (cfg.output == 'datastream') {
403
- location.href = data.replace('image/jpeg', 'image/octet-stream');
404
- }
405
-
406
- if (externalCallback)
407
- externalCallback.apply(_this, [blob]);
408
- }
409
-
410
- }
411
-
412
- return _this.generateOutput(cfg, internalCallback);
413
- },
414
-
415
- /* PUBLIC
416
- Polifies missing attributes to the SVG and replaces images to embedded base64 images
417
- @param none
418
- */
419
- polifySVG: function(svg) {
420
- var _this = this;
421
-
422
- // Recursive function to force the attributes
423
- function recursiveChange(svg, tag) {
424
- var items = svg.getElementsByTagName(tag);
425
- var i = items.length;
426
-
427
- while(i--) {
428
- if (_this.cfg.removeImagery) {
429
- items[i].parentNode.removeChild(items[i]);
430
-
431
- } else {
432
- var image = document.createElement('img');
433
- var canvas = document.createElement('canvas');
434
- var ctx = canvas.getContext('2d');
435
-
436
- canvas.width = items[i].getAttribute('width');
437
- canvas.height = items[i].getAttribute('height');
438
- image.src = items[i].getAttribute('xlink:href');
439
- image.width = items[i].getAttribute('width');
440
- image.height = items[i].getAttribute('height');
441
-
442
- try {
443
- ctx.drawImage(image, 0, 0, image.width, image.height);
444
- datastring = canvas.toDataURL(); // image.src; // canvas.toDataURL(); //
445
- } catch (err) {
446
- datastring = image.src; // image.src; // canvas.toDataURL(); //
447
-
448
- _this.log('Tainted canvas, reached browser CORS security; origin from imagery must be equal to the server!');
449
- throw new Error(err);
450
- }
451
-
452
- items[i].setAttribute('xlink:href', datastring);
453
- }
454
-
455
- if (_this.DEBUG == 10) {
456
- _this.log('POLIFIED', items[i]);
457
- } // DEBUG
458
- }
459
- }
460
-
461
- // Put some attrs to it; fixed 20/03/14 xmlns is required to produce a valid svg file
462
- if (AmCharts.IEversion == 0) {
463
- svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
464
- if ( !_this.cfg.removeImagery ) {
465
- svg.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
466
- }
467
- }
468
-
469
- // DEBUG
470
- if (_this.DEBUG == 10) {
471
- _this.log('POLIFIED', svg);
472
- }
473
-
474
- // Force link adaption
475
- recursiveChange(svg, 'pattern');
476
- recursiveChange(svg, 'image');
477
-
478
- _this.svgs.push(svg);
479
-
480
- return svg;
481
- },
482
-
483
-
484
- /* PUBLIC
485
- Stacks multiple SVGs into one
486
- @param none
487
- */
488
- generateSVG: function() {
489
- var _this = this;
490
- var context = document.createElement('svg');
491
- context.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
492
- context.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
493
-
494
- for (var i = 0; i < _this.processing.buffer.length; i++) {
495
- var group = document.createElement('g'),
496
- data = _this.processing.buffer[i];
497
-
498
- data[0].setAttribute('xmlns', 'http://www.w3.org/2000/svg');
499
- data[0].setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
500
-
501
- group.setAttribute('transform', 'translate('+data[1].x+','+data[1].y+')');
502
- group.appendChild(data[0]);
503
- context.appendChild(group);
504
- }
505
-
506
- return new XMLSerializer().serializeToString(context);
507
- },
508
-
509
- /* PUBLIC
510
- Generates the canvas with the given SVGs and configured renderer
511
- @param callback; function(); gets called after drawing process on the canvas has been finished
512
- */
513
- generateOutput: function(cfg, callback) {
514
- var _this = this,
515
- coll = [],
516
- svgs = [],
517
- canvas = document.createElement('canvas'),
518
- context = canvas.getContext('2d'),
519
- offset = {
520
- y: 0,
521
- x: 0
522
- },
523
-
524
- // Push svgs into array
525
- coll = _this.chart.div.getElementsByTagName('svg');
526
- for ( var i = 0; i < coll.length; i++ ) svgs.push(coll[i]);
527
-
528
- // Add external legend
529
- if ( _this.chart.legend && _this.chart.legend.position == 'outside' ) {
530
- _this.chart.legend.container.container.externalLegend = true
531
- svgs.push(_this.chart.legend.container.container);
532
-
533
- // Add offset
534
- if ( _this.cfg.legendPosition == 'left' ) {
535
- offset.x = _this.chart.legend.div.offsetWidth;
536
- } else if ( _this.cfg.legendPosition == 'top' ) {
537
- offset.y = _this.chart.legend.div.offsetHeight;
538
- } else if ( typeof _this.cfg.legendPosition == 'object' ) {
539
- offset.y = _this.cfg.legendPosition.chartTop;
540
- offset.x = _this.cfg.legendPosition.chartLeft;
541
- }
542
- }
543
-
544
- // Reset
545
- _this.processing.buffer = [];
546
- _this.processing.drawn = 0;
547
- _this.canvas = canvas;
548
- _this.svgs = [];
549
-
550
- // Walkthroug SVGs
551
- if (_this.DEBUG == 10) {
552
- _this.log('START EXPORT');
553
- } // DEBUG
554
- if (_this.DEBUG == 10) {
555
- _this.log('START BUFFERING');
556
- } // DEBUG
557
- for (var i = 0; i < svgs.length; i++) {
558
- var parent = svgs[i].parentNode,
559
- svgX = Number(parent.style.left.slice(0, -2)),
560
- svgY = Number(parent.style.top.slice(0, -2)),
561
- svgClone = _this.polifySVG(svgs[i].cloneNode(true)),
562
- tmp = AmCharts.extend({}, offset);
563
-
564
- // Add external legend
565
- if ( svgs[i].externalLegend ) {
566
- if ( _this.cfg.legendPosition == 'right' ) {
567
- offset.y = 0;
568
- offset.x = _this.chart.divRealWidth;
569
-
570
- } else if ( _this.cfg.legendPosition == 'bottom' ) {
571
- offset.y = svgY ? svgY : offset.y;
572
-
573
- } else if ( typeof _this.cfg.legendPosition == 'object' ) {
574
- offset.x = _this.cfg.legendPosition.left;
575
- offset.y = _this.cfg.legendPosition.top;
576
-
577
- } else {
578
- offset.x = 0;
579
- offset.y = 0;
580
- }
581
-
582
- // Overtake parent position if given; fixed 20/03/14 distinguish between relativ and others
583
- } else {
584
- if ( parent.style.position == 'relative' ) {
585
- offset.x = svgX ? svgX : offset.x;
586
- offset.y = svgY ? svgY : offset.y;
587
- } else {
588
- offset.x = svgX;
589
- offset.y = svgY;
590
- }
591
- }
592
-
593
- _this.processing.buffer.push([svgClone, AmCharts.extend({}, offset)]);
594
-
595
- // Put back from "cache"
596
- if (svgY && svgX) {
597
- offset = tmp;
598
-
599
- // New offset for next one
600
- } else {
601
- offset.y += svgY ? 0 : parent.offsetHeight;
602
- }
603
-
604
- if (_this.DEBUG == 10) {
605
- _this.log('BUFFERED', svgs[i], offset);
606
- } // DEBUG
607
- }
608
- if (_this.DEBUG == 10) {
609
- _this.log('END BUFFERING');
610
- } // DEBUG
611
-
612
- // Apply background
613
- if (_this.DEBUG == 10) {
614
- _this.log('START DRAWING', cfg.render);
615
- } // DEBUG
616
- if (_this.DEBUG == 10) {
617
- _this.log('FILL BACKGROUND');
618
- } // DEBUG
619
- canvas.id = AmCharts.getUniqueId();
620
- canvas.width = _this.chart.divRealWidth;
621
- canvas.height = _this.chart.divRealHeight;
622
-
623
- // External legend exception
624
- if ( _this.chart.legend && _this.chart.legend.position == "outside" ) {
625
- if ( ['left','right'].indexOf(_this.cfg.legendPosition) != -1 ) {
626
- canvas.width += _this.chart.legend.div.offsetWidth;
627
-
628
- } else if ( typeof _this.cfg.legendPosition == 'object' ) {
629
- canvas.width += _this.cfg.legendPosition.width;
630
- canvas.height += _this.cfg.legendPosition.height;
631
-
632
- } else {
633
- canvas.height += _this.chart.legend.div.offsetHeight;
634
- }
635
- }
636
-
637
- // Stockchart exception
638
- var adapted = {
639
- width: false,
640
- height: false
641
- };
642
- if ( _this.chart.periodSelector ) {
643
- if ( ['left','right'].indexOf(_this.chart.periodSelector.position) != -1 ) {
644
- canvas.width -= _this.chart.periodSelector.div.offsetWidth + 16;
645
- adapted.width = true;
646
- } else {
647
- canvas.height -= _this.chart.periodSelector.div.offsetHeight;
648
- adapted.height = true;
649
- }
650
- }
651
-
652
- if ( _this.chart.dataSetSelector ) {
653
- if ( ['left','right'].indexOf(_this.chart.dataSetSelector.position) != -1 ) {
654
- if ( !adapted.width ) {
655
- canvas.width -= _this.chart.dataSetSelector.div.offsetWidth + 16;
656
- }
657
- } else {
658
- canvas.height -= _this.chart.dataSetSelector.div.offsetHeight;
659
- }
660
- }
661
-
662
- // Set given background; jpeg default
663
- if (cfg.backgroundColor || cfg.format == 'image/jpeg') {
664
- context.fillStyle = cfg.backgroundColor || '#FFFFFF';
665
- context.fillRect(0, 0, canvas.width, canvas.height);
666
- }
667
-
668
- /* PRIVATE
669
- Recursive function to draw the images to the canvas;
670
- @param none;
671
- */
672
- function drawItWhenItsLoaded() {
673
- var img, buffer, offset, source;
674
-
675
- // DRAWING PROCESS DONE
676
- if (_this.processing.buffer.length == _this.processing.drawn || cfg.format == 'svg' ) {
677
- if (_this.DEBUG == 10) {
678
- _this.log('END DRAWING');
679
- } // DEBUG
680
- return callback();
681
-
682
- // LOOPING LUI
683
- } else {
684
- if (_this.DEBUG == 10) {
685
- _this.log('DRAW', _this.processing.drawn + 1, 'OF', _this.processing.buffer.length);
686
- } // DEBUG
687
-
688
- buffer = _this.processing.buffer[_this.processing.drawn];
689
- source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
690
- offset = buffer[1];
691
-
692
- if (_this.DEBUG == 10) {
693
- _this.log('SOURCE', source);
694
- } // DEBUG
695
-
696
- // NATIVE
697
- if (cfg.render == 'browser') {
698
- img = new Image();
699
- img.id = AmCharts.getUniqueId();
700
- source = 'data:image/svg+xml;base64,' + btoa(source);
701
-
702
- //img.crossOrigin = "Anonymous";
703
- img.onload = function() {
704
- context.drawImage(this, buffer[1].x, buffer[1].y);
705
- _this.processing.drawn++;
706
-
707
- if (_this.DEBUG == 10) {
708
- _this.log('ONLOAD', this);
709
- } // DEBUG
710
- drawItWhenItsLoaded();
711
- };
712
- img.onerror = function() {
713
- if (_this.DEBUG == 10) {
714
- _this.log('ONERROR', this);
715
- } // DEBUG
716
- context.drawImage(this, buffer[1].x, buffer[1].y);
717
- _this.processing.drawn++;
718
- drawItWhenItsLoaded();
719
- };
720
- img.src = source;
721
-
722
- if (_this.DEBUG == 10) {
723
- _this.log('ADD', img);
724
- } // DEBUG
725
- if (img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined) {
726
- if (_this.DEBUG == 10) {
727
- _this.log('FORCE ONLOAD', img);
728
- } // DEBUG
729
- img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
730
- img.src = source;
731
- }
732
-
733
- // CANVG
734
- } else if (cfg.render == 'canvg') {
735
- canvg(canvas, source, {
736
- offsetX: offset.x,
737
- offsetY: offset.y,
738
- ignoreMouse: true,
739
- ignoreAnimation: true,
740
- ignoreDimensions: true,
741
- ignoreClear: true,
742
- renderCallback: function() {
743
- _this.processing.drawn++;
744
- drawItWhenItsLoaded();
745
- }
746
- });
747
- }
748
- }
749
- }
750
- return drawItWhenItsLoaded();
751
- },
752
-
753
- /*
754
- Generates the export menu to trigger the exportation
755
- @param none;
756
- */
757
- generateButtons: function() {
758
- var _this = this,lvl = 0;
759
-
760
- if(_this.div){
761
- div = _this.div;
762
- div.innerHTML = "";
763
- }
764
- else{
765
- div = document.createElement('div'),
766
- _this.div = div;
767
- }
768
-
769
- // Push sublings
770
- function createList(items) {
771
- var ul = document.createElement('ul');
772
-
773
- ul.setAttribute('style', 'list-style: none; margin: 0; padding: 0;');
774
-
775
- // Walkthrough items
776
- for (var i = 0; i < items.length; i++) {
777
- var li = document.createElement('li'),
778
- img = document.createElement('img'),
779
- a = document.createElement('a'),
780
- item = items[i],
781
- children = null,
782
- itemStyle = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemStyle), items[i]);
783
-
784
- // MERGE CFG
785
- item = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), item);
786
-
787
- // ICON
788
- if (item['icon']) {
789
- img.alt = '';
790
- img.src = item['icon'];
791
- img.setAttribute('style', 'margin: 0 auto;border: none;outline: none');
792
- if (item['iconTitle']) {
793
- img.title = item['iconTitle'];
794
- }
795
- a.appendChild(img);
796
- }
797
-
798
- // TITLE; STYLING
799
- a.href = '#';
800
- if (item['title']) {
801
- img.setAttribute('style', 'margin: 0px 5px;');
802
- a.innerHTML += item.title;
803
- }
804
- a.setAttribute('style', 'display: block;');
805
- AmCharts.extend(a.style, itemStyle);
806
-
807
- // ONCLICK
808
- a.onclick = item.onclick.bind(a, _this, item);
809
- li.appendChild(a);
810
-
811
- // APPEND SIBLINGS
812
- if (item.items) {
813
- children = createList(item.items);
814
- li.appendChild(children);
815
-
816
- li.onmouseover = function() {
817
- children.style.display = 'block';
818
- };
819
- li.onmouseout = function() {
820
- children.style.display = 'none';
821
- };
822
- children.style.display = 'none';
823
- }
824
-
825
- // Append to parent
826
- ul.appendChild(li);
827
-
828
- // Apply hover
829
- a.onmouseover = function() {
830
- this.style.backgroundColor = itemStyle.rollOverBackgroundColor;
831
- this.style.color = itemStyle.rollOverColor;
832
- this.style.borderColor = itemStyle.rollOverBorderColor;
833
- };
834
- a.onmouseout = function() {
835
- this.style.backgroundColor = itemStyle.backgroundColor;
836
- this.style.color = itemStyle.color;
837
- this.style.borderColor = itemStyle.borderColor;
838
- };
839
- }
840
- lvl++;
841
-
842
- if (_this.DEBUG == 10) {
843
- _this.log('MENU', ul);
844
- } // DEBUG
845
-
846
- return ul;
847
- }
848
-
849
- // Style wrapper; Push into chart div
850
- div.setAttribute('style', 'position: absolute;top:' + _this.cfg.menuTop + ';right:' + _this.cfg.menuRight + ';bottom:' + _this.cfg.menuBottom + ';left:' + _this.cfg.menuLeft + ';');
851
- div.setAttribute('class', 'amExportButton');
852
- div.appendChild(createList(_this.cfg.menuItems));
853
- _this.chart.containerDiv.appendChild(div);
854
- }
1
+ AmCharts.AmExport = AmCharts.Class({
2
+ construct: function(chart, cfg, init ) {
3
+ var _this = this;
4
+ _this.DEBUG = false;
5
+ _this.chart = chart;
6
+ _this.canvas = null;
7
+ _this.svgs = [];
8
+ _this.userCFG = cfg;
9
+
10
+ _this.buttonIcon = 'export.png';
11
+ _this.exportPNG = true;
12
+ _this.exportPDF = false;
13
+ _this.exportJPG = false;
14
+ _this.exportSVG = false;
15
+ //_this.left;
16
+ _this.right = 0;
17
+ //_this.bottom;
18
+ _this.top = 0;
19
+ //_this.color;
20
+ _this.buttonRollOverColor = "#EFEFEF";
21
+ //_this.buttonColor = "#FFFFFF";
22
+ //_this.buttonRollOverAlpha = 0.5;
23
+ _this.textRollOverColor = "#CC0000";
24
+ _this.buttonTitle = "Save chart as an image";
25
+ _this.buttonAlpha = 0.75;
26
+ _this.imageFileName = "amChart";
27
+ _this.imageBackgroundColor = "#FFFFFF";
28
+
29
+ if (init) {
30
+ _this.init();
31
+ }
32
+ },
33
+
34
+ toCoordinate:function(value){
35
+ if(value === undefined){
36
+ return "auto";
37
+ }
38
+ if(String(value).indexOf("%") != -1){
39
+ return value;
40
+ }
41
+ else{
42
+ return value + "px";
43
+ }
44
+ },
45
+
46
+ init: function(){
47
+ var _this = this;
48
+
49
+ var formats = [];
50
+ if (_this.exportPNG) {
51
+ formats.push("png");
52
+ }
53
+ if (_this.exportPDF) {
54
+ formats.push("pdf");
55
+ }
56
+ if (_this.exportJPG) {
57
+ formats.push("jpg");
58
+ }
59
+ if (_this.exportSVG) {
60
+ formats.push("svg");
61
+ }
62
+
63
+ var menuItems = [];
64
+ if(formats.length == 1){
65
+ var format = formats[0];
66
+ menuItems.push({format:format, iconTitle:_this.buttonTitle, icon:_this.chart.pathToImages + _this.buttonIcon})
67
+ }
68
+ else if(formats.length > 1){
69
+ var subItems = [];
70
+ for(var i = 0; i < formats.length; i++){
71
+ subItems.push({format:formats[i], title:formats[i].toUpperCase()});
72
+ }
73
+ menuItems.push({onclick: function() {}, icon:_this.chart.pathToImages + _this.buttonIcon, items:subItems})
74
+ }
75
+
76
+
77
+ var color = _this.color;
78
+ if(color === undefined){
79
+ color = _this.chart.color;
80
+ }
81
+
82
+ var buttonColor = _this.buttonColor;
83
+ if(buttonColor === undefined){
84
+ buttonColor = "transparent";
85
+ }
86
+
87
+
88
+ _this.cfg = {
89
+ menuTop : _this.toCoordinate(_this.top),
90
+ menuLeft : _this.toCoordinate(_this.left),
91
+ menuRight : _this.toCoordinate(_this.right),
92
+ menuBottom : _this.toCoordinate(_this.bottom),
93
+ menuItems : menuItems,
94
+ menuItemStyle: {
95
+ backgroundColor : buttonColor,
96
+ opacity :_this.buttonAlpha,
97
+ rollOverBackgroundColor : _this.buttonRollOverColor,
98
+ color : color,
99
+ rollOverColor : _this.textRollOverColor,
100
+ paddingTop : '6px',
101
+ paddingRight : '6px',
102
+ paddingBottom : '6px',
103
+ paddingLeft : '6px',
104
+ marginTop : '0px',
105
+ marginRight : '0px',
106
+ marginBottom : '0px',
107
+ marginLeft : '0px',
108
+ textAlign : 'left',
109
+ textDecoration : 'none',
110
+ fontFamily : _this.chart.fontFamily,
111
+ fontSize : _this.chart.fontSize + 'px'
112
+ },
113
+ menuItemOutput: {
114
+ backgroundColor : _this.imageBackgroundColor,
115
+ fileName : _this.imageFileName,
116
+ format : 'png',
117
+ output : 'dataurlnewwindow',
118
+ render : 'browser',
119
+ dpi : 90,
120
+ onclick : function(instance, config, event) {
121
+ event.preventDefault();
122
+ instance.output(config);
123
+ }
124
+ },
125
+ removeImagery: true
126
+ };
127
+
128
+ _this.processing = {
129
+ buffer: [],
130
+ drawn: 0,
131
+ timer: 0
132
+ };
133
+
134
+ // Config dependency adaption
135
+ if (typeof(window.canvg) != 'undefined' && typeof(window.RGBColor) != 'undefined') {
136
+ _this.cfg.menuItemOutput.render = 'canvg';
137
+ }
138
+ if (typeof(window.saveAs) != 'undefined') {
139
+ _this.cfg.menuItemOutput.output = 'save';
140
+ }
141
+ if (AmCharts.isIE && AmCharts.IEversion < 10) {
142
+ _this.cfg.menuItemOutput.output = 'dataurlnewwindow';
143
+ }
144
+
145
+ // Merge given configs
146
+ var cfg = _this.userCFG;
147
+ if (cfg) {
148
+ cfg.menuItemOutput = AmCharts.extend(_this.cfg.menuItemOutput, cfg.menuItemOutput || {});
149
+ cfg.menuItemStyle = AmCharts.extend(_this.cfg.menuItemStyle, cfg.menuItemStyle || {});
150
+ _this.cfg = AmCharts.extend(_this.cfg, cfg);
151
+ }
152
+
153
+ // Add reference to chart
154
+ _this.chart.AmExport = _this;
155
+
156
+ // Listen to the drawer
157
+ _this.chart.addListener('rendered', function() {
158
+ _this.setup();
159
+ });
160
+
161
+ // DEBUG; Public reference
162
+ if (_this.DEBUG) {
163
+ window.AmExport = _this;
164
+ }
165
+ },
166
+
167
+
168
+ /*
169
+ Simple log function for internal purpose
170
+ @param **args
171
+ */
172
+ log: function() {
173
+ console.log('AmExport: ', arguments);
174
+ },
175
+
176
+ /* PUBLIC
177
+ Prepares everything to get exported
178
+ @param none
179
+ */
180
+ setup: function() {
181
+ var _this = this;
182
+
183
+ if (_this.DEBUG == 10) {
184
+ _this.log('SETUP START');
185
+ } // DEBUG
186
+
187
+
188
+ if (!AmCharts.isIE || (AmCharts.isIE && AmCharts.IEversion > 9)) {
189
+ // Build Buttons
190
+ _this.generateButtons();
191
+ if (_this.DEBUG == 10) {
192
+ _this.log('SETUP END');
193
+ } // DEBUG
194
+ } else {
195
+ if (_this.DEBUG == 10) {
196
+ _this.log('< IE10 NOT SUPPORTED');
197
+ } // DEBUG
198
+ }
199
+ },
200
+
201
+ /* PUBLIC
202
+ Decodes base64 string to binary array
203
+ @param base64_string
204
+ @copyright Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
205
+ */
206
+ generateBinaryArray: function(base64_string) {
207
+ var
208
+ len = base64_string.length,
209
+ buffer = new Uint8Array(len / 4 * 3 | 0),
210
+ i = 0,
211
+ outptr = 0,
212
+ last = [0, 0],
213
+ state = 0,
214
+ save = 0,
215
+ rank, code, undef, base64_ranks = new Uint8Array([
216
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
217
+ ]);
218
+ while (len--) {
219
+ code = base64_string.charCodeAt(i++);
220
+ rank = base64_ranks[code - 43];
221
+ if (rank !== 255 && rank !== undef) {
222
+ last[1] = last[0];
223
+ last[0] = code;
224
+ save = (save << 6) | rank;
225
+ state++;
226
+ if (state === 4) {
227
+ buffer[outptr++] = save >>> 16;
228
+ if (last[1] !== 61 /* padding character */ ) {
229
+ buffer[outptr++] = save >>> 8;
230
+ }
231
+ if (last[0] !== 61 /* padding character */ ) {
232
+ buffer[outptr++] = save;
233
+ }
234
+ state = 0;
235
+ }
236
+ }
237
+ }
238
+ // 2/3 chance there's going to be some null bytes at the end, but that
239
+ // doesn't really matter with most image formats.
240
+ // If it somehow matters for you, truncate the buffer up outptr.
241
+ return buffer;
242
+ },
243
+
244
+ /*
245
+ Creates blob object
246
+ @param base64_datastring string
247
+ @param type string
248
+ */
249
+ generateBlob: function(datastring, type) {
250
+ var _this = this,
251
+ header_end = type!='image/svg+xml'?datastring.indexOf(',') + 1:0,
252
+ header = datastring.substring(0, header_end),
253
+ data = datastring,
254
+ blob = new Blob();
255
+
256
+ if (header.indexOf('base64') != -1) {
257
+ data = _this.generateBinaryArray(datastring.substring(header_end));
258
+ }
259
+
260
+ // Fake blob for IE
261
+ if (AmCharts.isIE && AmCharts.IEversion < 10) {
262
+ blob.data = data;
263
+ blob.size = data.length;
264
+ blob.type = type;
265
+ blob.encoding = 'base64';
266
+ } else {
267
+ blob = new Blob([data], {
268
+ type: type
269
+ });
270
+ }
271
+ return blob;
272
+ },
273
+
274
+ /*
275
+ Creates PDF object
276
+ @param config object
277
+ */
278
+ generatePDF: function(cfg) {
279
+ var _this = this,
280
+ pdf = {
281
+ output: function() {
282
+ return '';
283
+ }
284
+ },
285
+ data = _this.canvas.toDataURL('image/jpeg'), // JSPDF ONLY SUPPORTS JPG
286
+ width = (_this.canvas.width * 25.4) / cfg.dpi,
287
+ height = (_this.canvas.height * 25.4) / cfg.dpi;
288
+
289
+ // Check
290
+ if (window.jsPDF) {
291
+ pdf = new jsPDF();
292
+ if (pdf.addImage) {
293
+ pdf.addImage(data, 'JPEG', 0, 0, width, height);
294
+ } else {
295
+ alert("Missing jsPDF plugin; Please add the 'addImage' plugin.");
296
+ }
297
+ } else {
298
+ alert("Missing jsPDF lib; Don't forget to add the addImage plugin.");
299
+ }
300
+
301
+ return pdf;
302
+ },
303
+
304
+ /*
305
+ Creates the CANVAS to receive the image data
306
+ @param format void()
307
+ @param callback; given callback function which returns the blob or datastring of the configured ouput type
308
+ */
309
+ output: function(cfg, externalCallback) {
310
+ var _this = this;
311
+ cfg = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), cfg || {});
312
+
313
+ // Prepare chart
314
+ if(_this.chart.prepareForExport){
315
+ _this.chart.prepareForExport();
316
+ }
317
+
318
+ /* PRIVATE
319
+ Callback function which gets called after the drawing process is done
320
+ @param none
321
+ */
322
+ function internalCallback() {
323
+ var data = null;
324
+ var blob;
325
+ if (_this.DEBUG == 10) {
326
+ _this.log('OUTPUT', cfg.format);
327
+ } // DEBUG
328
+
329
+ // SVG
330
+ if (cfg.format == 'image/svg+xml' || cfg.format == 'svg') {
331
+ data = _this.generateSVG();
332
+ blob = _this.generateBlob(data, 'image/svg+xml');
333
+
334
+ if (cfg.output == 'save') {
335
+ saveAs(blob, cfg.fileName + '.svg');
336
+ } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
337
+ blob = 'data:image/svg+xml;base64,' + btoa(data);
338
+ } else if (cfg.output == 'dataurlnewwindow') {
339
+ window.open('data:image/svg+xml;base64,' + btoa(data));
340
+ } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
341
+ location.href = 'data:image/svg+xml;base64,' + btoa(data);
342
+ } else if (cfg.output == 'datastream') {
343
+ location.href = 'data:image/octet-stream;base64,' + data;
344
+ }
345
+
346
+ if (externalCallback)
347
+ externalCallback.apply(_this, [blob]);
348
+
349
+ // PDF
350
+ } else if (cfg.format == 'application/pdf' || cfg.format == 'pdf') {
351
+ data = _this.generatePDF(cfg).output('dataurlstring');
352
+ blob = _this.generateBlob(data, 'application/pdf');
353
+
354
+ if (cfg.output == 'save') {
355
+ saveAs(blob, cfg.fileName + '.pdf');
356
+ } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
357
+ blob = data;
358
+ } else if (cfg.output == 'dataurlnewwindow') {
359
+ window.open(data);
360
+ } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
361
+ location.href = data;
362
+ } else if (cfg.output == 'datastream') {
363
+ location.href = data.replace('application/pdf', 'application/octet-stream');
364
+ }
365
+
366
+ if (externalCallback)
367
+ externalCallback.apply(_this, [blob]);
368
+
369
+ // PNG
370
+ } else if (cfg.format == 'image/png' || cfg.format == 'png') {
371
+ data = _this.canvas.toDataURL('image/png');
372
+ blob = _this.generateBlob(data, 'image/png');
373
+
374
+ if (cfg.output == 'save') {
375
+ saveAs(blob, cfg.fileName + '.png');
376
+ } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
377
+ blob = data;
378
+ } else if (cfg.output == 'dataurlnewwindow') {
379
+ window.open(data);
380
+ } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
381
+ location.href = data;
382
+ } else if (cfg.output == 'datastream') {
383
+ location.href = data.replace('image/png', 'image/octet-stream');
384
+ }
385
+
386
+ if (externalCallback)
387
+ externalCallback.apply(_this, [blob]);
388
+
389
+ // JPG
390
+ } else if (cfg.format == 'image/jpeg' || cfg.format == 'jpeg' || cfg.format == 'jpg') {
391
+ data = _this.canvas.toDataURL('image/jpeg');
392
+ blob = _this.generateBlob(data, 'image/jpeg');
393
+
394
+ if (cfg.output == 'save') {
395
+ saveAs(blob, cfg.fileName + '.jpg');
396
+ } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') {
397
+ blob = data;
398
+ } else if (cfg.output == 'dataurlnewwindow') {
399
+ window.open(data);
400
+ } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') {
401
+ location.href = data;
402
+ } else if (cfg.output == 'datastream') {
403
+ location.href = data.replace('image/jpeg', 'image/octet-stream');
404
+ }
405
+
406
+ if (externalCallback)
407
+ externalCallback.apply(_this, [blob]);
408
+ }
409
+
410
+ }
411
+
412
+ return _this.generateOutput(cfg, internalCallback);
413
+ },
414
+
415
+ /* PUBLIC
416
+ Polifies missing attributes to the SVG and replaces images to embedded base64 images
417
+ @param none
418
+ */
419
+ polifySVG: function(svg) {
420
+ var _this = this;
421
+
422
+ // Recursive function to force the attributes
423
+ function recursiveChange(svg, tag) {
424
+ var items = svg.getElementsByTagName(tag);
425
+ var i = items.length;
426
+
427
+ while(i--) {
428
+ if (_this.cfg.removeImagery) {
429
+ items[i].parentNode.removeChild(items[i]);
430
+
431
+ } else {
432
+ var image = document.createElement('img');
433
+ var canvas = document.createElement('canvas');
434
+ var ctx = canvas.getContext('2d');
435
+
436
+ canvas.width = items[i].getAttribute('width');
437
+ canvas.height = items[i].getAttribute('height');
438
+ image.src = items[i].getAttribute('xlink:href');
439
+ image.width = items[i].getAttribute('width');
440
+ image.height = items[i].getAttribute('height');
441
+
442
+ try {
443
+ ctx.drawImage(image, 0, 0, image.width, image.height);
444
+ datastring = canvas.toDataURL(); // image.src; // canvas.toDataURL(); //
445
+ } catch (err) {
446
+ datastring = image.src; // image.src; // canvas.toDataURL(); //
447
+
448
+ _this.log('Tainted canvas, reached browser CORS security; origin from imagery must be equal to the server!');
449
+ throw new Error(err);
450
+ }
451
+
452
+ items[i].setAttribute('xlink:href', datastring);
453
+ }
454
+
455
+ if (_this.DEBUG == 10) {
456
+ _this.log('POLIFIED', items[i]);
457
+ } // DEBUG
458
+ }
459
+ }
460
+
461
+ // Put some attrs to it; fixed 20/03/14 xmlns is required to produce a valid svg file
462
+ if (AmCharts.IEversion == 0) {
463
+ svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
464
+ if ( !_this.cfg.removeImagery ) {
465
+ svg.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
466
+ }
467
+ }
468
+
469
+ // DEBUG
470
+ if (_this.DEBUG == 10) {
471
+ _this.log('POLIFIED', svg);
472
+ }
473
+
474
+ // Force link adaption
475
+ recursiveChange(svg, 'pattern');
476
+ recursiveChange(svg, 'image');
477
+
478
+ _this.svgs.push(svg);
479
+
480
+ return svg;
481
+ },
482
+
483
+
484
+ /* PUBLIC
485
+ Stacks multiple SVGs into one
486
+ @param none
487
+ */
488
+ generateSVG: function() {
489
+ var _this = this;
490
+ var context = document.createElement('svg');
491
+ context.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
492
+ context.setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
493
+
494
+ for (var i = 0; i < _this.processing.buffer.length; i++) {
495
+ var group = document.createElement('g'),
496
+ data = _this.processing.buffer[i];
497
+
498
+ data[0].setAttribute('xmlns', 'http://www.w3.org/2000/svg');
499
+ data[0].setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
500
+
501
+ group.setAttribute('transform', 'translate('+data[1].x+','+data[1].y+')');
502
+ group.appendChild(data[0]);
503
+ context.appendChild(group);
504
+ }
505
+
506
+ return new XMLSerializer().serializeToString(context);
507
+ },
508
+
509
+ /* PUBLIC
510
+ Generates the canvas with the given SVGs and configured renderer
511
+ @param callback; function(); gets called after drawing process on the canvas has been finished
512
+ */
513
+ generateOutput: function(cfg, callback) {
514
+ var _this = this,
515
+ coll = [],
516
+ svgs = [],
517
+ canvas = document.createElement('canvas'),
518
+ context = canvas.getContext('2d'),
519
+ offset = {
520
+ y: 0,
521
+ x: 0
522
+ },
523
+
524
+ // Push svgs into array
525
+ coll = _this.chart.div.getElementsByTagName('svg');
526
+ for ( var i = 0; i < coll.length; i++ ) svgs.push(coll[i]);
527
+
528
+ // Add external legend
529
+ if ( _this.chart.legend && _this.chart.legend.position == 'outside' ) {
530
+ _this.chart.legend.container.container.externalLegend = true
531
+ svgs.push(_this.chart.legend.container.container);
532
+
533
+ // Add offset
534
+ if ( _this.cfg.legendPosition == 'left' ) {
535
+ offset.x = _this.chart.legend.div.offsetWidth;
536
+ } else if ( _this.cfg.legendPosition == 'top' ) {
537
+ offset.y = _this.chart.legend.div.offsetHeight;
538
+ } else if ( typeof _this.cfg.legendPosition == 'object' ) {
539
+ offset.y = _this.cfg.legendPosition.chartTop;
540
+ offset.x = _this.cfg.legendPosition.chartLeft;
541
+ }
542
+ }
543
+
544
+ // Reset
545
+ _this.processing.buffer = [];
546
+ _this.processing.drawn = 0;
547
+ _this.canvas = canvas;
548
+ _this.svgs = [];
549
+
550
+ // Walkthroug SVGs
551
+ if (_this.DEBUG == 10) {
552
+ _this.log('START EXPORT');
553
+ } // DEBUG
554
+ if (_this.DEBUG == 10) {
555
+ _this.log('START BUFFERING');
556
+ } // DEBUG
557
+ for (var i = 0; i < svgs.length; i++) {
558
+ var parent = svgs[i].parentNode,
559
+ svgX = Number(parent.style.left.slice(0, -2)),
560
+ svgY = Number(parent.style.top.slice(0, -2)),
561
+ svgClone = _this.polifySVG(svgs[i].cloneNode(true)),
562
+ tmp = AmCharts.extend({}, offset);
563
+
564
+ // Add external legend
565
+ if ( svgs[i].externalLegend ) {
566
+ if ( _this.cfg.legendPosition == 'right' ) {
567
+ offset.y = 0;
568
+ offset.x = _this.chart.divRealWidth;
569
+
570
+ } else if ( _this.cfg.legendPosition == 'bottom' ) {
571
+ offset.y = svgY ? svgY : offset.y;
572
+
573
+ } else if ( typeof _this.cfg.legendPosition == 'object' ) {
574
+ offset.x = _this.cfg.legendPosition.left;
575
+ offset.y = _this.cfg.legendPosition.top;
576
+
577
+ } else {
578
+ offset.x = 0;
579
+ offset.y = 0;
580
+ }
581
+
582
+ // Overtake parent position if given; fixed 20/03/14 distinguish between relativ and others
583
+ } else {
584
+ if ( parent.style.position == 'relative' ) {
585
+ offset.x = svgX ? svgX : offset.x;
586
+ offset.y = svgY ? svgY : offset.y;
587
+ } else {
588
+ offset.x = svgX;
589
+ offset.y = svgY;
590
+ }
591
+ }
592
+
593
+ _this.processing.buffer.push([svgClone, AmCharts.extend({}, offset)]);
594
+
595
+ // Put back from "cache"
596
+ if (svgY && svgX) {
597
+ offset = tmp;
598
+
599
+ // New offset for next one
600
+ } else {
601
+ offset.y += svgY ? 0 : parent.offsetHeight;
602
+ }
603
+
604
+ if (_this.DEBUG == 10) {
605
+ _this.log('BUFFERED', svgs[i], offset);
606
+ } // DEBUG
607
+ }
608
+ if (_this.DEBUG == 10) {
609
+ _this.log('END BUFFERING');
610
+ } // DEBUG
611
+
612
+ // Apply background
613
+ if (_this.DEBUG == 10) {
614
+ _this.log('START DRAWING', cfg.render);
615
+ } // DEBUG
616
+ if (_this.DEBUG == 10) {
617
+ _this.log('FILL BACKGROUND');
618
+ } // DEBUG
619
+ canvas.id = AmCharts.getUniqueId();
620
+ canvas.width = _this.chart.divRealWidth;
621
+ canvas.height = _this.chart.divRealHeight;
622
+
623
+ // External legend exception
624
+ if ( _this.chart.legend && _this.chart.legend.position == "outside" ) {
625
+ if ( ['left','right'].indexOf(_this.cfg.legendPosition) != -1 ) {
626
+ canvas.width += _this.chart.legend.div.offsetWidth;
627
+
628
+ } else if ( typeof _this.cfg.legendPosition == 'object' ) {
629
+ canvas.width += _this.cfg.legendPosition.width;
630
+ canvas.height += _this.cfg.legendPosition.height;
631
+
632
+ } else {
633
+ canvas.height += _this.chart.legend.div.offsetHeight;
634
+ }
635
+ }
636
+
637
+ // Stockchart exception
638
+ var adapted = {
639
+ width: false,
640
+ height: false
641
+ };
642
+ if ( _this.chart.periodSelector ) {
643
+ if ( ['left','right'].indexOf(_this.chart.periodSelector.position) != -1 ) {
644
+ canvas.width -= _this.chart.periodSelector.div.offsetWidth + 16;
645
+ adapted.width = true;
646
+ } else {
647
+ canvas.height -= _this.chart.periodSelector.div.offsetHeight;
648
+ adapted.height = true;
649
+ }
650
+ }
651
+
652
+ if ( _this.chart.dataSetSelector ) {
653
+ if ( ['left','right'].indexOf(_this.chart.dataSetSelector.position) != -1 ) {
654
+ if ( !adapted.width ) {
655
+ canvas.width -= _this.chart.dataSetSelector.div.offsetWidth + 16;
656
+ }
657
+ } else {
658
+ canvas.height -= _this.chart.dataSetSelector.div.offsetHeight;
659
+ }
660
+ }
661
+
662
+ // Set given background; jpeg default
663
+ if (cfg.backgroundColor || cfg.format == 'image/jpeg') {
664
+ context.fillStyle = cfg.backgroundColor || '#FFFFFF';
665
+ context.fillRect(0, 0, canvas.width, canvas.height);
666
+ }
667
+
668
+ /* PRIVATE
669
+ Recursive function to draw the images to the canvas;
670
+ @param none;
671
+ */
672
+ function drawItWhenItsLoaded() {
673
+ var img, buffer, offset, source;
674
+
675
+ // DRAWING PROCESS DONE
676
+ if (_this.processing.buffer.length == _this.processing.drawn || cfg.format == 'svg' ) {
677
+ if (_this.DEBUG == 10) {
678
+ _this.log('END DRAWING');
679
+ } // DEBUG
680
+ return callback();
681
+
682
+ // LOOPING LUI
683
+ } else {
684
+ if (_this.DEBUG == 10) {
685
+ _this.log('DRAW', _this.processing.drawn + 1, 'OF', _this.processing.buffer.length);
686
+ } // DEBUG
687
+
688
+ buffer = _this.processing.buffer[_this.processing.drawn];
689
+ source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
690
+ offset = buffer[1];
691
+
692
+ if (_this.DEBUG == 10) {
693
+ _this.log('SOURCE', source);
694
+ } // DEBUG
695
+
696
+ // NATIVE
697
+ if (cfg.render == 'browser') {
698
+ img = new Image();
699
+ img.id = AmCharts.getUniqueId();
700
+ source = 'data:image/svg+xml;base64,' + btoa(source);
701
+
702
+ //img.crossOrigin = "Anonymous";
703
+ img.onload = function() {
704
+ context.drawImage(this, buffer[1].x, buffer[1].y);
705
+ _this.processing.drawn++;
706
+
707
+ if (_this.DEBUG == 10) {
708
+ _this.log('ONLOAD', this);
709
+ } // DEBUG
710
+ drawItWhenItsLoaded();
711
+ };
712
+ img.onerror = function() {
713
+ if (_this.DEBUG == 10) {
714
+ _this.log('ONERROR', this);
715
+ } // DEBUG
716
+ context.drawImage(this, buffer[1].x, buffer[1].y);
717
+ _this.processing.drawn++;
718
+ drawItWhenItsLoaded();
719
+ };
720
+ img.src = source;
721
+
722
+ if (_this.DEBUG == 10) {
723
+ _this.log('ADD', img);
724
+ } // DEBUG
725
+ if (img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined) {
726
+ if (_this.DEBUG == 10) {
727
+ _this.log('FORCE ONLOAD', img);
728
+ } // DEBUG
729
+ img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
730
+ img.src = source;
731
+ }
732
+
733
+ // CANVG
734
+ } else if (cfg.render == 'canvg') {
735
+ canvg(canvas, source, {
736
+ offsetX: offset.x,
737
+ offsetY: offset.y,
738
+ ignoreMouse: true,
739
+ ignoreAnimation: true,
740
+ ignoreDimensions: true,
741
+ ignoreClear: true,
742
+ renderCallback: function() {
743
+ _this.processing.drawn++;
744
+ drawItWhenItsLoaded();
745
+ }
746
+ });
747
+ }
748
+ }
749
+ }
750
+ return drawItWhenItsLoaded();
751
+ },
752
+
753
+ /*
754
+ Generates the export menu to trigger the exportation
755
+ @param none;
756
+ */
757
+ generateButtons: function() {
758
+ var _this = this,lvl = 0;
759
+
760
+ if(_this.div){
761
+ div = _this.div;
762
+ div.innerHTML = "";
763
+ }
764
+ else{
765
+ div = document.createElement('div'),
766
+ _this.div = div;
767
+ }
768
+
769
+ // Push sublings
770
+ function createList(items) {
771
+ var ul = document.createElement('ul');
772
+
773
+ ul.setAttribute('style', 'list-style: none; margin: 0; padding: 0;');
774
+
775
+ // Walkthrough items
776
+ for (var i = 0; i < items.length; i++) {
777
+ var li = document.createElement('li'),
778
+ img = document.createElement('img'),
779
+ a = document.createElement('a'),
780
+ item = items[i],
781
+ children = null,
782
+ itemStyle = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemStyle), items[i]);
783
+
784
+ // MERGE CFG
785
+ item = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), item);
786
+
787
+ // ICON
788
+ if (item['icon']) {
789
+ img.alt = '';
790
+ img.src = item['icon'];
791
+ img.setAttribute('style', 'margin: 0 auto;border: none;outline: none');
792
+ if (item['iconTitle']) {
793
+ img.title = item['iconTitle'];
794
+ }
795
+ a.appendChild(img);
796
+ }
797
+
798
+ // TITLE; STYLING
799
+ a.href = '#';
800
+ if (item['title']) {
801
+ img.setAttribute('style', 'margin: 0px 5px;');
802
+ a.innerHTML += item.title;
803
+ }
804
+ a.setAttribute('style', 'display: block;');
805
+ AmCharts.extend(a.style, itemStyle);
806
+
807
+ // ONCLICK
808
+ a.onclick = item.onclick.bind(a, _this, item);
809
+ li.appendChild(a);
810
+
811
+ // APPEND SIBLINGS
812
+ if (item.items) {
813
+ children = createList(item.items);
814
+ li.appendChild(children);
815
+
816
+ li.onmouseover = function() {
817
+ children.style.display = 'block';
818
+ };
819
+ li.onmouseout = function() {
820
+ children.style.display = 'none';
821
+ };
822
+ children.style.display = 'none';
823
+ }
824
+
825
+ // Append to parent
826
+ ul.appendChild(li);
827
+
828
+ // Apply hover
829
+ a.onmouseover = function() {
830
+ this.style.backgroundColor = itemStyle.rollOverBackgroundColor;
831
+ this.style.color = itemStyle.rollOverColor;
832
+ this.style.borderColor = itemStyle.rollOverBorderColor;
833
+ };
834
+ a.onmouseout = function() {
835
+ this.style.backgroundColor = itemStyle.backgroundColor;
836
+ this.style.color = itemStyle.color;
837
+ this.style.borderColor = itemStyle.borderColor;
838
+ };
839
+ }
840
+ lvl++;
841
+
842
+ if (_this.DEBUG == 10) {
843
+ _this.log('MENU', ul);
844
+ } // DEBUG
845
+
846
+ return ul;
847
+ }
848
+
849
+ // Style wrapper; Push into chart div
850
+ div.setAttribute('style', 'position: absolute;top:' + _this.cfg.menuTop + ';right:' + _this.cfg.menuRight + ';bottom:' + _this.cfg.menuBottom + ';left:' + _this.cfg.menuLeft + ';');
851
+ div.setAttribute('class', 'amExportButton');
852
+ div.appendChild(createList(_this.cfg.menuItems));
853
+ _this.chart.containerDiv.appendChild(div);
854
+ }
855
855
  });