diff --git a/dist/js/tableexport.js b/dist/js/tableexport.js index fd56ea0..fa6e72c 100644 --- a/dist/js/tableexport.js +++ b/dist/js/tableexport.js @@ -18,7 +18,7 @@ */ -;(function (root, factory) { +; (function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD define(function (require) { @@ -42,327 +42,341 @@ root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); } }(this, function ($, Blob, saveAs, XLSX) { - 'use strict'; + 'use strict'; + /** + * TableExport main library constructor + * @param selectors {jQuery} jQuery selector(s) + * @param options {Object} TableExport configuration options + * @constructor + */ + var TableExport = function (selectors, options) { + var self = this; + + if (!selectors) return _handleError('"selectors" is required. \nUsage: TableExport(selectors, options)'); + if (!self) return new TableExport(selectors, options); + /** - * TableExport main library constructor - * @param selectors {jQuery} jQuery selector(s) - * @param options {Object} TableExport configuration options - * @constructor + * TableExport configuration options (user-defined w/ default fallback) */ - var TableExport = function (selectors, options) { - var self = this; - - if (!selectors) return _handleError('"selectors" is required. \nUsage: TableExport(selectors, options)'); - if (!self) return new TableExport(selectors, options); + self.settings = _extend({}, self.defaults, options); + /** + * Selectors (tables) to apply the library to + */ + self.selectors = _nodesArray(selectors); + + var settings = self.settings; + settings.ignoreRows = settings.ignoreRows instanceof Array ? settings.ignoreRows : [settings.ignoreRows]; + settings.ignoreCols = settings.ignoreCols instanceof Array ? settings.ignoreCols : [settings.ignoreCols]; + settings.ignoreCSS = self.ignoreCSS instanceof Array ? self.ignoreCSS : [self.ignoreCSS]; + settings.emptyCSS = self.emptyCSS instanceof Array ? self.emptyCSS : [self.emptyCSS]; + settings.formatValue = self.formatValue.bind(this, settings.trimWhitespace); + settings.bootstrapSettings = _getBootstrapSettings(settings.bootstrap, self.bootstrapConfig, self.defaultButton); + + var _exportData = {}; + self.getExportData = function () { + return _exportData; + }; + self.selectors.forEach(function (el) { + var context = {}; + + context.rows = _nodesArray(el.querySelectorAll('tbody > tr')); + context.rows = settings.headers ? _nodesArray(el.querySelectorAll('thead > tr')).concat(context.rows) : context.rows; + context.rows = settings.footers ? context.rows.concat(_nodesArray(el.querySelectorAll('tfoot > tr'))) : context.rows; + context.thAdj = settings.headers ? el.querySelectorAll('thead > tr').length : 0; + context.filename = settings.filename === 'id' + ? (el.getAttribute('id') ? el.getAttribute('id') : self.defaultFilename) + : (settings.filename ? settings.filename : self.defaultFilename); + context.uuid = _uuid(el); + context.el = el.cloneNode(true); /** - * TableExport configuration options (user-defined w/ default fallback) - */ - self.settings = _extend({}, self.defaults, options); - /** - * Selectors (tables) to apply the library to + * Initializes table caption with export buttons + * @param exportButton {HTMLButtonElement} */ - self.selectors = _nodesArray(selectors); - - var settings = self.settings; - settings.ignoreRows = settings.ignoreRows instanceof Array ? settings.ignoreRows : [settings.ignoreRows]; - settings.ignoreCols = settings.ignoreCols instanceof Array ? settings.ignoreCols : [settings.ignoreCols]; - settings.ignoreCSS = self.ignoreCSS instanceof Array ? self.ignoreCSS : [self.ignoreCSS]; - settings.emptyCSS = self.emptyCSS instanceof Array ? self.emptyCSS : [self.emptyCSS]; - settings.formatValue = self.formatValue.bind(this, settings.trimWhitespace); - settings.bootstrapSettings = _getBootstrapSettings(settings.bootstrap, self.bootstrapConfig, self.defaultButton); - - var _exportData = {}; - self.getExportData = function () { - return _exportData; + context.checkCaption = function (exportButton) { + var caption = el.querySelectorAll('caption.' + self.defaultCaptionClass); + if (caption.length) { + caption[0].appendChild(exportButton); + } else { + caption = document.createElement('caption'); + caption.className = settings.bootstrapSettings.bootstrapSpacing + settings.position + ' ' + self.defaultCaptionClass; + caption.appendChild(exportButton); + el.insertBefore(caption, el.firstChild); + } }; - self.selectors.forEach(function (el) { - var context = {}; - - context.rows = _nodesArray(el.querySelectorAll('tbody > tr')); - context.rows = settings.headers ? _nodesArray(el.querySelectorAll('thead > tr')).concat(context.rows) : context.rows; - context.rows = settings.footers ? context.rows.concat(_nodesArray(el.querySelectorAll('tfoot > tr'))) : context.rows; - context.thAdj = settings.headers ? el.querySelectorAll('thead > tr').length : 0; - context.filename = settings.filename === 'id' - ? (el.getAttribute('id') ? el.getAttribute('id') : self.defaultFilename) - : (settings.filename ? settings.filename : self.defaultFilename); - context.uuid = _uuid(el); - - /** - * Initializes table caption with export buttons - * @param exportButton {HTMLButtonElement} - */ - context.checkCaption = function (exportButton) { - var caption = el.querySelectorAll('caption.' + self.defaultCaptionClass); - if (caption.length) { - caption[0].appendChild(exportButton); - } else { - caption = document.createElement('caption'); - caption.className = settings.bootstrapSettings.bootstrapSpacing + settings.position + ' ' + self.defaultCaptionClass; - caption.appendChild(exportButton); - el.insertBefore(caption, el.firstChild); - } + context.setExportData = (function () { + return function (exporter) { + var data = Storage.getInstance().getItem(exporter); + var type = exporter.substring(exporter.indexOf('-') + 1); + _exportData[context.uuid] = _exportData[context.uuid] || {}; + _exportData[context.uuid][type] = JSON.parse(data); }; - - context.setExportData = (function () { - return function (exporter) { - var data = Storage.getInstance().getItem(exporter); - var type = exporter.substring(exporter.indexOf('-') + 1); - _exportData[context.uuid] = _exportData[context.uuid] || {}; - _exportData[context.uuid][type] = JSON.parse(data); - }; - })(); - - context.rcMap = new RowColMap().build(context, settings); - - var formatMap = _FORMAT_LIST - .reduce(function (acc, cur) { - acc[cur] = 0; - return acc; - }, {}); - - settings.formats.forEach( - function (key) { - if (!_isValidFormat(key)) { - return _handleError('"' + key + '" is not a valid format. \nFormats: ' + _FORMAT_LIST.join(', ')) - } else if (!_hasDependencies(key)) { - // TODO: provide a fallback option to XLS? - return _handleError('"' + key + '" requires "js-xlsx".'); - } else if (!formatMap[key]) { - context.setExportData(self.exporters.build.call(self, context, key)); - formatMap[key]++; - } + })(); + + context.rcMap = new RowColMap().build(context, settings); + + var formatMap = _FORMAT_LIST + .reduce(function (acc, cur) { + acc[cur] = 0; + return acc; + }, {}); + + settings.formats.forEach( + function (key) { + if (!_isValidFormat(key)) { + return _handleError('"' + key + '" is not a valid format. \nFormats: ' + _FORMAT_LIST.join(', ')) + } else if (!_hasDependencies(key)) { + // TODO: provide a fallback option to XLS? + return _handleError('"' + key + '" requires "js-xlsx".'); + } else if (!formatMap[key]) { + context.setExportData(self.exporters.build.call(self, context, key)); + formatMap[key]++; } - ); - }); + } + ); + }); - var exportButton = document.querySelectorAll('button[' + self.storageKey + ']'); - _on(exportButton, 'click', self.downloadHandler, self); + var exportButton = document.querySelectorAll('button[' + self.storageKey + ']'); + _on(exportButton, 'click', self.downloadHandler, self); - return self; - }; + return self; + }; - TableExport.prototype = { - /** - * Version. - * @memberof TableExport.prototype - */ - version: '5.0.3', - /** - * Default library options. - * @memberof TableExport.prototype - */ - defaults: { - headers: true, // (Boolean), display table headers (th or td elements) in the , (default: true) - footers: true, // (Boolean), display table footers (th or td elements) in the
, (default: false) - formats: ['xlsx', 'csv', 'txt'], // (String[]), filetype(s) for the export, (default: ['xlsx', 'csv', 'txt']) - filename: 'id', // (id, String), filename for the downloaded file, (default: 'id') - bootstrap: false, // (Boolean), style buttons using bootstrap, (default: true) - exportButtons: true, // (Boolean), automatically generate the built-in export buttons for each of the specified formats (default: true) - position: 'bottom', // (top, bottom), position of the caption element relative to table, (default: 'bottom') - ignoreRows: null, // (Number, Number[]), row indices to exclude from the exported file(s) (default: null) - ignoreCols: null, // (Number, Number[]), column indices to exclude from the exported file(s) (default: null) - trimWhitespace: true // (Boolean), remove all leading/trailing newlines, spaces, and tabs from cell text in the exported file(s) (default: false) + TableExport.prototype = { + /** + * Version. + * @memberof TableExport.prototype + */ + version: '5.0.3', + /** + * Default library options. + * @memberof TableExport.prototype + */ + defaults: { + headers: true, // (Boolean), display table headers (th or td elements) in the , (default: true) + footers: true, // (Boolean), display table footers (th or td elements) in the , (default: false) + formats: ['xlsx', 'csv', 'txt', 'doc'], // (String[]), filetype(s) for the export, (default: ['xlsx', 'csv', 'txt']) + filename: 'id', // (id, String), filename for the downloaded file, (default: 'id') + bootstrap: false, // (Boolean), style buttons using bootstrap, (default: true) + exportButtons: true, // (Boolean), automatically generate the built-in export buttons for each of the specified formats (default: true) + position: 'bottom', // (top, bottom), position of the caption element relative to table, (default: 'bottom') + ignoreRows: null, // (Number, Number[]), row indices to exclude from the exported file(s) (default: null) + ignoreCols: null, // (Number, Number[]), column indices to exclude from the exported file(s) (default: null) + trimWhitespace: true // (Boolean), remove all leading/trailing newlines, spaces, and tabs from cell text in the exported file(s) (default: false) + }, + /** + * Constants + * @memberof TableExport.prototype + */ + CONSTANTS: { + FORMAT: { + XLSX: 'xlsx', + XLSM: 'xlsm', + XLSB: 'xlsb', + BIFF2: 'biff2', + XLS: 'xls', + CSV: 'csv', + TXT: 'txt', + DOC: 'doc' }, + TYPE: { + STRING: 's', + NUMBER: 'n', + BOOLEAN: 'b', + DATE: 'd' + } + }, + /** + * Character set (character encoding) of the HTML. + * @memberof TableExport.prototype + */ + charset: 'charset=utf-8', + /** + * Filename fallback for exported files. + * @memberof TableExport.prototype + */ + defaultFilename: 'myDownload', + /** + * Class applied to each export button element. + * @memberof TableExport.prototype + */ + defaultButton: 'button-default', + /** + * Class applied to each table caption. + * @memberof TableExport.prototype + */ + defaultCaptionClass: 'tableexport-caption', + /** + * Namespace (i.e. prefix) applied to each table UUID and Storage key. + * @memberof TableExport.prototype + */ + defaultNamespace: 'tableexport-', + /** + * Attribute applied to each table element used to generate each Storage key. + * @memberof TableExport.prototype + */ + tableKey: 'tableexport-key', + /** + * Attribute applied to each export button element used to reference a Storage key. + * @memberof TableExport.prototype + */ + storageKey: 'tableexport-id', + /** + * CSS selector or selector[] to exclude/remove cells from the exported file(s). + * @type {selector|selector[]} + * @memberof TableExport.prototype + */ + ignoreCSS: '.tableexport-ignore', + /** + * CSS selector or selector[] to replace cells with an empty string in the exported file(s). + * @type {selector|selector[]} + * @memberof TableExport.prototype + */ + emptyCSS: '.tableexport-empty', + /** + * Bootstrap configuration classes ['base', 'theme', 'container']. + * @memberof TableExport.prototype + */ + bootstrapConfig: ['btn', 'btn-default', 'btn-toolbar'], + /** + * Row delimeter + * @memberof TableExport.prototype + */ + rowDel: '\r\n', + /** + * HTML entity mapping for special characters. + * @memberof TableExport.prototype + */ + entityMap: { '&': '&', '<': '<', '>': '>', "'": ''', '/': '/' }, + /** + * Format configuration + * @memberof TableExport.prototype + */ + formatConfig: { /** - * Constants + * XLSX (Open XML spreadsheet) file extension configuration * @memberof TableExport.prototype */ - CONSTANTS: { - FORMAT: { - XLSX: 'xlsx', - XLSM: 'xlsm', - XLSB: 'xlsb', - BIFF2: 'biff2', - XLS: 'xls', - CSV: 'csv', - TXT: 'txt' - }, - TYPE: { - STRING: 's', - NUMBER: 'n', - BOOLEAN: 'b', - DATE: 'd' - } + xlsx: { + defaultClass: 'xlsx', + buttonContent: 'Export to xlsx', + mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + fileExtension: '.xlsx' + }, + xlsm: { + defaultClass: 'xlsm', + buttonContent: 'Export to xlsm', + mimeType: 'application/vnd.ms-excel.sheet.macroEnabled.main+xml', + fileExtension: '.xlsm' + }, + xlsb: { + defaultClass: 'xlsb', + buttonContent: 'Export to xlsb', + mimeType: 'application/vnd.ms-excel.sheet.binary.macroEnabled.main', + fileExtension: '.xlsb' }, /** - * Character set (character encoding) of the HTML. - * @memberof TableExport.prototype - */ - charset: 'charset=utf-8', - /** - * Filename fallback for exported files. - * @memberof TableExport.prototype - */ - defaultFilename: 'myDownload', - /** - * Class applied to each export button element. - * @memberof TableExport.prototype - */ - defaultButton: 'button-default', - /** - * Class applied to each table caption. - * @memberof TableExport.prototype - */ - defaultCaptionClass: 'tableexport-caption', - /** - * Namespace (i.e. prefix) applied to each table UUID and Storage key. - * @memberof TableExport.prototype - */ - defaultNamespace: 'tableexport-', - /** - * Attribute applied to each table element used to generate each Storage key. - * @memberof TableExport.prototype - */ - tableKey: 'tableexport-key', - /** - * Attribute applied to each export button element used to reference a Storage key. - * @memberof TableExport.prototype - */ - storageKey: 'tableexport-id', - /** - * CSS selector or selector[] to exclude/remove cells from the exported file(s). - * @type {selector|selector[]} - * @memberof TableExport.prototype - */ - ignoreCSS: '.tableexport-ignore', - /** - * CSS selector or selector[] to replace cells with an empty string in the exported file(s). - * @type {selector|selector[]} - * @memberof TableExport.prototype - */ - emptyCSS: '.tableexport-empty', - /** - * Bootstrap configuration classes ['base', 'theme', 'container']. + * XLS (Binary spreadsheet) file extension configuration * @memberof TableExport.prototype */ - bootstrapConfig: ['btn', 'btn-default', 'btn-toolbar'], + xls: { + defaultClass: 'xls', + buttonContent: 'Export to xls', + separator: '\t', + mimeType: 'application/vnd.ms-excel', + fileExtension: '.xls', + enforceStrictRFC4180: false + }, /** - * Row delimeter + * CSV (Comma Separated Values) file extension configuration * @memberof TableExport.prototype */ - rowDel: '\r\n', + csv: { + defaultClass: 'csv', + buttonContent: 'Export to csv', + separator: ',', + mimeType: 'text/csv', + fileExtension: '.csv', + enforceStrictRFC4180: true + }, /** - * HTML entity mapping for special characters. + * TXT (Plain Text) file extension configuration * @memberof TableExport.prototype */ - entityMap: {'&': '&', '<': '<', '>': '>', "'": ''', '/': '/'}, + txt: { + defaultClass: 'txt', + buttonContent: 'Export to txt', + separator: ' ', + mimeType: 'text/plain', + fileExtension: '.txt', + enforceStrictRFC4180: true + }, /** - * Format configuration + * DOC (Microsoft Office Word) file extension configuration * @memberof TableExport.prototype */ - formatConfig: { - /** - * XLSX (Open XML spreadsheet) file extension configuration - * @memberof TableExport.prototype - */ - xlsx: { - defaultClass: 'xlsx', - buttonContent: 'Export to xlsx', - mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - fileExtension: '.xlsx' - }, - xlsm: { - defaultClass: 'xlsm', - buttonContent: 'Export to xlsm', - mimeType: 'application/vnd.ms-excel.sheet.macroEnabled.main+xml', - fileExtension: '.xlsm' - }, - xlsb: { - defaultClass: 'xlsb', - buttonContent: 'Export to xlsb', - mimeType: 'application/vnd.ms-excel.sheet.binary.macroEnabled.main', - fileExtension: '.xlsb' - }, - /** - * XLS (Binary spreadsheet) file extension configuration - * @memberof TableExport.prototype - */ - xls: { - defaultClass: 'xls', - buttonContent: 'Export to xls', - separator: '\t', - mimeType: 'application/vnd.ms-excel', - fileExtension: '.xls', - enforceStrictRFC4180: false - }, - /** - * CSV (Comma Separated Values) file extension configuration - * @memberof TableExport.prototype - */ - csv: { - defaultClass: 'csv', - buttonContent: 'Export to csv', - separator: ',', - mimeType: 'text/csv', - fileExtension: '.csv', - enforceStrictRFC4180: true - }, - /** - * TXT (Plain Text) file extension configuration - * @memberof TableExport.prototype - */ - txt: { - defaultClass: 'txt', - buttonContent: 'Export to txt', - separator: ' ', - mimeType: 'text/plain', - fileExtension: '.txt', - enforceStrictRFC4180: true + doc: { + defaultClass: 'doc', + buttonContent: 'Export to Microsoft Office Word', + separator: ' ', + mimeType: 'application/msword', + fileExtension: '.doc', + enforceStrictRFC4180: true + } + }, + /** + * Cell-types override and assertion configuration + * @memberof TableExport.prototype + */ + typeConfig: { + string: { + defaultClass: 'tableexport-string' + }, + number: { + defaultClass: 'tableexport-number', + assert: function (v) { + return !isNaN(v); } }, - /** - * Cell-types override and assertion configuration - * @memberof TableExport.prototype - */ - typeConfig: { - string: { - defaultClass: 'tableexport-string' - }, - number: { - defaultClass: 'tableexport-number', - assert: function (v) { - return !isNaN(v); - } - }, - boolean: { - defaultClass: 'tableexport-boolean', - assert: function (v) { - return v.toLowerCase() === 'true' || v.toLowerCase() === 'false'; - } - }, - date: { - defaultClass: 'tableexport-date', - assert: function (v) { - return !(/.*%/.test(v)) && !isNaN(Date.parse(v)) - } + boolean: { + defaultClass: 'tableexport-boolean', + assert: function (v) { + return v.toLowerCase() === 'true' || v.toLowerCase() === 'false'; } }, - exporters: { - build: function (context, key) { - var self = this; - var settings = self.settings; - var format = self.formatConfig[key]; - var colDel = format.separator; - var rcMap = context.rcMap; - - var getReturn = function (val) { - if (_isEnhanced(key)) { - return { - v: settings.formatValue(val.textContent), - t: self.getType(val.className) - } - } - switch (key) { - case _FORMAT.CSV: - return '"' + settings.formatValue(val.textContent.replace(/"/g, '""')) + '"'; - default: - return settings.formatValue(val.textContent); - } - }; + date: { + defaultClass: 'tableexport-date', + assert: function (v) { + return !(/.*%/.test(v)) && !isNaN(Date.parse(v)) + } + } + }, + exporters: { + build: function (context, key) { + var self = this; + var settings = self.settings; + var format = self.formatConfig[key]; + var colDel = format.separator; + var rcMap = context.rcMap; - var dataURI = _nodesArray(context.rows).map(function (val, ir) { + var getReturn = function (val) { + if (_isEnhanced(key)) { + return { + v: settings.formatValue(val.textContent), + t: self.getType(val.className) + } + } + switch (key) { + case _FORMAT.CSV: + return '"' + settings.formatValue(val.textContent.replace(/"/g, '""')) + '"'; + default: + return settings.formatValue(val.textContent); + } + }; + var dataURI = null; + if (key !== "doc") { + dataURI = _nodesArray(context.rows).map(function (val, ir) { if (rcMap.isIgnore(ir)) { return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); } else if (rcMap.isEmpty(ir)) { @@ -380,748 +394,753 @@ } }).processCols(key, colDel); }).processRows(key, self.rowDel); - - var dataObject = JSON.stringify({ - data: dataURI, - filename: context.filename, - mimeType: format.mimeType, - fileExtension: format.fileExtension, - merges: rcMap.merges - }); - - var hashKey = _hashCode({uuid: context.uuid, type: key}); - - settings.exportButtons && context.checkCaption(self.createObjButton( - hashKey, - dataObject, - format.buttonContent, - format.defaultClass, - settings.bootstrapSettings - )); - return Storage.getInstance().setItem(hashKey, dataObject, true); - } - }, - /** - * Creates file export buttons - * @param hashKey {String} - * @param dataObject {String} - * @param myContent {String} - * @param myClass {String} - * @param bootstrapSettings {Object} - * @returns Element - */ - createObjButton: function (hashKey, dataObject, myContent, myClass, bootstrapSettings) { - var exportButton = document.createElement('button'); - exportButton.setAttribute('type', 'button'); - exportButton.setAttribute(this.storageKey, hashKey); - exportButton.className = bootstrapSettings.bootstrapClass + bootstrapSettings.bootstrapTheme + myClass; - exportButton.textContent = myContent; - return exportButton - }, - /** - * Escapes special characters with HTML entities - * @memberof TableExport.prototype - * @param string {String} - * @returns {String} escaped string - */ - escapeHtml: function (string) { - var self = this; - return String(string).replace(/[&<>'\/]/g, function (s) { - return self.entityMap[s]; - }); - }, - /** - * Unescapes HTML entities to special characters - * @memberof TableExport.prototype - * @param string {String} - * @returns {String} unescaped string - */ - unescapeHtml: function (string) { - var str = String(string); - for (var key in this.entityMap) { - str = str.replace(RegExp(this.entityMap[key], 'g'), key); - } - return str; - }, - /** - * Removes leading/trailing whitespace from cell string - * @param isTrimWhitespace {Boolean} - * @param string {String} - * @returns {String} trimmed string - */ - formatValue: function (isTrimWhitespace, string) { - return isTrimWhitespace ? string.trim() : string; - }, - /** - * Get cell data-type - * @param string {String} - * @returns {String} data-type - */ - getType: function (string) { - if (!string) return ''; - var types = this.typeConfig; - if (~string.indexOf(types.string.defaultClass)) { - return _TYPE.STRING; - } else if (~string.indexOf(types.number.defaultClass)) { - return _TYPE.NUMBER; - } else if (~string.indexOf(types.boolean.defaultClass)) { - return _TYPE.BOOLEAN; - } else if (~string.indexOf(types.date.defaultClass)) { - return _TYPE.DATE; - } else { - return ''; - } - }, - /** - * Formats datetimes for compatibility with Excel - * @memberof TableExport.prototype - * @param v {Number} - * @param date1904 {Date} - * @returns {Number} epoch time - */ - dateNum: function (v, date1904) { - if (date1904) v += 1462; - var epoch = Date.parse(v); - var result = (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); - return Math.floor(result); - }, - /** - * Creates an Excel spreadsheet from a data string - * @memberof TableExport.prototype - * @param data {String} - * @param merges {Object[]} - */ - createSheet: function (data, merges) { - var ws = {}; - var range = {s: {c: 10000000, r: 10000000}, e: {c: 0, r: 0}}; - var types = this.typeConfig; - for (var R = 0; R !== data.length; ++R) { - for (var C = 0; C !== data[R].length; ++C) { - if (range.s.r > R) range.s.r = R; - if (range.s.c > C) range.s.c = C; - if (range.e.r < R) range.e.r = R; - if (range.e.c < C) range.e.c = C; - var cell = data[R][C]; - if (!cell || !cell.v) continue; - var cell_ref = XLSX.utils.encode_cell({c: C, r: R}); - - if (!cell.t) { - if (types.number.assert(cell.v)) cell.t = _TYPE.NUMBER; - else if (types.boolean.assert(cell.v)) cell.t = _TYPE.BOOLEAN; - else if (types.date.assert(cell.v)) cell.t = _TYPE.DATE; - else cell.t = _TYPE.STRING; - } - if (cell.t === _TYPE.DATE) { - cell.t = _TYPE.NUMBER; - cell.z = XLSX.SSF._table[14]; - cell.v = this.dateNum(cell.v); - } - ws[cell_ref] = cell; - } - } - ws['!merges'] = merges; - if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); - return ws; - }, - /** - * Click handler for export button "download" - * @memberof TableExport.prototype - */ - downloadHandler: function (event) { - var target = event.target; - var object = JSON.parse(Storage.getInstance().getItem(target.getAttribute(this.storageKey))), - data = object.data, - filename = object.filename, - mimeType = object.mimeType, - fileExtension = object.fileExtension, - merges = object.merges; - this.export2file(data, mimeType, filename, fileExtension, merges); - }, - /** - * Excel Workbook constructor - * @memberof TableExport.prototype - * @constructor - */ - Workbook: function () { - this.SheetNames = []; - this.Sheets = {}; - }, - /** - * Converts a string to an arraybuffer - * @param s {String} - * @memberof TableExport.prototype - * @returns {ArrayBuffer} - */ - string2ArrayBuffer: function (s) { - var buf = new ArrayBuffer(s.length); - var view = new Uint8Array(buf); - for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; - return buf; - }, - /** - * Exports and downloads the file - * @memberof TableExport.prototype - * @param data {String} - * @param mime {String} mime type - * @param name {String} filename - * @param extension {String} file extension - * @param merges {Object[]} - */ - export2file: function (data, mime, name, extension, merges) { - data = this.getRawData(data, extension, name, merges); - - if (_isMobile) { - // TODO: fix dataURI on iphone (xlsx & xls) - var dataURI = 'data:' + mime + ';' + this.charset + ',' + data; - this.downloadDataURI(dataURI, name, extension); } else { - // TODO: error and fallback when `saveAs` not available - saveAs(new Blob([data], - {type: mime + ';' + this.charset}), - name + extension, true); - } - }, - downloadDataURI: function (dataURI, name, extension) { - var encodedUri = encodeURI(dataURI); - var link = document.createElement('a'); - link.setAttribute('href', encodedUri); - link.setAttribute('download', name + extension); - document.body.appendChild(link); - link.click(); - }, - getBookType: function (key) { - switch (key) { - case _FORMAT.XLS: - return _FORMAT.BIFF2; - default: - return key; + // Remove the buttons + context.el.querySelectorAll('button').forEach(e => e.parentNode.removeChild(e)); + dataURI = context.el.outerHTML } - }, - getRawData: function (data, extension, name, merges) { - var key = extension.substring(1); - if (_isEnhanced(key)) { - var wb = new this.Workbook(), - ws = this.createSheet(data, merges), - bookType = this.getBookType(key); - - name = name || ''; - wb.SheetNames.push(name); - wb.Sheets[name] = ws; - var wopts = { - bookType: bookType, - bookSST: false, - type: 'binary' - }, - wbout = XLSX.write(wb, wopts); - - data = this.string2ArrayBuffer(wbout); - } - return data; - }, - getFileSize: function (data, extension) { - var binary = this.getRawData(data, extension); - return binary instanceof ArrayBuffer - ? binary.byteLength - : this.string2ArrayBuffer(binary).byteLength; - }, - /** - * Updates the library instance with new/updated options - * @param options {Object} TableExport configuration options - * @returns {TableExport} updated TableExport instance - */ - update: function (options) { - this.remove(); - return new TableExport(this.selectors, _extend({}, this.defaults, options)); - }, - /** - * Reset the library instance to its original state - * @returns {TableExport} original TableExport instance - */ - reset: function () { - this.remove(); - return new TableExport(this.selectors, this.settings); - }, - /** - * Remove the instance (i.e. caption containing the export buttons) - */ - remove: function () { - var self = this; - this.selectors.forEach(function (el) { - var caption = el.querySelector('caption.' + self.defaultCaptionClass); - caption && el.removeChild(caption); + var dataObject = JSON.stringify({ + data: dataURI, + filename: context.filename, + mimeType: format.mimeType, + fileExtension: format.fileExtension, + merges: rcMap.merges }); - } - }; + var hashKey = _hashCode({ uuid: context.uuid, type: key }); + + settings.exportButtons && context.checkCaption(self.createObjButton( + hashKey, + dataObject, + format.buttonContent, + format.defaultClass, + settings.bootstrapSettings + )); + return Storage.getInstance().setItem(hashKey, dataObject, true); + } + }, /** - * Storage main interface constructor + * Creates file export buttons + * @param hashKey {String} + * @param dataObject {String} + * @param myContent {String} + * @param myClass {String} + * @param bootstrapSettings {Object} + * @returns Element + */ + createObjButton: function (hashKey, dataObject, myContent, myClass, bootstrapSettings) { + var exportButton = document.createElement('button'); + exportButton.setAttribute('type', 'button'); + exportButton.setAttribute(this.storageKey, hashKey); + exportButton.className = bootstrapSettings.bootstrapClass + bootstrapSettings.bootstrapTheme + myClass; + exportButton.textContent = myContent; + return exportButton + }, + /** + * Escapes special characters with HTML entities * @memberof TableExport.prototype - * @constructor + * @param string {String} + * @returns {String} escaped string */ - var Storage = function () { - this._instance = null; - - this.store = sessionStorage; - this.namespace = TableExport.prototype.defaultNamespace; - this.getKey = function (key) { - return this.namespace + key; - }; - this.setItem = function (_key, value, overwrite) { - var key = this.getKey(_key); - if (this.exists(_key) && !overwrite) { - return; + escapeHtml: function (string) { + var self = this; + return String(string).replace(/[&<>'\/]/g, function (s) { + return self.entityMap[s]; + }); + }, + /** + * Unescapes HTML entities to special characters + * @memberof TableExport.prototype + * @param string {String} + * @returns {String} unescaped string + */ + unescapeHtml: function (string) { + var str = String(string); + for (var key in this.entityMap) { + str = str.replace(RegExp(this.entityMap[key], 'g'), key); + } + return str; + }, + /** + * Removes leading/trailing whitespace from cell string + * @param isTrimWhitespace {Boolean} + * @param string {String} + * @returns {String} trimmed string + */ + formatValue: function (isTrimWhitespace, string) { + return isTrimWhitespace ? string.trim() : string; + }, + /** + * Get cell data-type + * @param string {String} + * @returns {String} data-type + */ + getType: function (string) { + if (!string) return ''; + var types = this.typeConfig; + if (~string.indexOf(types.string.defaultClass)) { + return _TYPE.STRING; + } else if (~string.indexOf(types.number.defaultClass)) { + return _TYPE.NUMBER; + } else if (~string.indexOf(types.boolean.defaultClass)) { + return _TYPE.BOOLEAN; + } else if (~string.indexOf(types.date.defaultClass)) { + return _TYPE.DATE; + } else { + return ''; + } + }, + /** + * Formats datetimes for compatibility with Excel + * @memberof TableExport.prototype + * @param v {Number} + * @param date1904 {Date} + * @returns {Number} epoch time + */ + dateNum: function (v, date1904) { + if (date1904) v += 1462; + var epoch = Date.parse(v); + var result = (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000); + return Math.floor(result); + }, + /** + * Creates an Excel spreadsheet from a data string + * @memberof TableExport.prototype + * @param data {String} + * @param merges {Object[]} + */ + createSheet: function (data, merges) { + var ws = {}; + var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } }; + var types = this.typeConfig; + for (var R = 0; R !== data.length; ++R) { + for (var C = 0; C !== data[R].length; ++C) { + if (range.s.r > R) range.s.r = R; + if (range.s.c > C) range.s.c = C; + if (range.e.r < R) range.e.r = R; + if (range.e.c < C) range.e.c = C; + var cell = data[R][C]; + if (!cell || !cell.v) continue; + var cell_ref = XLSX.utils.encode_cell({ c: C, r: R }); + + if (!cell.t) { + if (types.number.assert(cell.v)) cell.t = _TYPE.NUMBER; + else if (types.boolean.assert(cell.v)) cell.t = _TYPE.BOOLEAN; + else if (types.date.assert(cell.v)) cell.t = _TYPE.DATE; + else cell.t = _TYPE.STRING; + } + if (cell.t === _TYPE.DATE) { + cell.t = _TYPE.NUMBER; + cell.z = XLSX.SSF._table[14]; + cell.v = this.dateNum(cell.v); + } + ws[cell_ref] = cell; } - if (typeof value !== 'string') return _handleError('"value" must be a string.'); - this.store.setItem(key, value); - return _key; - }; - this.getItem = function (_key) { - var key = this.getKey(_key); - return this.store.getItem(key); - }; - this.exists = function (_key) { - var key = this.getKey(_key); - return this.store.getItem(key) !== null; - }; - this.removeItem = function (_key) { - var key = this.getKey(_key); - return this.store.removeItem(key); - }; - }; - Storage.getInstance = function () { - if (!this._instance) { - this._instance = new Storage(); } - return this._instance; - }; - + ws['!merges'] = merges; + if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); + return ws; + }, + /** + * Click handler for export button "download" + * @memberof TableExport.prototype + */ + downloadHandler: function (event) { + var target = event.target; + var object = JSON.parse(Storage.getInstance().getItem(target.getAttribute(this.storageKey))), + data = object.data, + filename = object.filename, + mimeType = object.mimeType, + fileExtension = object.fileExtension, + merges = object.merges; + this.export2file(data, mimeType, filename, fileExtension, merges); + }, /** - * RowColMap main interface constructor + * Excel Workbook constructor * @memberof TableExport.prototype * @constructor */ - var RowColMap = function () { - this.rcMap = []; - this.merges = []; + Workbook: function () { + this.SheetNames = []; + this.Sheets = {}; + }, + /** + * Converts a string to an arraybuffer + * @param s {String} + * @memberof TableExport.prototype + * @returns {ArrayBuffer} + */ + string2ArrayBuffer: function (s) { + var buf = new ArrayBuffer(s.length); + var view = new Uint8Array(buf); + for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF; + return buf; + }, + /** + * Exports and downloads the file + * @memberof TableExport.prototype + * @param data {String} + * @param mime {String} mime type + * @param name {String} filename + * @param extension {String} file extension + * @param merges {Object[]} + */ + export2file: function (data, mime, name, extension, merges) { + data = this.getRawData(data, extension, name, merges); - this.isIgnore = function (ir, ic) { - var _ignore = RowColMap.prototype.TYPE.IGNORE; - return this.getRowColMapProp(ir, ic, _ignore); - }; - this.isEmpty = function (ir, ic) { - var _empty = RowColMap.prototype.TYPE.EMPTY; - return this.getRowColMapProp(ir, ic, _empty); - }; - this.isRowSpan = function (ir) { - var _rowspan = RowColMap.prototype.TYPE.ROWSPAN; - return this.getRowColMapProp(ir, undefined, _rowspan); - }; - this.isColSpan = function (ir) { - var _colspan = RowColMap.prototype.TYPE.COLSPAN; - return this.getRowColMapProp(ir, undefined, _colspan); - }; - this.isSpan = function (ir) { - return this.isRowSpan(ir) || this.isColSpan(ir); - }; - this.isMerge = function (ir) { - return this.merges.length > 0; - }; - this.addMerge = function (ir, mergeObj) { - var _merge = RowColMap.prototype.TYPE.MERGE; - this.merges.push(mergeObj); - this.setRowColMapProp(ir, undefined, _merge, this.merges); - }; - this.getRowColMapProp = function (ir, ic, key) { - if (this.rcMap[ir]) { - if (typeof key === 'undefined') { - return this.rcMap[ir][ic]; - } else if (typeof ic === 'undefined') { - return this.rcMap[ir][key]; - } else if (this.rcMap[ir][ic]) { - return this.rcMap[ir][ic][key]; - } - } - return undefined; - }; - this.setRowColMapProp = function (ir, ic, key, value) { - this.rcMap[ir] = this.rcMap[ir] || []; + if (_isMobile) { + // TODO: fix dataURI on iphone (xlsx & xls) + var dataURI = 'data:' + mime + ';' + this.charset + ',' + data; + this.downloadDataURI(dataURI, name, extension); + } else { + // TODO: error and fallback when `saveAs` not available + saveAs(new Blob([data], + { type: mime + ';' + this.charset }), + name + extension, true); + } + }, + downloadDataURI: function (dataURI, name, extension) { + var encodedUri = encodeURI(dataURI); + var link = document.createElement('a'); + link.setAttribute('href', encodedUri); + link.setAttribute('download', name + extension); + document.body.appendChild(link); + link.click(); + }, + getBookType: function (key) { + switch (key) { + case _FORMAT.XLS: + return _FORMAT.BIFF2; + default: + return key; + } + }, + getRawData: function (data, extension, name, merges) { + var key = extension.substring(1); + + if (_isEnhanced(key)) { + var wb = new this.Workbook(), + ws = this.createSheet(data, merges), + bookType = this.getBookType(key); + + name = name || ''; + wb.SheetNames.push(name); + wb.Sheets[name] = ws; + var wopts = { + bookType: bookType, + bookSST: false, + type: 'binary' + }, + wbout = XLSX.write(wb, wopts); + + data = this.string2ArrayBuffer(wbout); + } + return data; + }, + getFileSize: function (data, extension) { + var binary = this.getRawData(data, extension); + return binary instanceof ArrayBuffer + ? binary.byteLength + : this.string2ArrayBuffer(binary).byteLength; + }, + /** + * Updates the library instance with new/updated options + * @param options {Object} TableExport configuration options + * @returns {TableExport} updated TableExport instance + */ + update: function (options) { + this.remove(); + return new TableExport(this.selectors, _extend({}, this.defaults, options)); + }, + /** + * Reset the library instance to its original state + * @returns {TableExport} original TableExport instance + */ + reset: function () { + this.remove(); + return new TableExport(this.selectors, this.settings); + }, + /** + * Remove the instance (i.e. caption containing the export buttons) + */ + remove: function () { + var self = this; + this.selectors.forEach(function (el) { + var caption = el.querySelector('caption.' + self.defaultCaptionClass); + caption && el.removeChild(caption); + }); + } + }; + + /** + * Storage main interface constructor + * @memberof TableExport.prototype + * @constructor + */ + var Storage = function () { + this._instance = null; + + this.store = sessionStorage; + this.namespace = TableExport.prototype.defaultNamespace; + this.getKey = function (key) { + return this.namespace + key; + }; + this.setItem = function (_key, value, overwrite) { + var key = this.getKey(_key); + if (this.exists(_key) && !overwrite) { + return; + } + if (typeof value !== 'string') return _handleError('"value" must be a string.'); + this.store.setItem(key, value); + return _key; + }; + this.getItem = function (_key) { + var key = this.getKey(_key); + return this.store.getItem(key); + }; + this.exists = function (_key) { + var key = this.getKey(_key); + return this.store.getItem(key) !== null; + }; + this.removeItem = function (_key) { + var key = this.getKey(_key); + return this.store.removeItem(key); + }; + }; + Storage.getInstance = function () { + if (!this._instance) { + this._instance = new Storage(); + } + return this._instance; + }; + + /** + * RowColMap main interface constructor + * @memberof TableExport.prototype + * @constructor + */ + var RowColMap = function () { + this.rcMap = []; + this.merges = []; + + this.isIgnore = function (ir, ic) { + var _ignore = RowColMap.prototype.TYPE.IGNORE; + return this.getRowColMapProp(ir, ic, _ignore); + }; + this.isEmpty = function (ir, ic) { + var _empty = RowColMap.prototype.TYPE.EMPTY; + return this.getRowColMapProp(ir, ic, _empty); + }; + this.isRowSpan = function (ir) { + var _rowspan = RowColMap.prototype.TYPE.ROWSPAN; + return this.getRowColMapProp(ir, undefined, _rowspan); + }; + this.isColSpan = function (ir) { + var _colspan = RowColMap.prototype.TYPE.COLSPAN; + return this.getRowColMapProp(ir, undefined, _colspan); + }; + this.isSpan = function (ir) { + return this.isRowSpan(ir) || this.isColSpan(ir); + }; + this.isMerge = function (ir) { + return this.merges.length > 0; + }; + this.addMerge = function (ir, mergeObj) { + var _merge = RowColMap.prototype.TYPE.MERGE; + this.merges.push(mergeObj); + this.setRowColMapProp(ir, undefined, _merge, this.merges); + }; + this.getRowColMapProp = function (ir, ic, key) { + if (this.rcMap[ir]) { if (typeof key === 'undefined') { - return this.rcMap[ir][ic] = value; + return this.rcMap[ir][ic]; } else if (typeof ic === 'undefined') { - return this.rcMap[ir][key] = value; - } else { - this.rcMap[ir][ic] = this.rcMap[ir][ic] || []; - return this.rcMap[ir][ic][key] = value; + return this.rcMap[ir][key]; + } else if (this.rcMap[ir][ic]) { + return this.rcMap[ir][ic][key]; } - }; - this.generateTotal = function (ir, ic) { - var VALUE = RowColMap.prototype.TYPE.VALUE; - var _total = 0; - - if (this.isRowSpan(ir) && this.isColSpan(ir)) { - _total = this.getRowColMapProp(ir, ic, VALUE) || 0; - } else if (this.getRowColMapProp(ir, ic, VALUE)) { - _total = this.getRowColMapProp(ir, ic, VALUE); - } - return _total; - }; - this.convertSpanToArray = function (ir, ic, key, _return, colDel) { - if (this.rcMap[ir] && this.isSpan(ir)) { - var total = this.generateTotal(ir, ic); + } + return undefined; + }; + this.setRowColMapProp = function (ir, ic, key, value) { + this.rcMap[ir] = this.rcMap[ir] || []; + if (typeof key === 'undefined') { + return this.rcMap[ir][ic] = value; + } else if (typeof ic === 'undefined') { + return this.rcMap[ir][key] = value; + } else { + this.rcMap[ir][ic] = this.rcMap[ir][ic] || []; + return this.rcMap[ir][ic][key] = value; + } + }; + this.generateTotal = function (ir, ic) { + var VALUE = RowColMap.prototype.TYPE.VALUE; + var _total = 0; + + if (this.isRowSpan(ir) && this.isColSpan(ir)) { + _total = this.getRowColMapProp(ir, ic, VALUE) || 0; + } else if (this.getRowColMapProp(ir, ic, VALUE)) { + _total = this.getRowColMapProp(ir, ic, VALUE); + } + return _total; + }; + this.convertSpanToArray = function (ir, ic, key, _return, colDel) { + if (this.rcMap[ir] && this.isSpan(ir)) { + var total = this.generateTotal(ir, ic); - if (_isEnhanced(key)) { - return new Array(total).concat(_return); - } else { - return new Array(total).concat(_return).join(colDel); - } - } - return _return; - }; - this.handleRowColMapProp = function (type, ir, ic, key, _return, colDel) { - switch (type) { - case RowColMap.prototype.TYPE.IGNORE: - return; - case RowColMap.prototype.TYPE.EMPTY: - return ' '; - case RowColMap.prototype.TYPE.DEFAULT: - default: - return this.convertSpanToArray(ir, ic, key, _return, colDel); + if (_isEnhanced(key)) { + return new Array(total).concat(_return); + } else { + return new Array(total).concat(_return).join(colDel); } - }; + } + return _return; }; - RowColMap.prototype = { - OFFSET: 1, - TYPE: { - IGNORE: 'ignore', - EMPTY: 'empty', - MERGE: 'merge', - ROWSPAN: 'rowspan', - ROWSPANTOTAL: 'rowspantotal', - COLSPAN: 'colspan', - COLSPANTOTAL: 'colspantotal', - DEFAULT: 'default', - VALUE: 'value' - }, - build: function (context, settings) { - var self = this; + this.handleRowColMapProp = function (type, ir, ic, key, _return, colDel) { + switch (type) { + case RowColMap.prototype.TYPE.IGNORE: + return; + case RowColMap.prototype.TYPE.EMPTY: + return ' '; + case RowColMap.prototype.TYPE.DEFAULT: + default: + return this.convertSpanToArray(ir, ic, key, _return, colDel); + } + }; + }; + RowColMap.prototype = { + OFFSET: 1, + TYPE: { + IGNORE: 'ignore', + EMPTY: 'empty', + MERGE: 'merge', + ROWSPAN: 'rowspan', + ROWSPANTOTAL: 'rowspantotal', + COLSPAN: 'colspan', + COLSPANTOTAL: 'colspantotal', + DEFAULT: 'default', + VALUE: 'value' + }, + build: function (context, settings) { + var self = this; - var OFFSET = self.OFFSET; - var rowLength = self.rowLength = context.rows.length; - // var colLength = self.colLength = Math.max.apply(null, - // _nodesArray(context.rows).map(function (val) { - // return val.querySelectorAll('th, td').length - // })); + var OFFSET = self.OFFSET; + var rowLength = self.rowLength = context.rows.length; + // var colLength = self.colLength = Math.max.apply(null, + // _nodesArray(context.rows).map(function (val) { + // return val.querySelectorAll('th, td').length + // })); - var handleIgnore = function (ir, ic) { - self.setRowColMapProp(ir, ic, self.TYPE.IGNORE, true); - }; - var handleEmpty = function (ir, ic) { - self.setRowColMapProp(ir, ic, self.TYPE.EMPTY, true); - }; - var handleRowSpan = function (val, ir, ic) { - var rowSpan = +val.getAttribute('rowspan'); - var colSpan = +val.getAttribute('colspan'); - var handledByColSpan, - countRowSpan, countColSpan, - totalRowSpan, totalColSpan, - irStart, irEnd, icStart, icEnd; - - for (var _row = 0; _row < rowSpan; _row++) { - if (_row + ir >= rowLength) { - return; - } - colSpan && (handledByColSpan = handleColSpan(val, _row + ir, ic, _row > 0, rowSpan)); + var handleIgnore = function (ir, ic) { + self.setRowColMapProp(ir, ic, self.TYPE.IGNORE, true); + }; + var handleEmpty = function (ir, ic) { + self.setRowColMapProp(ir, ic, self.TYPE.EMPTY, true); + }; + var handleRowSpan = function (val, ir, ic) { + var rowSpan = +val.getAttribute('rowspan'); + var colSpan = +val.getAttribute('colspan'); + var handledByColSpan, + countRowSpan, countColSpan, + totalRowSpan, totalColSpan, + irStart, irEnd, icStart, icEnd; + + for (var _row = 0; _row < rowSpan; _row++) { + if (_row + ir >= rowLength) { + return; + } + colSpan && (handledByColSpan = handleColSpan(val, _row + ir, ic, _row > 0, rowSpan)); - if (rowSpan <= 1) { - return false; - } - var cur = self.rcMap['c' + (ic - 1)] ? self.rcMap['c' + (ic - 1)][_row + ir] : 0; - if (cur) { - self.rcMap['c' + (ic)] = self.rcMap['c' + (ic)] || []; - self.rcMap['c' + (ic)][_row + ir] = (self.rcMap['c' + (ic)][_row + ir] || 0) + cur; - } + if (rowSpan <= 1) { + return false; + } + var cur = self.rcMap['c' + (ic - 1)] ? self.rcMap['c' + (ic - 1)][_row + ir] : 0; + if (cur) { + self.rcMap['c' + (ic)] = self.rcMap['c' + (ic)] || []; + self.rcMap['c' + (ic)][_row + ir] = (self.rcMap['c' + (ic)][_row + ir] || 0) + cur; + } - if (rowSpan && _row === 0 && colSpan > 1) { - for (var i = 0; i < rowSpan; i++) { - self.rcMap['c' + (ic + 1)] = self.rcMap['c' + (ic + 1)] || []; - self.rcMap['c' + (ic + 1)][_row + ir + i] = (self.rcMap['c' + (ic + 1)][_row + ir + i] || 0) + Math.max(1, colSpan); + if (rowSpan && _row === 0 && colSpan > 1) { + for (var i = 0; i < rowSpan; i++) { + self.rcMap['c' + (ic + 1)] = self.rcMap['c' + (ic + 1)] || []; + self.rcMap['c' + (ic + 1)][_row + ir + i] = (self.rcMap['c' + (ic + 1)][_row + ir + i] || 0) + Math.max(1, colSpan); - } } + } - if (_row >= 1) { - countRowSpan = self.getRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN) || 0; - self.setRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN, countRowSpan + 1); - - if (!handledByColSpan) { - totalRowSpan = self.getRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE) || 0; - self.setRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE, totalRowSpan + 1); - if (rowSpan > 1 && _row === 1) { - var _re = self.rcMap['c' + (ic)] && self.rcMap['c' + (ic)][_row + ir]; - totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; - countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; - irStart = ir; - irEnd = ir + rowSpan - 1; - icStart = ic + totalColSpan - countColSpan + (_re || 0); - icEnd = icStart + Math.max(1, colSpan) - 1; - handleMerge(irStart, icStart, irEnd, icEnd); - } + if (_row >= 1) { + countRowSpan = self.getRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN) || 0; + self.setRowColMapProp(_row + ir, undefined, self.TYPE.ROWSPAN, countRowSpan + 1); + + if (!handledByColSpan) { + totalRowSpan = self.getRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE) || 0; + self.setRowColMapProp(_row + ir, ic - countRowSpan, self.TYPE.VALUE, totalRowSpan + 1); + if (rowSpan > 1 && _row === 1) { + var _re = self.rcMap['c' + (ic)] && self.rcMap['c' + (ic)][_row + ir]; + totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; + countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; + irStart = ir; + irEnd = ir + rowSpan - 1; + icStart = ic + totalColSpan - countColSpan + (_re || 0); + icEnd = icStart + Math.max(1, colSpan) - 1; + handleMerge(irStart, icStart, irEnd, icEnd); } } } - }; + } + }; - var handleColSpan = function (val, ir, ic, isRowSpan, rowSpan) { - var irStart, irEnd, icStart, icEnd; - var colSpan = +val.getAttribute('colspan'); - var countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; - var totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; + var handleColSpan = function (val, ir, ic, isRowSpan, rowSpan) { + var irStart, irEnd, icStart, icEnd; + var colSpan = +val.getAttribute('colspan'); + var countColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPAN) || 0; + var totalColSpan = self.getRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL) || 0; - if (colSpan <= 1) { - return false; - } + if (colSpan <= 1) { + return false; + } - self.setRowColMapProp(ir, undefined, self.TYPE.COLSPAN, countColSpan + 1); - self.setRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL, totalColSpan + colSpan); - - if (isRowSpan) { - self.setRowColMapProp(ir, ic - countColSpan, self.TYPE.VALUE, colSpan); - return true; - } else { - irStart = ir; - irEnd = ir + (rowSpan || 1) - OFFSET; - icStart = ic + totalColSpan - countColSpan; - icEnd = ic + totalColSpan - countColSpan + (colSpan - OFFSET); - self.setRowColMapProp(ir, ic + OFFSET, self.TYPE.VALUE, colSpan - OFFSET); - handleMerge(irStart, icStart, irEnd, icEnd); - } - }; + self.setRowColMapProp(ir, undefined, self.TYPE.COLSPAN, countColSpan + 1); + self.setRowColMapProp(ir, undefined, self.TYPE.COLSPANTOTAL, totalColSpan + colSpan); + + if (isRowSpan) { + self.setRowColMapProp(ir, ic - countColSpan, self.TYPE.VALUE, colSpan); + return true; + } else { + irStart = ir; + irEnd = ir + (rowSpan || 1) - OFFSET; + icStart = ic + totalColSpan - countColSpan; + icEnd = ic + totalColSpan - countColSpan + (colSpan - OFFSET); + self.setRowColMapProp(ir, ic + OFFSET, self.TYPE.VALUE, colSpan - OFFSET); + handleMerge(irStart, icStart, irEnd, icEnd); + } + }; - var handleMerge = function (irs, ics, ire, ice) { - var merge = { - s: {r: irs, c: ics}, - e: {r: ire, c: ice} - }; - return self.addMerge(irs, merge); + var handleMerge = function (irs, ics, ire, ice) { + var merge = { + s: { r: irs, c: ics }, + e: { r: ire, c: ice } }; + return self.addMerge(irs, merge); + }; - _nodesArray(context.rows).map(function (val, ir) { - if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || _matches(val, settings.ignoreCSS)) { - handleIgnore(ir); + _nodesArray(context.rows).map(function (val, ir) { + if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || _matches(val, settings.ignoreCSS)) { + handleIgnore(ir); + } + if (_matches(val, settings.emptyCSS)) { + handleEmpty(ir); + } + var cols = val.querySelectorAll('th, td'); + return _nodesArray(cols).map(function (val, ic) { + if (!!~settings.ignoreCols.indexOf(ic) || _matches(val, settings.ignoreCSS)) { + handleIgnore(ir, ic); } if (_matches(val, settings.emptyCSS)) { - handleEmpty(ir); + handleEmpty(ir, ic); + } + if (val.hasAttribute('rowspan')) { + handleRowSpan(val, ir, ic); + } else if (val.hasAttribute('colspan')) { + handleColSpan(val, ir, ic); } - var cols = val.querySelectorAll('th, td'); - return _nodesArray(cols).map(function (val, ic) { - if (!!~settings.ignoreCols.indexOf(ic) || _matches(val, settings.ignoreCSS)) { - handleIgnore(ir, ic); - } - if (_matches(val, settings.emptyCSS)) { - handleEmpty(ir, ic); - } - if (val.hasAttribute('rowspan')) { - handleRowSpan(val, ir, ic); - } else if (val.hasAttribute('colspan')) { - handleColSpan(val, ir, ic); - } - }); }); + }); - return self; - } - }; - - var _isMobile = (function isMobile(ua) { - 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) - || /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)) - })(navigator.userAgent || navigator.vendor || window.opera); - - var _isEnhanced = (function () { - return function (key) { - return ( - XLSX - && !_isMobile - && !(TableExport.prototype.formatConfig[key].enforceStrictRFC4180) - ); - } - })(); + return self; + } + }; + + var _isMobile = (function isMobile(ua) { + 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) + || /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)) + })(navigator.userAgent || navigator.vendor || window.opera); + + var _isEnhanced = (function () { + return function (key) { + return ( + XLSX + && !_isMobile + && !(TableExport.prototype.formatConfig[key].enforceStrictRFC4180) + ); + } + })(); - var _FORMAT = (function () { - return TableExport.prototype.CONSTANTS.FORMAT; - })(); + var _FORMAT = (function () { + return TableExport.prototype.CONSTANTS.FORMAT; + })(); - var _FORMAT_LIST = (function () { - return Object.keys(_FORMAT).map(function (key) { - return _FORMAT[key]; - }); - })(); + var _FORMAT_LIST = (function () { + return Object.keys(_FORMAT).map(function (key) { + return _FORMAT[key]; + }); + })(); - var _TYPE = (function () { - return TableExport.prototype.CONSTANTS.TYPE; - })(); + var _TYPE = (function () { + return TableExport.prototype.CONSTANTS.TYPE; + })(); - Object.defineProperty(Array.prototype, 'processRows', { - enumerable: false, - value: function (key, rowDel) { + Object.defineProperty(Array.prototype, 'processRows', { + enumerable: false, + value: function (key, rowDel) { if (_isEnhanced(key)) { - return this.map(_toArray).filter(_defined); + return this.map(_toArray).filter(_defined); } else { - return this.filter(_defined).join(rowDel); + return this.filter(_defined).join(rowDel); } - } - }); + } + }); - Object.defineProperty(Array.prototype, 'processCols', { - enumerable: false, - value: function (key, colDel) { + Object.defineProperty(Array.prototype, 'processCols', { + enumerable: false, + value: function (key, colDel) { if (_isEnhanced(key)) { - return this.filter(_defined); + return this.filter(_defined); } else { - return this.filter(_defined).join(colDel); + return this.filter(_defined).join(colDel); } - } - }); + } + }); - var _uuid = (function () { - var uuid = 0; + var _uuid = (function () { + var uuid = 0; - return function (el) { - var tableKey = el.getAttribute(TableExport.prototype.tableKey); + return function (el) { + var tableKey = el.getAttribute(TableExport.prototype.tableKey); - if (!tableKey) { - tableKey = el.id - ? el.id - : TableExport.prototype.defaultNamespace + (++uuid); - el.setAttribute(TableExport.prototype.tableKey, tableKey); - } - return tableKey; - }; - })(); + if (!tableKey) { + tableKey = el.id + ? el.id + : TableExport.prototype.defaultNamespace + (++uuid); + el.setAttribute(TableExport.prototype.tableKey, tableKey); + } + return tableKey; + }; + })(); - var _hashCode = (function () { + var _hashCode = (function () { - return function (hashKey) { - var hash = 0, i, char; + return function (hashKey) { + var hash = 0, i, char; - var type = hashKey.type; - hashKey = JSON.stringify(hashKey); - if (hashKey.length === 0) return hash; - for (i = 0; i < hashKey.length; i++) { - char = hashKey.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash |= 0; - } - return hash.toString(16).substring(1) + '-' + type; + var type = hashKey.type; + hashKey = JSON.stringify(hashKey); + if (hashKey.length === 0) return hash; + for (i = 0; i < hashKey.length; i++) { + char = hashKey.charCodeAt(i); + hash = ((hash << 5) - hash) + char; + hash |= 0; } - })(); + return hash.toString(16).substring(1) + '-' + type; + } + })(); - var _on = (function () { - var prevFn = null; + var _on = (function () { + var prevFn = null; - return function (el, event, fn, context) { // , args - var curFn = fn.bind(context); - // var curFn = fn.bind.apply(fn, [context].concat(args)); // OR [].slice.call(arguments[4])) - for (var i = 0; i < el.length; ++i) { - prevFn && el[i].removeEventListener(event, prevFn, false); - el[i].addEventListener(event, curFn, false); - } - prevFn = curFn; + return function (el, event, fn, context) { // , args + var curFn = fn.bind(context); + // var curFn = fn.bind.apply(fn, [context].concat(args)); // OR [].slice.call(arguments[4])) + for (var i = 0; i < el.length; ++i) { + prevFn && el[i].removeEventListener(event, prevFn, false); + el[i].addEventListener(event, curFn, false); } - })(); - - function _extend() { - var args = arguments; - for (var i = 1; i < args.length; i++) - for (var key in args[i]) - if (args[i].hasOwnProperty(key)) - args[0][key] = args[i][key]; - return args[0]; - } - - function _nodesArray(els) { - return (typeof els.length === 'undefined') ? [].concat(els) : [].slice.call(els); + prevFn = curFn; } + })(); + + function _extend() { + var args = arguments; + for (var i = 1; i < args.length; i++) + for (var key in args[i]) + if (args[i].hasOwnProperty(key)) + args[0][key] = args[i][key]; + return args[0]; + } - function _hasClass(el, cls) { - return el.classList ? el.classList.contains(cls) : new RegExp('(^| )' + cls + '( |$)', 'gi').test(el.cls); - } + function _nodesArray(els) { + return (typeof els.length === 'undefined') ? [].concat(els) : [].slice.call(els); + } - function _matches(el, selectors) { - return selectors.filter(function (selector) { - return [].indexOf.call(document.querySelectorAll(selector), el) !== -1; - }).length > 0; - } + function _hasClass(el, cls) { + return el.classList ? el.classList.contains(cls) : new RegExp('(^| )' + cls + '( |$)', 'gi').test(el.cls); + } - function _numeric(val) { - return !isNaN(val); - } + function _matches(el, selectors) { + return selectors.filter(function (selector) { + return [].indexOf.call(document.querySelectorAll(selector), el) !== -1; + }).length > 0; + } - function _defined(val) { - return typeof val !== 'undefined'; - } + function _numeric(val) { + return !isNaN(val); + } - function _toArray(val) { - return val instanceof Array ? [].concat.apply([], val) : val; - } + function _defined(val) { + return typeof val !== 'undefined'; + } - function _isValidFormat(key) { - return ~_FORMAT_LIST.indexOf(key); - } + function _toArray(val) { + return val instanceof Array ? [].concat.apply([], val) : val; + } - function _hasDependencies(key) { - var hasDependencies; + function _isValidFormat(key) { + return ~_FORMAT_LIST.indexOf(key); + } - switch (key) { - case _FORMAT.TXT: - case _FORMAT.CSV: - case _FORMAT.XLS: - hasDependencies = true; - break; - default: - hasDependencies = _isEnhanced(key); - } - return hasDependencies + function _hasDependencies(key) { + var hasDependencies; + + switch (key) { + case _FORMAT.TXT: + case _FORMAT.CSV: + case _FORMAT.XLS: + hasDependencies = true; + break; + default: + hasDependencies = _isEnhanced(key); } + return hasDependencies + } - function _handleError(msg) { - console.error(msg); - return new Error(msg) - } + function _handleError(msg) { + console.error(msg); + return new Error(msg) + } - function _getBootstrapSettings(bootstrap, bootstrapConfig, defaultButton) { - var config = {}; - if (bootstrap) { - config.bootstrapClass = bootstrapConfig[0] + ' '; - config.bootstrapTheme = bootstrapConfig[1] + ' '; - config.bootstrapSpacing = bootstrapConfig[2] + ' '; - } else { - config.bootstrapClass = defaultButton + ' '; - config.bootstrapTheme = ''; - config.bootstrapSpacing = ''; - } - return config; + function _getBootstrapSettings(bootstrap, bootstrapConfig, defaultButton) { + var config = {}; + if (bootstrap) { + config.bootstrapClass = bootstrapConfig[0] + ' '; + config.bootstrapTheme = bootstrapConfig[1] + ' '; + config.bootstrapSpacing = bootstrapConfig[2] + ' '; + } else { + config.bootstrapClass = defaultButton + ' '; + config.bootstrapTheme = ''; + config.bootstrapSpacing = ''; } + return config; + } - if ($) { - /** - * jQuery TableExport wrapper - * @param options {Object} TableExport configuration options - * @returns {TableExport} TableExport instance - */ - $.fn.tableExport = function (options) { - return TableExport.call(_extend({}, TableExport.prototype, $.fn.tableExport), this, options); - }; + if ($) { + /** + * jQuery TableExport wrapper + * @param options {Object} TableExport configuration options + * @returns {TableExport} TableExport instance + */ + $.fn.tableExport = function (options) { + return TableExport.call(_extend({}, TableExport.prototype, $.fn.tableExport), this, options); + }; - // alias the TableExport prototype - for (var prop in TableExport.prototype) { - $.fn.tableExport[prop] = TableExport.prototype[prop]; - } + // alias the TableExport prototype + for (var prop in TableExport.prototype) { + $.fn.tableExport[prop] = TableExport.prototype[prop]; } + } - TableExport.TableExport = TableExport; + TableExport.TableExport = TableExport; - return TableExport; - } + return TableExport; +} ));