dist/js/tableexport.js
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);
    /**
     * Selectors (tables) to apply the library to
     */
    self.selectors = _nodesArray(selectors);

    var settings = self.settings;
    settings.ignoreRows = settings.ignoreRows instanceof Array ? settings.ignoreRows : [settings.ignoreRows];
    settings.ignoreCols = settings.ignoreCols instanceof Array ? settings.ignoreCols : [settings.ignoreCols];
    settings.ignoreCSS = self.ignoreCSS instanceof Array ? self.ignoreCSS : [self.ignoreCSS];
    settings.emptyCSS = self.emptyCSS instanceof Array ? self.emptyCSS : [self.emptyCSS];
    settings.formatValue = self.formatValue.bind(this, settings.trimWhitespace);
    settings.bootstrapSettings = _getBootstrapSettings(settings.bootstrap, self.bootstrapConfig, self.defaultButton);

    var _exportData = {};
    self.getExportData = function() {
      return _exportData;
    };

    self.selectors.forEach(function(el) {
      var context = {};

      context.rows = _nodesArray(el.querySelectorAll("tbody > tr"));
      context.rows = settings.headers ? _nodesArray(el.querySelectorAll("thead > tr")).concat(context.rows) : context.rows;
      context.rows = settings.footers ? context.rows.concat(_nodesArray(el.querySelectorAll("tfoot > tr"))) : context.rows;
      context.thAdj = settings.headers ? el.querySelectorAll("thead > tr").length : 0;
      context.filename = settings.filename === "id" ? el.getAttribute("id") || 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]++;
        }
      });
    });

    var exportButton = document.querySelectorAll("button[" + self.storageKey + "]");
    _on(exportButton, "click", self.downloadHandler, self);

    return self;
  }
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);
    /**
     * Selectors (tables) to apply the library to
     */
    self.selectors = _nodesArray(selectors);

    var settings = self.settings;
    settings.ignoreRows = settings.ignoreRows instanceof Array ? settings.ignoreRows : [settings.ignoreRows];
    settings.ignoreCols = settings.ignoreCols instanceof Array ? settings.ignoreCols : [settings.ignoreCols];
    settings.ignoreCSS = self.ignoreCSS instanceof Array ? self.ignoreCSS : [self.ignoreCSS];
    settings.emptyCSS = self.emptyCSS instanceof Array ? self.emptyCSS : [self.emptyCSS];
    settings.formatValue = self.formatValue.bind(this, settings.trimWhitespace);
    settings.bootstrapSettings = _getBootstrapSettings(settings.bootstrap, self.bootstrapConfig, self.defaultButton);

    var _exportData = {};
    self.getExportData = function() {
      return _exportData;
    };

    self.selectors.forEach(function(el) {
      var context = {};

      context.rows = _nodesArray(el.querySelectorAll("tbody > tr"));
      context.rows = settings.headers ? _nodesArray(el.querySelectorAll("thead > tr")).concat(context.rows) : context.rows;
      context.rows = settings.footers ? context.rows.concat(_nodesArray(el.querySelectorAll("tfoot > tr"))) : context.rows;
      context.thAdj = settings.headers ? el.querySelectorAll("thead > tr").length : 0;
      context.filename = settings.filename === "id" ? el.getAttribute("id") || 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]++;
        }
      });
    });

    var exportButton = document.querySelectorAll("button[" + self.storageKey + "]");
    _on(exportButton, "click", self.downloadHandler, self);

    return self;
  }
TableExport main library constructor
@param selectors {jQuery} jQuery selector(s)
@param options {Object} TableExport configuration options
@constructor