From 50d418494d11360baceece692d2dbe106d4ee68d Mon Sep 17 00:00:00 2001 From: Mike Alhayek Date: Wed, 8 Jan 2020 13:53:16 -0800 Subject: [PATCH] Fix #193, improve performance during load and during export. --- bower.json | 2 +- dist/css/tableexport.css | 2 +- dist/css/tableexport.min.css | 2 +- dist/js/tableexport.js | 2244 +++++++++++++++------------- dist/js/tableexport.min.js | 4 +- dist/tableexport.d.ts | 4 +- package.json | 2 +- src/stable/css/tableexport.css | 2 +- src/stable/css/tableexport.min.css | 2 +- src/stable/js/tableexport.js | 2244 +++++++++++++++------------- src/stable/js/tableexport.min.js | 4 +- 11 files changed, 2378 insertions(+), 2134 deletions(-) diff --git a/bower.json b/bower.json index aeefa85..870bc86 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "tableexport.js", - "version": "5.2.0", + "version": "5.2.1", "authors": [ "clarketm " ], diff --git a/dist/css/tableexport.css b/dist/css/tableexport.css index 397da86..594ad45 100644 --- a/dist/css/tableexport.css +++ b/dist/css/tableexport.css @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * diff --git a/dist/css/tableexport.min.css b/dist/css/tableexport.min.css index 860a131..54b05e2 100644 --- a/dist/css/tableexport.min.css +++ b/dist/css/tableexport.min.css @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * diff --git a/dist/js/tableexport.js b/dist/js/tableexport.js index 162c7c5..9bfcf01 100644 --- a/dist/js/tableexport.js +++ b/dist/js/tableexport.js @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * @@ -17,1116 +17,1238 @@ * */ -(function(root, factory) { - if (typeof define === "function" && define.amd) { - // AMD - define(function(require) { - var $; - try { - $ = require("jquery"); - } catch (e) {} - return factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); - }); - } else if (typeof exports === "object" && typeof exports.nodeName !== "string") { - // CommonJS - var $; - try { - $ = require("jquery"); - } catch (e) {} - module.exports = factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); - } else { - // Browser globals - root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); - } -})(this, function($, Blob, saveAs, XLSX) { - "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 configuration options (user-defined w/ default fallback) - */ - self.settings = _extend({}, self.defaults, options); +(function (root, factory) { + if (typeof define === "function" && define.amd) { + // AMD + define(function (require) { + var $; + try { + $ = require("jquery"); + } catch (e) { } + return factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); + }); + } else if (typeof exports === "object" && typeof exports.nodeName !== "string") { + // CommonJS + var $; + try { + $ = require("jquery"); + } catch (e) { } + module.exports = factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); + } else { + // Browser globals + root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); + } +})(this, function ($, Blob, saveAs, XLSX) { + "use strict"; /** - * Selectors (tables) to apply the library to + * TableExport main library constructor + * @param selectors {jQuery} jQuery selector(s) + * @param options {Object} TableExport configuration options + * @constructor */ - 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; - }; + var TableExport = function (selectors, options) { + var self = this; - 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") || self.defaultFilename : settings.filename || self.defaultFilename; - context.sheetname = settings.sheetname === "id" ? el.getAttribute("id") || self.defaultSheetname : settings.sheetname || self.defaultSheetname; - 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 + self.defaultCaptionClass; - caption.style.cssText = "caption-side: " + settings.position; - 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.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]++; + if (!selectors) return _handleError('"selectors" is required. \nUsage: TableExport(selectors, options)'); + if (!self) { + self = new TableExport(selectors, options); + + return self; } - }); - }); - var exportButton = document.querySelectorAll("button[" + self.storageKey + "]"); - _on(exportButton, "click", self.downloadHandler, self); + /** + * TableExport configuration options (user-defined w/ default fallback) + */ + self.settings = _extend({}, self.defaults, options); + /** + * Selectors (tables) to apply the library to + */ + self.selectors = _nodesArray(selectors); - return self; - }; + self.contexts = []; - TableExport.prototype = { - /** - * Version. - * @memberof TableExport.prototype - */ - version: "5.2.0", - /** - * 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) - RTL: false, // (Boolean), set direction of the worksheet to right-to-left (default: false) - sheetname: "id" // (id, String), sheet name for the exported spreadsheet, (default: 'id') - }, - /** - * Constants - * @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" - } - }, - /** - * Character set (character encoding) of the HTML. - * @memberof TableExport.prototype - */ - charset: "charset=utf-8", - /** - * Filename fallback for exported files. - * @memberof TableExport.prototype - */ - defaultFilename: "myDownload", - /** - * Sheetname fallback for exported files. - * @memberof TableExport.prototype - */ - defaultSheetname: "myWorksheet", - /** - * 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: { - /** - * 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 - } - }, - /** - * 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)); - } - } - }, - 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); - } + 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); + + if (!Array.isArray(settings.formats)) { + settings.formats = [settings.formats]; + } + + self.processAll = function () { + self.contexts.forEach(function (context) { + context.processAllData(); + }); }; - var dataURI = _nodesArray(context.rows) - .map(function(val, ir) { - if (rcMap.isIgnore(ir)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); - } else if (rcMap.isEmpty(ir)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + self.processByKey = function (dataElement, key) { + + var uuid = _uuid(dataElement); + + var context = self.contexts[uuid]; + if (context) { + context.processDataByKey(key); + } + }; + + var _exportData = {}; + self.getExportData = function () { + + self.processAll(); + + return _exportData; + }; + + self.selectors.forEach(function (el) { + var context = {}; + var theadRows = el.querySelectorAll("thead > tr"); + context.rows = _nodesArray(el.querySelectorAll("tbody > tr")); + if (settings.headers) { + context.rows = _nodesArray(theadRows).concat(context.rows); } - var cols = val.querySelectorAll("th, td"); - return _nodesArray(cols) - .map(function(val, ic) { - var _return = getReturn(val); - if (rcMap.isIgnore(ir, ic)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); - } else if (rcMap.isEmpty(ir, ic)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + if (settings.footers) { + context.rows = _nodesArray(el.querySelectorAll("tfoot > tr")).concat(context.rows); + } + context.thAdj = settings.headers ? theadRows.length : 0; + context.filename = (settings.filename === "id" ? el.getAttribute("id") : settings.filename) || self.defaultFilename; + context.sheetname = (settings.sheetname === "id" ? el.getAttribute("id") : settings.sheetname) || self.defaultSheetname; + 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 { - return rcMap.handleRowColMapProp(rcMap.TYPE.DEFAULT, ir, ic, key, _return, colDel); + caption = document.createElement("caption"); + caption.className = settings.bootstrapSettings.bootstrapSpacing + self.defaultCaptionClass; + caption.style.cssText = "caption-side: " + settings.position; + 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.rcMap = function () { + return new RowColMap().build(context, settings); + }; + + var formatMap = _FORMAT_LIST.reduce(function (acc, cur) { + acc[cur] = 0; + return acc; + }, {}); + + context.processDataByKey = function (key) { + if (!_isValidFormat(key)) { + return _handleError('"' + key + '" is not a valid format. \nFormats: ' + _FORMAT_LIST.join(", ")); } - }) - .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, - RTL: settings.RTL, - sheetname: settings.sheetname + if (!_hasDependencies(key)) { + // TODO: provide a fallback option to XLS? + return _handleError('"' + key + '" requires "js-xlsx".'); + } + + if (!formatMap[key]) { + var exporter = self.exporters.build.call(self, context, key); + context.setExportData(exporter); + formatMap[key]++; + } + }; + + context.processAllData = function () { + return settings.formats.forEach(function (key) { + context.processDataByKey(key); + }); + }; + + if (settings.exportButtons) { + settings.formats.forEach(function (key) { + var format = self.formatConfig[key]; + + var hashKey = _hashCode({ + uuid: context.uuid, + type: key + }); + context.checkCaption(self.createObjButton(hashKey, format.buttonContent, format.defaultClass, settings.bootstrapSettings)); + }); + } + + self.contexts[context.uuid] = context; }); - var hashKey = _hashCode({ uuid: context.uuid, type: key }); + var exportButton = document.querySelectorAll("button[" + self.storageKey + "]"); - 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; + _on(exportButton, "click", self.downloadHandler, self); + + return self; + }; + + TableExport.prototype = { + /** + * Version. + * @memberof TableExport.prototype + */ + version: "5.2.1", + /** + * 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) + RTL: false, // (Boolean), set direction of the worksheet to right-to-left (default: false) + sheetname: "id" // (id, String), sheet name for the exported spreadsheet, (default: 'id') + }, + /** + * Constants + * @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" + } + }, + /** + * Character set (character encoding) of the HTML. + * @memberof TableExport.prototype + */ + charset: "charset=utf-8", + /** + * Filename fallback for exported files. + * @memberof TableExport.prototype + */ + defaultFilename: "myDownload", + /** + * Sheetname fallback for exported files. + * @memberof TableExport.prototype + */ + defaultSheetname: "myWorksheet", + /** + * 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: { + /** + * XLSX (Open XML spreadsheet) file extension configuration + * @memberof TableExport.prototype + */ + xlsx: { + defaultClass: "export-type-xlsx xlsx", + buttonContent: "Export to xlsx", + mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + fileExtension: ".xlsx" + }, + xlsm: { + defaultClass: "export-type-xlsm xlsm", + buttonContent: "Export to xlsm", + mimeType: "application/vnd.ms-excel.sheet.macroEnabled.main+xml", + fileExtension: ".xlsm" + }, + xlsb: { + defaultClass: "export-type-xlsb 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: "export-type-xls 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: "export-type-csv csv", + buttonContent: "Export to csv", + separator: ",", + mimeType: "text/csv", + fileExtension: ".csv", + enforceStrictRFC4180: true + }, + /** + * TXT (Plain Text) file extension configuration + * @memberof TableExport.prototype + */ + txt: { + defaultClass: "export-type-txt txt", + buttonContent: "Export to txt", + separator: " ", + mimeType: "text/plain", + fileExtension: ".txt", + 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); + } + }, + 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)); + } + } + }, + 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); + } + }; + + var dataURI = _nodesArray(context.rows) + .map(function (val, ir) { + if (rcMap.isIgnore(ir)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); + } else if (rcMap.isEmpty(ir)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + } + var cols = val.querySelectorAll("th, td"); + return _nodesArray(cols) + .map(function (val, ic) { + var _return = getReturn(val); + if (rcMap.isIgnore(ir, ic)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); + } else if (rcMap.isEmpty(ir, ic)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + } else { + return rcMap.handleRowColMapProp(rcMap.TYPE.DEFAULT, ir, ic, key, _return, colDel); + } + }) + .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, + RTL: settings.RTL, + sheetname: settings.sheetname + }); + + var hashKey = _hashCode({ + uuid: context.uuid, + type: key + }); + + return Storage.getInstance().setItem(hashKey, dataObject, true); + } + }, + /** + * Creates file export buttons + * @param hashKey {String} + * @param myContent {String} + * @param myClass {String} + * @param bootstrapSettings {Object} + * @returns Element + */ + createObjButton: function (hashKey, 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) { + + event.target.disabled = true; + + var items = event.target.className.split(' ').filter(function (className) { + return className.startsWith('export-type-'); + }); + var key = event.target.getAttribute(this.storageKey); + + var contextKey = ''; + if (items.length !== 1) { + console.log('Unable to find the export type'); + if (!Storage.getInstance().exists(key)) { + this.processAll(); + } + + } else { + contextKey = items[0].substring(12); + + if (!Storage.getInstance().exists(key)) { + this.processByKey(event.target.closest('caption').parentNode, contextKey); + } + + } + + var object = JSON.parse(Storage.getInstance().getItem(key)); + + this.export2file(object.data, + object.mimeType, + object.filename, + object.fileExtension, + object.merges, + object.RTL, + object.sheetname); + + event.target.disabled = false; + }, + /** + * Excel Workbook constructor + * @memberof TableExport.prototype + * @constructor + */ + Workbook: function () { + this.Workbook = { + Views: [] + }; + 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[]} + * @param RTL {Boolean} + */ + export2file: function (data, mime, name, extension, merges, RTL, sheetname) { + var format = extension.slice(1); + data = this.getRawData(data, extension, name, merges, RTL, sheetname); + + if (_isMobile && (format === _FORMAT.CSV || format === _FORMAT.TXT)) { + 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, RTL, sheetname) { + var key = extension.substring(1); + + if (_isEnhanced(key)) { + var wb = new this.Workbook(), + ws = this.createSheet(data, merges), + bookType = this.getBookType(key); + + sheetname = sheetname || ""; + wb.SheetNames.push(sheetname); + wb.Sheets[sheetname] = ws; + wb.Workbook.Views[0] = { + RTL: RTL + }; + 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); + }); } - } - 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, - RTL = object.RTL, - sheetname = object.sheetname; - this.export2file(data, mimeType, filename, fileExtension, merges, RTL, sheetname); - }, + }; + /** - * Excel Workbook constructor + * Storage main interface constructor * @memberof TableExport.prototype * @constructor */ - Workbook: function() { - this.Workbook = { Views: [] }; - 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[]} - * @param RTL {Boolean} - */ - export2file: function(data, mime, name, extension, merges, RTL, sheetname) { - var format = extension.slice(1); - data = this.getRawData(data, extension, name, merges, RTL, sheetname); - - if (_isMobile && (format === _FORMAT.CSV || format === _FORMAT.TXT)) { - 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, RTL, sheetname) { - var key = extension.substring(1); - - if (_isEnhanced(key)) { - var wb = new this.Workbook(), - ws = this.createSheet(data, merges), - bookType = this.getBookType(key); - - sheetname = sheetname || ""; - wb.SheetNames.push(sheetname); - wb.Sheets[sheetname] = ws; - wb.Workbook.Views[0] = { RTL: RTL }; - 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]; - } 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 (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; + 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) { + + console.log('Starting the save to store'); + 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); + console.log('Ended the save to store'); + + 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); + }; + }; - 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); + Storage.getInstance = function () { + if (!this._instance) { + this._instance = new Storage(); } - } - 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); - } + + return this._instance; }; - }; - 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 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 && _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); + + /** + * RowColMap main interface constructor + * @memberof TableExport.prototype + * @constructor + */ + var RowColMap = function () { + this.rcMap = []; + this.merges = []; + + this.isIgnore = function (ir, ic) { + return this.getRowColMapProp(ir, ic, RowColMap.prototype.TYPE.IGNORE); + }; + this.isEmpty = function (ir, ic) { + return this.getRowColMapProp(ir, ic, RowColMap.prototype.TYPE.EMPTY); + }; + this.isRowSpan = function (ir) { + return this.getRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.ROWSPAN); + }; + this.isColSpan = function (ir) { + return this.getRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.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) { + this.merges.push(mergeObj); + this.setRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.MERGE, this.merges); + }; + this.getRowColMapProp = function (ir, ic, key) { + if (this.rcMap[ir]) { + if (_undefined(key)) { + return this.rcMap[ir][ic]; + } + if (_undefined(ic)) { + return this.rcMap[ir][key]; + } + 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 (_undefined(key) && _undefined(ic)) { + // bailout + return; + } + + if (_defined(ic) && _undefined(key)) { + return this.rcMap[ir][ic] = value; + } + + if (_undefined(ic) && _defined(key)) { + return this.rcMap[ir][key] = value; + } + + 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); + } + + return new Array(total).concat(_return).join(colDel); } - } - - 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; + 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); + } + }; + }; + 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 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 < 2) { + 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 (_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; + + if (colSpan < 2) { + 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; + } + irStart = ir; - irEnd = ir + rowSpan - 1; - icStart = ic + totalColSpan - countColSpan + (_re || 0); - icEnd = icStart + Math.max(1, colSpan) - 1; + 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 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 handleMerge = function (irs, ics, ire, ice) { + var merge = { + s: { + r: irs, + c: ics + }, + e: { + r: ire, + c: ice + } + }; + return self.addMerge(irs, merge); + }; - if (colSpan <= 1) { - return false; - } + context.rows.map(function (val, ir) { + + if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || val.matches(settings.ignoreCSS)) { + handleIgnore(ir); + } + + if (val.matches(settings.emptyCSS)) { + handleEmpty(ir); + } - 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 cols = val.querySelectorAll("th, td"); + var final = _nodesArray(cols).map(function (val, ic) { + + if (!!~settings.ignoreCols.indexOf(ic) || val.matches(settings.ignoreCSS)) { + handleIgnore(ir, ic); + } + if (val.matches(settings.emptyCSS)) { + handleEmpty(ir, ic); + } + if (val.hasAttribute("rowspan")) { + handleRowSpan(val, ir, ic); + } else if (val.hasAttribute("colspan")) { + handleColSpan(val, ir, ic); + } + }); + + return final; + }); + + return self; } - }; + }; - var handleMerge = function(irs, ics, ire, ice) { - var merge = { - s: { r: irs, c: ics }, - e: { r: ire, c: ice } + 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 && !TableExport.prototype.formatConfig[key].enforceStrictRFC4180; }; - return self.addMerge(irs, merge); - }; + })(); + + var _FORMAT = (function () { + return TableExport.prototype.CONSTANTS.FORMAT; + })(); + + var _FORMAT_LIST = (function () { + return Object.keys(_FORMAT).map(function (key) { + return _FORMAT[key]; + }); + })(); + + var _TYPE = (function () { + return TableExport.prototype.CONSTANTS.TYPE; + })(); - _nodesArray(context.rows).map(function(val, ir) { - if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || _matches(val, settings.ignoreCSS)) { - handleIgnore(ir); + Object.defineProperty(Array.prototype, "processRows", { + enumerable: false, + value: function (key, rowDel) { + if (_isEnhanced(key)) { + return this.map(_toArray).filter(_defined); + } + + return this.filter(_defined).join(rowDel); } - if (_matches(val, settings.emptyCSS)) { - handleEmpty(ir); + }); + + Object.defineProperty(Array.prototype, "processCols", { + enumerable: false, + value: function (key, colDel) { + if (_isEnhanced(key)) { + return this.filter(_defined); + } + + return this.filter(_defined).join(colDel); } - 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 _uuid = (function () { + var uuid = 0; + + 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; + }; + })(); + + var _hashCode = (function () { + 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 _on = (function () { + var prevFn = null; + + return function (el, event, fn, context) { + var curFn = fn.bind(context); + for (var i = 0; i < el.length; ++i) { + prevFn && el[i].removeEventListener(event, prevFn, false); + el[i].addEventListener(event, curFn, false); + } + 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]; } - }; - - 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 && !TableExport.prototype.formatConfig[key].enforceStrictRFC4180; - }; - })(); - var _FORMAT = (function() { - return TableExport.prototype.CONSTANTS.FORMAT; - })(); + function _nodesArray(els) { + return _undefined(els.length) ? [].concat(els) : [].slice.call(els); + } - var _FORMAT_LIST = (function() { - return Object.keys(_FORMAT).map(function(key) { - return _FORMAT[key]; - }); - })(); - - var _TYPE = (function() { - return TableExport.prototype.CONSTANTS.TYPE; - })(); - - Object.defineProperty(Array.prototype, "processRows", { - enumerable: false, - value: function(key, rowDel) { - if (_isEnhanced(key)) { - return this.map(_toArray).filter(_defined); - } else { - return this.filter(_defined).join(rowDel); - } + function _defined(val) { + return typeof val !== "undefined"; } - }); - - Object.defineProperty(Array.prototype, "processCols", { - enumerable: false, - value: function(key, colDel) { - if (_isEnhanced(key)) { - return this.filter(_defined); - } else { - return this.filter(_defined).join(colDel); - } + + function _undefined(val) { + return typeof val === "undefined"; } - }); - var _uuid = (function() { - var uuid = 0; + function _toArray(val) { + return val instanceof Array ? [].concat.apply([], val) : val; + } - return function(el) { - var tableKey = el.getAttribute(TableExport.prototype.tableKey); + function _isValidFormat(key) { + return ~_FORMAT_LIST.indexOf(key); + } - if (!tableKey) { - tableKey = el.id ? el.id : TableExport.prototype.defaultNamespace + ++uuid; - el.setAttribute(TableExport.prototype.tableKey, tableKey); - } - return tableKey; - }; - })(); - - var _hashCode = (function() { - 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 _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; - }; - })(); - - 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); - } - - function _hasClass(el, cls) { - return el.classList ? el.classList.contains(cls) : new RegExp("(^| )" + cls + "( |$)", "gi").test(el.cls); - } - - function _matches(el, selectors) { - return ( - selectors.filter(function(selector) { - return [].indexOf.call(document.querySelectorAll(selector), el) !== -1; - }).length > 0 - ); - } - - function _numeric(val) { - return !isNaN(val); - } - - function _defined(val) { - return typeof val !== "undefined"; - } - - function _toArray(val) { - return val instanceof Array ? [].concat.apply([], val) : val; - } - - function _isValidFormat(key) { - return ~_FORMAT_LIST.indexOf(key); - } - - function _hasDependencies(key) { - var hasDependencies; - - switch (key) { - case _FORMAT.TXT: - case _FORMAT.CSV: - case _FORMAT.XLS: - hasDependencies = true; - break; - default: - hasDependencies = _isEnhanced(key); + 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; } - return hasDependencies; - } - - 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 = ""; + + function _handleError(msg) { + console.error(msg); + return new Error(msg); } - 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); - }; + function _getBootstrapSettings(bootstrap, bootstrapConfig, defaultButton) { + var config = { + bootstrapClass: defaultButton + " ", + bootstrapTheme: "", + bootstrapSpacing: "" + }; - // alias the TableExport prototype - for (var prop in TableExport.prototype) { - $.fn.tableExport[prop] = TableExport.prototype[prop]; + if (bootstrap) { + config.bootstrapClass = bootstrapConfig[0] + " "; + config.bootstrapTheme = bootstrapConfig[1] + " "; + config.bootstrapSpacing = bootstrapConfig[2] + " "; + } + + 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); + }; + + // 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; }); diff --git a/dist/js/tableexport.min.js b/dist/js/tableexport.min.js index 96ef205..efda15b 100644 --- a/dist/js/tableexport.min.js +++ b/dist/js/tableexport.min.js @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * @@ -16,4 +16,4 @@ * limitations under the License. * */ -!function(t,r){if("function"==typeof define&&define.amd)define(function(t){var e;try{e=t("jquery")}catch(t){}return r(e,t("blobjs"),t("file-saverjs"),t("xlsx"))});else if("object"==typeof exports&&"string"!=typeof exports.nodeName){var e;try{e=require("jquery")}catch(t){}module.exports=r(e,require("blobjs"),require("file-saverjs"),require("xlsx"))}else t.TableExport=r(t.jQuery,t.Blob,t.saveAs,t.XLSX)}(this,function(e,p,c,f){"use strict";var o=function(t,e){var a=this;if(!t)return S('"selectors" is required. \nUsage: TableExport(selectors, options)');if(!a)return new o(t,e);a.settings=C({},a.defaults,e),a.selectors=w(t);var i=a.settings;i.ignoreRows=i.ignoreRows instanceof Array?i.ignoreRows:[i.ignoreRows],i.ignoreCols=i.ignoreCols instanceof Array?i.ignoreCols:[i.ignoreCols],i.ignoreCSS=a.ignoreCSS instanceof Array?a.ignoreCSS:[a.ignoreCSS],i.emptyCSS=a.emptyCSS instanceof Array?a.emptyCSS:[a.emptyCSS],i.formatValue=a.formatValue.bind(this,i.trimWhitespace),i.bootstrapSettings=function(t,e,r){var o={};o.bootstrapSpacing=t?(o.bootstrapClass=e[0]+" ",o.bootstrapTheme=e[1]+" ",e[2]+" "):(o.bootstrapClass=r+" ",o.bootstrapTheme="","");return o}(i.bootstrap,a.bootstrapConfig,a.defaultButton);var s={};a.getExportData=function(){return s},a.selectors.forEach(function(r){var o={};o.rows=w(r.querySelectorAll("tbody > tr")),o.rows=i.headers?w(r.querySelectorAll("thead > tr")).concat(o.rows):o.rows,o.rows=i.footers?o.rows.concat(w(r.querySelectorAll("tfoot > tr"))):o.rows,o.thAdj=i.headers?r.querySelectorAll("thead > tr").length:0,o.filename="id"===i.filename?r.getAttribute("id")||a.defaultFilename:i.filename||a.defaultFilename,o.sheetname="id"===i.sheetname?r.getAttribute("id")||a.defaultSheetname:i.sheetname||a.defaultSheetname,o.uuid=v(r),o.checkCaption=function(t){var e=r.querySelectorAll("caption."+a.defaultCaptionClass);e.length?e[0].appendChild(t):((e=document.createElement("caption")).className=i.bootstrapSettings.bootstrapSpacing+a.defaultCaptionClass,e.style.cssText="caption-side: "+i.position,e.appendChild(t),r.insertBefore(e,r.firstChild))},o.setExportData=function(t){var e=u.getInstance().getItem(t),r=t.substring(t.indexOf("-")+1);s[o.uuid]=s[o.uuid]||{},s[o.uuid][r]=JSON.parse(e)},o.rcMap=(new l).build(o,i);var n=g.reduce(function(t,e){return t[e]=0,t},{});i.formats.forEach(function(t){return e=t,~g.indexOf(e)?function(t){var e;switch(t){case m.TXT:case m.CSV:case m.XLS:e=!0;break;default:e=d(t)}return e}(t)?void(n[t]||(o.setExportData(a.exporters.build.call(a,o,t)),n[t]++)):S('"'+t+'" requires "js-xlsx".'):S('"'+t+'" is not a valid format. \nFormats: '+g.join(", "));var e})});var r=document.querySelectorAll("button["+a.storageKey+"]");return n(r,"click",a.downloadHandler,a),a};o.prototype={version:"5.2.0",defaults:{headers:!0,footers:!0,formats:["xlsx","csv","txt"],filename:"id",bootstrap:!1,exportButtons:!0,position:"bottom",ignoreRows:null,ignoreCols:null,trimWhitespace:!0,RTL:!1,sheetname:"id"},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"}},charset:"charset=utf-8",defaultFilename:"myDownload",defaultSheetname:"myWorksheet",defaultButton:"button-default",defaultCaptionClass:"tableexport-caption",defaultNamespace:"tableexport-",tableKey:"tableexport-key",storageKey:"tableexport-id",ignoreCSS:".tableexport-ignore",emptyCSS:".tableexport-empty",bootstrapConfig:["btn","btn-default","btn-toolbar"],rowDel:"\r\n",entityMap:{"&":"&","<":"<",">":">","'":"'","/":"/"},formatConfig:{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:{defaultClass:"xls",buttonContent:"Export to xls",separator:"\t",mimeType:"application/vnd.ms-excel",fileExtension:".xls",enforceStrictRFC4180:!1},csv:{defaultClass:"csv",buttonContent:"Export to csv",separator:",",mimeType:"text/csv",fileExtension:".csv",enforceStrictRFC4180:!0},txt:{defaultClass:"txt",buttonContent:"Export to txt",separator:" ",mimeType:"text/plain",fileExtension:".txt",enforceStrictRFC4180:!0}},typeConfig:{string:{defaultClass:"tableexport-string"},number:{defaultClass:"tableexport-number",assert:function(t){return!isNaN(t)}},boolean:{defaultClass:"tableexport-boolean",assert:function(t){return"true"===t.toLowerCase()||"false"===t.toLowerCase()}},date:{defaultClass:"tableexport-date",assert:function(t){return!/.*%/.test(t)&&!isNaN(Date.parse(t))}}},exporters:{build:function(t,n){var a=this,i=a.settings,e=a.formatConfig[n],s=e.separator,l=t.rcMap,r=w(t.rows).map(function(t,o){return l.isIgnore(o)?l.handleRowColMapProp(l.TYPE.IGNORE):l.isEmpty(o)?l.handleRowColMapProp(l.TYPE.EMPTY):w(t.querySelectorAll("th, td")).map(function(t,e){var r=function(t){if(d(n))return{v:i.formatValue(t.textContent),t:a.getType(t.className)};switch(n){case m.CSV:return'"'+i.formatValue(t.textContent.replace(/"/g,'""'))+'"';default:return i.formatValue(t.textContent)}}(t);return l.isIgnore(o,e)?l.handleRowColMapProp(l.TYPE.IGNORE):l.isEmpty(o,e)?l.handleRowColMapProp(l.TYPE.EMPTY):l.handleRowColMapProp(l.TYPE.DEFAULT,o,e,n,r,s)}).processCols(n,s)}).processRows(n,a.rowDel),o=JSON.stringify({data:r,filename:t.filename,mimeType:e.mimeType,fileExtension:e.fileExtension,merges:l.merges,RTL:i.RTL,sheetname:i.sheetname}),p=y({uuid:t.uuid,type:n});return i.exportButtons&&t.checkCaption(a.createObjButton(p,o,e.buttonContent,e.defaultClass,i.bootstrapSettings)),u.getInstance().setItem(p,o,!0)}},createObjButton:function(t,e,r,o,n){var a=document.createElement("button");return a.setAttribute("type","button"),a.setAttribute(this.storageKey,t),a.className=n.bootstrapClass+n.bootstrapTheme+o,a.textContent=r,a},escapeHtml:function(t){var e=this;return String(t).replace(/[&<>'\/]/g,function(t){return e.entityMap[t]})},unescapeHtml:function(t){var e=String(t);for(var r in this.entityMap)e=e.replace(RegExp(this.entityMap[r],"g"),r);return e},formatValue:function(t,e){return t?e.trim():e},getType:function(t){if(!t)return"";var e=this.typeConfig;return~t.indexOf(e.string.defaultClass)?b.STRING:~t.indexOf(e.number.defaultClass)?b.NUMBER:~t.indexOf(e.boolean.defaultClass)?b.BOOLEAN:~t.indexOf(e.date.defaultClass)?b.DATE:""},dateNum:function(t,e){e&&(t+=1462);var r=(Date.parse(t)-new Date(Date.UTC(1899,11,30)))/864e5;return Math.floor(r)},createSheet:function(t,e){for(var r={},o={s:{c:1e7,r:1e7},e:{c:0,r:0}},n=this.typeConfig,a=0;a!==t.length;++a)for(var i=0;i!==t[a].length;++i){o.s.r>a&&(o.s.r=a),o.s.c>i&&(o.s.c=i),o.e.r tr");o.rows=C(r.querySelectorAll("tbody > tr")),s.headers&&(o.rows=C(t).concat(o.rows)),s.footers&&(o.rows=C(r.querySelectorAll("tfoot > tr")).concat(o.rows)),o.thAdj=s.headers?t.length:0,o.filename=("id"===s.filename?r.getAttribute("id"):s.filename)||a.defaultFilename,o.sheetname=("id"===s.sheetname?r.getAttribute("id"):s.sheetname)||a.defaultSheetname,o.uuid=b(r),o.checkCaption=function(t){var e=r.querySelectorAll("caption."+a.defaultCaptionClass);e.length?e[0].appendChild(t):((e=document.createElement("caption")).className=s.bootstrapSettings.bootstrapSpacing+a.defaultCaptionClass,e.style.cssText="caption-side: "+s.position,e.appendChild(t),r.insertBefore(e,r.firstChild))},o.setExportData=function(t){var e=u.getInstance().getItem(t),r=t.substring(t.indexOf("-")+1);i[o.uuid]=i[o.uuid]||{},i[o.uuid][r]=JSON.parse(e)},o.rcMap=function(){return(new p).build(o,s)};var n=g.reduce(function(t,e){return t[e]=0,t},{});o.processDataByKey=function(t){if(e=t,!~g.indexOf(e))return S('"'+t+'" is not a valid format. \nFormats: '+g.join(", "));var e;if(!function(t){var e;switch(t){case d.TXT:case d.CSV:case d.XLS:e=!0;break;default:e=m(t)}return e}(t))return S('"'+t+'" requires "js-xlsx".');if(!n[t]){var r=a.exporters.build.call(a,o,t);o.setExportData(r),n[t]++}},o.processAllData=function(){return s.formats.forEach(function(t){o.processDataByKey(t)})},s.exportButtons&&s.formats.forEach(function(t){var e=a.formatConfig[t],r=v({uuid:o.uuid,type:t});o.checkCaption(a.createObjButton(r,e.buttonContent,e.defaultClass,s.bootstrapSettings))}),a.contexts[o.uuid]=o});var r=document.querySelectorAll("button["+a.storageKey+"]");return n(r,"click",a.downloadHandler,a),a};o.prototype={version:"5.2.1",defaults:{headers:!0,footers:!0,formats:["xlsx","csv","txt"],filename:"id",bootstrap:!1,exportButtons:!0,position:"bottom",ignoreRows:null,ignoreCols:null,trimWhitespace:!0,RTL:!1,sheetname:"id"},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"}},charset:"charset=utf-8",defaultFilename:"myDownload",defaultSheetname:"myWorksheet",defaultButton:"button-default",defaultCaptionClass:"tableexport-caption",defaultNamespace:"tableexport-",tableKey:"tableexport-key",storageKey:"tableexport-id",ignoreCSS:".tableexport-ignore",emptyCSS:".tableexport-empty",bootstrapConfig:["btn","btn-default","btn-toolbar"],rowDel:"\r\n",entityMap:{"&":"&","<":"<",">":">","'":"'","/":"/"},formatConfig:{xlsx:{defaultClass:"export-type-xlsx xlsx",buttonContent:"Export to xlsx",mimeType:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",fileExtension:".xlsx"},xlsm:{defaultClass:"export-type-xlsm xlsm",buttonContent:"Export to xlsm",mimeType:"application/vnd.ms-excel.sheet.macroEnabled.main+xml",fileExtension:".xlsm"},xlsb:{defaultClass:"export-type-xlsb xlsb",buttonContent:"Export to xlsb",mimeType:"application/vnd.ms-excel.sheet.binary.macroEnabled.main",fileExtension:".xlsb"},xls:{defaultClass:"export-type-xls xls",buttonContent:"Export to xls",separator:"\t",mimeType:"application/vnd.ms-excel",fileExtension:".xls",enforceStrictRFC4180:!1},csv:{defaultClass:"export-type-csv csv",buttonContent:"Export to csv",separator:",",mimeType:"text/csv",fileExtension:".csv",enforceStrictRFC4180:!0},txt:{defaultClass:"export-type-txt txt",buttonContent:"Export to txt",separator:" ",mimeType:"text/plain",fileExtension:".txt",enforceStrictRFC4180:!0}},typeConfig:{string:{defaultClass:"tableexport-string"},number:{defaultClass:"tableexport-number",assert:function(t){return!isNaN(t)}},boolean:{defaultClass:"tableexport-boolean",assert:function(t){return"true"===t.toLowerCase()||"false"===t.toLowerCase()}},date:{defaultClass:"tableexport-date",assert:function(t){return!/.*%/.test(t)&&!isNaN(Date.parse(t))}}},exporters:{build:function(t,n){var a=this,s=a.settings,e=a.formatConfig[n],i=e.separator,p=t.rcMap(),r=C(t.rows).map(function(t,o){return p.isIgnore(o)?p.handleRowColMapProp(p.TYPE.IGNORE):p.isEmpty(o)?p.handleRowColMapProp(p.TYPE.EMPTY):C(t.querySelectorAll("th, td")).map(function(t,e){var r=function(t){if(m(n))return{v:s.formatValue(t.textContent),t:a.getType(t.className)};switch(n){case d.CSV:return'"'+s.formatValue(t.textContent.replace(/"/g,'""'))+'"';default:return s.formatValue(t.textContent)}}(t);return p.isIgnore(o,e)?p.handleRowColMapProp(p.TYPE.IGNORE):p.isEmpty(o,e)?p.handleRowColMapProp(p.TYPE.EMPTY):p.handleRowColMapProp(p.TYPE.DEFAULT,o,e,n,r,i)}).processCols(n,i)}).processRows(n,a.rowDel),o=JSON.stringify({data:r,filename:t.filename,mimeType:e.mimeType,fileExtension:e.fileExtension,merges:p.merges,RTL:s.RTL,sheetname:s.sheetname}),l=v({uuid:t.uuid,type:n});return u.getInstance().setItem(l,o,!0)}},createObjButton:function(t,e,r,o){var n=document.createElement("button");return n.setAttribute("type","button"),n.setAttribute(this.storageKey,t),n.className=o.bootstrapClass+o.bootstrapTheme+r,n.textContent=e,n},escapeHtml:function(t){var e=this;return String(t).replace(/[&<>'\/]/g,function(t){return e.entityMap[t]})},unescapeHtml:function(t){var e=String(t);for(var r in this.entityMap)e=e.replace(RegExp(this.entityMap[r],"g"),r);return e},formatValue:function(t,e){return t?e.trim():e},getType:function(t){if(!t)return"";var e=this.typeConfig;return~t.indexOf(e.string.defaultClass)?y.STRING:~t.indexOf(e.number.defaultClass)?y.NUMBER:~t.indexOf(e.boolean.defaultClass)?y.BOOLEAN:~t.indexOf(e.date.defaultClass)?y.DATE:""},dateNum:function(t,e){e&&(t+=1462);var r=(Date.parse(t)-new Date(Date.UTC(1899,11,30)))/864e5;return Math.floor(r)},createSheet:function(t,e){for(var r={},o={s:{c:1e7,r:1e7},e:{c:0,r:0}},n=this.typeConfig,a=0;a!==t.length;++a)for(var s=0;s!==t[a].length;++s){o.s.r>a&&(o.s.r=a),o.s.c>s&&(o.s.c=s),o.e.r /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * diff --git a/package.json b/package.json index fadc196..6e83839 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tableexport", - "version": "5.2.0", + "version": "5.2.1", "author": "Travis Clarke (https://www.travismclarke.com/)", "description": "The simple, easy-to-implement library to export HTML tables to xlsx, xls, csv, and txt files", "main": "dist/js/tableexport.js", diff --git a/src/stable/css/tableexport.css b/src/stable/css/tableexport.css index 397da86..594ad45 100644 --- a/src/stable/css/tableexport.css +++ b/src/stable/css/tableexport.css @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * diff --git a/src/stable/css/tableexport.min.css b/src/stable/css/tableexport.min.css index 860a131..54b05e2 100644 --- a/src/stable/css/tableexport.min.css +++ b/src/stable/css/tableexport.min.css @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * diff --git a/src/stable/js/tableexport.js b/src/stable/js/tableexport.js index 162c7c5..9bfcf01 100644 --- a/src/stable/js/tableexport.js +++ b/src/stable/js/tableexport.js @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * @@ -17,1116 +17,1238 @@ * */ -(function(root, factory) { - if (typeof define === "function" && define.amd) { - // AMD - define(function(require) { - var $; - try { - $ = require("jquery"); - } catch (e) {} - return factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); - }); - } else if (typeof exports === "object" && typeof exports.nodeName !== "string") { - // CommonJS - var $; - try { - $ = require("jquery"); - } catch (e) {} - module.exports = factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); - } else { - // Browser globals - root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); - } -})(this, function($, Blob, saveAs, XLSX) { - "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 configuration options (user-defined w/ default fallback) - */ - self.settings = _extend({}, self.defaults, options); +(function (root, factory) { + if (typeof define === "function" && define.amd) { + // AMD + define(function (require) { + var $; + try { + $ = require("jquery"); + } catch (e) { } + return factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); + }); + } else if (typeof exports === "object" && typeof exports.nodeName !== "string") { + // CommonJS + var $; + try { + $ = require("jquery"); + } catch (e) { } + module.exports = factory($, require("blobjs"), require("file-saverjs"), require("xlsx")); + } else { + // Browser globals + root.TableExport = factory(root.jQuery, root.Blob, root.saveAs, root.XLSX); + } +})(this, function ($, Blob, saveAs, XLSX) { + "use strict"; /** - * Selectors (tables) to apply the library to + * TableExport main library constructor + * @param selectors {jQuery} jQuery selector(s) + * @param options {Object} TableExport configuration options + * @constructor */ - 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; - }; + var TableExport = function (selectors, options) { + var self = this; - 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") || self.defaultFilename : settings.filename || self.defaultFilename; - context.sheetname = settings.sheetname === "id" ? el.getAttribute("id") || self.defaultSheetname : settings.sheetname || self.defaultSheetname; - 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 + self.defaultCaptionClass; - caption.style.cssText = "caption-side: " + settings.position; - 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.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]++; + if (!selectors) return _handleError('"selectors" is required. \nUsage: TableExport(selectors, options)'); + if (!self) { + self = new TableExport(selectors, options); + + return self; } - }); - }); - var exportButton = document.querySelectorAll("button[" + self.storageKey + "]"); - _on(exportButton, "click", self.downloadHandler, self); + /** + * TableExport configuration options (user-defined w/ default fallback) + */ + self.settings = _extend({}, self.defaults, options); + /** + * Selectors (tables) to apply the library to + */ + self.selectors = _nodesArray(selectors); - return self; - }; + self.contexts = []; - TableExport.prototype = { - /** - * Version. - * @memberof TableExport.prototype - */ - version: "5.2.0", - /** - * 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) - RTL: false, // (Boolean), set direction of the worksheet to right-to-left (default: false) - sheetname: "id" // (id, String), sheet name for the exported spreadsheet, (default: 'id') - }, - /** - * Constants - * @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" - } - }, - /** - * Character set (character encoding) of the HTML. - * @memberof TableExport.prototype - */ - charset: "charset=utf-8", - /** - * Filename fallback for exported files. - * @memberof TableExport.prototype - */ - defaultFilename: "myDownload", - /** - * Sheetname fallback for exported files. - * @memberof TableExport.prototype - */ - defaultSheetname: "myWorksheet", - /** - * 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: { - /** - * 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 - } - }, - /** - * 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)); - } - } - }, - 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); - } + 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); + + if (!Array.isArray(settings.formats)) { + settings.formats = [settings.formats]; + } + + self.processAll = function () { + self.contexts.forEach(function (context) { + context.processAllData(); + }); }; - var dataURI = _nodesArray(context.rows) - .map(function(val, ir) { - if (rcMap.isIgnore(ir)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); - } else if (rcMap.isEmpty(ir)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + self.processByKey = function (dataElement, key) { + + var uuid = _uuid(dataElement); + + var context = self.contexts[uuid]; + if (context) { + context.processDataByKey(key); + } + }; + + var _exportData = {}; + self.getExportData = function () { + + self.processAll(); + + return _exportData; + }; + + self.selectors.forEach(function (el) { + var context = {}; + var theadRows = el.querySelectorAll("thead > tr"); + context.rows = _nodesArray(el.querySelectorAll("tbody > tr")); + if (settings.headers) { + context.rows = _nodesArray(theadRows).concat(context.rows); } - var cols = val.querySelectorAll("th, td"); - return _nodesArray(cols) - .map(function(val, ic) { - var _return = getReturn(val); - if (rcMap.isIgnore(ir, ic)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); - } else if (rcMap.isEmpty(ir, ic)) { - return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + if (settings.footers) { + context.rows = _nodesArray(el.querySelectorAll("tfoot > tr")).concat(context.rows); + } + context.thAdj = settings.headers ? theadRows.length : 0; + context.filename = (settings.filename === "id" ? el.getAttribute("id") : settings.filename) || self.defaultFilename; + context.sheetname = (settings.sheetname === "id" ? el.getAttribute("id") : settings.sheetname) || self.defaultSheetname; + 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 { - return rcMap.handleRowColMapProp(rcMap.TYPE.DEFAULT, ir, ic, key, _return, colDel); + caption = document.createElement("caption"); + caption.className = settings.bootstrapSettings.bootstrapSpacing + self.defaultCaptionClass; + caption.style.cssText = "caption-side: " + settings.position; + 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.rcMap = function () { + return new RowColMap().build(context, settings); + }; + + var formatMap = _FORMAT_LIST.reduce(function (acc, cur) { + acc[cur] = 0; + return acc; + }, {}); + + context.processDataByKey = function (key) { + if (!_isValidFormat(key)) { + return _handleError('"' + key + '" is not a valid format. \nFormats: ' + _FORMAT_LIST.join(", ")); } - }) - .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, - RTL: settings.RTL, - sheetname: settings.sheetname + if (!_hasDependencies(key)) { + // TODO: provide a fallback option to XLS? + return _handleError('"' + key + '" requires "js-xlsx".'); + } + + if (!formatMap[key]) { + var exporter = self.exporters.build.call(self, context, key); + context.setExportData(exporter); + formatMap[key]++; + } + }; + + context.processAllData = function () { + return settings.formats.forEach(function (key) { + context.processDataByKey(key); + }); + }; + + if (settings.exportButtons) { + settings.formats.forEach(function (key) { + var format = self.formatConfig[key]; + + var hashKey = _hashCode({ + uuid: context.uuid, + type: key + }); + context.checkCaption(self.createObjButton(hashKey, format.buttonContent, format.defaultClass, settings.bootstrapSettings)); + }); + } + + self.contexts[context.uuid] = context; }); - var hashKey = _hashCode({ uuid: context.uuid, type: key }); + var exportButton = document.querySelectorAll("button[" + self.storageKey + "]"); - 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; + _on(exportButton, "click", self.downloadHandler, self); + + return self; + }; + + TableExport.prototype = { + /** + * Version. + * @memberof TableExport.prototype + */ + version: "5.2.1", + /** + * 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) + RTL: false, // (Boolean), set direction of the worksheet to right-to-left (default: false) + sheetname: "id" // (id, String), sheet name for the exported spreadsheet, (default: 'id') + }, + /** + * Constants + * @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" + } + }, + /** + * Character set (character encoding) of the HTML. + * @memberof TableExport.prototype + */ + charset: "charset=utf-8", + /** + * Filename fallback for exported files. + * @memberof TableExport.prototype + */ + defaultFilename: "myDownload", + /** + * Sheetname fallback for exported files. + * @memberof TableExport.prototype + */ + defaultSheetname: "myWorksheet", + /** + * 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: { + /** + * XLSX (Open XML spreadsheet) file extension configuration + * @memberof TableExport.prototype + */ + xlsx: { + defaultClass: "export-type-xlsx xlsx", + buttonContent: "Export to xlsx", + mimeType: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + fileExtension: ".xlsx" + }, + xlsm: { + defaultClass: "export-type-xlsm xlsm", + buttonContent: "Export to xlsm", + mimeType: "application/vnd.ms-excel.sheet.macroEnabled.main+xml", + fileExtension: ".xlsm" + }, + xlsb: { + defaultClass: "export-type-xlsb 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: "export-type-xls 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: "export-type-csv csv", + buttonContent: "Export to csv", + separator: ",", + mimeType: "text/csv", + fileExtension: ".csv", + enforceStrictRFC4180: true + }, + /** + * TXT (Plain Text) file extension configuration + * @memberof TableExport.prototype + */ + txt: { + defaultClass: "export-type-txt txt", + buttonContent: "Export to txt", + separator: " ", + mimeType: "text/plain", + fileExtension: ".txt", + 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); + } + }, + 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)); + } + } + }, + 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); + } + }; + + var dataURI = _nodesArray(context.rows) + .map(function (val, ir) { + if (rcMap.isIgnore(ir)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); + } else if (rcMap.isEmpty(ir)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + } + var cols = val.querySelectorAll("th, td"); + return _nodesArray(cols) + .map(function (val, ic) { + var _return = getReturn(val); + if (rcMap.isIgnore(ir, ic)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.IGNORE); + } else if (rcMap.isEmpty(ir, ic)) { + return rcMap.handleRowColMapProp(rcMap.TYPE.EMPTY); + } else { + return rcMap.handleRowColMapProp(rcMap.TYPE.DEFAULT, ir, ic, key, _return, colDel); + } + }) + .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, + RTL: settings.RTL, + sheetname: settings.sheetname + }); + + var hashKey = _hashCode({ + uuid: context.uuid, + type: key + }); + + return Storage.getInstance().setItem(hashKey, dataObject, true); + } + }, + /** + * Creates file export buttons + * @param hashKey {String} + * @param myContent {String} + * @param myClass {String} + * @param bootstrapSettings {Object} + * @returns Element + */ + createObjButton: function (hashKey, 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) { + + event.target.disabled = true; + + var items = event.target.className.split(' ').filter(function (className) { + return className.startsWith('export-type-'); + }); + var key = event.target.getAttribute(this.storageKey); + + var contextKey = ''; + if (items.length !== 1) { + console.log('Unable to find the export type'); + if (!Storage.getInstance().exists(key)) { + this.processAll(); + } + + } else { + contextKey = items[0].substring(12); + + if (!Storage.getInstance().exists(key)) { + this.processByKey(event.target.closest('caption').parentNode, contextKey); + } + + } + + var object = JSON.parse(Storage.getInstance().getItem(key)); + + this.export2file(object.data, + object.mimeType, + object.filename, + object.fileExtension, + object.merges, + object.RTL, + object.sheetname); + + event.target.disabled = false; + }, + /** + * Excel Workbook constructor + * @memberof TableExport.prototype + * @constructor + */ + Workbook: function () { + this.Workbook = { + Views: [] + }; + 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[]} + * @param RTL {Boolean} + */ + export2file: function (data, mime, name, extension, merges, RTL, sheetname) { + var format = extension.slice(1); + data = this.getRawData(data, extension, name, merges, RTL, sheetname); + + if (_isMobile && (format === _FORMAT.CSV || format === _FORMAT.TXT)) { + 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, RTL, sheetname) { + var key = extension.substring(1); + + if (_isEnhanced(key)) { + var wb = new this.Workbook(), + ws = this.createSheet(data, merges), + bookType = this.getBookType(key); + + sheetname = sheetname || ""; + wb.SheetNames.push(sheetname); + wb.Sheets[sheetname] = ws; + wb.Workbook.Views[0] = { + RTL: RTL + }; + 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); + }); } - } - 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, - RTL = object.RTL, - sheetname = object.sheetname; - this.export2file(data, mimeType, filename, fileExtension, merges, RTL, sheetname); - }, + }; + /** - * Excel Workbook constructor + * Storage main interface constructor * @memberof TableExport.prototype * @constructor */ - Workbook: function() { - this.Workbook = { Views: [] }; - 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[]} - * @param RTL {Boolean} - */ - export2file: function(data, mime, name, extension, merges, RTL, sheetname) { - var format = extension.slice(1); - data = this.getRawData(data, extension, name, merges, RTL, sheetname); - - if (_isMobile && (format === _FORMAT.CSV || format === _FORMAT.TXT)) { - 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, RTL, sheetname) { - var key = extension.substring(1); - - if (_isEnhanced(key)) { - var wb = new this.Workbook(), - ws = this.createSheet(data, merges), - bookType = this.getBookType(key); - - sheetname = sheetname || ""; - wb.SheetNames.push(sheetname); - wb.Sheets[sheetname] = ws; - wb.Workbook.Views[0] = { RTL: RTL }; - 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]; - } 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 (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; + 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) { + + console.log('Starting the save to store'); + 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); + console.log('Ended the save to store'); + + 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); + }; + }; - 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); + Storage.getInstance = function () { + if (!this._instance) { + this._instance = new Storage(); } - } - 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); - } + + return this._instance; }; - }; - 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 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 && _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); + + /** + * RowColMap main interface constructor + * @memberof TableExport.prototype + * @constructor + */ + var RowColMap = function () { + this.rcMap = []; + this.merges = []; + + this.isIgnore = function (ir, ic) { + return this.getRowColMapProp(ir, ic, RowColMap.prototype.TYPE.IGNORE); + }; + this.isEmpty = function (ir, ic) { + return this.getRowColMapProp(ir, ic, RowColMap.prototype.TYPE.EMPTY); + }; + this.isRowSpan = function (ir) { + return this.getRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.ROWSPAN); + }; + this.isColSpan = function (ir) { + return this.getRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.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) { + this.merges.push(mergeObj); + this.setRowColMapProp(ir, undefined, RowColMap.prototype.TYPE.MERGE, this.merges); + }; + this.getRowColMapProp = function (ir, ic, key) { + if (this.rcMap[ir]) { + if (_undefined(key)) { + return this.rcMap[ir][ic]; + } + if (_undefined(ic)) { + return this.rcMap[ir][key]; + } + 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 (_undefined(key) && _undefined(ic)) { + // bailout + return; + } + + if (_defined(ic) && _undefined(key)) { + return this.rcMap[ir][ic] = value; + } + + if (_undefined(ic) && _defined(key)) { + return this.rcMap[ir][key] = value; + } + + 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); + } + + return new Array(total).concat(_return).join(colDel); } - } - - 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; + 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); + } + }; + }; + 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 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 < 2) { + 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 (_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; + + if (colSpan < 2) { + 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; + } + irStart = ir; - irEnd = ir + rowSpan - 1; - icStart = ic + totalColSpan - countColSpan + (_re || 0); - icEnd = icStart + Math.max(1, colSpan) - 1; + 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 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 handleMerge = function (irs, ics, ire, ice) { + var merge = { + s: { + r: irs, + c: ics + }, + e: { + r: ire, + c: ice + } + }; + return self.addMerge(irs, merge); + }; - if (colSpan <= 1) { - return false; - } + context.rows.map(function (val, ir) { + + if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || val.matches(settings.ignoreCSS)) { + handleIgnore(ir); + } + + if (val.matches(settings.emptyCSS)) { + handleEmpty(ir); + } - 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 cols = val.querySelectorAll("th, td"); + var final = _nodesArray(cols).map(function (val, ic) { + + if (!!~settings.ignoreCols.indexOf(ic) || val.matches(settings.ignoreCSS)) { + handleIgnore(ir, ic); + } + if (val.matches(settings.emptyCSS)) { + handleEmpty(ir, ic); + } + if (val.hasAttribute("rowspan")) { + handleRowSpan(val, ir, ic); + } else if (val.hasAttribute("colspan")) { + handleColSpan(val, ir, ic); + } + }); + + return final; + }); + + return self; } - }; + }; - var handleMerge = function(irs, ics, ire, ice) { - var merge = { - s: { r: irs, c: ics }, - e: { r: ire, c: ice } + 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 && !TableExport.prototype.formatConfig[key].enforceStrictRFC4180; }; - return self.addMerge(irs, merge); - }; + })(); + + var _FORMAT = (function () { + return TableExport.prototype.CONSTANTS.FORMAT; + })(); + + var _FORMAT_LIST = (function () { + return Object.keys(_FORMAT).map(function (key) { + return _FORMAT[key]; + }); + })(); + + var _TYPE = (function () { + return TableExport.prototype.CONSTANTS.TYPE; + })(); - _nodesArray(context.rows).map(function(val, ir) { - if (!!~settings.ignoreRows.indexOf(ir - context.thAdj) || _matches(val, settings.ignoreCSS)) { - handleIgnore(ir); + Object.defineProperty(Array.prototype, "processRows", { + enumerable: false, + value: function (key, rowDel) { + if (_isEnhanced(key)) { + return this.map(_toArray).filter(_defined); + } + + return this.filter(_defined).join(rowDel); } - if (_matches(val, settings.emptyCSS)) { - handleEmpty(ir); + }); + + Object.defineProperty(Array.prototype, "processCols", { + enumerable: false, + value: function (key, colDel) { + if (_isEnhanced(key)) { + return this.filter(_defined); + } + + return this.filter(_defined).join(colDel); } - 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 _uuid = (function () { + var uuid = 0; + + 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; + }; + })(); + + var _hashCode = (function () { + 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 _on = (function () { + var prevFn = null; + + return function (el, event, fn, context) { + var curFn = fn.bind(context); + for (var i = 0; i < el.length; ++i) { + prevFn && el[i].removeEventListener(event, prevFn, false); + el[i].addEventListener(event, curFn, false); + } + 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]; } - }; - - 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 && !TableExport.prototype.formatConfig[key].enforceStrictRFC4180; - }; - })(); - var _FORMAT = (function() { - return TableExport.prototype.CONSTANTS.FORMAT; - })(); + function _nodesArray(els) { + return _undefined(els.length) ? [].concat(els) : [].slice.call(els); + } - var _FORMAT_LIST = (function() { - return Object.keys(_FORMAT).map(function(key) { - return _FORMAT[key]; - }); - })(); - - var _TYPE = (function() { - return TableExport.prototype.CONSTANTS.TYPE; - })(); - - Object.defineProperty(Array.prototype, "processRows", { - enumerable: false, - value: function(key, rowDel) { - if (_isEnhanced(key)) { - return this.map(_toArray).filter(_defined); - } else { - return this.filter(_defined).join(rowDel); - } + function _defined(val) { + return typeof val !== "undefined"; } - }); - - Object.defineProperty(Array.prototype, "processCols", { - enumerable: false, - value: function(key, colDel) { - if (_isEnhanced(key)) { - return this.filter(_defined); - } else { - return this.filter(_defined).join(colDel); - } + + function _undefined(val) { + return typeof val === "undefined"; } - }); - var _uuid = (function() { - var uuid = 0; + function _toArray(val) { + return val instanceof Array ? [].concat.apply([], val) : val; + } - return function(el) { - var tableKey = el.getAttribute(TableExport.prototype.tableKey); + function _isValidFormat(key) { + return ~_FORMAT_LIST.indexOf(key); + } - if (!tableKey) { - tableKey = el.id ? el.id : TableExport.prototype.defaultNamespace + ++uuid; - el.setAttribute(TableExport.prototype.tableKey, tableKey); - } - return tableKey; - }; - })(); - - var _hashCode = (function() { - 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 _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; - }; - })(); - - 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); - } - - function _hasClass(el, cls) { - return el.classList ? el.classList.contains(cls) : new RegExp("(^| )" + cls + "( |$)", "gi").test(el.cls); - } - - function _matches(el, selectors) { - return ( - selectors.filter(function(selector) { - return [].indexOf.call(document.querySelectorAll(selector), el) !== -1; - }).length > 0 - ); - } - - function _numeric(val) { - return !isNaN(val); - } - - function _defined(val) { - return typeof val !== "undefined"; - } - - function _toArray(val) { - return val instanceof Array ? [].concat.apply([], val) : val; - } - - function _isValidFormat(key) { - return ~_FORMAT_LIST.indexOf(key); - } - - function _hasDependencies(key) { - var hasDependencies; - - switch (key) { - case _FORMAT.TXT: - case _FORMAT.CSV: - case _FORMAT.XLS: - hasDependencies = true; - break; - default: - hasDependencies = _isEnhanced(key); + 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; } - return hasDependencies; - } - - 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 = ""; + + function _handleError(msg) { + console.error(msg); + return new Error(msg); } - 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); - }; + function _getBootstrapSettings(bootstrap, bootstrapConfig, defaultButton) { + var config = { + bootstrapClass: defaultButton + " ", + bootstrapTheme: "", + bootstrapSpacing: "" + }; - // alias the TableExport prototype - for (var prop in TableExport.prototype) { - $.fn.tableExport[prop] = TableExport.prototype[prop]; + if (bootstrap) { + config.bootstrapClass = bootstrapConfig[0] + " "; + config.bootstrapTheme = bootstrapConfig[1] + " "; + config.bootstrapSpacing = bootstrapConfig[2] + " "; + } + + 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); + }; + + // 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; }); diff --git a/src/stable/js/tableexport.min.js b/src/stable/js/tableexport.min.js index 96ef205..efda15b 100644 --- a/src/stable/js/tableexport.min.js +++ b/src/stable/js/tableexport.min.js @@ -1,5 +1,5 @@ /*! - * TableExport.js v5.2.0 (https://www.travismclarke.com) + * TableExport.js v5.2.1 (https://www.travismclarke.com) * * Copyright (c) 2018 - Travis Clarke - https://www.travismclarke.com * @@ -16,4 +16,4 @@ * limitations under the License. * */ -!function(t,r){if("function"==typeof define&&define.amd)define(function(t){var e;try{e=t("jquery")}catch(t){}return r(e,t("blobjs"),t("file-saverjs"),t("xlsx"))});else if("object"==typeof exports&&"string"!=typeof exports.nodeName){var e;try{e=require("jquery")}catch(t){}module.exports=r(e,require("blobjs"),require("file-saverjs"),require("xlsx"))}else t.TableExport=r(t.jQuery,t.Blob,t.saveAs,t.XLSX)}(this,function(e,p,c,f){"use strict";var o=function(t,e){var a=this;if(!t)return S('"selectors" is required. \nUsage: TableExport(selectors, options)');if(!a)return new o(t,e);a.settings=C({},a.defaults,e),a.selectors=w(t);var i=a.settings;i.ignoreRows=i.ignoreRows instanceof Array?i.ignoreRows:[i.ignoreRows],i.ignoreCols=i.ignoreCols instanceof Array?i.ignoreCols:[i.ignoreCols],i.ignoreCSS=a.ignoreCSS instanceof Array?a.ignoreCSS:[a.ignoreCSS],i.emptyCSS=a.emptyCSS instanceof Array?a.emptyCSS:[a.emptyCSS],i.formatValue=a.formatValue.bind(this,i.trimWhitespace),i.bootstrapSettings=function(t,e,r){var o={};o.bootstrapSpacing=t?(o.bootstrapClass=e[0]+" ",o.bootstrapTheme=e[1]+" ",e[2]+" "):(o.bootstrapClass=r+" ",o.bootstrapTheme="","");return o}(i.bootstrap,a.bootstrapConfig,a.defaultButton);var s={};a.getExportData=function(){return s},a.selectors.forEach(function(r){var o={};o.rows=w(r.querySelectorAll("tbody > tr")),o.rows=i.headers?w(r.querySelectorAll("thead > tr")).concat(o.rows):o.rows,o.rows=i.footers?o.rows.concat(w(r.querySelectorAll("tfoot > tr"))):o.rows,o.thAdj=i.headers?r.querySelectorAll("thead > tr").length:0,o.filename="id"===i.filename?r.getAttribute("id")||a.defaultFilename:i.filename||a.defaultFilename,o.sheetname="id"===i.sheetname?r.getAttribute("id")||a.defaultSheetname:i.sheetname||a.defaultSheetname,o.uuid=v(r),o.checkCaption=function(t){var e=r.querySelectorAll("caption."+a.defaultCaptionClass);e.length?e[0].appendChild(t):((e=document.createElement("caption")).className=i.bootstrapSettings.bootstrapSpacing+a.defaultCaptionClass,e.style.cssText="caption-side: "+i.position,e.appendChild(t),r.insertBefore(e,r.firstChild))},o.setExportData=function(t){var e=u.getInstance().getItem(t),r=t.substring(t.indexOf("-")+1);s[o.uuid]=s[o.uuid]||{},s[o.uuid][r]=JSON.parse(e)},o.rcMap=(new l).build(o,i);var n=g.reduce(function(t,e){return t[e]=0,t},{});i.formats.forEach(function(t){return e=t,~g.indexOf(e)?function(t){var e;switch(t){case m.TXT:case m.CSV:case m.XLS:e=!0;break;default:e=d(t)}return e}(t)?void(n[t]||(o.setExportData(a.exporters.build.call(a,o,t)),n[t]++)):S('"'+t+'" requires "js-xlsx".'):S('"'+t+'" is not a valid format. \nFormats: '+g.join(", "));var e})});var r=document.querySelectorAll("button["+a.storageKey+"]");return n(r,"click",a.downloadHandler,a),a};o.prototype={version:"5.2.0",defaults:{headers:!0,footers:!0,formats:["xlsx","csv","txt"],filename:"id",bootstrap:!1,exportButtons:!0,position:"bottom",ignoreRows:null,ignoreCols:null,trimWhitespace:!0,RTL:!1,sheetname:"id"},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"}},charset:"charset=utf-8",defaultFilename:"myDownload",defaultSheetname:"myWorksheet",defaultButton:"button-default",defaultCaptionClass:"tableexport-caption",defaultNamespace:"tableexport-",tableKey:"tableexport-key",storageKey:"tableexport-id",ignoreCSS:".tableexport-ignore",emptyCSS:".tableexport-empty",bootstrapConfig:["btn","btn-default","btn-toolbar"],rowDel:"\r\n",entityMap:{"&":"&","<":"<",">":">","'":"'","/":"/"},formatConfig:{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:{defaultClass:"xls",buttonContent:"Export to xls",separator:"\t",mimeType:"application/vnd.ms-excel",fileExtension:".xls",enforceStrictRFC4180:!1},csv:{defaultClass:"csv",buttonContent:"Export to csv",separator:",",mimeType:"text/csv",fileExtension:".csv",enforceStrictRFC4180:!0},txt:{defaultClass:"txt",buttonContent:"Export to txt",separator:" ",mimeType:"text/plain",fileExtension:".txt",enforceStrictRFC4180:!0}},typeConfig:{string:{defaultClass:"tableexport-string"},number:{defaultClass:"tableexport-number",assert:function(t){return!isNaN(t)}},boolean:{defaultClass:"tableexport-boolean",assert:function(t){return"true"===t.toLowerCase()||"false"===t.toLowerCase()}},date:{defaultClass:"tableexport-date",assert:function(t){return!/.*%/.test(t)&&!isNaN(Date.parse(t))}}},exporters:{build:function(t,n){var a=this,i=a.settings,e=a.formatConfig[n],s=e.separator,l=t.rcMap,r=w(t.rows).map(function(t,o){return l.isIgnore(o)?l.handleRowColMapProp(l.TYPE.IGNORE):l.isEmpty(o)?l.handleRowColMapProp(l.TYPE.EMPTY):w(t.querySelectorAll("th, td")).map(function(t,e){var r=function(t){if(d(n))return{v:i.formatValue(t.textContent),t:a.getType(t.className)};switch(n){case m.CSV:return'"'+i.formatValue(t.textContent.replace(/"/g,'""'))+'"';default:return i.formatValue(t.textContent)}}(t);return l.isIgnore(o,e)?l.handleRowColMapProp(l.TYPE.IGNORE):l.isEmpty(o,e)?l.handleRowColMapProp(l.TYPE.EMPTY):l.handleRowColMapProp(l.TYPE.DEFAULT,o,e,n,r,s)}).processCols(n,s)}).processRows(n,a.rowDel),o=JSON.stringify({data:r,filename:t.filename,mimeType:e.mimeType,fileExtension:e.fileExtension,merges:l.merges,RTL:i.RTL,sheetname:i.sheetname}),p=y({uuid:t.uuid,type:n});return i.exportButtons&&t.checkCaption(a.createObjButton(p,o,e.buttonContent,e.defaultClass,i.bootstrapSettings)),u.getInstance().setItem(p,o,!0)}},createObjButton:function(t,e,r,o,n){var a=document.createElement("button");return a.setAttribute("type","button"),a.setAttribute(this.storageKey,t),a.className=n.bootstrapClass+n.bootstrapTheme+o,a.textContent=r,a},escapeHtml:function(t){var e=this;return String(t).replace(/[&<>'\/]/g,function(t){return e.entityMap[t]})},unescapeHtml:function(t){var e=String(t);for(var r in this.entityMap)e=e.replace(RegExp(this.entityMap[r],"g"),r);return e},formatValue:function(t,e){return t?e.trim():e},getType:function(t){if(!t)return"";var e=this.typeConfig;return~t.indexOf(e.string.defaultClass)?b.STRING:~t.indexOf(e.number.defaultClass)?b.NUMBER:~t.indexOf(e.boolean.defaultClass)?b.BOOLEAN:~t.indexOf(e.date.defaultClass)?b.DATE:""},dateNum:function(t,e){e&&(t+=1462);var r=(Date.parse(t)-new Date(Date.UTC(1899,11,30)))/864e5;return Math.floor(r)},createSheet:function(t,e){for(var r={},o={s:{c:1e7,r:1e7},e:{c:0,r:0}},n=this.typeConfig,a=0;a!==t.length;++a)for(var i=0;i!==t[a].length;++i){o.s.r>a&&(o.s.r=a),o.s.c>i&&(o.s.c=i),o.e.r tr");o.rows=C(r.querySelectorAll("tbody > tr")),s.headers&&(o.rows=C(t).concat(o.rows)),s.footers&&(o.rows=C(r.querySelectorAll("tfoot > tr")).concat(o.rows)),o.thAdj=s.headers?t.length:0,o.filename=("id"===s.filename?r.getAttribute("id"):s.filename)||a.defaultFilename,o.sheetname=("id"===s.sheetname?r.getAttribute("id"):s.sheetname)||a.defaultSheetname,o.uuid=b(r),o.checkCaption=function(t){var e=r.querySelectorAll("caption."+a.defaultCaptionClass);e.length?e[0].appendChild(t):((e=document.createElement("caption")).className=s.bootstrapSettings.bootstrapSpacing+a.defaultCaptionClass,e.style.cssText="caption-side: "+s.position,e.appendChild(t),r.insertBefore(e,r.firstChild))},o.setExportData=function(t){var e=u.getInstance().getItem(t),r=t.substring(t.indexOf("-")+1);i[o.uuid]=i[o.uuid]||{},i[o.uuid][r]=JSON.parse(e)},o.rcMap=function(){return(new p).build(o,s)};var n=g.reduce(function(t,e){return t[e]=0,t},{});o.processDataByKey=function(t){if(e=t,!~g.indexOf(e))return S('"'+t+'" is not a valid format. \nFormats: '+g.join(", "));var e;if(!function(t){var e;switch(t){case d.TXT:case d.CSV:case d.XLS:e=!0;break;default:e=m(t)}return e}(t))return S('"'+t+'" requires "js-xlsx".');if(!n[t]){var r=a.exporters.build.call(a,o,t);o.setExportData(r),n[t]++}},o.processAllData=function(){return s.formats.forEach(function(t){o.processDataByKey(t)})},s.exportButtons&&s.formats.forEach(function(t){var e=a.formatConfig[t],r=v({uuid:o.uuid,type:t});o.checkCaption(a.createObjButton(r,e.buttonContent,e.defaultClass,s.bootstrapSettings))}),a.contexts[o.uuid]=o});var r=document.querySelectorAll("button["+a.storageKey+"]");return n(r,"click",a.downloadHandler,a),a};o.prototype={version:"5.2.1",defaults:{headers:!0,footers:!0,formats:["xlsx","csv","txt"],filename:"id",bootstrap:!1,exportButtons:!0,position:"bottom",ignoreRows:null,ignoreCols:null,trimWhitespace:!0,RTL:!1,sheetname:"id"},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"}},charset:"charset=utf-8",defaultFilename:"myDownload",defaultSheetname:"myWorksheet",defaultButton:"button-default",defaultCaptionClass:"tableexport-caption",defaultNamespace:"tableexport-",tableKey:"tableexport-key",storageKey:"tableexport-id",ignoreCSS:".tableexport-ignore",emptyCSS:".tableexport-empty",bootstrapConfig:["btn","btn-default","btn-toolbar"],rowDel:"\r\n",entityMap:{"&":"&","<":"<",">":">","'":"'","/":"/"},formatConfig:{xlsx:{defaultClass:"export-type-xlsx xlsx",buttonContent:"Export to xlsx",mimeType:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",fileExtension:".xlsx"},xlsm:{defaultClass:"export-type-xlsm xlsm",buttonContent:"Export to xlsm",mimeType:"application/vnd.ms-excel.sheet.macroEnabled.main+xml",fileExtension:".xlsm"},xlsb:{defaultClass:"export-type-xlsb xlsb",buttonContent:"Export to xlsb",mimeType:"application/vnd.ms-excel.sheet.binary.macroEnabled.main",fileExtension:".xlsb"},xls:{defaultClass:"export-type-xls xls",buttonContent:"Export to xls",separator:"\t",mimeType:"application/vnd.ms-excel",fileExtension:".xls",enforceStrictRFC4180:!1},csv:{defaultClass:"export-type-csv csv",buttonContent:"Export to csv",separator:",",mimeType:"text/csv",fileExtension:".csv",enforceStrictRFC4180:!0},txt:{defaultClass:"export-type-txt txt",buttonContent:"Export to txt",separator:" ",mimeType:"text/plain",fileExtension:".txt",enforceStrictRFC4180:!0}},typeConfig:{string:{defaultClass:"tableexport-string"},number:{defaultClass:"tableexport-number",assert:function(t){return!isNaN(t)}},boolean:{defaultClass:"tableexport-boolean",assert:function(t){return"true"===t.toLowerCase()||"false"===t.toLowerCase()}},date:{defaultClass:"tableexport-date",assert:function(t){return!/.*%/.test(t)&&!isNaN(Date.parse(t))}}},exporters:{build:function(t,n){var a=this,s=a.settings,e=a.formatConfig[n],i=e.separator,p=t.rcMap(),r=C(t.rows).map(function(t,o){return p.isIgnore(o)?p.handleRowColMapProp(p.TYPE.IGNORE):p.isEmpty(o)?p.handleRowColMapProp(p.TYPE.EMPTY):C(t.querySelectorAll("th, td")).map(function(t,e){var r=function(t){if(m(n))return{v:s.formatValue(t.textContent),t:a.getType(t.className)};switch(n){case d.CSV:return'"'+s.formatValue(t.textContent.replace(/"/g,'""'))+'"';default:return s.formatValue(t.textContent)}}(t);return p.isIgnore(o,e)?p.handleRowColMapProp(p.TYPE.IGNORE):p.isEmpty(o,e)?p.handleRowColMapProp(p.TYPE.EMPTY):p.handleRowColMapProp(p.TYPE.DEFAULT,o,e,n,r,i)}).processCols(n,i)}).processRows(n,a.rowDel),o=JSON.stringify({data:r,filename:t.filename,mimeType:e.mimeType,fileExtension:e.fileExtension,merges:p.merges,RTL:s.RTL,sheetname:s.sheetname}),l=v({uuid:t.uuid,type:n});return u.getInstance().setItem(l,o,!0)}},createObjButton:function(t,e,r,o){var n=document.createElement("button");return n.setAttribute("type","button"),n.setAttribute(this.storageKey,t),n.className=o.bootstrapClass+o.bootstrapTheme+r,n.textContent=e,n},escapeHtml:function(t){var e=this;return String(t).replace(/[&<>'\/]/g,function(t){return e.entityMap[t]})},unescapeHtml:function(t){var e=String(t);for(var r in this.entityMap)e=e.replace(RegExp(this.entityMap[r],"g"),r);return e},formatValue:function(t,e){return t?e.trim():e},getType:function(t){if(!t)return"";var e=this.typeConfig;return~t.indexOf(e.string.defaultClass)?y.STRING:~t.indexOf(e.number.defaultClass)?y.NUMBER:~t.indexOf(e.boolean.defaultClass)?y.BOOLEAN:~t.indexOf(e.date.defaultClass)?y.DATE:""},dateNum:function(t,e){e&&(t+=1462);var r=(Date.parse(t)-new Date(Date.UTC(1899,11,30)))/864e5;return Math.floor(r)},createSheet:function(t,e){for(var r={},o={s:{c:1e7,r:1e7},e:{c:0,r:0}},n=this.typeConfig,a=0;a!==t.length;++a)for(var s=0;s!==t[a].length;++s){o.s.r>a&&(o.s.r=a),o.s.c>s&&(o.s.c=s),o.e.r