Subversion Repositories Integrator Subversion

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 espaco 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
});