1/** 2 * @preserve tableExport.jquery.plugin 3 * 4 * Copyright (c) 2015,2016 hhurz, https://github.com/hhurz/tableExport.jquery.plugin 5 * Original work Copyright (c) 2014 Giri Raj, https://github.com/kayalshri/ 6 * 7 * Licensed under the MIT License, http://opensource.org/licenses/mit-license 8 */ 9 10(function ($) { 11 $.fn.extend({ 12 tableExport: function (options) { 13 var defaults = { 14 consoleLog: false, 15 csvEnclosure: '"', 16 csvSeparator: ',', 17 csvUseBOM: true, 18 displayTableName: false, 19 escape: false, 20 excelstyles: [], // e.g. ['border-bottom', 'border-top', 'border-left', 'border-right'] 21 fileName: 'tableExport', 22 htmlContent: false, 23 ignoreColumn: [], 24 ignoreRow:[], 25 jsonScope: 'all', // head, data, all 26 jspdf: {orientation: 'p', 27 unit: 'pt', 28 format: 'a4', // jspdf page format or 'bestfit' for autmatic paper format selection 29 margins: {left: 20, right: 10, top: 10, bottom: 10}, 30 autotable: {styles: {cellPadding: 2, 31 rowHeight: 12, 32 fontSize: 8, 33 fillColor: 255, // color value or 'inherit' to use css background-color from html table 34 textColor: 50, // color value or 'inherit' to use css color from html table 35 fontStyle: 'normal', // normal, bold, italic, bolditalic or 'inherit' to use css font-weight and fonst-style from html table 36 overflow: 'ellipsize', // visible, hidden, ellipsize or linebreak 37 halign: 'left', // left, center, right 38 valign: 'middle' // top, middle, bottom 39 }, 40 headerStyles: {fillColor: [52, 73, 94], 41 textColor: 255, 42 fontStyle: 'bold', 43 halign: 'center' 44 }, 45 alternateRowStyles: {fillColor: 245 46 }, 47 tableExport: {onAfterAutotable: null, 48 onBeforeAutotable: null, 49 onTable: null 50 } 51 } 52 }, 53 numbers: {html: {decimalMark: '.', 54 thousandsSeparator: ',' 55 }, 56 output: {decimalMark: '.', 57 thousandsSeparator: ',' 58 } 59 }, 60 onCellData: null, 61 onCellHtmlData: null, 62 outputMode: 'file', // 'file', 'string' or 'base64' 63 tbodySelector: 'tr', 64 theadSelector: 'tr', 65 tableName: 'myTableName', 66 type: 'csv', // 'csv', 'txt', 'sql', 'json', 'xml', 'excel', 'doc', 'png' or 'pdf' 67 worksheetName: 'xlsWorksheetName' 68 }; 69 70 var FONT_ROW_RATIO = 1.15; 71 var el = this; 72 var DownloadEvt = null; 73 var $hrows = []; 74 var $rows = []; 75 var rowIndex = 0; 76 var rowspans = []; 77 var trData = ''; 78 var colNames = []; 79 80 $.extend(true, defaults, options); 81 82 colNames = GetColumnNames (el); 83 84 if (defaults.type == 'csv' || defaults.type == 'txt') { 85 86 var csvData = ""; 87 var rowlength = 0; 88 rowIndex = 0; 89 90 function CollectCsvData (tgroup, tselector, rowselector, length) { 91 92 $rows = $(el).find(tgroup).first().find(tselector); 93 $rows.each(function () { 94 trData = ""; 95 ForEachVisibleCell(this, rowselector, rowIndex, length + $rows.length, 96 function (cell, row, col) { 97 trData += csvString(cell, row, col) + defaults.csvSeparator; 98 }); 99 trData = $.trim(trData).substring(0, trData.length - 1); 100 if (trData.length > 0) { 101 102 if (csvData.length > 0) 103 csvData += "\n"; 104 105 csvData += trData; 106 } 107 rowIndex++; 108 }); 109 110 return $rows.length; 111 } 112 113 rowlength += CollectCsvData ('thead', defaults.theadSelector, 'th,td', rowlength); 114 rowlength += CollectCsvData ('tbody', defaults.tbodySelector, 'td', rowlength); 115 CollectCsvData ('tfoot', defaults.tbodySelector, 'td', rowlength); 116 117 csvData += "\n"; 118 119 //output 120 if (defaults.consoleLog === true) 121 console.log(csvData); 122 123 if (defaults.outputMode === 'string') 124 return csvData; 125 126 if (defaults.outputMode === 'base64') 127 return base64encode(csvData); 128 129 try { 130 var blob = new Blob([csvData], {type: "text/" + (defaults.type == 'csv' ? 'csv' : 'plain') + ";charset=utf-8"}); 131 saveAs(blob, defaults.fileName + '.' + defaults.type, (defaults.type != 'csv' || defaults.csvUseBOM === false)); 132 } 133 catch (e) { 134 downloadFile(defaults.fileName + '.' + defaults.type, 135 'data:text/' + (defaults.type == 'csv' ? 'csv' : 'plain') + ';charset=utf-8,' + ((defaults.type == 'csv' && defaults.csvUseBOM)? '\ufeff' : ''), 136 csvData); 137 } 138 139 } else if (defaults.type == 'sql') { 140 141 // Header 142 rowIndex = 0; 143 var tdData = "INSERT INTO `" + defaults.tableName + "` (";
144 $hrows = $(el).find('thead').first().find(defaults.theadSelector); 145 $hrows.each(function () { 146 ForEachVisibleCell(this, 'th,td', rowIndex, $hrows.length, 147 function (cell, row, col) { 148 tdData += "'" + parseString(cell, row, col) + "',"; 149 }); 150 rowIndex++; 151 tdData = $.trim(tdData); 152 tdData = $.trim(tdData).substring(0, tdData.length - 1); 153 }); 154 tdData += ") VALUES "; 155 // Row vs Column 156 $rows = $(el).find('tbody').first().find(defaults.tbodySelector); 157 $rows.each(function () { 158 trData = ""; 159 ForEachVisibleCell(this, 'td', rowIndex, $hrows.length + $rows.length, 160 function (cell, row, col) { 161 trData += "'" + parseString(cell, row, col) + "',"; 162 }); 163 if (trData.length > 3) { 164 tdData += "(" + trData; 165 tdData = $.trim(tdData).substring(0, tdData.length - 1); 166 tdData += "),"; 167 } 168 rowIndex++; 169 }); 170 171 tdData = $.trim(tdData).substring(0, tdData.length - 1); 172 tdData += ";"; 173 174 //output 175 if (defaults.consoleLog === true) 176 console.log(tdData); 177 178 if (defaults.outputMode === 'string') 179 return tdData; 180 181 if (defaults.outputMode === 'base64') 182 return base64encode(tdData); 183 184 try { 185 var blob = new Blob([tdData], {type: "text/plain;charset=utf-8"}); 186 saveAs(blob, defaults.fileName + '.sql'); 187 } 188 catch (e) { 189 downloadFile(defaults.fileName + '.sql', 190 'data:application/sql;charset=utf-8,', 191 tdData); 192 } 193 194 } else if (defaults.type == 'json') { 195 196 var jsonHeaderArray = []; 197 $hrows = $(el).find('thead').first().find(defaults.theadSelector); 198 $hrows.each(function () { 199 var jsonArrayTd = []; 200 201 ForEachVisibleCell(this, 'th,td', rowIndex, $hrows.length, 202 function (cell, row, col) { 203 jsonArrayTd.push(parseString(cell, row, col)); 204 }); 205 jsonHeaderArray.push(jsonArrayTd); 206 }); 207 208 var jsonArray = []; 209 $rows = $(el).find('tbody').first().find(defaults.tbodySelector); 210 $rows.each(function () { 211 var jsonObjectTd = {}; 212 213 var colIndex = 0; 214 ForEachVisibleCell(this, 'td', rowIndex, $hrows.length + $rows.length, 215 function (cell, row, col) { 216 if (jsonHeaderArray.length) { 217 jsonObjectTd[jsonHeaderArray[jsonHeaderArray.length-1][colIndex]] = parseString(cell, row, col); 218 } else { 219 jsonObjectTd[colIndex] = parseString(cell, row, col); 220 } 221 colIndex++; 222 }); 223 if ($.isEmptyObject(jsonObjectTd) == false) 224 jsonArray.push(jsonObjectTd); 225 226 rowIndex++; 227 }); 228 229 var sdata = ""; 230 231 if (defaults.jsonScope == 'head') 232 sdata = JSON.stringify(jsonHeaderArray); 233 else if (defaults.jsonScope == 'data') 234 sdata = JSON.stringify(jsonArray); 235 else // all 236 sdata = JSON.stringify({header: jsonHeaderArray, data: jsonArray}); 237 238 if (defaults.consoleLog === true) 239 console.log(sdata); 240 241 if (defaults.outputMode === 'string') 242 return sdata; 243 244 if (defaults.outputMode === 'base64') 245 return base64encode(sdata); 246 247 try { 248 var blob = new Blob([sdata], {type: "application/json;charset=utf-8"}); 249 saveAs(blob, defaults.fileName + '.json'); 250 } 251 catch (e) { 252 downloadFile(defaults.fileName + '.json', 253 'data:application/json;charset=utf-8;base64,', 254 sdata); 255 } 256 257 } else if (defaults.type === 'xml') { 258 259 rowIndex = 0; 260 var xml = '<?xml version="1.0" encoding="utf-8"?>'; 261 xml += '<tabledata><fields>'; 262 263 // Header
264 $hrows = $(el).find('thead').first().find(defaults.theadSelector); 265 $hrows.each(function () { 266 267 ForEachVisibleCell(this, 'th,td', rowIndex, $rows.length, 268 function (cell, row, col) { 269 xml += "<field>" + parseString(cell, row, col) + "</field>"; 270 }); 271 rowIndex++; 272 }); 273 xml += '</fields><data>'; 274 275 // Row Vs Column 276 var rowCount = 1; 277 $rows = $(el).find('tbody').first().find(defaults.tbodySelector); 278 $rows.each(function () { 279 var colCount = 1; 280 trData = ""; 281 ForEachVisibleCell(this, 'td', rowIndex, $hrows.length + $rows.length, 282 function (cell, row, col) { 283 trData += "<column-" + colCount + ">" + parseString(cell, row, col) + "</column-" + colCount + ">"; 284 colCount++; 285 }); 286 if (trData.length > 0 && trData != "<column-1></column-1>") { 287 xml += '<row id="' + rowCount + '">' + trData + '</row>'; 288 rowCount++; 289 } 290 291 rowIndex++; 292 }); 293 xml += '</data></tabledata>'; 294 295 //output 296 if (defaults.consoleLog === true) 297 console.log(xml); 298 299 if (defaults.outputMode === 'string') 300 return xml; 301 302 if (defaults.outputMode === 'base64') 303 return base64encode(xml); 304 305 try { 306 var blob = new Blob([xml], {type: "application/xml;charset=utf-8"}); 307 saveAs(blob, defaults.fileName + '.xml'); 308 } 309 catch (e) { 310 downloadFile(defaults.fileName + '.xml', 311 'data:application/xml;charset=utf-8;base64,', 312 xml); 313 } 314 315 } else if (defaults.type == 'excel' || defaults.type == 'xls' || defaults.type == 'word' || defaults.type == 'doc') { 316 317 var MSDocType = (defaults.type == 'excel' || defaults.type == 'xls') ? 'excel' : 'word'; 318 var MSDocExt = (MSDocType == 'excel') ? 'xls' : 'doc'; 319 var MSDocSchema = (MSDocExt == 'xls') ? 'xmlns:x="urn:schemas-microsoft-com:office:excel"' : 'xmlns:w="urn:schemas-microsoft-com:office:word"'; 320 var $tables = $(el).filter(function() { 321 return $(this).data("tableexport-display") != 'none' && 322 ($(this).is(':visible') || 323 $(this).data("tableexport-display") == 'always'); 324 }); 325 var docData = ''; 326 327 $tables.each(function(){ 328 rowIndex = 0; 329 330 colNames = GetColumnNames (this); 331 332 docData += '<table><thead>'; 333 // Header 334 $hrows = $(this).find('thead').first().find(defaults.theadSelector); 335 $hrows.each(function() { 336 trData = ""; 337 ForEachVisibleCell(this, 'th,td', rowIndex, $hrows.length, 338 function(cell, row, col) { 339 if (cell != null) { 340 var thstyle = ''; 341 trData += '<th'; 342 for (var styles in defaults.excelstyles) { 343 if (defaults.excelstyles.hasOwnProperty(styles)) { 344 var thcss = $(cell).css(defaults.excelstyles[styles]); 345 if (thcss != '' && thcss !='0px none rgb(0, 0, 0)') { 346 if (thstyle == '') 347 thstyle = 'style="'; 348 thstyle += defaults.excelstyles[styles] + ':' + thcss + ';'; 349 } 350 } 351 } 352 if (thstyle != '' ) 353 trData += ' ' + thstyle + '"'; 354 if ($(cell).is("[colspan]")) 355 trData += ' colspan="' + $(cell).attr('colspan') + '"'; 356 if ($(cell).is("[rowspan]")) 357 trData += ' rowspan="' + $(cell).attr('rowspan') + '"'; 358 trData += '>' + parseString(cell, row, col) + '</th>'; 359 } 360 }); 361 if (trData.length > 0) 362 docData += '<tr>' + trData + '</tr>'; 363 rowIndex++; 364 }); 365 366 docData += '</thead><tbody>'; 367 // Row Vs Column 368 $rows = $(this).find('tbody').first().find(defaults.tbodySelector); 369 $rows.each(function() { 370 trData = ""; 371 ForEachVisibleCell(this, 'td', rowIndex, $hrows.length + $rows.length, 372 function(cell, row, col) { 373 if (cell != null) { 374 var tdstyle = ''; 375 var tdcss = $(cell).data("tableexport-msonumberformat"); 376 377 if (typeof tdcss == 'undefined' && typeof defaults.onMsoNumberFormat === 'function') 378 tdcss = defaults.onMsoNumberFormat(cell, row, col); 379 380 if (typeof tdcss != 'undefined' && tdcss != '') { 381 if (tdstyle == '') 382 tdstyle = 'style="'; 383 tdstyle = 'style="mso-number-format:' + tdcss + ';'; 384 } 385 386 trData += '<td'; 387 for (var styles in defaults.excelstyles) { 388 if (defaults.excelstyles.hasOwnProperty(styles)) { 389 tdcss = $(cell).css(defaults.excelstyles[styles]); 390 if (tdcss != '' && tdcss !='0px none rgb(0, 0, 0)') { 391 if (tdstyle == '') 392 tdstyle = 'style="'; 393 tdstyle += defaults.excelstyles[styles] + ':' + tdcss + ';'; 394 } 395 } 396 } 397 if (tdstyle != '' ) 398 trData += ' ' + tdstyle + '"'; 399 if ($(cell).is("[colspan]")) 400 trData += ' colspan="' + $(cell).attr('colspan') + '"'; 401 if ($(cell).is("[rowspan]")) 402 trData += ' rowspan="' + $(cell).attr('rowspan') + '"'; 403 trData += '>' + parseString(cell, row, col) + '</td>'; 404 } 405 }); 406 if (trData.length > 0) 407 docData += '<tr>' + trData + '</tr>'; 408 rowIndex++; 409 }); 410 411 if (defaults.displayTableName) 412 docData += '<tr><td></td></tr><tr><td></td></tr><tr><td>' + parseString($('<p>' + defaults.tableName + '</p>')) + '</td></tr>'; 413 414 docData += '</tbody></table>'; 415 416 if (defaults.consoleLog === true) 417 console.log(docData); 418 }); 419
420 var docFile = '<html xmlns:o="urn:schemas-microsoft-com:office:office" ' + MSDocSchema + ' xmlns="http://www.w3.org/TR/REC-html40">'; 421 docFile += '<meta http-equiv="content-type" content="application/vnd.ms-' + MSDocType + '; charset=UTF-8">'; 422 docFile += "<head>"; 423 if (MSDocType === 'excel') { 424 docFile += "<!--[if gte mso 9]>"; 425 docFile += "<xml>"; 426 docFile += "<x:ExcelWorkbook>"; 427 docFile += "<x:ExcelWorksheets>"; 428 docFile += "<x:ExcelWorksheet>"; 429 docFile += "<x:Name>"; 430 docFile += defaults.worksheetName; 431 docFile += "</x:Name>"; 432 docFile += "<x:WorksheetOptions>"; 433 docFile += "<x:DisplayGridlines/>"; 434 docFile += "</x:WorksheetOptions>"; 435 docFile += "</x:ExcelWorksheet>"; 436 docFile += "</x:ExcelWorksheets>"; 437 docFile += "</x:ExcelWorkbook>"; 438 docFile += "</xml>"; 439 docFile += "<![endif]-->"; 440 } 441 docFile += "</head>"; 442 docFile += "<body>"; 443 docFile += docData; 444 docFile += "</body>"; 445 docFile += "</html>"; 446 447 if (defaults.consoleLog === true) 448 console.log(docFile); 449 450 if (defaults.outputMode === 'string') 451 return docFile; 452 453 if (defaults.outputMode === 'base64') 454 return base64encode(docFile); 455 456 try { 457 var blob = new Blob([docFile], {type: 'application/vnd.ms-' + defaults.type}); 458 saveAs(blob, defaults.fileName + '.' + MSDocExt); 459 } 460 catch (e) { 461 downloadFile(defaults.fileName + '.' + MSDocExt, 462 'data:application/vnd.ms-' + MSDocType + ';base64,', 463 docFile); 464 } 465 466 } else if (defaults.type == 'png') { 467 html2canvas($(el)[0], { 468 allowTaint: true, 469 background: '#fff', 470 onrendered: function (canvas) { 471 472 var image = canvas.toDataURL(); 473 image = image.substring(22); // remove data stuff 474 475 var byteString = atob(image); 476 var buffer = new ArrayBuffer(byteString.length); 477 var intArray = new Uint8Array(buffer); 478 479 for (var i = 0; i < byteString.length; i++) 480 intArray[i] = byteString.charCodeAt(i); 481 482 if (defaults.consoleLog === true) 483 console.log(byteString); 484 485 if (defaults.outputMode === 'string') 486 return byteString; 487 488 if (defaults.outputMode === 'base64') 489 return base64encode(image); 490 491 try { 492 var blob = new Blob([buffer], {type: "image/png"}); 493 saveAs(blob, defaults.fileName + '.png'); 494 } 495 catch (e) { 496 downloadFile(defaults.fileName + '.png', 497 'data:image/png;base64,', 498 image); 499 } 500 } 501 }); 502 503 } else if (defaults.type == 'pdf') { 504 if (defaults.jspdf.autotable === false) { 505 var addHtmlOptions = { 506 dim: { 507 w: getPropertyUnitValue($(el).first().get(0), 'width', 'mm'), 508 h: getPropertyUnitValue($(el).first().get(0), 'height', 'mm') 509 }, 510 pagesplit: false 511 }; 512 513 var doc = new jsPDF(defaults.jspdf.orientation, defaults.jspdf.unit, defaults.jspdf.format); 514 doc.addHTML($(el).first(), 515 defaults.jspdf.margins.left, 516 defaults.jspdf.margins.top, 517 addHtmlOptions, 518 function () { 519 jsPdfOutput(doc); 520 }); 521 //delete doc; 522 } 523 else { 524 // pdf output using jsPDF AutoTable plugin 525 // https://github.com/simonbengtsson/jsPDF-AutoTable 526 527 var teOptions = defaults.jspdf.autotable.tableExport; 528 529 // When setting jspdf.format to 'bestfit' tableExport tries to choose 530 // the minimum required paper format and orientation in which the table 531 // (or tables in multitable mode) completely fits without column adjustment 532 if (typeof defaults.jspdf.format === 'string' && defaults.jspdf.format.toLowerCase() === 'bestfit') { 533 var pageFormats = { 534 'a0': [2383.94, 3370.39], 'a1': [1683.78, 2383.94], 535 'a2': [1190.55, 1683.78], 'a3': [841.89, 1190.55], 536 'a4': [595.28, 841.89] 537 }; 538 var rk = '', ro = ''; 539 var mw = 0; 540 541 $(el).filter(':visible').each(function () { 542 if ($(this).css('display') != 'none') { 543 var w = getPropertyUnitValue($(this).get(0), 'width', 'pt'); 544 545 if (w > mw) { 546 if (w > pageFormats['a0'][0]) { 547 rk = 'a0'; 548 ro = 'l'; 549 } 550 for (var key in pageFormats) { 551 if (pageFormats.hasOwnProperty(key)) { 552 if (pageFormats[key][1] > w) { 553 rk = key; 554 ro = 'l'; 555 if (pageFormats[key][0] > w) 556 ro = 'p'; 557 } 558 } 559 } 560 mw = w; 561 } 562 } 563 }); 564 defaults.jspdf.format = (rk == '' ? 'a4' : rk); 565 defaults.jspdf.orientation = (ro == '' ? 'w' : ro); 566 } 567 568 // The jsPDF doc object is stored in defaults.jspdf.autotable.tableExport, 569 // thus it can be accessed from any callback function 570 teOptions.doc = new jsPDF(defaults.jspdf.orientation, 571 defaults.jspdf.unit, 572 defaults.jspdf.format); 573 574 $(el).filter(function() { 575 return $(this).data("tableexport-display") != 'none' && 576 ($(this).is(':visible') || 577 $(this).data("tableexport-display") == 'always'); 578 }).each(function () { 579 var colKey; 580 var rowIndex = 0; 581 582 colNames = GetColumnNames (this); 583 584 teOptions.columns = []; 585 teOptions.rows = []; 586 teOptions.rowoptions = {}; 587 588 // onTable: optional callback function for every matching table that can be used 589 // to modify the tableExport options or to skip the output of a particular table 590 // if the table selector targets multiple tables 591 if (typeof teOptions.onTable === 'function') 592 if (teOptions.onTable($(this), defaults) === false) 593 return true; // continue to next iteration step (table) 594 595 // each table works with an own copy of AutoTable options 596 defaults.jspdf.autotable.tableExport = null; // avoid deep recursion error 597 var atOptions = $.extend(true, {}, defaults.jspdf.autotable); 598 defaults.jspdf.autotable.tableExport = teOptions; 599 600 atOptions.margin = {}; 601 $.extend(true, atOptions.margin, defaults.jspdf.margins); 602 atOptions.tableExport = teOptions; 603 604 // Fix jsPDF Autotable's row height calculation 605 if (typeof atOptions.beforePageContent !== 'function') { 606 atOptions.beforePageContent = function (data) { 607 if (data.pageCount == 1) { 608 var all = data.table.rows.concat(data.table.headerRow); 609 all.forEach(function (row) { 610 if ( row.height > 0 ) { 611 row.height += (2 - FONT_ROW_RATIO) / 2 * row.styles.fontSize; 612 data.table.height += (2 - FONT_ROW_RATIO) / 2 * row.styles.fontSize; 613 } 614 }); 615 } 616 } 617 } 618 619 if (typeof atOptions.createdHeaderCell !== 'function') { 620 // apply some original css styles to pdf header cells 621 atOptions.createdHeaderCell = function (cell, data) { 622 623 // jsPDF AutoTable plugin v2.0.14 fix: each cell needs its own styles object 624 cell.styles = $.extend({}, data.row.styles); 625 626 if (typeof teOptions.columns [data.column.dataKey] != 'undefined') { 627 var col = teOptions.columns [data.column.dataKey]; 628 629 if (typeof col.rect != 'undefined') { 630 var rh; 631 632 cell.contentWidth = col.rect.width; 633 634 if (typeof teOptions.heightRatio == 'undefined' || teOptions.heightRatio == 0) { 635 if (data.row.raw [data.column.dataKey].rowspan) 636 rh = data.row.raw [data.column.dataKey].rect.height / data.row.raw [data.column.dataKey].rowspan; 637 else 638 rh = data.row.raw [data.column.dataKey].rect.height; 639 640 teOptions.heightRatio = cell.styles.rowHeight / rh; 641 } 642 643 rh = data.row.raw [data.column.dataKey].rect.height * teOptions.heightRatio; 644 if (rh > cell.styles.rowHeight) 645 cell.styles.rowHeight = rh; 646 } 647 648 if (typeof col.style != 'undefined' && col.style.hidden !== true) { 649 cell.styles.halign = col.style.align; 650 if (atOptions.styles.fillColor === 'inherit') 651 cell.styles.fillColor = col.style.bcolor; 652 if (atOptions.styles.textColor === 'inherit') 653 cell.styles.textColor = col.style.color; 654 if (atOptions.styles.fontStyle === 'inherit')
655 cell.styles.fontStyle = col.style.fstyle; 656 } 657 } 658 } 659 } 660 661 if (typeof atOptions.createdCell !== 'function') { 662 // apply some original css styles to pdf table cells 663 atOptions.createdCell = function (cell, data) { 664 var rowopt = teOptions.rowoptions [data.row.index + ":" + data.column.dataKey]; 665 666 if (typeof rowopt != 'undefined' && 667 typeof rowopt.style != 'undefined' && 668 rowopt.style.hidden !== true) { 669 cell.styles.halign = rowopt.style.align; 670 if (atOptions.styles.fillColor === 'inherit') 671 cell.styles.fillColor = rowopt.style.bcolor; 672 if (atOptions.styles.textColor === 'inherit') 673 cell.styles.textColor = rowopt.style.color; 674 if (atOptions.styles.fontStyle === 'inherit') 675 cell.styles.fontStyle = rowopt.style.fstyle; 676 } 677 } 678 } 679 680 if (typeof atOptions.drawHeaderCell !== 'function') { 681 atOptions.drawHeaderCell = function (cell, data) { 682 var colopt = teOptions.columns [data.column.dataKey]; 683 684 if ((colopt.style.hasOwnProperty("hidden") != true || colopt.style.hidden !== true) && 685 colopt.rowIndex >= 0 ) 686 return prepareAutoTableText (cell, data, colopt); 687 else 688 return false; // cell is hidden 689 } 690 } 691 692 if (typeof atOptions.drawCell !== 'function') { 693 atOptions.drawCell = function (cell, data) { 694 var rowopt = teOptions.rowoptions [data.row.index + ":" + data.column.dataKey]; 695 if ( prepareAutoTableText (cell, data, rowopt) ) { 696 697 teOptions.doc.rect(cell.x, cell.y, cell.width, cell.height, cell.styles.fillStyle); 698 699 if (typeof rowopt != 'undefined' && typeof rowopt.kids != 'undefined' && rowopt.kids.length > 0) { 700 701 var dh = cell.height / rowopt.rect.height; 702 if ( dh > teOptions.dh || typeof teOptions.dh == 'undefined' ) 703 teOptions.dh = dh; 704 teOptions.dw = cell.width / rowopt.rect.width; 705 706 drawCellElements (cell, rowopt.kids, teOptions); 707 } 708 teOptions.doc.autoTableText(cell.text, cell.textPos.x, cell.textPos.y, { 709 halign: cell.styles.halign, 710 valign: cell.styles.valign 711 }); 712 } 713 return false; 714 } 715 } 716 717 // collect header and data rows 718 teOptions.headerrows = []; 719 $hrows = $(this).find('thead').find(defaults.theadSelector); 720 $hrows.each(function () { 721 colKey = 0; 722 723 teOptions.headerrows[rowIndex] = []; 724 725 ForEachVisibleCell(this, 'th,td', rowIndex, $hrows.length, 726 function (cell, row, col) { 727 var obj = getCellStyles (cell); 728 obj.title = parseString(cell, row, col); 729 obj.key = colKey++; 730 obj.rowIndex = rowIndex; 731 teOptions.headerrows[rowIndex].push(obj); 732 }); 733 rowIndex++; 734 }); 735 736 if (rowIndex > 0) { 737 // iterate through last row 738 $.each(teOptions.headerrows[rowIndex-1], function () { 739 if (rowIndex > 1 && this.rect == null) 740 obj = teOptions.headerrows[rowIndex-2][this.key]; 741 else 742 obj = this; 743 744 if (obj != null) 745 teOptions.columns.push(obj); 746 }); 747 } 748 749 var rowCount = 0; 750 $rows = $(this).find('tbody').find(defaults.tbodySelector); 751 $rows.each(function () { 752 var rowData = []; 753 colKey = 0; 754 755 ForEachVisibleCell(this, 'td', rowIndex, $hrows.length + $rows.length, 756 function (cell, row, col) { 757 if (typeof teOptions.columns[colKey] === 'undefined') { 758 // jsPDF-Autotable needs columns. Thus define hidden ones for tables without thead 759 var obj = { 760 title: '', 761 key: colKey, 762 style: { 763 hidden: true 764 } 765 }; 766 teOptions.columns.push(obj); 767 } 768 if (typeof cell !== 'undefined' && cell != null) { 769 var obj = getCellStyles (cell); 770 obj.kids = $(cell).children();
771 teOptions.rowoptions [rowCount + ":" + colKey++] = obj; 772 } 773 else { 774 var obj = $.extend(true, {}, teOptions.rowoptions [rowCount + ":" + (colKey-1)]); 775 obj.colspan = -1; 776 teOptions.rowoptions [rowCount + ":" + colKey++] = obj; 777 } 778 779 rowData.push(parseString(cell, row, col)); 780 }); 781 if (rowData.length) { 782 teOptions.rows.push(rowData); 783 rowCount++ 784 } 785 rowIndex++; 786 }); 787 788 // onBeforeAutotable: optional callback function before calling 789 // jsPDF AutoTable that can be used to modify the AutoTable options 790 if (typeof teOptions.onBeforeAutotable === 'function') 791 teOptions.onBeforeAutotable($(this), teOptions.columns, teOptions.rows, atOptions); 792 793 teOptions.doc.autoTable(teOptions.columns, teOptions.rows, atOptions); 794 795 // onAfterAutotable: optional callback function after returning 796 // from jsPDF AutoTable that can be used to modify the AutoTable options 797 if (typeof teOptions.onAfterAutotable === 'function') 798 teOptions.onAfterAutotable($(this), atOptions); 799 800 // set the start position for the next table (in case there is one) 801 defaults.jspdf.autotable.startY = teOptions.doc.autoTableEndPosY() + atOptions.margin.top; 802 }); 803 804 jsPdfOutput(teOptions.doc); 805 806 if (typeof teOptions.headerrows != 'undefined') 807 teOptions.headerrows.length = 0; 808 if (typeof teOptions.columns != 'undefined') 809 teOptions.columns.length = 0; 810 if (typeof teOptions.rows != 'undefined') 811 teOptions.rows.length = 0; 812 delete teOptions.doc; 813 teOptions.doc = null; 814 } 815 } 816 817 function FindColObject (objects, colIndex, rowIndex) { 818 var result = null; 819 $.each(objects, function () { 820 if (this.rowIndex == rowIndex && this.key == colIndex) { 821 result = this; 822 return false; 823 } 824 }); 825 return result; 826 } 827 828 function GetColumnNames (table) { 829 var result = []; 830 $(table).find('thead').first().find('th').each(function(index, el) { 831 if ($(el).attr("data-field") !== undefined) 832 result[index] = $(el).attr("data-field"); 833 }); 834 return result; 835 } 836 837 function isColumnIgnored($row, colIndex) { 838 var result = false; 839 if (defaults.ignoreColumn.length > 0) { 840 if (typeof defaults.ignoreColumn[0] == 'string') { 841 if (colNames.length > colIndex && typeof colNames[colIndex] != 'undefined') 842 if ($.inArray(colNames[colIndex], defaults.ignoreColumn) != -1) 843 result = true; 844 } 845 else if (typeof defaults.ignoreColumn[0] == 'number') { 846 if ($.inArray(colIndex, defaults.ignoreColumn) != -1 || 847 $.inArray(colIndex-$row.length, defaults.ignoreColumn) != -1) 848 result = true; 849 } 850 } 851 return result; 852 } 853 854 function ForEachVisibleCell(tableRow, selector, rowIndex, rowCount, cellcallback) { 855 if ($.inArray(rowIndex, defaults.ignoreRow) == -1 && 856 $.inArray(rowIndex-rowCount, defaults.ignoreRow) == -1) { 857 858 var $row = $(tableRow).filter(function() { 859 return $(this).data("tableexport-display") != 'none' && 860 ($(this).is(':visible') || 861 $(this).data("tableexport-display") == 'always' || 862 $(this).closest('table').data("tableexport-display") == 'always'); 863 }).find(selector); 864 865 var rowColspan = 0; 866 var rowColIndex = 0; 867 868 $row.each(function (colIndex) { 869 if ($(this).data("tableexport-display") == 'always' || 870 ($(this).css('display') != 'none' && 871 $(this).css('visibility') != 'hidden' && 872 $(this).data("tableexport-display") != 'none')) {
873 if (isColumnIgnored($row, colIndex) == false) { 874 if (typeof (cellcallback) === "function") { 875 var c, Colspan = 0; 876 var r, Rowspan = 0; 877 878 // handle rowspans from previous rows 879 if (typeof rowspans[rowIndex] != 'undefined' && rowspans[rowIndex].length > 0) { 880 for (c = 0; c <= colIndex; c++) { 881 if (typeof rowspans[rowIndex][c] != 'undefined') { 882 cellcallback(null, rowIndex, c); 883 delete rowspans[rowIndex][c]; 884 colIndex++; 885 } 886 } 887 } 888 rowColIndex = colIndex; 889 890 if ($(this).is("[colspan]")) { 891 Colspan = parseInt($(this).attr('colspan')); 892 rowColspan += Colspan > 0 ? Colspan - 1 : 0; 893 } 894 895 if ($(this).is("[rowspan]")) 896 Rowspan = parseInt($(this).attr('rowspan')); 897 898 // output content of current cell 899 cellcallback(this, rowIndex, colIndex); 900 901 // handle colspan of current cell 902 for (c = 0; c < Colspan - 1; c++) 903 cellcallback(null, rowIndex, colIndex + c); 904 905 // store rowspan for following rows 906 if (Rowspan) { 907 for (r = 1; r < Rowspan; r++) { 908 if (typeof rowspans[rowIndex + r] == 'undefined') 909 rowspans[rowIndex + r] = []; 910 911 rowspans[rowIndex + r][colIndex + rowColspan] = ""; 912 913 for (c = 1; c < Colspan; c++) 914 rowspans[rowIndex + r][colIndex + rowColspan - c] = ""; 915 } 916 } 917 } 918 } 919 } 920 }); 921 // handle rowspans from previous rows 922 if (typeof rowspans[rowIndex] != 'undefined' && rowspans[rowIndex].length > 0) { 923 for (c = 0; c <= rowspans[rowIndex].length; c++) { 924 if (typeof rowspans[rowIndex][c] != 'undefined') { 925 cellcallback(null, rowIndex, c); 926 delete rowspans[rowIndex][c]; 927 } 928 } 929 } 930 } 931 } 932 933 function jsPdfOutput(doc) { 934 if (defaults.consoleLog === true) 935 console.log(doc.output()); 936 937 if (defaults.outputMode === 'string') 938 return doc.output(); 939 940 if (defaults.outputMode === 'base64') 941 return base64encode(doc.output()); 942 943 try { 944 var blob = doc.output('blob'); 945 saveAs(blob, defaults.fileName + '.pdf'); 946 } 947 catch (e) { 948 downloadFile(defaults.fileName + '.pdf', 949 'data:application/pdf;base64,', 950 doc.output()); 951 } 952 } 953 954 function prepareAutoTableText (cell, data, cellopt) { 955 var cs = 0; 956 if ( typeof cellopt != 'undefined' ) 957 cs = cellopt.colspan; 958 959 if ( cs >= 0 ) { 960 // colspan handling 961 var cellWidth = cell.width; 962 var textPosX = cell.textPos.x; 963 var i = data.table.columns.indexOf(data.column); 964 965 for (var c = 1; c < cs; c++) { 966 var column = data.table.columns[i+c]; 967 cellWidth += column.width; 968 } 969 970 if ( cs > 1 ) { 971 if ( cell.styles.halign === 'right' ) 972 textPosX = cell.textPos.x + cellWidth - cell.width; 973 else if ( cell.styles.halign === 'center' ) 974 textPosX = cell.textPos.x + (cellWidth - cell.width) / 2; 975 } 976 977 cell.width = cellWidth; 978 cell.textPos.x = textPosX; 979 980 if ( typeof cellopt != 'undefined' && cellopt.rowspan > 1 ) 981 cell.height = cell.height * cellopt.rowspan; 982 983 // fix jsPDF's calculation of text position 984 if ( cell.styles.valign === 'middle' || cell.styles.valign === 'bottom' ) { 985 var splittedText = typeof cell.text === 'string' ? cell.text.split(/\r\n|\r|\n/g) : cell.text; 986 var lineCount = splittedText.length || 1; 987 if (lineCount > 2) 988 cell.textPos.y -= ((2 - FONT_ROW_RATIO) / 2 * data.row.styles.fontSize) * (lineCount-2) / 3 ; 989 } 990 return true; 991 } 992 else 993 return false; // cell is hidden (colspan = -1), don't draw it 994 } 995 996 function drawCellElements (cell, elements, teOptions) { 997 elements.each(function () { 998 var kids = $(this).children();
999 1000 if ( $(this).is("div") ) { 1001 var bcolor = rgb2array(getStyle(this, 'background-color'), [255, 255, 255]); 1002 var lcolor = rgb2array(getStyle(this, 'border-top-color'), [0, 0, 0]); 1003 var lwidth = getPropertyUnitValue(this, 'border-top-width', defaults.jspdf.unit); 1004 1005 var r = this.getBoundingClientRect(); 1006 var ux = this.offsetLeft * teOptions.dw; 1007 var uy = this.offsetTop * teOptions.dh; 1008 var uw = r.width * teOptions.dw; 1009 var uh = r.height * teOptions.dh; 1010 1011 teOptions.doc.setDrawColor.apply (undefined, lcolor); 1012 teOptions.doc.setFillColor.apply (undefined, bcolor); 1013 teOptions.doc.setLineWidth (lwidth); 1014 teOptions.doc.rect(cell.x + ux, cell.y + uy, uw, uh, lwidth ? "FD" : "F"); 1015 } 1016 1017 if (typeof kids != 'undefined' && kids.length > 0) 1018 drawCellElements (cell, kids, teOptions); 1019 }); 1020 } 1021 1022 function escapeRegExp(string) { 1023 return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 1024 } 1025 1026 function replaceAll(string, find, replace) { 1027 return string.replace(new RegExp(escapeRegExp(find), 'g'), replace); 1028 } 1029 1030 // Takes a string and encapsulates it (by default in double-quotes) if it 1031 // contains the csv field separator, spaces, or linebreaks. 1032 function csvString(cell, rowIndex, colIndex) { 1033 var result = ''; 1034 1035 if (cell != null) { 1036 var dataString = parseString(cell, rowIndex, colIndex); 1037 1038 var csvValue = (dataString === null || dataString == '') ? '' : dataString.toString(); 1039 1040 if (dataString instanceof Date) 1041 result = defaults.csvEnclosure + dataString.toLocaleString() + defaults.csvEnclosure; 1042 else { 1043 result = replaceAll(csvValue, defaults.csvEnclosure, defaults.csvEnclosure + defaults.csvEnclosure); 1044 1045 if (result.indexOf(defaults.csvSeparator) >= 0 || /[\r\n ]/g.test(result)) 1046 result = defaults.csvEnclosure + result + defaults.csvEnclosure; 1047 } 1048 } 1049 1050 return result; 1051 } 1052 1053 function parseNumber(value) { 1054 value = value || "0"; 1055 value = replaceAll(value, defaults.numbers.html.decimalMark, '.'); 1056 value = replaceAll(value, defaults.numbers.html.thousandsSeparator, ''); 1057 1058 return typeof value === "number" || jQuery.isNumeric(value) !== false ? value : false; 1059 } 1060 1061 1062 function parseString(cell, rowIndex, colIndex) { 1063 var result = ''; 1064 1065 if (cell != null) { 1066 var $cell = $(cell); 1067 var htmlData; 1068 1069 if ($cell[0].hasAttribute("data-tableexport-value")) 1070 htmlData = $cell.data("tableexport-value"); 1071 else 1072 htmlData = $cell.html(); 1073 1074 if (typeof defaults.onCellHtmlData === 'function') 1075 htmlData = defaults.onCellHtmlData($cell, rowIndex, colIndex, htmlData); 1076 1077 if (defaults.htmlContent === true) { 1078 result = $.trim(htmlData); 1079 } 1080 else { 1081 var text = htmlData.replace(/\n/g,'\u2028').replace(/<br\s*[\/]?>/gi, '\u2060'); 1082 var obj = $('<div/>').html(text).contents(); 1083 text = ''; 1084 $.each(obj.text().split("\u2028"), function(i, v) { 1085 if (i > 0) 1086 text += " "; 1087 text += $.trim(v); 1088 }); 1089 1090 $.each(text.split("\u2060"), function(i, v) { 1091 if (i > 0) 1092 result += "\n"; 1093 result += $.trim(v).replace(/\u00AD/g, ""); // remove soft hyphens 1094 }); 1095 1096 if (defaults.numbers.html.decimalMark != defaults.numbers.output.decimalMark || 1097 defaults.numbers.html.thousandsSeparator != defaults.numbers.output.thousandsSeparator) { 1098 var number = parseNumber (result); 1099 1100 if ( number !== false ) { 1101 var frac = ("" + number).split('.'); 1102 if ( frac.length == 1 ) 1103 frac[1] = ""; 1104 var mod = frac[0].length > 3 ? frac[0].length % 3 : 0; 1105 1106 result = (number < 0 ? "-" : "") + 1107 (defaults.numbers.output.thousandsSeparator ? ((mod ? frac[0].substr(0, mod) + defaults.numbers.output.thousandsSeparator : "") + frac[0].substr(mod).replace(/(\d{3})(?=\d)/g, "$1" + defaults.numbers.output.thousandsSeparator)) : frac[0]) + 1108 (frac[1].length ? defaults.numbers.output.decimalMark + frac[1] : ""); 1109 } 1110 } 1111 } 1112 1113 if (defaults.escape === true) { 1114 result = escape(result); 1115 } 1116 1117 if (typeof defaults.onCellData === 'function') { 1118 result = defaults.onCellData($cell, rowIndex, colIndex, result); 1119 } 1120 } 1121 1122 return result; 1123 } 1124 1125 function hyphenate(a, b, c) { 1126 return b + "-" + c.toLowerCase(); 1127 } 1128 1129 function rgb2array(rgb_string, default_result) { 1130 var re = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/; 1131 var bits = re.exec(rgb_string); 1132 var result = default_result; 1133 if (bits) 1134 result = [ parseInt(bits[1]), parseInt(bits[2]), parseInt(bits[3]) ]; 1135 return result; 1136 } 1137 1138 function getCellStyles (cell) { 1139 var a = getStyle(cell, 'text-align'); 1140 var fw = getStyle(cell, 'font-weight'); 1141 var fs = getStyle(cell, 'font-style'); 1142 var f = ''; 1143 if (a == 'start') 1144 a = getStyle(cell, 'direction') == 'rtl' ? 'right' : 'left'; 1145 if (fw >= 700) 1146 f = 'bold'; 1147 if (fs == 'italic') 1148 f += fs; 1149 if (f == '') 1150 f = 'normal'; 1151 1152 var result = { 1153 style: { 1154 align: a, 1155 bcolor: rgb2array(getStyle(cell, 'background-color'), [255, 255, 255]), 1156 color: rgb2array(getStyle(cell, 'color'), [0, 0, 0]), 1157 fstyle: f 1158 }, 1159 colspan: (parseInt($(cell).attr('colspan')) || 0), 1160 rowspan: (parseInt($(cell).attr('rowspan')) || 0) 1161 }; 1162 1163 if (cell !== null) { 1164 var r = cell.getBoundingClientRect(); 1165 result.rect = { 1166 width: r.width, 1167 height: r.height 1168 }; 1169 } 1170 1171 return result; 1172 } 1173 1174 // get computed style property 1175 function getStyle(target, prop) { 1176 try { 1177 if (window.getComputedStyle) { // gecko and webkit 1178 prop = prop.replace(/([a-z])([A-Z])/, hyphenate); // requires hyphenated, not camel 1179 return window.getComputedStyle(target, null).getPropertyValue(prop); 1180 } 1181 if (target.currentStyle) { // ie 1182 return target.currentStyle[prop]; 1183 } 1184 return target.style[prop]; 1185 } 1186 catch (e) { 1187 } 1188 return ""; 1189 } 1190
1191 function getUnitValue(parent, value, unit) { 1192 var baseline = 100; // any number serves 1193 1194 var temp = document.createElement("div"); // create temporary element 1195 temp.style.overflow = "hidden"; // in case baseline is set too low 1196 temp.style.visibility = "hidden"; // no need to show it 1197 1198 parent.appendChild(temp); // insert it into the parent for em, ex and % 1199 1200 temp.style.width = baseline + unit; 1201 var factor = baseline / temp.offsetWidth; 1202 1203 parent.removeChild(temp); // clean up 1204 1205 return (value * factor); 1206 } 1207 1208 function getPropertyUnitValue(target, prop, unit) { 1209 var value = getStyle(target, prop); // get the computed style value 1210 1211 var numeric = value.match(/\d+/); // get the numeric component 1212 if (numeric !== null) { 1213 numeric = numeric[0]; // get the string 1214 1215 return getUnitValue (target.parentElement, numeric, unit); 1216 } 1217 return 0; 1218 } 1219 1220 function downloadFile(filename, header, data) { 1221 1222 var ua = window.navigator.userAgent; 1223 if (ua.indexOf("MSIE ") > 0 || !!ua.match(/Trident.*rv\:11\./)) { 1224 // Internet Explorer (<= 9) workaround by Darryl (https://github.com/dawiong/tableExport.jquery.plugin) 1225 // based on sampopes answer on http://stackoverflow.com/questions/22317951 1226 // ! Not working for json and pdf format ! 1227 var frame = document.createElement("iframe"); 1228 1229 if (frame) { 1230 document.body.appendChild(frame); 1231 frame.setAttribute("style", "display:none"); 1232 frame.contentDocument.open("txt/html", "replace"); 1233 frame.contentDocument.write(data); 1234 frame.contentDocument.close(); 1235 frame.focus(); 1236 1237 frame.contentDocument.execCommand("SaveAs", true, filename); 1238 document.body.removeChild(frame); 1239 } 1240 } 1241 else { 1242 var DownloadLink = document.createElement('a'); 1243 1244 if (DownloadLink) { 1245 DownloadLink.style.display = 'none'; 1246 DownloadLink.download = filename; 1247 1248 if (header.toLowerCase().indexOf("base64,") >= 0) 1249 DownloadLink.href = header + base64encode(data); 1250 else 1251 DownloadLink.href = header + encodeURIComponent(data); 1252 1253 document.body.appendChild(DownloadLink); 1254 1255 if (document.createEvent) { 1256 if (DownloadEvt == null) 1257 DownloadEvt = document.createEvent('MouseEvents'); 1258 1259 DownloadEvt.initEvent('click', true, false); 1260 DownloadLink.dispatchEvent(DownloadEvt); 1261 } 1262 else if (document.createEventObject) 1263 DownloadLink.fireEvent('onclick'); 1264 else if (typeof DownloadLink.onclick == 'function') 1265 DownloadLink.onclick(); 1266 1267 document.body.removeChild(DownloadLink); 1268 } 1269 } 1270 } 1271 1272 function utf8Encode(string) { 1273 string = string.replace(/\x0d\x0a/g, "\x0a"); 1274 var utftext = ""; 1275 for (var n = 0; n < string.length; n++) { 1276 var c = string.charCodeAt(n); 1277 if (c < 128) { 1278 utftext += String.fromCharCode(c); 1279 } 1280 else if ((c > 127) && (c < 2048)) { 1281 utftext += String.fromCharCode((c >> 6) | 192); 1282 utftext += String.fromCharCode((c & 63) | 128); 1283 } 1284 else { 1285 utftext += String.fromCharCode((c >> 12) | 224); 1286 utftext += String.fromCharCode(((c >> 6) & 63) | 128); 1287 utftext += String.fromCharCode((c & 63) | 128); 1288 } 1289 } 1290 return utftext; 1291 } 1292 1293 function base64encode(input) { 1294 var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 1295 var output = ""; 1296 var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 1297 var i = 0; 1298 input = utf8Encode(input); 1299 while (i < input.length) { 1300 chr1 = input.charCodeAt(i++); 1301 chr2 = input.charCodeAt(i++); 1302 chr3 = input.charCodeAt(i++); 1303 enc1 = chr1 >> 2; 1304 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 1305 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 1306 enc4 = chr3 & 63; 1307 if (isNaN(chr2)) { 1308 enc3 = enc4 = 64; 1309 } else if (isNaN(chr3)) { 1310 enc4 = 64; 1311 } 1312 output = output + 1313 keyStr.charAt(enc1) + keyStr.charAt(enc2) + 1314 keyStr.charAt(enc3) + keyStr.charAt(enc4); 1315 } 1316 return output; 1317 } 1318 1319 return this; 1320 } 1321 }); 1322})(jQuery);
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.