amcharts.rb 3.11.2.14 → 3.11.2.16

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.
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
  });