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