amcharts.rb 3.1.1.3 → 3.2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ });