amcharts.rb 3.1.1.3 → 3.2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,40 @@
1
+ #### 3.2.0 ####################################################################################################################
2
+
3
+
4
+ # AmCharts.makeChart(divID, chartConfig); method added. divID is id of a div where your chart should appear. chartConfig is
5
+ JSON object with chart configuration. Check examples with _JSON_ prefix in samples folder to see this in action.
6
+
7
+ # type property added to AmChart class. It is required to specify type to one of the following, when creating charts from
8
+ JSON config: serial, xy, radar, pie, gauge, funnel, map, stock
9
+
10
+ # a possibility to export charts as image/pdf/svg added for all modern browsers except IE9 (IE10 is supported). The
11
+ exporting doesn't require any server side software and is made using js libraries only. Check samples with
12
+ _exporting_ prefix to see this in action. Exporting to SVG doesn't work very properly with stock chart or charts with
13
+ legend (will offer saving multiple files).
14
+
15
+ # You can set any legend items via legend.data property, for example:
16
+ legend.data = [{title:"first", color:"#CC0000", value:50}, {title:"second", color:"#00CC00", value:100}];
17
+ This allows creating any legend items you want. Call chart.legend.validateNow(); if you change legend's data at run time.
18
+
19
+ # AmAngularGauge supports legend now
20
+
21
+ # bug fix - if a chart with scrollbar was rotated after the chart is created, the scrollbar's graph was shifted to a wrong
22
+ position.
23
+
24
+ # bug fix - column graph type wasn't displayed in chart scrollbar (since 3.1.0)
25
+
26
+ # gridAboveGraphs property added to AmCoordinate chart. This allow to show grid lines above your graphs, as world-famous
27
+ data visualization guru Edward Tufte suggests. Note, this won't work properly with 3D charts.
28
+
29
+ # negative axis labels rotation possible. You can use values from -90 to -1 for labelRotation property since now.
30
+
31
+ # bug fix: step line with changing line color was rendered incorrectly if some values were missing.
32
+
33
+ # bug fix: labelPosition "inside" and "middle" for bar charts fixed.
34
+
35
+ # bug fix: AmAngularGauge chart wasn't firing "rendered" event.
36
+
37
+
1
38
  #### 3.1.1 ####################################################################################################################
2
39
 
3
40
  # FireFox error messages about style declarations fixed
@@ -0,0 +1,623 @@
1
+ AmCharts.AmExport = AmCharts.Class({
2
+ construct: function (chart,cfg) {
3
+ var _this = this;
4
+ _this.DEBUG = false;
5
+ _this.chart = chart;
6
+ _this.canvas = null;
7
+ _this.svgs = [];
8
+ _this.cfg = {
9
+ menuTop : 'auto',
10
+ menuLeft : 'auto',
11
+ menuRight : '0px',
12
+ menuBottom : '0px',
13
+ menuItems : [{
14
+ textAlign : 'center',
15
+ icon : _this.chart.pathToImages + 'export.png',
16
+ iconTitle : 'Save chart as an image',
17
+ format : 'png'
18
+ }],
19
+ menuItemStyle : {
20
+ backgroundColor : 'transparent',
21
+ rollOverBackgroundColor : '#EFEFEF',
22
+ color : '#000000',
23
+ rollOverColor : '#CC0000',
24
+ paddingTop : '6px',
25
+ paddingRight : '6px',
26
+ paddingBottom : '6px',
27
+ paddingLeft : '6px',
28
+ marginTop : '0px',
29
+ marginRight : '0px',
30
+ marginBottom : '0px',
31
+ marginLeft : '0px',
32
+ textAlign : 'left',
33
+ textDecoration : 'none',
34
+ fontFamily : _this.chart.fontFamily,
35
+ fontSize : _this.chart.fontSize + 'px'
36
+ },
37
+ menuItemOutput : {
38
+ backgroundColor : '#FFFFFF',
39
+ fileName : 'amChart',
40
+ format : 'png',
41
+ output : 'dataurlnewwindow',
42
+ render : 'browser',
43
+ dpi : 90,
44
+ onclick : function(instance,config,event) {
45
+ instance.output(config);
46
+ }
47
+ },
48
+ removeImagery : true
49
+ };
50
+ _this.processing = {
51
+ buffer : [],
52
+ drawn : 0,
53
+ timer : 0
54
+ }
55
+
56
+ // Config dependency adaption
57
+ if ( typeof(window.canvg) != 'undefined' && typeof(window.RGBColor) != 'undefined' ) {
58
+ _this.cfg.menuItemOutput.render = 'canvg';
59
+ }
60
+ if ( typeof(window.saveAs) != 'undefined' ) {
61
+ _this.cfg.menuItemOutput.output = 'save';
62
+ }
63
+ if ( AmCharts.isIE && AmCharts.IEversion < 10 ) {
64
+ _this.cfg.menuItemOutput.output = 'dataurlnewwindow';
65
+ }
66
+
67
+ // Merge given configs
68
+ if ( cfg ) {
69
+ cfg.menuItemOutput = AmCharts.extend(_this.cfg.menuItemOutput,cfg.menuItemOutput || {});
70
+ cfg.menuItemStyle = AmCharts.extend(_this.cfg.menuItemStyle,cfg.menuItemStyle || {});
71
+ _this.cfg = AmCharts.extend(_this.cfg,cfg);
72
+ }
73
+
74
+ // Add reference to chart
75
+ _this.chart.AmExport = _this;
76
+
77
+ // Listen to the drawer
78
+ _this.chart.addListener('rendered',function() {
79
+ _this.setup();
80
+ });
81
+
82
+ // DEBUG; Public reference
83
+ if ( _this.DEBUG ) { window.AmExport = _this };
84
+ },
85
+
86
+ /*
87
+ Simple log function for internal purpose
88
+ @param **args
89
+ */
90
+ log: function() {
91
+ console.log('AmExport: ',arguments);
92
+ },
93
+
94
+ /* PUBLIC
95
+ Prepares everything to get exported
96
+ @param none
97
+ */
98
+ setup: function() {
99
+ var _this = this;
100
+
101
+ if ( _this.DEBUG == 10 ) { _this.log('SETUP START'); } // DEBUG
102
+
103
+
104
+ if ( !AmCharts.isIE || ( AmCharts.isIE && AmCharts.IEversion > 9 ) ) {
105
+ window.clearTimeout(_this.processing.timer);
106
+ _this.processing.timer = setTimeout(function() {
107
+ // Polify SVG; needs to wait
108
+ _this.polifySVG();
109
+
110
+ // Build Buttons
111
+ _this.generateButtons();
112
+ if ( _this.DEBUG == 10 ) { _this.log('SETUP END'); } // DEBUG
113
+ },1000);
114
+ } else {
115
+ if ( _this.DEBUG == 10 ) { _this.log('< IE10 NOT SUPPORTED'); } // DEBUG
116
+ }
117
+ },
118
+
119
+ /* PUBLIC
120
+ Decodes base64 string to binary array
121
+ @param base64_string
122
+ @copyright Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
123
+ */
124
+ generateBinaryArray: function(base64_string) {
125
+ var
126
+ len = base64_string.length
127
+ , buffer = new Uint8Array(len / 4 * 3 | 0)
128
+ , i = 0
129
+ , outptr = 0
130
+ , last = [0, 0]
131
+ , state = 0
132
+ , save = 0
133
+ , rank
134
+ , code
135
+ , undef
136
+ , base64_ranks = new Uint8Array([
137
+ 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
138
+ , -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
139
+ , 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
140
+ , -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
141
+ , 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
142
+ ]);
143
+ while (len--) {
144
+ code = base64_string.charCodeAt(i++);
145
+ rank = base64_ranks[code-43];
146
+ if (rank !== 255 && rank !== undef) {
147
+ last[1] = last[0];
148
+ last[0] = code;
149
+ save = (save << 6) | rank;
150
+ state++;
151
+ if (state === 4) {
152
+ buffer[outptr++] = save >>> 16;
153
+ if (last[1] !== 61 /* padding character */) {
154
+ buffer[outptr++] = save >>> 8;
155
+ }
156
+ if (last[0] !== 61 /* padding character */) {
157
+ buffer[outptr++] = save;
158
+ }
159
+ state = 0;
160
+ }
161
+ }
162
+ }
163
+ // 2/3 chance there's going to be some null bytes at the end, but that
164
+ // doesn't really matter with most image formats.
165
+ // If it somehow matters for you, truncate the buffer up outptr.
166
+ return buffer;
167
+ },
168
+
169
+ /*
170
+ Creates blob object
171
+ @param base64_datastring string
172
+ @param type string
173
+ */
174
+ generateBlob: function(datastring,type) {
175
+ var _this = this,
176
+ header_end = datastring.indexOf(',') + 1,
177
+ header = datastring.substring(0,header_end),
178
+ data = datastring,
179
+ blob = new Blob();
180
+
181
+ if ( header.indexOf('base64') != -1 ) {
182
+ data = _this.generateBinaryArray(datastring.substring(header_end));
183
+ }
184
+
185
+ // Fake blob for IE
186
+ if ( AmCharts.isIE && AmCharts.IEversion < 10 ) {
187
+ blob.data = data;
188
+ blob.size = data.length;
189
+ blob.type = type;
190
+ blob.encoding = 'base64';
191
+ } else {
192
+ blob = new Blob([data],{type: type});
193
+ }
194
+ return blob
195
+ },
196
+
197
+ /*
198
+ Creates PDF object
199
+ @param config object
200
+ */
201
+ generatePDF: function(cfg) {
202
+ var _this = this,
203
+ pdf = {output: function() {return ''}},
204
+ data = _this.canvas.toDataURL('image/jpeg'), // JSPDF ONLY SUPPORTS JPG
205
+ width = (_this.canvas.width * 25.4) / cfg.dpi,
206
+ height = (_this.canvas.height * 25.4) / cfg.dpi;
207
+
208
+ // Check
209
+ if ( window.jsPDF ) {
210
+ pdf = new jsPDF();
211
+ if ( pdf.addImage ) {
212
+ pdf.addImage(data, 'JPEG', 0, 0, width, height);
213
+ } else {
214
+ alert("Missing jsPDF plugin; Please add the 'addImage' plugin.");
215
+ }
216
+ } else {
217
+ alert("Missing jsPDF lib; Don't forget to add the addImage plugin.");
218
+ }
219
+
220
+ return pdf;
221
+ },
222
+
223
+ /*
224
+ Creates the CANVAS to receive the image data
225
+ @param format void()
226
+ @param callback; given callback function which returns the blob or datastring of the configured ouput type
227
+ */
228
+ output: function(cfg,externalCallback) {
229
+ var _this = this,
230
+ cfg = AmCharts.extend(AmCharts.extend({},_this.cfg.menuItemOutput),cfg||{});
231
+
232
+ /* PRIVATE
233
+ Callback function which gets called after the drawing process is done
234
+ @param none
235
+ */
236
+ function internalCallback() {
237
+ var data = null;
238
+ if ( _this.DEBUG == 10 ) { _this.log('OUTPUT',format); } // DEBUG
239
+
240
+ // SVG
241
+ if ( cfg.format == 'image/svg+xml' || cfg.format == 'svg' ) {
242
+ for ( var i = 0; i < _this.processing.buffer.length; i++ ) {
243
+ data = new XMLSerializer().serializeToString(_this.processing.buffer[i][0]),
244
+ blob = _this.generateBlob(data,'image/svg+xml');
245
+
246
+ if ( cfg.output == 'save' ) {
247
+ saveAs(blob, cfg.fileName + '.svg');
248
+ } else if ( cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring' ) {
249
+ blob = 'data:image/svg+xml;base64,' + btoa(data);
250
+ } else if ( cfg.output == 'dataurlnewwindow' ) {
251
+ window.open('data:image/svg+xml;base64,' + btoa(data));
252
+ } else if ( cfg.output == 'datauri' || cfg.output == 'dataurl' ) {
253
+ location.href = 'data:image/svg+xml;base64,' + btoa(data);
254
+ } else if ( cfg.output == 'datastream' ) {
255
+ location.href = 'data:image/octet-stream;base64,' + btoa(data);
256
+ }
257
+
258
+ if ( externalCallback )
259
+ externalCallback.apply(_this,[blob]);
260
+ }
261
+ // PDF
262
+ } else if ( cfg.format == 'application/pdf' || cfg.format == 'pdf' ) {
263
+ data = _this.generatePDF(cfg).output('dataurlstring'),
264
+ blob = _this.generateBlob(data,'application/pdf');
265
+
266
+ if ( cfg.output == 'save' ) {
267
+ saveAs(blob, cfg.fileName + '.pdf');
268
+ } else if ( cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring' ) {
269
+ blob = data;
270
+ } else if ( cfg.output == 'dataurlnewwindow' ) {
271
+ window.open(data);
272
+ } else if ( cfg.output == 'datauri' || cfg.output == 'dataurl' ) {
273
+ location.href = data;
274
+ } else if ( cfg.output == 'datastream' ) {
275
+ location.href = data.replace('application/pdf','application/octet-stream');
276
+ }
277
+
278
+ if ( externalCallback )
279
+ externalCallback.apply(_this,[blob]);
280
+
281
+ // PNG
282
+ } else if ( cfg.format == 'image/png' || cfg.format == 'png' ) {
283
+ data = _this.canvas.toDataURL('image/png'),
284
+ blob = _this.generateBlob(data,'image/png');
285
+
286
+ if ( cfg.output == 'save' ) {
287
+ saveAs(blob, cfg.fileName + '.png');
288
+ } else if ( cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring' ) {
289
+ blob = data;
290
+ } else if ( cfg.output == 'dataurlnewwindow' ) {
291
+ window.open(data);
292
+ } else if ( cfg.output == 'datauri' || cfg.output == 'dataurl' ) {
293
+ location.href = data;
294
+ } else if ( cfg.output == 'datastream' ) {
295
+ location.href = data.replace('image/png','image/octet-stream');
296
+ }
297
+
298
+ if ( externalCallback )
299
+ externalCallback.apply(_this,[blob]);
300
+
301
+ // JPG
302
+ } else if ( cfg.format == 'image/jpeg' || cfg.format == 'jpeg' || cfg.format == 'jpg' ) {
303
+ data = _this.canvas.toDataURL('image/jpeg'),
304
+ blob = _this.generateBlob(data,'image/jpeg');
305
+
306
+ if ( cfg.output == 'save' ) {
307
+ saveAs(blob, cfg.fileName + '.jpg');
308
+ } else if ( cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring' ) {
309
+ blob = data;
310
+ } else if ( cfg.output == 'dataurlnewwindow' ) {
311
+ window.open(data);
312
+ } else if ( cfg.output == 'datauri' || cfg.output == 'dataurl' ) {
313
+ location.href = data;
314
+ } else if ( cfg.output == 'datastream' ) {
315
+ location.href = data.replace('image/jpeg','image/octet-stream');
316
+ }
317
+
318
+ if ( externalCallback )
319
+ externalCallback.apply(_this,[blob]);
320
+ }
321
+
322
+ }
323
+
324
+ return _this.generateOutput(cfg,internalCallback);
325
+ },
326
+
327
+ /* PUBLIC
328
+ Polifies missing attributes to the SVG and replaces images to embedded base64 images
329
+ @param none
330
+ */
331
+ polifySVG: function() {
332
+ var _this = this;
333
+ var svgs = _this.chart.div.getElementsByTagName('svg');
334
+
335
+ // Recursive function to force the attributes
336
+ function recursiveChange(svg,tag) {
337
+ var items = svg.getElementsByTagName(tag);
338
+
339
+ for ( var i = 0; i < items.length; i++) {
340
+
341
+ if ( _this.cfg.removeImagery ) {
342
+ items[i].parentNode.removeChild(items[i]);
343
+
344
+ } else {
345
+ var image = document.createElement('img');
346
+ var canvas = document.createElement('canvas');
347
+ var ctx = canvas.getContext('2d');
348
+
349
+ canvas.width = items[i].getAttribute('width');
350
+ canvas.height = items[i].getAttribute('height');
351
+ image.src = items[i].getAttribute('xlink:href');
352
+ image.width = items[i].getAttribute('width');
353
+ image.height = items[i].getAttribute('height');
354
+
355
+ try {
356
+ ctx.drawImage(image,0,0,image.width,image.height);
357
+ datastring = canvas.toDataURL(); // image.src; // canvas.toDataURL(); //
358
+ } catch(err) {
359
+ datastring = image.src; // image.src; // canvas.toDataURL(); //
360
+
361
+ _this.log('Tainted canvas, reached browser CORS security; origin from imagery must be equal to the server!');
362
+ throw new Error(err);
363
+ }
364
+
365
+ items[i].setAttribute('xlink:href',datastring);
366
+ }
367
+
368
+ if ( _this.DEBUG == 10 ) { _this.log('POLIFIED',items[i]); } // DEBUG
369
+ }
370
+ }
371
+
372
+ // Loop through svgs to add some standardization
373
+ for ( var i = 0; i < svgs.length; i++ ) {
374
+ var parent = svgs[i].parentNode;
375
+
376
+ // Put some attrs to it
377
+ /*
378
+ if ( !AmCharts.isIE ) {
379
+ svgs[i].setAttribute('xmlns','http://www.w3.org/2000/svg');
380
+ svgs[i].setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink');
381
+ }
382
+ svgs[i].setAttribute('width',parent.style.width);
383
+ svgs[i].setAttribute('height',parent.style.height);
384
+ */
385
+
386
+ if ( _this.DEBUG == 10 ) { _this.log('POLIFIED',svgs[i]); } // DEBUG
387
+
388
+ // Force link adaption
389
+ recursiveChange(svgs[i],'pattern');
390
+ recursiveChange(svgs[i],'image');
391
+
392
+ _this.svgs.push(svgs[i]);
393
+ }
394
+
395
+ return svgs;
396
+ },
397
+
398
+ /* PUBLIC
399
+ Generates the canvas with the given SVGs and configured renderer
400
+ @param callback; function(); gets called after drawing process on the canvas has been finished
401
+ */
402
+ generateOutput: function(cfg,callback) {
403
+ var _this = this,
404
+ svgs = _this.chart.div.getElementsByTagName('svg'),
405
+ canvas = document.createElement('canvas'),
406
+ context = canvas.getContext('2d'),
407
+ offset = {
408
+ y: 0,
409
+ x: 0
410
+ },
411
+ tmp = {};
412
+
413
+ // Reset
414
+ _this.processing.buffer = [];
415
+ _this.processing.drawn = 0;
416
+ _this.canvas = canvas;
417
+
418
+ // Walkthroug SVGs
419
+ if ( _this.DEBUG == 10 ) { _this.log('START EXPORT'); } // DEBUG
420
+ if ( _this.DEBUG == 10 ) { _this.log('START BUFFERING'); } // DEBUG
421
+ for ( var i = 0; i < svgs.length; i++ ) {
422
+ var parent = svgs[i].parentNode,
423
+ svgX = Number(parent.style.left.slice(0,-2)),
424
+ svgY = Number(parent.style.top.slice(0,-2));
425
+ tmp = AmCharts.extend({},offset);
426
+
427
+ // Overtake parent position if givwn
428
+ offset.x = svgX?svgX:offset.x;
429
+ offset.y = svgY?svgY:offset.y;
430
+
431
+ _this.processing.buffer.push([svgs[i],AmCharts.extend({},offset)]);
432
+
433
+ // Put back from "cache"
434
+ if ( svgY&&svgX ) {
435
+ offset = tmp;
436
+
437
+ // New offset for next one
438
+ } else {
439
+ offset.y += svgY?0:parent.offsetHeight;
440
+ }
441
+
442
+ if ( _this.DEBUG == 10 ) { _this.log('BUFFERED',svgs[i],offset); } // DEBUG
443
+ }
444
+ if ( _this.DEBUG == 10 ) { _this.log('END BUFFERING'); } // DEBUG
445
+
446
+ // Apply background
447
+ if ( _this.DEBUG == 10 ) { _this.log('START DRAWING',cfg.render); } // DEBUG
448
+ if ( _this.DEBUG == 10 ) { _this.log('FILL BACKGROUND'); } // DEBUG
449
+ canvas.id = AmCharts.getUniqueId();
450
+ canvas.width = _this.chart.divRealWidth;
451
+ canvas.height = _this.chart.divRealHeight;
452
+
453
+ // Set given background; jpeg default
454
+ if ( cfg.backgroundColor || format == 'image/jpeg' ) {
455
+ context.fillStyle = cfg.backgroundColor || '#FFFFFF';
456
+ context.fillRect(0,0,canvas.width,canvas.height);
457
+ }
458
+
459
+ /* PRIVATE
460
+ Recursive function to draw the images to the canvas;
461
+ @param none;
462
+ */
463
+ function drawItWhenItsLoaded() {
464
+ var img,buffer,offset,source;
465
+
466
+ // DRAWING PROCESS DONE
467
+ if ( _this.processing.buffer.length == _this.processing.drawn ) {
468
+ if ( _this.DEBUG == 10 ) { _this.log('END DRAWING'); } // DEBUG
469
+ return callback();
470
+
471
+ // LOOPING LUI
472
+ } else {
473
+ if ( _this.DEBUG == 10 ) { _this.log('DRAW',_this.processing.drawn + 1,'OF',_this.processing.buffer.length); } // DEBUG
474
+
475
+ buffer = _this.processing.buffer[_this.processing.drawn];
476
+ source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
477
+ offset = buffer[1];
478
+
479
+ if ( _this.DEBUG == 10 ) { _this.log('SOURCE',source); } // DEBUG
480
+
481
+ // NATIVE
482
+ if ( cfg.render == 'browser' ) {
483
+ img = new Image();
484
+ img.id = AmCharts.getUniqueId();
485
+ source = 'data:image/svg+xml;base64,' + btoa(source);
486
+
487
+ //img.crossOrigin = "Anonymous";
488
+ img.onload = function() {
489
+ context.drawImage(this,buffer[1].x,buffer[1].y);
490
+ _this.processing.drawn++;
491
+
492
+ if ( _this.DEBUG == 10 ) { _this.log('ONLOAD',this); } // DEBUG
493
+ drawItWhenItsLoaded();
494
+ }
495
+ img.onerror = function() {
496
+ if ( _this.DEBUG == 10 ) { _this.log('ONERROR',this); } // DEBUG
497
+ context.drawImage(this,buffer[1].x,buffer[1].y);
498
+ _this.processing.drawn++;
499
+ drawItWhenItsLoaded();
500
+ }
501
+ img.src = source;
502
+
503
+ if ( _this.DEBUG == 10 ) { _this.log('ADD',img); } // DEBUG
504
+ if ( img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined ) {
505
+ if ( _this.DEBUG == 10 ) { _this.log('FORCE ONLOAD',img); } // DEBUG
506
+ img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
507
+ img.src = source;
508
+ }
509
+
510
+ // CANVG
511
+ } else if ( cfg.render == 'canvg' ) {
512
+ canvg(canvas,source,{
513
+ offsetX : offset.x,
514
+ offsetY : offset.y,
515
+ ignoreMouse : true,
516
+ ignoreAnimation : true,
517
+ ignoreDimensions : true,
518
+ ignoreClear : true,
519
+ renderCallback : function() {
520
+ _this.processing.drawn++;
521
+ drawItWhenItsLoaded();
522
+ }
523
+ });
524
+ }
525
+ }
526
+ }
527
+ return drawItWhenItsLoaded();
528
+ },
529
+
530
+ /*
531
+ Generates the export menu to trigger the exportation
532
+ @param none;
533
+ */
534
+ generateButtons: function() {
535
+ var _this = this,
536
+ div = document.createElement('div'),
537
+ lvl = 0;
538
+
539
+ // Push sublings
540
+ function createList(items) {
541
+ var ul = document.createElement('ul');
542
+
543
+ ul.setAttribute('style','list-style: none; margin: 0; padding: 0;');
544
+
545
+ // Walkthrough items
546
+ for ( var i = 0; i < items.length; i++ ) {
547
+ var li = document.createElement('li'),
548
+ img = document.createElement('img'),
549
+ a = document.createElement('a'),
550
+ item = items[i],
551
+ children = null,
552
+ itemStyle = AmCharts.extend(AmCharts.extend({},_this.cfg.menuItemStyle),items[i]);
553
+
554
+ // MERGE CFG
555
+ item = AmCharts.extend(AmCharts.extend({},_this.cfg.menuItemOutput),item);
556
+
557
+ // ICON
558
+ if ( item['icon'] ) {
559
+ img.alt = '';
560
+ img.src = item['icon'];
561
+ img.setAttribute('style','margin: 0 auto;border: none;outline: none');
562
+ if ( item['iconTitle'] ) {
563
+ img.title=item['iconTitle'];
564
+ }
565
+ a.appendChild(img);
566
+ }
567
+
568
+ // TITLE; STYLING
569
+ a.href = '#';
570
+ if ( item['title'] ) {
571
+ img.setAttribute('style','margin-right: 5px;');
572
+ a.innerHTML += item.title;
573
+ }
574
+ a.setAttribute('style','display: block;');
575
+ AmCharts.extend(a.style,itemStyle)
576
+
577
+ // ONCLICK
578
+ a.onclick = item.onclick.bind(a,_this,item);
579
+ li.appendChild(a);
580
+
581
+ // APPEND SIBLINGS
582
+ if ( item.items ) {
583
+ children = createList(item.items);
584
+ li.appendChild(children);
585
+
586
+ li.onmouseover = function() {
587
+ children.style.display = 'block';
588
+ }
589
+ li.onmouseout = function() {
590
+ children.style.display = 'none';
591
+ }
592
+ children.style.display = 'none';
593
+ }
594
+
595
+ // Append to parent
596
+ ul.appendChild(li);
597
+
598
+ // Apply hover
599
+ a.onmouseover = function() {
600
+ this.style.backgroundColor = itemStyle.rollOverBackgroundColor;
601
+ this.style.color = itemStyle.rollOverColor;
602
+ this.style.borderColor = itemStyle.rollOverBorderColor;
603
+ }
604
+ a.onmouseout = function() {
605
+ this.style.backgroundColor = itemStyle.backgroundColor;
606
+ this.style.color = itemStyle.color;
607
+ this.style.borderColor = itemStyle.borderColor;
608
+ }
609
+ }
610
+ lvl++;
611
+
612
+ if ( _this.DEBUG == 10 ) { _this.log('MENU',ul); } // DEBUG
613
+
614
+ return ul;
615
+ }
616
+
617
+ // Style wrapper; Push into chart div
618
+ div.setAttribute('style','position: absolute;top:'+ _this.cfg.menuTop +';right:'+ _this.cfg.menuRight +';bottom:'+ _this.cfg.menuBottom +';left:'+ _this.cfg.menuLeft +';box-shadow:0px 0px 1px 0px rgba(0,0,0,0);');
619
+ div.appendChild(createList(_this.cfg.menuItems));
620
+ _this.chart.div.style.position = 'relative';
621
+ _this.chart.div.appendChild(div);
622
+ }
623
+ });