1AmCharts.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.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 30 toCoordinate:function(value){ 31 if(value === undefined){ 32 return "auto"; 33 } 34 if(String(value).indexOf("%") != -1){ 35 return value; 36 } 37 else{ 38 return value + "px"; 39 } 40 }, 41 42 init: function(){ 43 var _this = this; 44 45 var formats = []; 46 if (_this.exportPNG) { 47 formats.push("png"); 48 } 49 if (_this.exportPDF) { 50 formats.push("pdf"); 51 } 52 if (_this.exportJPG) { 53 formats.push("jpg"); 54 } 55 if (_this.exportSVG) { 56 formats.push("svg"); 57 } 58 59 var menuItems = []; 60 if(formats.length == 1){ 61 var format = formats[0]; 62 menuItems.push({format:format, iconTitle:_this.buttonTitle, icon:_this.chart.pathToImages + _this.buttonIcon}) 63 } 64 else if(formats.length > 1){ 65 var subItems = []; 66 for(var i = 0; i < formats.length; i++){ 67 subItems.push({format:formats[i], title:formats[i].toUpperCase()}); 68 } 69 menuItems.push({onclick: function() {}, icon:_this.chart.pathToImages + _this.buttonIcon, items:subItems}) 70 } 71 72 73 var color = _this.color; 74 if(color === undefined){ 75 color = _this.chart.color; 76 } 77 78 var buttonColor = _this.buttonColor; 79 if(buttonColor === undefined){ 80 buttonColor = "transparent"; 81 } 82 83 84 _this.cfg = { 85 menuTop: _this.toCoordinate(_this.top), 86 menuLeft: _this.toCoordinate(_this.left), 87 menuRight: _this.toCoordinate(_this.right), 88 menuBottom: _this.toCoordinate(_this.bottom), 89 menuItems: menuItems, 90 menuItemStyle: { 91 backgroundColor: buttonColor, 92 opacity:_this.buttonAlpha, 93 rollOverBackgroundColor: _this.buttonRollOverColor, 94 color: color, 95 rollOverColor: _this.textRollOverColor, 96 paddingTop: '6px', 97 paddingRight: '6px', 98 paddingBottom: '6px', 99 paddingLeft: '6px', 100 marginTop: '0px', 101 marginRight: '0px', 102 marginBottom: '0px', 103 marginLeft: '0px', 104 textAlign: 'left', 105 textDecoration: 'none', 106 fontFamily: _this.chart.fontFamily, 107 fontSize: _this.chart.fontSize + 'px' 108 }, 109 menuItemOutput: { 110 backgroundColor: _this.imageBackgroundColor, 111 fileName: _this.imageFileName, 112 format: 'png', 113 output: 'dataurlnewwindow', 114 render: 'browser', 115 dpi: 90, 116 onclick: function(instance, config, event) { 117 event.preventDefault(); 118 // Polify SVG; needs to wait 119 instance.polifySVG(); 120 instance.output(config); 121 } 122 }, 123 removeImagery: false 124 }; 125 126 _this.processing = { 127 buffer: [], 128 drawn: 0, 129 timer: 0 130 }; 131 132 // Config dependency adaption 133 if (typeof(window.canvg) != 'undefined' && typeof(window.RGBColor) != 'undefined') { 134 _this.cfg.menuItemOutput.render = 'canvg'; 135 } 136 if (typeof(window.saveAs) != 'undefined') { 137 _this.cfg.menuItemOutput.output = 'save'; 138 } 139 if (AmCharts.isIE && AmCharts.IEversion < 10) { 140 _this.cfg.menuItemOutput.output = 'dataurlnewwindow'; 141 } 142 143 // Merge given configs 144 var cfg = _this.userCFG; 145 if (cfg) { 146 cfg.menuItemOutput = AmCharts.extend(_this.cfg.menuItemOutput, cfg.menuItemOutput || {}); 147 cfg.menuItemStyle = AmCharts.extend(_this.cfg.menuItemStyle, cfg.menuItemStyle || {}); 148 _this.cfg = AmCharts.extend(_this.cfg, cfg); 149 } 150 151 // Add reference to chart 152 _this.chart.AmExport = _this; 153 154 // Listen to the drawer 155 _this.chart.addListener('rendered', function() { 156 _this.setup(); 157 }); 158 159 // DEBUG; Public reference 160 if (_this.DEBUG) { 161 window.AmExport = _this; 162 } 163 }, 164 165 166 /* 167 Simple log function for internal purpose 168 @param **args 169 */ 170 log: function() { 171 console.log('AmExport: ', arguments); 172 }, 173 174 /* PUBLIC 175 Prepares everything to get exported 176 @param none 177 */ 178 setup: function() { 179 var _this = this; 180 181 if (_this.DEBUG == 10) { 182 _this.log('SETUP START'); 183 } // DEBUG 184 185 186 if (!AmCharts.isIE || (AmCharts.isIE && AmCharts.IEversion > 9)) { 187 // Build Buttons 188 _this.generateButtons(); 189 if (_this.DEBUG == 10) { 190 _this.log('SETUP END'); 191 } // DEBUG 192 } else { 193 if (_this.DEBUG == 10) { 194 _this.log('< IE10 NOT SUPPORTED'); 195 } // DEBUG 196 } 197 }, 198 199 /* PUBLIC 200 Decodes base64 string to binary array 201 @param base64_string 202 @copyright Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr 203 */ 204 generateBinaryArray: function(base64_string) { 205 var 206 len = base64_string.length, 207 buffer = new Uint8Array(len / 4 * 3 | 0), 208 i = 0, 209 outptr = 0, 210 last = [0, 0], 211 state = 0, 212 save = 0, 213 rank, code, undef, base64_ranks = new Uint8Array([ 214 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 215 ]); 216 while (len--) { 217 code = base64_string.charCodeAt(i++);
218 rank = base64_ranks[code - 43]; 219 if (rank !== 255 && rank !== undef) { 220 last[1] = last[0]; 221 last[0] = code; 222 save = (save << 6) | rank; 223 state++; 224 if (state === 4) { 225 buffer[outptr++] = save >>> 16; 226 if (last[1] !== 61 /* padding character */ ) { 227 buffer[outptr++] = save >>> 8; 228 } 229 if (last[0] !== 61 /* padding character */ ) { 230 buffer[outptr++] = save; 231 } 232 state = 0; 233 } 234 } 235 } 236 // 2/3 chance there's going to be some null bytes at the end, but that 237 // doesn't really matter with most image formats. 238 // If it somehow matters for you, truncate the buffer up outptr. 239 return buffer; 240 }, 241 242 /* 243 Creates blob object 244 @param base64_datastring string 245 @param type string 246 */ 247 generateBlob: function(datastring, type) { 248 var _this = this, 249 header_end = datastring.indexOf(',') + 1, 250 header = datastring.substring(0, header_end), 251 data = datastring, 252 blob = new Blob(); 253 254 if (header.indexOf('base64') != -1) { 255 data = _this.generateBinaryArray(datastring.substring(header_end)); 256 } 257 258 // Fake blob for IE 259 if (AmCharts.isIE && AmCharts.IEversion < 10) { 260 blob.data = data; 261 blob.size = data.length; 262 blob.type = type; 263 blob.encoding = 'base64'; 264 } else { 265 blob = new Blob([data], { 266 type: type 267 }); 268 } 269 return blob; 270 }, 271 272 /* 273 Creates PDF object 274 @param config object 275 */ 276 generatePDF: function(cfg) { 277 var _this = this, 278 pdf = { 279 output: function() { 280 return ''; 281 } 282 }, 283 data = _this.canvas.toDataURL('image/jpeg'), // JSPDF ONLY SUPPORTS JPG 284 width = (_this.canvas.width * 25.4) / cfg.dpi, 285 height = (_this.canvas.height * 25.4) / cfg.dpi; 286 287 // Check 288 if (window.jsPDF) { 289 pdf = new jsPDF(); 290 if (pdf.addImage) { 291 pdf.addImage(data, 'JPEG', 0, 0, width, height); 292 } else { 293 alert("Missing jsPDF plugin; Please add the 'addImage' plugin."); 294 } 295 } else { 296 alert("Missing jsPDF lib; Don't forget to add the addImage plugin."); 297 } 298 299 return pdf; 300 }, 301 302 /* 303 Creates the CANVAS to receive the image data 304 @param format void() 305 @param callback; given callback function which returns the blob or datastring of the configured ouput type 306 */ 307 output: function(cfg, externalCallback) { 308 var _this = this; 309 cfg = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), cfg || {}); 310 311 /* PRIVATE 312 Callback function which gets called after the drawing process is done 313 @param none 314 */ 315 function internalCallback() { 316 var data = null; 317 var blob; 318 if (_this.DEBUG == 10) { 319 _this.log('OUTPUT', format); 320 } // DEBUG 321 322 // SVG 323 if (cfg.format == 'image/svg+xml' || cfg.format == 'svg') { 324 for (var i = 0; i < _this.processing.buffer.length; i++) { 325 data = new XMLSerializer().serializeToString(_this.processing.buffer[i][0]); 326 blob = _this.generateBlob(data, 'image/svg+xml'); 327 328 if (cfg.output == 'save') { 329 saveAs(blob, cfg.fileName + '.svg'); 330 } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') { 331 blob = 'data:image/svg+xml;base64,' + btoa(data); 332 } else if (cfg.output == 'dataurlnewwindow') { 333 window.open('data:image/svg+xml;base64,' + btoa(data)); 334 } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') { 335 location.href = 'data:image/svg+xml;base64,' + btoa(data); 336 } else if (cfg.output == 'datastream') { 337 location.href = 'data:image/octet-stream;base64,' + btoa(data); 338 } 339 340 if (externalCallback) 341 externalCallback.apply(_this, [blob]); 342 } 343 // PDF 344 } else if (cfg.format == 'application/pdf' || cfg.format == 'pdf') { 345 data = _this.generatePDF(cfg).output('dataurlstring'); 346 blob = _this.generateBlob(data, 'application/pdf'); 347 348 if (cfg.output == 'save') { 349 saveAs(blob, cfg.fileName + '.pdf'); 350 } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') { 351 blob = data; 352 } else if (cfg.output == 'dataurlnewwindow') { 353 window.open(data); 354 } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') { 355 location.href = data; 356 } else if (cfg.output == 'datastream') { 357 location.href = data.replace('application/pdf', 'application/octet-stream'); 358 } 359 360 if (externalCallback) 361 externalCallback.apply(_this, [blob]); 362 363 // PNG 364 } else if (cfg.format == 'image/png' || cfg.format == 'png') { 365 data = _this.canvas.toDataURL('image/png'); 366 blob = _this.generateBlob(data, 'image/png'); 367 368 if (cfg.output == 'save') { 369 saveAs(blob, cfg.fileName + '.png'); 370 } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') { 371 blob = data; 372 } else if (cfg.output == 'dataurlnewwindow') { 373 window.open(data); 374 } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') { 375 location.href = data; 376 } else if (cfg.output == 'datastream') { 377 location.href = data.replace('image/png', 'image/octet-stream'); 378 } 379 380 if (externalCallback) 381 externalCallback.apply(_this, [blob]); 382 383 // JPG 384 } else if (cfg.format == 'image/jpeg' || cfg.format == 'jpeg' || cfg.format == 'jpg') { 385 data = _this.canvas.toDataURL('image/jpeg'); 386 blob = _this.generateBlob(data, 'image/jpeg'); 387 388 if (cfg.output == 'save') { 389 saveAs(blob, cfg.fileName + '.jpg'); 390 } else if (cfg.output == 'datastring' || cfg.output == 'datauristring' || cfg.output == 'dataurlstring') { 391 blob = data; 392 } else if (cfg.output == 'dataurlnewwindow') { 393 window.open(data); 394 } else if (cfg.output == 'datauri' || cfg.output == 'dataurl') { 395 location.href = data; 396 } else if (cfg.output == 'datastream') { 397 location.href = data.replace('image/jpeg', 'image/octet-stream'); 398 } 399 400 if (externalCallback) 401 externalCallback.apply(_this, [blob]); 402 } 403 404 } 405 406 return _this.generateOutput(cfg, internalCallback); 407 }, 408 409 /* PUBLIC 410 Polifies missing attributes to the SVG and replaces images to embedded base64 images 411 @param none 412 */ 413 polifySVG: function() { 414 var _this = this; 415 var svgs = _this.chart.div.getElementsByTagName('svg'); 416 417 // Recursive function to force the attributes 418 function recursiveChange(svg, tag) { 419 var items = svg.getElementsByTagName(tag); 420 421 for (var i = 0; i < items.length; i++) { 422
423 if (_this.cfg.removeImagery) { 424 items[i].parentNode.removeChild(items[i]); 425 426 } else { 427 var image = document.createElement('img'); 428 var canvas = document.createElement('canvas'); 429 var ctx = canvas.getContext('2d'); 430 431 canvas.width = items[i].getAttribute('width'); 432 canvas.height = items[i].getAttribute('height'); 433 image.src = items[i].getAttribute('xlink:href'); 434 image.width = items[i].getAttribute('width'); 435 image.height = items[i].getAttribute('height'); 436 437 try { 438 ctx.drawImage(image, 0, 0, image.width, image.height); 439 datastring = canvas.toDataURL(); // image.src; // canvas.toDataURL(); // 440 } catch (err) { 441 datastring = image.src; // image.src; // canvas.toDataURL(); // 442 443 _this.log('Tainted canvas, reached browser CORS security; origin from imagery must be equal to the server!'); 444 throw new Error(err); 445 } 446 447 items[i].setAttribute('xlink:href', datastring); 448 } 449 450 if (_this.DEBUG == 10) { 451 _this.log('POLIFIED', items[i]); 452 } // DEBUG 453 } 454 } 455 456 // Loop through svgs to add some standardization 457 for (var i = 0; i < svgs.length; i++) { 458 var parent = svgs[i].parentNode; 459 460 // Put some attrs to it; fixed 20/03/14 xmlns is required to produce a valid svg file 461 if (AmCharts.IEversion == 0) { 462 svgs[i].setAttribute('xmlns', 'http://www.w3.org/2000/svg'); 463 //svgs[i].setAttribute('xmlns:xlink','http://www.w3.org/1999/xlink'); 464 } 465 //svgs[i].setAttribute('width',parent.style.width); 466 //svgs[i].setAttribute('height',parent.style.height); 467 468 // DEBUG 469 if (_this.DEBUG == 10) { 470 _this.log('POLIFIED', svgs[i]); 471 } 472 473 // Force link adaption 474 recursiveChange(svgs[i], 'pattern'); 475 recursiveChange(svgs[i], 'image'); 476 477 _this.svgs.push(svgs[i]); 478 } 479 480 return svgs; 481 }, 482 483 /* PUBLIC 484 Generates the canvas with the given SVGs and configured renderer 485 @param callback; function(); gets called after drawing process on the canvas has been finished 486 */ 487 generateOutput: function(cfg, callback) { 488 var _this = this, 489 svgs = _this.chart.div.getElementsByTagName('svg'), 490 canvas = document.createElement('canvas'), 491 context = canvas.getContext('2d'), 492 offset = { 493 y: 0, 494 x: 0 495 }, 496 tmp = {}; 497 498 // Reset 499 _this.processing.buffer = []; 500 _this.processing.drawn = 0; 501 _this.canvas = canvas; 502 503 // Walkthroug SVGs 504 if (_this.DEBUG == 10) { 505 _this.log('START EXPORT'); 506 } // DEBUG 507 if (_this.DEBUG == 10) { 508 _this.log('START BUFFERING'); 509 } // DEBUG 510 for (var i = 0; i < svgs.length; i++) { 511 var parent = svgs[i].parentNode, 512 svgX = Number(parent.style.left.slice(0, -2)), 513 svgY = Number(parent.style.top.slice(0, -2)); 514 tmp = AmCharts.extend({}, offset); 515 516 // Overtake parent position if given; fixed 20/03/14 distinguish between relativ and others 517 if (parent.style.position == 'relative') { 518 offset.x = svgX ? svgX : offset.x; 519 offset.y = svgY ? svgY : offset.y; 520 } else { 521 offset.x = svgX; 522 offset.y = svgY; 523 } 524 525 _this.processing.buffer.push([svgs[i], AmCharts.extend({}, offset)]); 526 527 // Put back from "cache" 528 if (svgY && svgX) { 529 offset = tmp; 530 531 // New offset for next one 532 } else { 533 offset.y += svgY ? 0 : parent.offsetHeight; 534 } 535 536 if (_this.DEBUG == 10) { 537 _this.log('BUFFERED', svgs[i], offset); 538 } // DEBUG 539 } 540 if (_this.DEBUG == 10) { 541 _this.log('END BUFFERING'); 542 } // DEBUG 543 544 // Apply background 545 if (_this.DEBUG == 10) { 546 _this.log('START DRAWING', cfg.render); 547 } // DEBUG 548 if (_this.DEBUG == 10) { 549 _this.log('FILL BACKGROUND'); 550 } // DEBUG 551 canvas.id = AmCharts.getUniqueId(); 552 canvas.width = _this.chart.divRealWidth; 553 canvas.height = _this.chart.divRealHeight; 554 555 // Set given background; jpeg default 556 if (cfg.backgroundColor || cfg.format == 'image/jpeg') { 557 context.fillStyle = cfg.backgroundColor || '#FFFFFF'; 558 context.fillRect(0, 0, canvas.width, canvas.height); 559 } 560 561 /* PRIVATE 562 Recursive function to draw the images to the canvas; 563 @param none; 564 */ 565 function drawItWhenItsLoaded() { 566 var img, buffer, offset, source; 567 568 // DRAWING PROCESS DONE 569 if (_this.processing.buffer.length == _this.processing.drawn) { 570 if (_this.DEBUG == 10) { 571 _this.log('END DRAWING'); 572 } // DEBUG 573 return callback(); 574 575 // LOOPING LUI 576 } else { 577 if (_this.DEBUG == 10) { 578 _this.log('DRAW', _this.processing.drawn + 1, 'OF', _this.processing.buffer.length); 579 } // DEBUG 580 581 buffer = _this.processing.buffer[_this.processing.drawn]; 582 source = new XMLSerializer().serializeToString(buffer[0]); //source = 'data:image/svg+xml;base64,' + btoa();
583 offset = buffer[1]; 584 585 if (_this.DEBUG == 10) { 586 _this.log('SOURCE', source); 587 } // DEBUG 588 589 // NATIVE 590 if (cfg.render == 'browser') { 591 img = new Image(); 592 img.id = AmCharts.getUniqueId(); 593 source = 'data:image/svg+xml;base64,' + btoa(source); 594 595 //img.crossOrigin = "Anonymous"; 596 img.onload = function() { 597 context.drawImage(this, buffer[1].x, buffer[1].y); 598 _this.processing.drawn++; 599 600 if (_this.DEBUG == 10) { 601 _this.log('ONLOAD', this); 602 } // DEBUG 603 drawItWhenItsLoaded(); 604 }; 605 img.onerror = function() { 606 if (_this.DEBUG == 10) { 607 _this.log('ONERROR', this); 608 } // DEBUG 609 context.drawImage(this, buffer[1].x, buffer[1].y); 610 _this.processing.drawn++; 611 drawItWhenItsLoaded(); 612 }; 613 img.src = source; 614 615 if (_this.DEBUG == 10) { 616 _this.log('ADD', img); 617 } // DEBUG 618 if (img.complete || typeof(img.complete) == 'undefined' || img.complete === undefined) { 619 if (_this.DEBUG == 10) { 620 _this.log('FORCE ONLOAD', img); 621 } // DEBUG 622 img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="; 623 img.src = source; 624 } 625 626 // CANVG 627 } else if (cfg.render == 'canvg') { 628 canvg(canvas, source, { 629 offsetX: offset.x, 630 offsetY: offset.y, 631 ignoreMouse: true, 632 ignoreAnimation: true, 633 ignoreDimensions: true, 634 ignoreClear: true, 635 renderCallback: function() { 636 _this.processing.drawn++; 637 drawItWhenItsLoaded(); 638 } 639 }); 640 } 641 } 642 } 643 return drawItWhenItsLoaded(); 644 }, 645 646 /* 647 Generates the export menu to trigger the exportation 648 @param none; 649 */ 650 generateButtons: function() { 651 var _this = this, 652 div = document.createElement('div'), 653 lvl = 0; 654 655 // Push sublings 656 function createList(items) { 657 var ul = document.createElement('ul'); 658 659 ul.setAttribute('style', 'list-style: none; margin: 0; padding: 0;'); 660 661 // Walkthrough items 662 for (var i = 0; i < items.length; i++) { 663 var li = document.createElement('li'), 664 img = document.createElement('img'), 665 a = document.createElement('a'), 666 item = items[i], 667 children = null, 668 itemStyle = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemStyle), items[i]); 669 670 // MERGE CFG 671 item = AmCharts.extend(AmCharts.extend({}, _this.cfg.menuItemOutput), item); 672 673 // ICON 674 if (item['icon']) { 675 img.alt = ''; 676 img.src = item['icon']; 677 img.setAttribute('style', 'margin: 0 auto;border: none;outline: none'); 678 if (item['iconTitle']) { 679 img.title = item['iconTitle']; 680 } 681 a.appendChild(img); 682 } 683 684 // TITLE; STYLING 685 a.href = '#'; 686 if (item['title']) { 687 img.setAttribute('style', 'margin-right: 5px;'); 688 a.innerHTML += item.title; 689 } 690 a.setAttribute('style', 'display: block;'); 691 AmCharts.extend(a.style, itemStyle); 692 693 // ONCLICK 694 a.onclick = item.onclick.bind(a, _this, item); 695 li.appendChild(a); 696 697 // APPEND SIBLINGS 698 if (item.items) { 699 children = createList(item.items); 700 li.appendChild(children); 701 702 li.onmouseover = function() { 703 children.style.display = 'block'; 704 }; 705 li.onmouseout = function() { 706 children.style.display = 'none'; 707 }; 708 children.style.display = 'none'; 709 } 710 711 // Append to parent 712 ul.appendChild(li); 713 714 // Apply hover 715 a.onmouseover = function() { 716 this.style.backgroundColor = itemStyle.rollOverBackgroundColor; 717 this.style.color = itemStyle.rollOverColor; 718 this.style.borderColor = itemStyle.rollOverBorderColor; 719 }; 720 a.onmouseout = function() { 721 this.style.backgroundColor = itemStyle.backgroundColor; 722 this.style.color = itemStyle.color; 723 this.style.borderColor = itemStyle.borderColor; 724 }; 725 } 726 lvl++; 727 728 if (_this.DEBUG == 10) { 729 _this.log('MENU', ul); 730 } // DEBUG 731 732 return ul; 733 } 734 735 // Style wrapper; Push into chart div 736 div.setAttribute('style', 'width:39px; height:28px; 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);'); 737 div.setAttribute('class', 'amExportButton'); 738 div.appendChild(createList(_this.cfg.menuItems)); 739 //_this.chart.div.style.position = 'relative'; 740 _this.chart.containerDiv.appendChild(div); 741 } 742});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.