PageSourceSearch

http://sp2000.org.cn/plugins/tableExport/tableExport.js

js sp2000.org.cn collected 2026-09-25 02:34:59 UTC 52,256 bytes, 1,322 lines download raw bytes

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.