1/*! 2 * TableExport.js v5.0.5 (https://www.travismclarke.com) 3 * 4 * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 10 * http://www.apache.org/licenses/LICENSE-2.0 11 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 21;(function (root, factory) { 22 if (typeof define === 'function' && define.amd) { 23 // AMD 24 define(function (require) { 25 var $; 26 try { 27 $ = require('jquery') 28 } catch (e) { 29 } 30 return factory($, require('blobjs'), require('file-saverjs'), require('xlsx')); 31 }); 32 } else if (typeof exports === 'object' && typeof exports.nodeName !== 'string') { 33 // CommonJS 34 var $; 35 try { 36 $ = require('jquery') 37 } catch (e) { 38 } 39 module.exports = factory($, require('blobjs'), require('file-saverjs'), require('xlsx')); 40 } else { 41 // Browser globals 42 root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); 43 } 44}(this, function ($, Blob, saveAs, XLSX) { 45 'use strict'; 46 /** 47 * TableExport main library constructor 48 * @param selectors {jQuery} jQuery selector(s) 49 * @param options {Object} TableExport configuration options 50 * @constructor 51 */ 52 var TableExport = function (selectors, options) { 53 var self = this; 54 55 if (!selectors) return _handleError('"selectors" is required. \nUsage: TableExport(selectors, options)'); 56 if (!self) return new TableExport(selectors, options); 57 58 /** 59 * TableExport configuration options (user-defined w/ default fallback) 60 */ 61 self.settings = _extend({}, self.defaults, options); 62 /** 63 * Selectors (tables) to apply the library to 64 */ 65 self.selectors = _nodesArray(selectors); 66 67 var settings = self.settings; 68 settings.ignoreRows = settings.ignoreRows instanceof Array ? settings.ignoreRows : [settings.ignoreRows]; 69 settings.ignoreCols = settings.ignoreCols instanceof Array ? settings.ignoreCols : [settings.ignoreCols]; 70 settings.ignoreCSS = self.ignoreCSS instanceof Array ? self.ignoreCSS : [self.ignoreCSS]; 71 settings.emptyCSS = self.emptyCSS instanceof Array ? self.emptyCSS : [self.emptyCSS]; 72 settings.formatValue = self.formatValue.bind(this, settings.trimWhitespace); 73 settings.bootstrapSettings = _getBootstrapSettings(settings.bootstrap, self.bootstrapConfig, self.defaultButton); 74 75 var _exportData = {}; 76 self.getExportData = function () { 77 return _exportData; 78 }; 79 80 self.selectors.forEach(function (el) { 81 var context = {}; 82 83 context.rows = _nodesArray(el.querySelectorAll('tbody > tr')); 84 context.rows = settings.headers ? _nodesArray(el.querySelectorAll('thead > tr')).concat(context.rows) : context.rows; 85 context.rows = settings.footers ? context.rows.concat(_nodesArray(el.querySelectorAll('tfoot > tr'))) : context.rows; 86 context.thAdj = settings.headers ? el.querySelectorAll('thead > tr').length : 0; 87 context.filename = settings.filename === 'id' 88 ? (el.getAttribute('id') ? el.getAttribute('id') : self.defaultFilename) 89 : (settings.filename ? settings.filename : self.defaultFilename); 90 context.uuid = _uuid(el); 91 92 /** 93 * Initializes table caption with export buttons 94 * @param exportButton {HTMLButtonElement} 95 */ 96 context.checkCaption = function (exportButton) { 97 var caption = el.querySelectorAll('caption.' + self.defaultCaptionClass); 98 if (caption.length) { 99 caption[0].appendChild(exportButton); 100 } else { 101 caption = document.createElement('caption'); 102 caption.className = settings.bootstrapSettings.bootstrapSpacing + self.defaultCaptionClass; 103 caption.style = 'caption-side: ' + settings.position; 104 caption.appendChild(exportButton); 105 el.insertBefore(caption, el.firstChild); 106 } 107 }; 108 109 context.setExportData = (function () { 110 return function (exporter) { 111 var data = Storage.getInstance().getItem(exporter); 112 var type = exporter.substring(exporter.indexOf('-') + 1); 113 _exportData[context.uuid] = _exportData[context.uuid] || {}; 114 _exportData[context.uuid][type] = JSON.parse(data); 115 }; 116 })(); 117 118 context.rcMap = new RowColMap().build(context, settings); 119 120 var formatMap = _FORMAT_LIST 121 .reduce(function (acc, cur) { 122 acc[cur] = 0; 123 return acc; 124 }, {}); 125
126 settings.formats.forEach( 127 function (key) { 128 if (!_isValidFormat(key)) { 129 return _handleError('"' + key + '" is not a valid format. \nFormats: ' + _FORMAT_LIST.join(', ')) 130 } else if (!_hasDependencies(key)) { 131 // TODO: provide a fallback option to XLS? 132 return _handleError('"' + key + '" requires "js-xlsx".'); 133 } else if (!formatMap[key]) { 134 context.setExportData(self.exporters.build.call(self, context, key)); 135 formatMap[key]++; 136 } 137 } 138 ); 139 }); 140 141 var exportButton = document.querySelectorAll('button[' + self.storageKey + ']'); 142 _on(exportButton, 'click', self.downloadHandler, self); 143 144 return self; 145 }; 146 147 TableExport.prototype = { 148 /** 149 * Version. 150 * @memberof TableExport.prototype 151 */ 152 version: '5.0.5', 153 /** 154 * Default library options. 155 * @memberof TableExport.prototype 156 */ 157 defaults: { 158 headers: true, // (Boolean), display table headers (th or td elements) in the <thead>, (default: true) 159 footers: true, // (Boolean), display table footers (th or td elements) in the <tfoot>, (default: false) 160 formats: ['xlsx', 'csv', 'txt'], // (String[]), filetype(s) for the export, (default: ['xlsx', 'csv', 'txt']) 161 filename: 'id', // (id, String), filename for the downloaded file, (default: 'id') 162 bootstrap: false, // (Boolean), style buttons using bootstrap, (default: true) 163 exportButtons: true, // (Boolean), automatically generate the built-in export buttons for each of the specified formats (default: true) 164 position: 'bottom', // (top, bottom), position of the caption element relative to table, (default: 'bottom') 165 ignoreRows: null, // (Number, Number[]), row indices to exclude from the exported file(s) (default: null) 166 ignoreCols: null, // (Number, Number[]), column indices to exclude from the exported file(s) (default: null) 167 trimWhitespace: true // (Boolean), remove all leading/trailing newlines, spaces, and tabs from cell text in the exported file(s) (default: false) 168 }, 169 /** 170 * Constants 171 * @memberof TableExport.prototype 172 */ 173 CONSTANTS: { 174 FORMAT: { 175 XLSX: 'xlsx', 176 XLSM: 'xlsm', 177 XLSB: 'xlsb', 178 BIFF2: 'biff2', 179 XLS: 'xls', 180 CSV: 'csv', 181 TXT: 'txt' 182 }, 183 TYPE: { 184 STRING: 's', 185 NUMBER: 'n', 186 BOOLEAN: 'b', 187 DATE: 'd' 188 } 189 }, 190 /** 191 * Character set (character encoding) of the HTML. 192 * @memberof TableExport.prototype 193 */ 194 charset: 'charset=utf-8', 195 /** 196 * Filename fallback for exported files. 197 * @memberof TableExport.prototype 198 */ 199 defaultFilename: 'myDownload', 200 /** 201 * Class applied to each export button element. 202 * @memberof TableExport.prototype 203 */ 204 defaultButton: 'button-default', 205 /** 206 * Class applied to each table caption. 207 * @memberof TableExport.prototype 208 */ 209 defaultCaptionClass: 'tableexport-caption', 210 /** 211 * Namespace (i.e. prefix) applied to each table UUID and Storage key. 212 * @memberof TableExport.prototype 213 */ 214 defaultNamespace: 'tableexport-', 215 /** 216 * Attribute applied to each table element used to generate each Storage key. 217 * @memberof TableExport.prototype 218 */ 219 tableKey: 'tableexport-key', 220 /** 221 * Attribute applied to each export button element used to reference a Storage key. 222 * @memberof TableExport.prototype 223 */ 224 storageKey: 'tableexport-id', 225 /** 226 * CSS selector or selector[] to exclude/remove cells from the exported file(s). 227 * @type {selector|selector[]} 228 * @memberof TableExport.prototype 229 */ 230 ignoreCSS: '.tableexport-ignore', 231 /** 232 * CSS selector or selector[] to replace cells with an empty string in the exported file(s). 233 * @type {selector|selector[]} 234 * @memberof TableExport.prototype 235 */ 236 emptyCSS: '.tableexport-empty', 237 /** 238 * Bootstrap configuration classes ['base', 'theme', 'container']. 239 * @memberof TableExport.prototype 240 */ 241 bootstrapConfig: ['btn', 'btn-default', 'btn-toolbar'], 242 /** 243 * Row delimeter 244 * @memberof TableExport.prototype 245 */ 246 rowDel: '\r\n', 247 /** 248 * HTML entity mapping for special characters. 249 * @memberof TableExport.prototype 250 */ 251 entityMap: {'&': '&', '<': '<', '>': '>', "'": ''', '/': '/'}, 252 /** 253 * Format configuration 254 * @memberof TableExport.prototype 255 */ 256 formatConfig: { 257 /** 258 * XLSX (Open XML spreadsheet) file extension configuration 259 * @memberof TableExport.prototype 260 */ 261 xlsx: { 262 defaultClass: 'xlsx', 263 buttonContent: 'Export to xlsx', 264 mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 265 fileExtension: '.xlsx' 266 }, 267 xlsm: { 268 defaultClass: 'xlsm', 269 buttonContent: 'Export to xlsm', 270 mimeType: 'application/vnd.ms-excel.sheet.macroEnabled.main+xml', 271 fileExtension: '.xlsm' 272 }, 273 xlsb: { 274 defaultClass: 'xlsb', 275 buttonContent: 'Export to xlsb', 276 mimeType: 'application/vnd.ms-excel.sheet.binary.macroEnabled.main', 277 fileExtension: '.xlsb' 278 }, 279 /** 280 * XLS (Binary spreadsheet) file extension configuration 281 * @memberof TableExport.prototype 282 */ 283 xls: { 284 defaultClass: 'xls', 285 buttonContent: 'Export to xls', 286 separator: '\t', 287 mimeType: 'application/vnd.ms-excel', 288 fileExtension: '.xls', 289 enforceStrictRFC4180: false 290 }, 291 /** 292 * CSV (Comma Separated Values) file extension configuration 293 * @memberof TableExport.prototype 294 */ 295 csv: { 296 defaultClass: 'csv', 297 buttonContent: 'Export to csv', 298 separator: ',', 299 mimeType: 'text/csv', 300 fileExtension: '.csv', 301 enforceStrictRFC4180: true 302 }, 303 /** 304 * TXT (Plain Text) file extension configuration 305 * @memberof TableExport.prototype 306 */ 307 txt: { 308 defaultClass: 'txt', 309 buttonContent: 'Export to txt', 310 separator: ' ', 311 mimeType: 'text/plain', 312 fileExtension: '.txt', 313 enforceStrictRFC4180: true 314 } 315 }, 316 /** 317 * Cell-types override and assertion configuration 318 * @memberof TableExport.prototype 319 */ 320 typeConfig: {
321 string: { 322 defaultClass: 'tableexport-string' 323 }, 324 number: { 325 defaultClass: 'tableexport-number', 326 assert: function (v) { 327 return !isNaN(v); 328 } 329 }, 330 boolean: { 331 defaultClass: 'tableexport-boolean', 332 assert: function (v) { 333 return v.toLowerCase() === 'true' || v.toLowerCase() === 'false'; 334 } 335 }, 336 date: { 337 defaultClass: 'tableexport-date', 338 assert: function (v) { 339 return !(/.*%/.test(v)) && !isNaN(Date.parse(v)) 340 } 341 } 342 }, 343 exporters: { 344 build: function (context, key) { 345 var self = this; 346 var settings = self.settings; 347 var format = self.formatConfig[key]; 348 var colDel = format.separator; 349 var rcMap = context.rcMap; 350 351 var getReturn = function (val) { 352 if (_isEnhanced(key)) { 353 return { 354 v: settings.formatValue(val.textContent), 355 t: self.getType(val.className) 356 } 357 } 358 switch (key) { 359 case _FORMAT.CSV: 360 return '"' + settings.formatValue(val.textContent.replace(/"/g, '""')) + '"'; 361 default: 362 return settings.formatValue(val.textContent); 363 } 364 }; 365 366 var dataURI = _nodesArray(context.rows).map(function (val, ir) { 367 if (rcMap.isIgnore(ir)) { 368 return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); 369 } else if (rcMap.isEmpty(ir)) { 370 return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); 371 } 372 var cols = val.querySelectorAll('th, td'); 373 return _nodesArray(cols).map(function (val, ic) { 374 var _return = getReturn(val); 375 if (rcMap.isIgnore(ir, ic)) { 376 return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); 377 } else if (rcMap.isEmpty(ir, ic)) { 378 return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); 379 } else { 380 return rcMap.handleRowColMapProp(rcMap.TYPE.DEFAULT, ir, ic, key, _return, colDel); 381 } 382 }).processCols(key, colDel); 383 }).processRows(key, self.rowDel); 384 385 var dataObject = JSON.stringify({ 386 data: dataURI, 387 filename: context.filename, 388 mimeType: format.mimeType, 389 fileExtension: format.fileExtension, 390 merges: rcMap.merges 391 }); 392 393 var hashKey = _hashCode({uuid: context.uuid, type: key}); 394 395 settings.exportButtons && context.checkCaption(self.createObjButton( 396 hashKey, 397 dataObject, 398 format.buttonContent, 399 format.defaultClass, 400 settings.bootstrapSettings 401 )); 402 return Storage.getInstance().setItem(hashKey, dataObject, true); 403 } 404 }, 405 /** 406 * Creates file export buttons 407 * @param hashKey {String} 408 * @param dataObject {String} 409 * @param myContent {String} 410 * @param myClass {String} 411 * @param bootstrapSettings {Object} 412 * @returns Element 413 */ 414 createObjButton: function (hashKey, dataObject, myContent, myClass, bootstrapSettings) { 415 var exportButton = document.createElement('button'); 416 exportButton.setAttribute('type', 'button'); 417 exportButton.setAttribute(this.storageKey, hashKey); 418 exportButton.className = bootstrapSettings.bootstrapClass + bootstrapSettings.bootstrapTheme + myClass; 419 exportButton.textContent = myContent; 420 return exportButton 421 }, 422 /** 423 * Escapes special characters with HTML entities 424 * @memberof TableExport.prototype
425 * @param string {String} 426 * @returns {String} escaped string 427 */ 428 escapeHtml: function (string) { 429 var self = this; 430 return String(string).replace(/[&<>'\/]/g, function (s) { 431 return self.entityMap[s]; 432 }); 433 }, 434 /** 435 * Unescapes HTML entities to special characters 436 * @memberof TableExport.prototype 437 * @param string {String} 438 * @returns {String} unescaped string 439 */ 440 unescapeHtml: function (string) { 441 var str = String(string); 442 for (var key in this.entityMap) { 443 str = str.replace(RegExp(this.entityMap[key], 'g'), key); 444 } 445 return str; 446 }, 447 /** 448 * Removes leading/trailing whitespace from cell string 449 * @param isTrimWhitespace {Boolean} 450 * @param string {String} 451 * @returns {String} trimmed string 452 */ 453 formatValue: function (isTrimWhitespace, string) { 454 return isTrimWhitespace ? string.trim() : string; 455 }, 456 /** 457 * Get cell data-type 458 * @param string {String} 459 * @returns {String} data-type 460 */ 461 getType: function (string) { 462 if (!string) return ''; 463 var types = this.typeConfig; 464 if (~string.indexOf(types.string.defaultClass)) { 465 return _TYPE.STRING; 466 } else if (~string.indexOf(types.number.defaultClass)) { 467 return _TYPE.NUMBER; 468 } else if (~string.indexOf(types.boolean.defaultClass)) { 469 return _TYPE.BOOLEAN; 470 } else if (~string.indexOf(types.date.defaultClass)) { 471 return _TYPE.DATE; 472 } else { 473 return ''; 474 } 475 }, 476 /** 477 * Formats datetimes for compatibility with Excel 478 * @memberof TableExport.prototype 479 * @param v {Number} 480 * @param date1904 {Date} 481 * @returns {Number} epoch time 482 */ 483 dateNum: function (v, date1904) { 484 if (date1904) v += 1462; 485 var epoch = Date.parse(v); 486 var result = (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); 487 return Math.floor(result); 488 }, 489 /** 490 * Creates an Excel spreadsheet from a data string 491 * @memberof TableExport.prototype 492 * @param data {String} 493 * @param merges {Object[]} 494 */ 495 createSheet: function (data, merges) { 496 var ws = {}; 497 var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}}; 498 var types = this.typeConfig; 499 for (var R = 0; R !== data.length; ++R) { 500 for (var C = 0; C !== data[R].length; ++C) { 501 if (range.s.r > R) range.s.r = R; 502 if (range.s.c > C) range.s.c = C; 503 if (range.e.r < R) range.e.r = R; 504 if (range.e.c < C) range.e.c = C; 505 var cell = data[R][C]; 506 if (!cell || !cell.v) continue; 507 var cell_ref = XLSX.utils.encode_cell({c: C, r: R}); 508 509 if (!cell.t) { 510 if (types.number.assert(cell.v)) cell.t = _TYPE.NUMBER; 511 else if (types.boolean.assert(cell.v)) cell.t = _TYPE.BOOLEAN; 512 else if (types.date.assert(cell.v)) cell.t = _TYPE.DATE; 513 else cell.t = _TYPE.STRING; 514 } 515 if (cell.t === _TYPE.DATE) { 516 cell.t = _TYPE.NUMBER; 517 cell.z = XLSX.SSF._table[14]; 518 cell.v = this.dateNum(cell.v); 519 } 520 ws[cell_ref] = cell; 521 } 522 } 523 ws['!merges'] = merges; 524 if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); 525 return ws; 526 }, 527 /** 528 * Click handler for export button "download" 529 * @memberof TableExport.prototype 530 */ 531 downloadHandler: function (event) { 532 var target = event.target; 533 var object = JSON.parse(Storage.getInstance().getItem(target.getAttribute(this.storageKey))), 534 data = object.data, 535 filename = object.filename, 536 mimeType = object.mimeType, 537 fileExtension = object.fileExtension, 538 merges = object.merges; 539 this.export2file(data, mimeType, filename, fileExtension, merges); 540 }, 541 /** 542 * Excel Workbook constructor 543 * @memberof TableExport.prototype 544 * @constructor 545 */ 546 Workbook: function () { 547 this.SheetNames = []; 548 this.Sheets = {}; 549 }, 550 /** 551 * Converts a string to an arraybuffer 552 * @param s {String} 553 * @memberof TableExport.prototype 554 * @returns {ArrayBuffer} 555 */
556 string2ArrayBuffer: function (s) { 557 var buf = new ArrayBuffer(s.length); 558 var view = new Uint8Array(buf); 559 for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; 560 return buf; 561 }, 562 /** 563 * Exports and downloads the file 564 * @memberof TableExport.prototype 565 * @param data {String} 566 * @param mime {String} mime type 567 * @param name {String} filename 568 * @param extension {String} file extension 569 * @param merges {Object[]} 570 */ 571 export2file: function (data, mime, name, extension, merges) { 572 var format = extension.slice(1); 573 data = this.getRawData(data, extension, name, merges); 574 575 if (_isMobile && (format === _FORMAT.CSV || format === _FORMAT.TXT)) { 576 var dataURI = 'data:' + mime + ';' + this.charset + ',' + data; 577 this.downloadDataURI(dataURI, name, extension); 578 } else { 579 // TODO: error and fallback when `saveAs` not available 580 saveAs(new Blob([data], 581 {type: mime + ';' + this.charset}), 582 name + extension, true); 583 } 584 }, 585 downloadDataURI: function (dataURI, name, extension) { 586 var encodedUri = encodeURI(dataURI); 587 var link = document.createElement('a'); 588 link.setAttribute('href', encodedUri); 589 link.setAttribute('download', name + extension); 590 document.body.appendChild(link); 591 link.click(); 592 }, 593 getBookType: function (key) { 594 switch (key) { 595 case _FORMAT.XLS: 596 return _FORMAT.BIFF2; 597 default: 598 return key; 599 } 600 }, 601 getRawData: function (data, extension, name, merges) { 602 var key = extension.substring(1); 603 604 if (_isEnhanced(key)) { 605 var wb = new this.Workbook(), 606 ws = this.createSheet(data, merges), 607 bookType = this.getBookType(key); 608 609 name = name || ''; 610 wb.SheetNames.push(name); 611 wb.Sheets[name] = ws; 612 var wopts = { 613 bookType: bookType, 614 bookSST: false, 615 type: 'binary' 616 }, 617 wbout = XLSX.write(wb, wopts); 618 619 data = this.string2ArrayBuffer(wbout); 620 } 621 return data; 622 }, 623 getFileSize: function (data, extension) { 624 var binary = this.getRawData(data, extension); 625 return binary instanceof ArrayBuffer 626 ? binary.byteLength 627 : this.string2ArrayBuffer(binary).byteLength; 628 }, 629 /** 630 * Updates the library instance with new/updated options 631 * @param options {Object} TableExport configuration options 632 * @returns {TableExport} updated TableExport instance 633 */ 634 update: function (options) { 635 this.remove(); 636 return new TableExport(this.selectors, _extend({}, this.defaults, options)); 637 }, 638 /** 639 * Reset the library instance to its original state 640 * @returns {TableExport} original TableExport instance 641 */ 642 reset: function () { 643 this.remove(); 644 return new TableExport(this.selectors, this.settings); 645 }, 646 /** 647 * Remove the instance (i.e. caption containing the export buttons) 648 */ 649 remove: function () { 650 var self = this; 651 this.selectors.forEach(function (el) { 652 var caption = el.querySelector('caption.' + self.defaultCaptionClass); 653 caption && el.removeChild(caption); 654 }); 655 } 656 }; 657 658 /** 659 * Storage main interface constructor 660 * @memberof TableExport.prototype 661 * @constructor 662 */ 663 var Storage = function () { 664 this._instance = null; 665 666 this.store = sessionStorage; 667 this.namespace = TableExport.prototype.defaultNamespace; 668 this.getKey = function (key) { 669 return this.namespace + key; 670 }; 671 this.setItem = function (_key, value, overwrite) { 672 var key = this.getKey(_key); 673 if (this.exists(_key) && !overwrite) { 674 return; 675 } 676 if (typeof value !== 'string') return _handleError('"value" must be a string.'); 677 this.store.setItem(key, value); 678 return _key; 679 }; 680 this.getItem = function (_key) { 681 var key = this.getKey(_key); 682 return this.store.getItem(key); 683 }; 684 this.exists = function (_key) { 685 var key = this.getKey(_key); 686 return this.store.getItem(key) !== null; 687 }; 688 this.removeItem = function (_key) { 689 var key = this.getKey(_key); 690 return this.store.removeItem(key); 691 }; 692 }; 693 Storage.getInstance = function () { 694 if (!this._instance) { 695 this._instance = new Storage(); 696 } 697 return this._instance; 698 }; 699 700 /** 701 * RowColMap main interface constructor 702 * @memberof TableExport.prototype 703 * @constructor 704 */ 705 var RowColMap = function () { 706 this.rcMap = []; 707 this.merges = []; 708 709 this.isIgnore = function (ir, ic) { 710 var _ignore = RowColMap.prototype.TYPE.IGNORE; 711 return this.getRowColMapProp(ir, ic, _ignore); 712 }; 713 this.isEmpty = function (ir, ic) {
714 var _empty = RowColMap.prototype.TYPE.EMPTY; 715 return this.getRowColMapProp(ir, ic, _empty); 716 }; 717 this.isRowSpan = function (ir) { 718 var _rowspan = RowColMap.prototype.TYPE.ROWSPAN; 719 return this.getRowColMapProp(ir, undefined, _rowspan); 720 }; 721 this.isColSpan = function (ir) { 722 var _colspan = RowColMap.prototype.TYPE.COLSPAN; 723 return this.getRowColMapProp(ir, undefined, _colspan); 724 }; 725 this.isSpan = function (ir) { 726 return this.isRowSpan(ir) || this.isColSpan(ir); 727 }; 728 this.isMerge = function (ir) { 729 return this.merges.length > 0; 730 }; 731 this.addMerge = function (ir, mergeObj) { 732 var _merge = RowColMap.prototype.TYPE.MERGE; 733 this.merges.push(mergeObj); 734 this.setRowColMapProp(ir, undefined, _merge, this.merges); 735 }; 736 this.getRowColMapProp = function (ir, ic, key) { 737 if (this.rcMap[ir]) { 738 if (typeof key === 'undefined') { 739 return this.rcMap[ir][ic]; 740 } else if (typeof ic === 'undefined') { 741 return this.rcMap[ir][key]; 742 } else if (this.rcMap[ir][ic]) { 743 return this.rcMap[ir][ic][key]; 744 } 745 } 746 return undefined; 747 }; 748 this.setRowColMapProp = function (ir, ic, key, value) { 749 this.rcMap[ir] = this.rcMap[ir] || []; 750 if (typeof key === 'undefined') { 751 return this.rcMap[ir][ic] = value; 752 } else if (typeof ic === 'undefined') { 753 return this.rcMap[ir][key] = value; 754 } else { 755 this.rcMap[ir][ic] = this.rcMap[ir][ic] || []; 756 return this.rcMap[ir][ic][key] = value; 757 } 758 }; 759 this.generateTotal = function (ir, ic) { 760 var VALUE = RowColMap.prototype.TYPE.VALUE; 761 var _total = 0; 762 763 if (this.isRowSpan(ir) && this.isColSpan(ir)) { 764 _total = this.getRowColMapProp(ir, ic, VALUE) || 0; 765 } else if (this.getRowColMapProp(ir, ic, VALUE)) { 766 _total = this.getRowColMapProp(ir, ic, VALUE); 767 } 768 return _total; 769 }; 770 this.convertSpanToArray = function (ir, ic, key, _return, colDel) { 771 if (this.rcMap[ir] && this.isSpan(ir)) { 772 var total = this.generateTotal(ir, ic); 773 774 if (_isEnhanced(key)) { 775 return new Array(total).concat(_return); 776 } else { 777 return new Array(total).concat(_return).join(colDel); 778 } 779 } 780 return _return; 781 }; 782 this.handleRowColMapProp = function (type, ir, ic, key, _return, colDel) { 783 switch (type) { 784 case RowColMap.prototype.TYPE.IGNORE: 785 return; 786 case RowColMap.prototype.TYPE.EMPTY: 787 return ' '; 788 case RowColMap.prototype.TYPE.DEFAULT: 789 default: 790 return this.convertSpanToArray(ir, ic, key, _return, colDel); 791 } 792 }; 793 }; 794 RowColMap.prototype = { 795 OFFSET: 1, 796 TYPE: { 797 IGNORE: 'ignore', 798 EMPTY: 'empty', 799 MERGE: 'merge', 800 ROWSPAN: 'rowspan', 801 ROWSPANTOTAL: 'rowspantotal', 802 COLSPAN: 'colspan', 803 COLSPANTOTAL: 'colspantotal', 804 DEFAULT: 'default', 805 VALUE: 'value' 806 }, 807 build: function (context, settings) { 808 var self = this; 809 810 var OFFSET = self.OFFSET; 811 var rowLength = self.rowLength = context.rows.length; 812 // var colLength = self.colLength = Math.max.apply(null, 813 // _nodesArray(context.rows).map(function (val) { 814 // return val.querySelectorAll('th, td').length 815 // })); 816 817 var handleIgnore = function (ir, ic) { 818 self.setRowColMapProp(ir, ic, self.TYPE.IGNORE, true); 819 }; 820 var handleEmpty = function (ir, ic) {
821 self.setRowColMapProp(ir, ic, self.TYPE.EMPTY, true); 822 }; 823 var handleRowSpan = function (val, ir, ic) { 824 var rowSpan = +val.getAttribute('rowspan'); 825 var colSpan = +val.getAttribute('colspan'); 826 var handledByColSpan, 827 countRowSpan, countColSpan, 828 totalRowSpan, totalColSpan, 829 irStart, irEnd, icStart, icEnd; 830 831 for (var _row = 0; _row < rowSpan; _row++) { 832 if (_row + ir >= rowLength) { 833 return; 834 } 835 colSpan && (handledByColSpan = handleColSpan(val, _row + ir, ic, _row > 0, rowSpan)); 836 837 if (rowSpan <= 1) { 838 return false; 839 } 840 var cur = self.rcMap['c' + (ic - 1)] ? self.rcMap['c' + (ic - 1)][_row + ir] : 0; 841 if (cur) { 842 self.rcMap['c' + (ic)] = self.rcMap['c' + (ic)] || []; 843 self.rcMap['c' + (ic)][_row + ir] = (self.rcMap['c' + (ic)][_row + ir] || 0) + cur; 844 } 845 846 if (rowSpan && _row === 0 && colSpan > 1) { 847 for (var i = 0; i < rowSpan; i++) { 848 self.rcMap['c' + (ic + 1)] = self.rcMap['c' + (ic + 1)] || []; 849 self.rcMap['c' + (ic + 1)][_row + ir + i] = (self.rcMap['c' + (ic + 1)][_row + ir + i] || 0) + Math.max(1, colSpan); 850 851 } 852 } 853 854 if (_row >= 1) { 855 countRowSpan = self.getRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN) || 0; 856 self.setRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN, countRowSpan + 1); 857 858 if (!handledByColSpan) { 859 totalRowSpan = self.getRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE) || 0; 860 self.setRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE, totalRowSpan + 1); 861 if (rowSpan > 1 && _row === 1) { 862 var _re = self.rcMap['c' + (ic)] && self.rcMap['c' + (ic)][_row + ir]; 863 totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; 864 countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; 865 irStart = ir; 866 irEnd = ir + rowSpan - 1; 867 icStart = ic + totalColSpan - countColSpan + (_re || 0); 868 icEnd = icStart + Math.max(1, colSpan) - 1; 869 handleMerge(irStart, icStart, irEnd, icEnd); 870 } 871 } 872 } 873 } 874 }; 875 876 var handleColSpan = function (val, ir, ic, isRowSpan, rowSpan) { 877 var irStart, irEnd, icStart, icEnd; 878 var colSpan = +val.getAttribute('colspan'); 879 var countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; 880 var totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; 881 882 if (colSpan <= 1) { 883 return false; 884 } 885 886 self.setRowColMapProp(ir, undefined, self.TYPE.COLSPAN, countColSpan + 1); 887 self.setRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL, totalColSpan + colSpan); 888 889 if (isRowSpan) { 890 self.setRowColMapProp(ir, ic - countColSpan, self.TYPE.VALUE, colSpan); 891 return true; 892 } else { 893 irStart = ir; 894 irEnd = ir + (rowSpan || 1) - OFFSET; 895 icStart = ic + totalColSpan - countColSpan; 896 icEnd = ic + totalColSpan - countColSpan + (colSpan - OFFSET); 897 self.setRowColMapProp(ir, ic + OFFSET, self.TYPE.VALUE, colSpan - OFFSET); 898 handleMerge(irStart, icStart, irEnd, icEnd); 899 } 900 }; 901 902 var handleMerge = function (irs, ics, ire, ice) { 903 var merge = { 904 s: {r: irs, c: ics}, 905 e: {r: ire, c: ice} 906 }; 907 return self.addMerge(irs, merge); 908 }; 909 910 _nodesArray(context.rows).map(function (val, ir) { 911 if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || _matches(val, settings.ignoreCSS)) { 912 handleIgnore(ir); 913 } 914 if (_matches(val, settings.emptyCSS)) { 915 handleEmpty(ir); 916 } 917 var cols = val.querySelectorAll('th, td'); 918 return _nodesArray(cols).map(function (val, ic) { 919 if (!!~settings.ignoreCols.indexOf(ic) || _matches(val, settings.ignoreCSS)) { 920 handleIgnore(ir, ic); 921 } 922 if (_matches(val, settings.emptyCSS)) { 923 handleEmpty(ir, ic); 924 } 925 if (val.hasAttribute('rowspan')) {
926 handleRowSpan(val, ir, ic); 927 } else if (val.hasAttribute('colspan')) { 928 handleColSpan(val, ir, ic); 929 } 930 }); 931 }); 932 933 return self; 934 } 935 }; 936 937 var _isMobile = (function isMobile(ua) { 938 return /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(ua) 939 || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(ua.substring(0, 4)) 940 })(navigator.userAgent || navigator.vendor || window.opera); 941 942 var _isEnhanced = (function () { 943 return function (key) { 944 return ( 945 XLSX 946 && !(TableExport.prototype.formatConfig[key].enforceStrictRFC4180) 947 ); 948 } 949 })(); 950 951 var _FORMAT = (function () { 952 return TableExport.prototype.CONSTANTS.FORMAT; 953 })(); 954 955 var _FORMAT_LIST = (function () { 956 return Object.keys(_FORMAT).map(function (key) {
957 return _FORMAT[key]; 958 }); 959 })(); 960 961 var _TYPE = (function () { 962 return TableExport.prototype.CONSTANTS.TYPE; 963 })(); 964 965 Object.defineProperty(Array.prototype, 'processRows', { 966 enumerable: false, 967 value: function (key, rowDel) { 968 if (_isEnhanced(key)) { 969 return this.map(_toArray).filter(_defined); 970 } else { 971 return this.filter(_defined).join(rowDel); 972 } 973 } 974 }); 975 976 Object.defineProperty(Array.prototype, 'processCols', { 977 enumerable: false, 978 value: function (key, colDel) { 979 if (_isEnhanced(key)) { 980 return this.filter(_defined); 981 } else { 982 return this.filter(_defined).join(colDel); 983 } 984 } 985 }); 986 987 var _uuid = (function () { 988 var uuid = 0; 989 990 return function (el) { 991 var tableKey = el.getAttribute(TableExport.prototype.tableKey); 992 993 if (!tableKey) { 994 tableKey = el.id 995 ? el.id 996 : TableExport.prototype.defaultNamespace + (++uuid); 997 el.setAttribute(TableExport.prototype.tableKey, tableKey); 998 } 999 return tableKey; 1000 }; 1001 })(); 1002 1003 var _hashCode = (function () { 1004 1005 return function (hashKey) { 1006 var hash = 0, i, char; 1007 1008 var type = hashKey.type; 1009 hashKey = JSON.stringify(hashKey); 1010 if (hashKey.length === 0) return hash; 1011 for (i = 0; i < hashKey.length; i++) { 1012 char = hashKey.charCodeAt(i); 1013 hash = ((hash << 5) - hash) + char; 1014 hash |= 0; 1015 } 1016 return hash.toString(16).substring(1) + '-' + type; 1017 } 1018 })(); 1019 1020 var _on = (function () { 1021 var prevFn = null; 1022 1023 return function (el, event, fn, context) { // , args 1024 var curFn = fn.bind(context); 1025 // var curFn = fn.bind.apply(fn, [context].concat(args)); // OR [].slice.call(arguments[4])) 1026 for (var i = 0; i < el.length; ++i) { 1027 prevFn && el[i].removeEventListener(event, prevFn, false); 1028 el[i].addEventListener(event, curFn, false); 1029 } 1030 prevFn = curFn; 1031 } 1032 })(); 1033 1034 function _extend() { 1035 var args = arguments; 1036 for (var i = 1; i < args.length; i++) 1037 for (var key in args[i]) 1038 if (args[i].hasOwnProperty(key)) 1039 args[0][key] = args[i][key]; 1040 return args[0]; 1041 } 1042 1043 function _nodesArray(els) { 1044 return (typeof els.length === 'undefined') ? [].concat(els) : [].slice.call(els); 1045 } 1046 1047 function _hasClass(el, cls) { 1048 return el.classList ? el.classList.contains(cls) : new RegExp('(^| )' + cls + '( |$)', 'gi').test(el.cls); 1049 } 1050 1051 function _matches(el, selectors) { 1052 return selectors.filter(function (selector) { 1053 return [].indexOf.call(document.querySelectorAll(selector), el) !== -1; 1054 }).length > 0; 1055 } 1056 1057 function _numeric(val) { 1058 return !isNaN(val); 1059 } 1060 1061 function _defined(val) { 1062 return typeof val !== 'undefined'; 1063 } 1064 1065 function _toArray(val) { 1066 return val instanceof Array ? [].concat.apply([], val) : val; 1067 } 1068 1069 function _isValidFormat(key) { 1070 return ~_FORMAT_LIST.indexOf(key); 1071 } 1072 1073 function _hasDependencies(key) { 1074 var hasDependencies; 1075 1076 switch (key) { 1077 case _FORMAT.TXT: 1078 case _FORMAT.CSV: 1079 case _FORMAT.XLS: 1080 hasDependencies = true; 1081 break; 1082 default: 1083 hasDependencies = _isEnhanced(key); 1084 } 1085 return hasDependencies 1086 } 1087 1088 function _handleError(msg) { 1089 console.error(msg); 1090 return new Error(msg) 1091 } 1092 1093 function _getBootstrapSettings(bootstrap, bootstrapConfig, defaultButton) { 1094 var config = {}; 1095 if (bootstrap) { 1096 config.bootstrapClass = bootstrapConfig[0] + ' '; 1097 config.bootstrapTheme = bootstrapConfig[1] + ' '; 1098 config.bootstrapSpacing = bootstrapConfig[2] + ' '; 1099 } else { 1100 config.bootstrapClass = defaultButton + ' '; 1101 config.bootstrapTheme = ''; 1102 config.bootstrapSpacing = ''; 1103 } 1104 return config; 1105 } 1106 1107 if ($) { 1108 /** 1109 * jQuery TableExport wrapper 1110 * @param options {Object} TableExport configuration options 1111 * @returns {TableExport} TableExport instance 1112 */ 1113 $.fn.tableExport = function (options) { 1114 return TableExport.call(_extend({}, TableExport.prototype, $.fn.tableExport), this, options); 1115 }; 1116 1117 // alias the TableExport prototype 1118 for (var prop in TableExport.prototype) { 1119 $.fn.tableExport[prop] = TableExport.prototype[prop]; 1120 } 1121 } 1122 1123 TableExport.TableExport = TableExport; 1124 1125 return TableExport; 1126 } 1127));
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.