diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..dc731ee Binary files /dev/null and b/.DS_Store differ diff --git a/bower.json b/bower.json index b77cfb6..50dc509 100644 --- a/bower.json +++ b/bower.json @@ -23,5 +23,10 @@ "iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.0.0", "web-component-tester": "^4.0.0", "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" + }, + "resolutions": { + "iron-ajax": "^2.1.3", + "paper-button": "^2.1.0", + "webcomponentsjs": "^v1.0.19" } } diff --git a/editable-table.html b/editable-table.html index 59c73b6..a0ccea1 100644 --- a/editable-table.html +++ b/editable-table.html @@ -5,6 +5,7 @@ + +
| ` | +| Add a Mini-Map | The little dots that appear next to the column navigation buttons. Use the `data-tablesaw-minimap` attribute: ` |
|---|
| Rank | +Movie Title | +Year | +Rating | + +Reviews | +
|---|
| inside of ?");
+ }
+
+ // assign an id if there is none
+ if (!this.$table.attr("id")) {
+ this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000));
+ }
+
+ this.createToolbar();
+
+ this._initCells();
+
+ this.$table.data(pluginName, this);
+
+ this.$table.trigger(events.create, [this]);
+ };
+
+ Table.prototype.getConfig = function(pluginSpecificConfig) {
+ // shoestring extend doesn’t support arbitrary args
+ var configs = $.extend(defaultConfig, pluginSpecificConfig || {});
+ return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {});
+ };
+
+ Table.prototype._getPrimaryHeaderRow = function() {
+ return this._getHeaderRows().eq(0);
+ };
+
+ Table.prototype._getHeaderRows = function() {
+ return this.$thead
+ .children()
+ .filter("tr")
+ .filter(function() {
+ return !$(this).is("[data-tablesaw-ignorerow]");
+ });
+ };
+
+ Table.prototype._getRowIndex = function($row) {
+ return $row.prevAll().length;
+ };
+
+ Table.prototype._getHeaderRowIndeces = function() {
+ var self = this;
+ var indeces = [];
+ this._getHeaderRows().each(function() {
+ indeces.push(self._getRowIndex($(this)));
+ });
+ return indeces;
+ };
+
+ Table.prototype._getPrimaryHeaderCells = function($row) {
+ return ($row || this._getPrimaryHeaderRow()).find("th");
+ };
+
+ Table.prototype._$getCells = function(th) {
+ var self = this;
+ return $(th)
+ .add(th.cells)
+ .filter(function() {
+ var $t = $(this);
+ var $row = $t.parent();
+ var hasColspan = $t.is("[colspan]");
+ // no subrows or ignored rows (keep cells in ignored rows that do not have a colspan)
+ return (
+ !$row.is("[" + self.attributes.subrow + "]") &&
+ (!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan)
+ );
+ });
+ };
+
+ Table.prototype._getVisibleColspan = function() {
+ var colspan = 0;
+ this._getPrimaryHeaderCells().each(function() {
+ var $t = $(this);
+ if ($t.css("display") !== "none") {
+ colspan += parseInt($t.attr("colspan"), 10) || 1;
+ }
+ });
+ return colspan;
+ };
+
+ Table.prototype.getColspanForCell = function($cell) {
+ var visibleColspan = this._getVisibleColspan();
+ var visibleSiblingColumns = 0;
+ if ($cell.closest("tr").data("tablesaw-rowspanned")) {
+ visibleSiblingColumns++;
+ }
+
+ $cell.siblings().each(function() {
+ var $t = $(this);
+ var colColspan = parseInt($t.attr("colspan"), 10) || 1;
+
+ if ($t.css("display") !== "none") {
+ visibleSiblingColumns += colColspan;
+ }
+ });
+ // console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns );
+
+ return visibleColspan - visibleSiblingColumns;
+ };
+
+ Table.prototype.isCellInColumn = function(header, cell) {
+ return $(header)
+ .add(header.cells)
+ .filter(function() {
+ return this === cell;
+ }).length;
+ };
+
+ Table.prototype.updateColspanCells = function(cls, header, userAction) {
+ var self = this;
+ var primaryHeaderRow = self._getPrimaryHeaderRow();
+
+ // find persistent column rowspans
+ this.$table.find("[rowspan][data-tablesaw-priority]").each(function() {
+ var $t = $(this);
+ if ($t.attr("data-tablesaw-priority") !== "persist") {
+ return;
+ }
+
+ var $row = $t.closest("tr");
+ var rowspan = parseInt($t.attr("rowspan"), 10);
+ if (rowspan > 1) {
+ $row = $row.next();
+
+ $row.data("tablesaw-rowspanned", true);
+
+ rowspan--;
+ }
+ });
+
+ this.$table
+ .find("[colspan],[data-tablesaw-maxcolspan]")
+ .filter(function() {
+ // is not in primary header row
+ return $(this).closest("tr")[0] !== primaryHeaderRow[0];
+ })
+ .each(function() {
+ var $cell = $(this);
+
+ if (userAction === undefined || self.isCellInColumn(header, this)) {
+ } else {
+ // if is not a user action AND the cell is not in the updating column, kill it
+ return;
+ }
+
+ var colspan = self.getColspanForCell($cell);
+
+ if (cls && userAction !== undefined) {
+ // console.log( colspan === 0 ? "addClass" : "removeClass", $cell );
+ $cell[colspan === 0 ? "addClass" : "removeClass"](cls);
+ }
+
+ // cache original colspan
+ var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10);
+ if (!maxColspan) {
+ $cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan"));
+ } else if (colspan > maxColspan) {
+ colspan = maxColspan;
+ }
+
+ // console.log( this, "setting colspan to ", colspan );
+ $cell.attr("colspan", colspan);
+ });
+ };
+
+ Table.prototype._findPrimaryHeadersForCell = function(cell) {
+ var $headerRow = this._getPrimaryHeaderRow();
+ var $headers = this._getPrimaryHeaderCells($headerRow);
+ var headerRowIndex = this._getRowIndex($headerRow);
+ var results = [];
+
+ for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) {
+ if (rowNumber === headerRowIndex) {
+ continue;
+ }
+ for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) {
+ if (this.headerMapping[rowNumber][colNumber] === cell) {
+ results.push($headers[colNumber]);
+ }
+ }
+ }
+ return results;
+ };
+
+ // used by init cells
+ Table.prototype.getRows = function() {
+ var self = this;
+ return this.$table.find("tr").filter(function() {
+ return $(this)
+ .closest("table")
+ .is(self.$table);
+ });
+ };
+
+ // used by sortable
+ Table.prototype.getBodyRows = function(tbody) {
+ return (tbody ? $(tbody) : this.$tbody).children().filter("tr");
+ };
+
+ Table.prototype.getHeaderCellIndex = function(cell) {
+ var lookup = this.headerMapping[0];
+ for (var colIndex = 0; colIndex < lookup.length; colIndex++) {
+ if (lookup[colIndex] === cell) {
+ return colIndex;
+ }
+ }
+
+ return -1;
+ };
+
+ Table.prototype._initCells = function() {
+ // re-establish original colspans
+ this.$table.find("[data-tablesaw-maxcolspan]").each(function() {
+ var $t = $(this);
+ $t.attr("colspan", $t.attr("data-tablesaw-maxcolspan"));
+ });
+
+ var $rows = this.getRows();
+ var columnLookup = [];
+
+ $rows.each(function(rowNumber) {
+ columnLookup[rowNumber] = [];
+ });
+
+ $rows.each(function(rowNumber) {
+ var coltally = 0;
+ var $t = $(this);
+ var children = $t.children();
+
+ children.each(function() {
+ var colspan = parseInt(
+ this.getAttribute("data-tablesaw-maxcolspan") || this.getAttribute("colspan"),
+ 10
+ );
+ var rowspan = parseInt(this.getAttribute("rowspan"), 10);
+
+ // set in a previous rowspan
+ while (columnLookup[rowNumber][coltally]) {
+ coltally++;
+ }
+
+ columnLookup[rowNumber][coltally] = this;
+
+ // TODO? both colspan and rowspan
+ if (colspan) {
+ for (var k = 0; k < colspan - 1; k++) {
+ coltally++;
+ columnLookup[rowNumber][coltally] = this;
+ }
+ }
+ if (rowspan) {
+ for (var j = 1; j < rowspan; j++) {
+ columnLookup[rowNumber + j][coltally] = this;
+ }
+ }
+
+ coltally++;
+ });
+ });
+
+ var headerRowIndeces = this._getHeaderRowIndeces();
+ for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) {
+ for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) {
+ var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber];
+
+ var rowNumber = headerRowIndeces[headerIndex];
+ var rowCell;
+
+ if (!headerCol.cells) {
+ headerCol.cells = [];
+ }
+
+ while (rowNumber < columnLookup.length) {
+ rowCell = columnLookup[rowNumber][colNumber];
+
+ if (headerCol !== rowCell) {
+ headerCol.cells.push(rowCell);
+ }
+
+ rowNumber++;
+ }
+ }
+ }
+
+ this.headerMapping = columnLookup;
+ };
+
+ Table.prototype.refresh = function() {
+ this._initCells();
+
+ this.$table.trigger(events.refresh, [this]);
+ };
+
+ Table.prototype._getToolbarAnchor = function() {
+ var $parent = this.$table.parent();
+ if ($parent.is(".tablesaw-overflow")) {
+ return $parent;
+ }
+ return this.$table;
+ };
+
+ Table.prototype._getToolbar = function($anchor) {
+ if (!$anchor) {
+ $anchor = this._getToolbarAnchor();
+ }
+ return $anchor.prev().filter("." + classes.toolbar);
+ };
+
+ Table.prototype.createToolbar = function() {
+ // Insert the toolbar
+ // TODO move this into a separate component
+ var $anchor = this._getToolbarAnchor();
+ var $toolbar = this._getToolbar($anchor);
+ if (!$toolbar.length) {
+ $toolbar = $(" ")
+ .addClass(classes.toolbar)
+ .insertBefore($anchor);
+ }
+ this.$toolbar = $toolbar;
+
+ if (this.mode) {
+ this.$toolbar.addClass("tablesaw-mode-" + this.mode);
+ }
+ };
+
+ Table.prototype.destroy = function() {
+ // Don’t remove the toolbar, just erase the classes on it.
+ // Some of the table features are not yet destroy-friendly.
+ this._getToolbar().each(function() {
+ this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, "");
+ });
+
+ var tableId = this.$table.attr("id");
+ $(document).off("." + tableId);
+ $(window).off("." + tableId);
+
+ // other plugins
+ this.$table.trigger(events.destroy, [this]);
+
+ this.$table.removeData(pluginName);
+ };
+
+ // Collection method.
+ $.fn[pluginName] = function() {
+ return this.each(function() {
+ var $t = $(this);
+
+ if ($t.data(pluginName)) {
+ return;
+ }
+
+ new Table(this);
+ });
+ };
+
+ var $doc = $(document);
+ $doc.on("enhance.tablesaw", function(e) {
+ // Cut the mustard
+ if (Tablesaw.mustard) {
+ $(e.target)
+ .find(initSelector)
+ .filter(initFilterSelector)
+ [pluginName]();
+ }
+ });
+
+ // Avoid a resize during scroll:
+ // Some Mobile devices trigger a resize during scroll (sometimes when
+ // doing elastic stretch at the end of the document or from the
+ // location bar hide)
+ var isScrolling = false;
+ var scrollTimeout;
+ $doc.on("scroll.tablesaw", function() {
+ isScrolling = true;
+
+ window.clearTimeout(scrollTimeout);
+ scrollTimeout = window.setTimeout(function() {
+ isScrolling = false;
+ }, 300); // must be greater than the resize timeout below
+ });
+
+ var resizeTimeout;
+ $(window).on("resize", function() {
+ if (!isScrolling) {
+ window.clearTimeout(resizeTimeout);
+ resizeTimeout = window.setTimeout(function() {
+ $doc.trigger(events.resize);
+ }, 150); // must be less than the scrolling timeout above.
+ }
+ });
+
+ Tablesaw.Table = Table;
+})();
+
+(function() {
+ var classes = {
+ stackTable: "tablesaw-stack",
+ cellLabels: "tablesaw-cell-label",
+ cellContentLabels: "tablesaw-cell-content"
+ };
+
+ var data = {
+ key: "tablesaw-stack"
+ };
+
+ var attrs = {
+ labelless: "data-tablesaw-no-labels",
+ hideempty: "data-tablesaw-hide-empty"
+ };
+
+ var Stack = function(element, tablesaw) {
+ this.tablesaw = tablesaw;
+ this.$table = $(element);
+
+ this.labelless = this.$table.is("[" + attrs.labelless + "]");
+ this.hideempty = this.$table.is("[" + attrs.hideempty + "]");
+
+ this.$table.data(data.key, this);
+ };
+
+ Stack.prototype.init = function() {
+ this.$table.addClass(classes.stackTable);
+
+ if (this.labelless) {
+ return;
+ }
+
+ var self = this;
+
+ this.$table
+ .find("th, td")
+ .filter(function() {
+ return !$(this).closest("thead").length;
+ })
+ .filter(function() {
+ return (
+ !$(this)
+ .closest("tr")
+ .is("[" + attrs.labelless + "]") &&
+ (!self.hideempty || !!$(this).html())
+ );
+ })
+ .each(function() {
+ var $newHeader = $(document.createElement("b")).addClass(classes.cellLabels);
+ var $cell = $(this);
+
+ $(self.tablesaw._findPrimaryHeadersForCell(this)).each(function(index) {
+ var $header = $(this.cloneNode(true));
+ // TODO decouple from sortable better
+ // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b
+ // to preserve structural html in headers, like
+ var $sortableButton = $header.find(".tablesaw-sortable-btn");
+ $header.find(".tablesaw-sortable-arrow").remove();
+
+ // TODO decouple from checkall better
+ var $checkall = $header.find("[data-tablesaw-checkall]");
+ $checkall.closest("label").remove();
+ if ($checkall.length) {
+ $newHeader = $([]);
+ return;
+ }
+
+ if (index > 0) {
+ $newHeader.append(document.createTextNode(", "));
+ }
+
+ var parentNode = $sortableButton.length ? $sortableButton[0] : $header[0];
+ var el;
+ while ((el = parentNode.firstChild)) {
+ $newHeader[0].appendChild(el);
+ }
+ });
+
+ if ($newHeader.length && !$cell.find("." + classes.cellContentLabels).length) {
+ $cell.wrapInner("");
+ }
+
+ // Update if already exists.
+ var $label = $cell.find("." + classes.cellLabels);
+ if (!$label.length) {
+ $cell.prepend($newHeader);
+ } else {
+ // only if changed
+ $label.replaceWith($newHeader);
+ }
+ });
+ };
+
+ Stack.prototype.destroy = function() {
+ this.$table.removeClass(classes.stackTable);
+ this.$table.find("." + classes.cellLabels).remove();
+ this.$table.find("." + classes.cellContentLabels).each(function() {
+ $(this).replaceWith(this.childNodes);
+ });
+ };
+
+ // on tablecreate, init
+ $(document)
+ .on(Tablesaw.events.create, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ var table = new Stack(tablesaw.table, tablesaw);
+ table.init();
+ }
+ })
+ .on(Tablesaw.events.refresh, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ $(tablesaw.table)
+ .data(data.key)
+ .init();
+ }
+ })
+ .on(Tablesaw.events.destroy, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ $(tablesaw.table)
+ .data(data.key)
+ .destroy();
+ }
+ });
+
+ Tablesaw.Stack = Stack;
+})();
+
+(function() {
+ var pluginName = "tablesawbtn",
+ methods = {
+ _create: function() {
+ return $(this).each(function() {
+ $(this)
+ .trigger("beforecreate." + pluginName)
+ [pluginName]("_init")
+ .trigger("create." + pluginName);
+ });
+ },
+ _init: function() {
+ var oEl = $(this),
+ sel = this.getElementsByTagName("select")[0];
+
+ if (sel) {
+ // TODO next major version: remove .btn-select
+ $(this)
+ .addClass("btn-select tablesaw-btn-select")
+ [pluginName]("_select", sel);
+ }
+ return oEl;
+ },
+ _select: function(sel) {
+ var update = function(oEl, sel) {
+ var opts = $(sel).find("option");
+ var label = document.createElement("span");
+ var el;
+ var children;
+ var found = false;
+
+ label.setAttribute("aria-hidden", "true");
+ label.innerHTML = " ";
+
+ opts.each(function() {
+ var opt = this;
+ if (opt.selected) {
+ label.innerHTML = opt.text;
+ }
+ });
+
+ children = oEl.childNodes;
+ if (opts.length > 0) {
+ for (var i = 0, l = children.length; i < l; i++) {
+ el = children[i];
+
+ if (el && el.nodeName.toUpperCase() === "SPAN") {
+ oEl.replaceChild(label, el);
+ found = true;
+ }
+ }
+
+ if (!found) {
+ oEl.insertBefore(label, oEl.firstChild);
+ }
+ }
+ };
+
+ update(this, sel);
+ // todo should this be tablesawrefresh?
+ $(this).on("change refresh", function() {
+ update(this, sel);
+ });
+ }
+ };
+
+ // Collection method.
+ $.fn[pluginName] = function(arrg, a, b, c) {
+ return this.each(function() {
+ // if it's a method
+ if (arrg && typeof arrg === "string") {
+ return $.fn[pluginName].prototype[arrg].call(this, a, b, c);
+ }
+
+ // don't re-init
+ if ($(this).data(pluginName + "active")) {
+ return $(this);
+ }
+
+ $(this).data(pluginName + "active", true);
+
+ $.fn[pluginName].prototype._create.call(this);
+ });
+ };
+
+ // add methods
+ $.extend($.fn[pluginName].prototype, methods);
+
+ // TODO OOP this and add to Tablesaw object
+})();
+
+(function() {
+ var data = {
+ key: "tablesaw-coltoggle"
+ };
+
+ var ColumnToggle = function(element) {
+ this.$table = $(element);
+
+ if (!this.$table.length) {
+ return;
+ }
+
+ this.tablesaw = this.$table.data("tablesaw");
+
+ this.attributes = {
+ btnTarget: "data-tablesaw-columntoggle-btn-target",
+ set: "data-tablesaw-columntoggle-set"
+ };
+
+ this.classes = {
+ columnToggleTable: "tablesaw-columntoggle",
+ columnBtnContain: "tablesaw-columntoggle-btnwrap tablesaw-advance",
+ columnBtn: "tablesaw-columntoggle-btn tablesaw-nav-btn down",
+ popup: "tablesaw-columntoggle-popup",
+ priorityPrefix: "tablesaw-priority-"
+ };
+
+ this.set = [];
+ this.$headers = this.tablesaw._getPrimaryHeaderCells();
+
+ this.$table.data(data.key, this);
+ };
+
+ // Column Toggle Sets (one column chooser can control multiple tables)
+ ColumnToggle.prototype.initSet = function() {
+ var set = this.$table.attr(this.attributes.set);
+ if (set) {
+ // Should not include the current table
+ var table = this.$table[0];
+ this.set = $("table[" + this.attributes.set + "='" + set + "']")
+ .filter(function() {
+ return this !== table;
+ })
+ .get();
+ }
+ };
+
+ ColumnToggle.prototype.init = function() {
+ if (!this.$table.length) {
+ return;
+ }
+
+ var tableId,
+ id,
+ $menuButton,
+ $popup,
+ $menu,
+ $btnContain,
+ self = this;
+
+ var cfg = this.tablesaw.getConfig({
+ getColumnToggleLabelTemplate: function(text) {
+ return "";
+ }
+ });
+
+ this.$table.addClass(this.classes.columnToggleTable);
+
+ tableId = this.$table.attr("id");
+ id = tableId + "-popup";
+ $btnContain = $("");
+ // TODO next major version: remove .btn
+ $menuButton = $(
+ "" +
+ "" +
+ Tablesaw.i18n.columnToggleButton +
+ ""
+ );
+ $popup = $("");
+ $menu = $("");
+
+ this.$popup = $popup;
+
+ var hasNonPersistentHeaders = false;
+ this.$headers.each(function() {
+ var $this = $(this),
+ priority = $this.attr("data-tablesaw-priority"),
+ $cells = self.tablesaw._$getCells(this);
+
+ if (priority && priority !== "persist") {
+ $cells.addClass(self.classes.priorityPrefix + priority);
+
+ $(cfg.getColumnToggleLabelTemplate($this.text()))
+ .appendTo($menu)
+ .find('input[type="checkbox"]')
+ .data("tablesaw-header", this);
+
+ hasNonPersistentHeaders = true;
+ }
+ });
+
+ if (!hasNonPersistentHeaders) {
+ $menu.append("");
+ }
+
+ $menu.appendTo($popup);
+
+ function onToggleCheckboxChange(checkbox) {
+ var checked = checkbox.checked;
+
+ var header = self.getHeaderFromCheckbox(checkbox);
+ var $cells = self.tablesaw._$getCells(header);
+
+ $cells[!checked ? "addClass" : "removeClass"]("tablesaw-toggle-cellhidden");
+ $cells[checked ? "addClass" : "removeClass"]("tablesaw-toggle-cellvisible");
+
+ self.updateColspanCells(header, checked);
+
+ self.$table.trigger("tablesawcolumns");
+ }
+
+ // bind change event listeners to inputs - TODO: move to a private method?
+ $menu.find('input[type="checkbox"]').on("change", function(e) {
+ onToggleCheckboxChange(e.target);
+
+ if (self.set.length) {
+ var index;
+ $(self.$popup)
+ .find("input[type='checkbox']")
+ .each(function(j) {
+ if (this === e.target) {
+ index = j;
+ return false;
+ }
+ });
+
+ $(self.set).each(function() {
+ var checkbox = $(this)
+ .data(data.key)
+ .$popup.find("input[type='checkbox']")
+ .get(index);
+ if (checkbox) {
+ checkbox.checked = e.target.checked;
+ onToggleCheckboxChange(checkbox);
+ }
+ });
+ }
+ });
+
+ $menuButton.appendTo($btnContain);
+
+ // Use a different target than the toolbar
+ var $btnTarget = $(this.$table.attr(this.attributes.btnTarget));
+ $btnContain.appendTo($btnTarget.length ? $btnTarget : this.tablesaw.$toolbar);
+
+ function closePopup(event) {
+ // Click came from inside the popup, ignore.
+ if (event && $(event.target).closest("." + self.classes.popup).length) {
+ return;
+ }
+
+ $(document).off("click." + tableId);
+ $menuButton.removeClass("up").addClass("down");
+ $btnContain.removeClass("visible");
+ }
+
+ var closeTimeout;
+ function openPopup() {
+ $btnContain.addClass("visible");
+ $menuButton.removeClass("down").addClass("up");
+
+ $(document).off("click." + tableId, closePopup);
+
+ window.clearTimeout(closeTimeout);
+ closeTimeout = window.setTimeout(function() {
+ $(document).on("click." + tableId, closePopup);
+ }, 15);
+ }
+
+ $menuButton.on("click.tablesaw", function(event) {
+ event.preventDefault();
+
+ if (!$btnContain.is(".visible")) {
+ openPopup();
+ } else {
+ closePopup();
+ }
+ });
+
+ $popup.appendTo($btnContain);
+
+ this.$menu = $menu;
+
+ // Fix for iOS not rendering shadows correctly when using `-webkit-overflow-scrolling`
+ var $overflow = this.$table.closest(".tablesaw-overflow");
+ if ($overflow.css("-webkit-overflow-scrolling")) {
+ var timeout;
+ $overflow.on("scroll", function() {
+ var $div = $(this);
+ window.clearTimeout(timeout);
+ timeout = window.setTimeout(function() {
+ $div.css("-webkit-overflow-scrolling", "auto");
+ window.setTimeout(function() {
+ $div.css("-webkit-overflow-scrolling", "touch");
+ }, 0);
+ }, 100);
+ });
+ }
+
+ $(window).on(Tablesaw.events.resize + "." + tableId, function() {
+ self.refreshToggle();
+ });
+
+ this.initSet();
+ this.refreshToggle();
+ };
+
+ ColumnToggle.prototype.getHeaderFromCheckbox = function(checkbox) {
+ return $(checkbox).data("tablesaw-header");
+ };
+
+ ColumnToggle.prototype.refreshToggle = function() {
+ var self = this;
+ var invisibleColumns = 0;
+ this.$menu.find("input").each(function() {
+ var header = self.getHeaderFromCheckbox(this);
+ this.checked =
+ self.tablesaw
+ ._$getCells(header)
+ .eq(0)
+ .css("display") === "table-cell";
+ });
+
+ this.updateColspanCells();
+ };
+
+ ColumnToggle.prototype.updateColspanCells = function(header, userAction) {
+ this.tablesaw.updateColspanCells("tablesaw-toggle-cellhidden", header, userAction);
+ };
+
+ ColumnToggle.prototype.destroy = function() {
+ this.$table.removeClass(this.classes.columnToggleTable);
+ this.$table.find("th, td").each(function() {
+ var $cell = $(this);
+ $cell.removeClass("tablesaw-toggle-cellhidden").removeClass("tablesaw-toggle-cellvisible");
+
+ this.className = this.className.replace(/\bui\-table\-priority\-\d\b/g, "");
+ });
+ };
+
+ // on tablecreate, init
+ $(document).on(Tablesaw.events.create, function(e, tablesaw) {
+ if (tablesaw.mode === "columntoggle") {
+ var table = new ColumnToggle(tablesaw.table);
+ table.init();
+ }
+ });
+
+ $(document).on(Tablesaw.events.destroy, function(e, tablesaw) {
+ if (tablesaw.mode === "columntoggle") {
+ $(tablesaw.table)
+ .data(data.key)
+ .destroy();
+ }
+ });
+
+ $(document).on(Tablesaw.events.refresh, function(e, tablesaw) {
+ if (tablesaw.mode === "columntoggle") {
+ $(tablesaw.table)
+ .data(data.key)
+ .refreshPriority();
+ }
+ });
+
+ Tablesaw.ColumnToggle = ColumnToggle;
+})();
+
+(function() {
+ function getSortValue(cell) {
+ var text = [];
+ $(cell.childNodes).each(function() {
+ var $el = $(this);
+ if ($el.is("input, select")) {
+ text.push($el.val());
+ } else if ($el.is(".tablesaw-cell-label")) {
+ } else {
+ text.push(($el.text() || "").replace(/^\s+|\s+$/g, ""));
+ }
+ });
+
+ return text.join("");
+ }
+
+ var pluginName = "tablesaw-sortable",
+ initSelector = "table[data-" + pluginName + "]",
+ sortableSwitchSelector = "[data-" + pluginName + "-switch]",
+ attrs = {
+ sortCol: "data-tablesaw-sortable-col",
+ defaultCol: "data-tablesaw-sortable-default-col",
+ numericCol: "data-tablesaw-sortable-numeric",
+ subRow: "data-tablesaw-subrow",
+ ignoreRow: "data-tablesaw-ignorerow"
+ },
+ classes = {
+ head: pluginName + "-head",
+ ascend: pluginName + "-ascending",
+ descend: pluginName + "-descending",
+ switcher: pluginName + "-switch",
+ tableToolbar: "tablesaw-bar-section",
+ sortButton: pluginName + "-btn"
+ },
+ methods = {
+ _create: function(o) {
+ return $(this).each(function() {
+ var init = $(this).data(pluginName + "-init");
+ if (init) {
+ return false;
+ }
+ $(this)
+ .data(pluginName + "-init", true)
+ .trigger("beforecreate." + pluginName)
+ [pluginName]("_init", o)
+ .trigger("create." + pluginName);
+ });
+ },
+ _init: function() {
+ var el = $(this);
+ var tblsaw = el.data("tablesaw");
+ var heads;
+ var $switcher;
+
+ function addClassToHeads(h) {
+ $.each(h, function(i, v) {
+ $(v).addClass(classes.head);
+ });
+ }
+
+ function makeHeadsActionable(h, fn) {
+ $.each(h, function(i, col) {
+ var b = $("");
+ b.on("click", { col: col }, fn);
+ $(col)
+ .wrapInner(b)
+ .find("button")
+ .append("");
+ });
+ }
+
+ function clearOthers(headcells) {
+ $.each(headcells, function(i, v) {
+ var col = $(v);
+ col.removeAttr(attrs.defaultCol);
+ col.removeClass(classes.ascend);
+ col.removeClass(classes.descend);
+ });
+ }
+
+ function headsOnAction(e) {
+ if ($(e.target).is("a[href]")) {
+ return;
+ }
+
+ e.stopPropagation();
+ var headCell = $(e.target).closest("[" + attrs.sortCol + "]"),
+ v = e.data.col,
+ newSortValue = heads.index(headCell[0]);
+
+ clearOthers(
+ headCell
+ .closest("thead")
+ .find("th")
+ .filter(function() {
+ return this !== headCell[0];
+ })
+ );
+ if (headCell.is("." + classes.descend) || !headCell.is("." + classes.ascend)) {
+ el[pluginName]("sortBy", v, true);
+ newSortValue += "_asc";
+ } else {
+ el[pluginName]("sortBy", v);
+ newSortValue += "_desc";
+ }
+ if ($switcher) {
+ $switcher
+ .find("select")
+ .val(newSortValue)
+ .trigger("refresh");
+ }
+
+ e.preventDefault();
+ }
+
+ function handleDefault(heads) {
+ $.each(heads, function(idx, el) {
+ var $el = $(el);
+ if ($el.is("[" + attrs.defaultCol + "]")) {
+ if (!$el.is("." + classes.descend)) {
+ $el.addClass(classes.ascend);
+ }
+ }
+ });
+ }
+
+ function addSwitcher(heads) {
+ $switcher = $(" ")
+ .addClass(classes.switcher)
+ .addClass(classes.tableToolbar);
+
+ var html = ["");
+
+ $switcher.html(html.join(""));
+
+ var $firstChild = tblsaw.$toolbar.children().eq(0);
+ if ($firstChild.length) {
+ $switcher.insertBefore($firstChild);
+ } else {
+ $switcher.appendTo(tblsaw.$toolbar);
+ }
+ $switcher.find(".tablesaw-btn").tablesawbtn();
+ $switcher.find("select").on("change", function() {
+ var val = $(this)
+ .val()
+ .split("_"),
+ head = heads.eq(val[0]);
+
+ clearOthers(head.siblings());
+ el[pluginName]("sortBy", head.get(0), val[1] === "asc");
+ });
+ }
+
+ el.addClass(pluginName);
+
+ heads = el
+ .children()
+ .filter("thead")
+ .find("th[" + attrs.sortCol + "]");
+
+ addClassToHeads(heads);
+ makeHeadsActionable(heads, headsOnAction);
+ handleDefault(heads);
+
+ if (el.is(sortableSwitchSelector)) {
+ addSwitcher(heads);
+ }
+ },
+ sortRows: function(rows, colNum, ascending, col, tbody) {
+ function convertCells(cellArr, belongingToTbody) {
+ var cells = [];
+ $.each(cellArr, function(i, cell) {
+ var row = cell.parentNode;
+ var $row = $(row);
+ // next row is a subrow
+ var subrows = [];
+ var $next = $row.next();
+ while ($next.is("[" + attrs.subRow + "]")) {
+ subrows.push($next[0]);
+ $next = $next.next();
+ }
+
+ var tbody = row.parentNode;
+
+ // current row is a subrow
+ if ($row.is("[" + attrs.subRow + "]")) {
+ } else if (tbody === belongingToTbody) {
+ cells.push({
+ element: cell,
+ cell: getSortValue(cell),
+ row: row,
+ subrows: subrows.length ? subrows : null,
+ ignored: $row.is("[" + attrs.ignoreRow + "]")
+ });
+ }
+ });
+ return cells;
+ }
+
+ function getSortFxn(ascending, forceNumeric) {
+ var fn,
+ regex = /[^\-\+\d\.]/g;
+ if (ascending) {
+ fn = function(a, b) {
+ if (a.ignored || b.ignored) {
+ return 0;
+ }
+ if (forceNumeric) {
+ return (
+ parseFloat(a.cell.replace(regex, "")) - parseFloat(b.cell.replace(regex, ""))
+ );
+ } else {
+ return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1;
+ }
+ };
+ } else {
+ fn = function(a, b) {
+ if (a.ignored || b.ignored) {
+ return 0;
+ }
+ if (forceNumeric) {
+ return (
+ parseFloat(b.cell.replace(regex, "")) - parseFloat(a.cell.replace(regex, ""))
+ );
+ } else {
+ return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1;
+ }
+ };
+ }
+ return fn;
+ }
+
+ function convertToRows(sorted) {
+ var newRows = [],
+ i,
+ l;
+ for (i = 0, l = sorted.length; i < l; i++) {
+ newRows.push(sorted[i].row);
+ if (sorted[i].subrows) {
+ newRows.push(sorted[i].subrows);
+ }
+ }
+ return newRows;
+ }
+
+ var fn;
+ var sorted;
+ var cells = convertCells(col.cells, tbody);
+
+ var customFn = $(col).data("tablesaw-sort");
+
+ fn =
+ (customFn && typeof customFn === "function" ? customFn(ascending) : false) ||
+ getSortFxn(
+ ascending,
+ $(col).is("[" + attrs.numericCol + "]") &&
+ !$(col).is("[" + attrs.numericCol + '="false"]')
+ );
+
+ sorted = cells.sort(fn);
+
+ rows = convertToRows(sorted);
+
+ return rows;
+ },
+ makeColDefault: function(col, a) {
+ var c = $(col);
+ c.attr(attrs.defaultCol, "true");
+ if (a) {
+ c.removeClass(classes.descend);
+ c.addClass(classes.ascend);
+ } else {
+ c.removeClass(classes.ascend);
+ c.addClass(classes.descend);
+ }
+ },
+ sortBy: function(col, ascending) {
+ var el = $(this);
+ var colNum;
+ var tbl = el.data("tablesaw");
+ tbl.$tbody.each(function() {
+ var tbody = this;
+ var $tbody = $(this);
+ var rows = tbl.getBodyRows(tbody);
+ var sortedRows;
+ var map = tbl.headerMapping[0];
+ var j, k;
+
+ // find the column number that we’re sorting
+ for (j = 0, k = map.length; j < k; j++) {
+ if (map[j] === col) {
+ colNum = j;
+ break;
+ }
+ }
+
+ sortedRows = el[pluginName]("sortRows", rows, colNum, ascending, col, tbody);
+
+ // replace Table rows
+ for (j = 0, k = sortedRows.length; j < k; j++) {
+ $tbody.append(sortedRows[j]);
+ }
+ });
+
+ el[pluginName]("makeColDefault", col, ascending);
+
+ el.trigger("tablesaw-sorted");
+ }
+ };
+
+ // Collection method.
+ $.fn[pluginName] = function(arrg) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ returnVal;
+
+ // if it's a method
+ if (arrg && typeof arrg === "string") {
+ returnVal = $.fn[pluginName].prototype[arrg].apply(this[0], args);
+ return typeof returnVal !== "undefined" ? returnVal : $(this);
+ }
+ // check init
+ if (!$(this).data(pluginName + "-active")) {
+ $(this).data(pluginName + "-active", true);
+ $.fn[pluginName].prototype._create.call(this, arrg);
+ }
+ return $(this);
+ };
+ // add methods
+ $.extend($.fn[pluginName].prototype, methods);
+
+ $(document).on(Tablesaw.events.create, function(e, Tablesaw) {
+ if (Tablesaw.$table.is(initSelector)) {
+ Tablesaw.$table[pluginName]();
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
+
+(function() {
+ var classes = {
+ hideBtn: "disabled",
+ persistWidths: "tablesaw-fix-persist",
+ hiddenCol: "tablesaw-swipe-cellhidden",
+ persistCol: "tablesaw-swipe-cellpersist",
+ allColumnsVisible: "tablesaw-all-cols-visible"
+ };
+ var attrs = {
+ disableTouchEvents: "data-tablesaw-no-touch",
+ ignorerow: "data-tablesaw-ignorerow",
+ subrow: "data-tablesaw-subrow"
+ };
+
+ function createSwipeTable(tbl, $table) {
+ var tblsaw = $table.data("tablesaw");
+
+ var $btns = $("");
+ // TODO next major version: remove .btn
+ var $prevBtn = $(
+ "" +
+ Tablesaw.i18n.swipePreviousColumn +
+ ""
+ ).appendTo($btns);
+ // TODO next major version: remove .btn
+ var $nextBtn = $(
+ "" +
+ Tablesaw.i18n.swipeNextColumn +
+ ""
+ ).appendTo($btns);
+
+ var $headerCells = tbl._getPrimaryHeaderCells();
+ var $headerCellsNoPersist = $headerCells.not('[data-tablesaw-priority="persist"]');
+ var headerWidths = [];
+ var $head = $(document.head || "head");
+ var tableId = $table.attr("id");
+
+ if (!$headerCells.length) {
+ throw new Error("tablesaw swipe: no header cells found.");
+ }
+
+ $table.addClass("tablesaw-swipe");
+
+ function initMinHeaderWidths() {
+ $table.css({
+ width: "1px"
+ });
+
+ // remove any hidden columns
+ $table.find("." + classes.hiddenCol).removeClass(classes.hiddenCol);
+
+ headerWidths = [];
+ // Calculate initial widths
+ $headerCells.each(function() {
+ headerWidths.push(this.offsetWidth);
+ });
+
+ // reset props
+ $table.css({
+ width: ""
+ });
+ }
+
+ initMinHeaderWidths();
+
+ $btns.appendTo(tblsaw.$toolbar);
+
+ if (!tableId) {
+ tableId = "tableswipe-" + Math.round(Math.random() * 10000);
+ $table.attr("id", tableId);
+ }
+
+ function showColumn(headerCell) {
+ tblsaw._$getCells(headerCell).removeClass(classes.hiddenCol);
+ }
+
+ function hideColumn(headerCell) {
+ tblsaw._$getCells(headerCell).addClass(classes.hiddenCol);
+ }
+
+ function persistColumn(headerCell) {
+ tblsaw._$getCells(headerCell).addClass(classes.persistCol);
+ }
+
+ function isPersistent(headerCell) {
+ return $(headerCell).is('[data-tablesaw-priority="persist"]');
+ }
+
+ function unmaintainWidths() {
+ $table.removeClass(classes.persistWidths);
+ $("#" + tableId + "-persist").remove();
+ }
+
+ function maintainWidths() {
+ var prefix = "#" + tableId + ".tablesaw-swipe ",
+ styles = [],
+ tableWidth = $table.width(),
+ hash = [],
+ newHash;
+
+ // save persistent column widths (as long as they take up less than 75% of table width)
+ $headerCells.each(function(index) {
+ var width;
+ if (isPersistent(this)) {
+ width = this.offsetWidth;
+
+ if (width < tableWidth * 0.75) {
+ hash.push(index + "-" + width);
+ styles.push(
+ prefix +
+ " ." +
+ classes.persistCol +
+ ":nth-child(" +
+ (index + 1) +
+ ") { width: " +
+ width +
+ "px; }"
+ );
+ }
+ }
+ });
+ newHash = hash.join("_");
+
+ if (styles.length) {
+ $table.addClass(classes.persistWidths);
+ var $style = $("#" + tableId + "-persist");
+ // If style element not yet added OR if the widths have changed
+ if (!$style.length || $style.data("tablesaw-hash") !== newHash) {
+ // Remove existing
+ $style.remove();
+
+ $("")
+ .attr("id", tableId + "-persist")
+ .data("tablesaw-hash", newHash)
+ .appendTo($head);
+ }
+ }
+ }
+
+ function getNext() {
+ var next = [],
+ checkFound;
+
+ $headerCellsNoPersist.each(function(i) {
+ var $t = $(this),
+ isHidden = $t.css("display") === "none" || $t.is("." + classes.hiddenCol);
+
+ if (!isHidden && !checkFound) {
+ checkFound = true;
+ next[0] = i;
+ } else if (isHidden && checkFound) {
+ next[1] = i;
+
+ return false;
+ }
+ });
+
+ return next;
+ }
+
+ function getPrev() {
+ var next = getNext();
+ return [next[1] - 1, next[0] - 1];
+ }
+
+ function nextpair(fwd) {
+ return fwd ? getNext() : getPrev();
+ }
+
+ function canAdvance(pair) {
+ return pair[1] > -1 && pair[1] < $headerCellsNoPersist.length;
+ }
+
+ function matchesMedia() {
+ var matchMedia = $table.attr("data-tablesaw-swipe-media");
+ return !matchMedia || ("matchMedia" in window && window.matchMedia(matchMedia).matches);
+ }
+
+ function fakeBreakpoints() {
+ if (!matchesMedia()) {
+ return;
+ }
+
+ var containerWidth = $table.parent().width(),
+ persist = [],
+ sum = 0,
+ sums = [],
+ visibleNonPersistantCount = $headerCells.length;
+
+ $headerCells.each(function(index) {
+ var $t = $(this),
+ isPersist = $t.is('[data-tablesaw-priority="persist"]');
+
+ persist.push(isPersist);
+ sum += headerWidths[index];
+ sums.push(sum);
+
+ // is persistent or is hidden
+ if (isPersist || sum > containerWidth) {
+ visibleNonPersistantCount--;
+ }
+ });
+
+ // We need at least one column to swipe.
+ var needsNonPersistentColumn = visibleNonPersistantCount === 0;
+
+ $headerCells.each(function(index) {
+ if (sums[index] > containerWidth) {
+ hideColumn(this);
+ }
+ });
+
+ $headerCells.each(function(index) {
+ if (persist[index]) {
+ // for visual box-shadow
+ persistColumn(this);
+ return;
+ }
+
+ if (sums[index] <= containerWidth || needsNonPersistentColumn) {
+ needsNonPersistentColumn = false;
+ showColumn(this);
+ tblsaw.updateColspanCells(classes.hiddenCol, this, true);
+ }
+ });
+
+ unmaintainWidths();
+
+ $table.trigger("tablesawcolumns");
+ }
+
+ function advance(fwd) {
+ var pair = nextpair(fwd);
+ if (canAdvance(pair)) {
+ if (isNaN(pair[0])) {
+ if (fwd) {
+ pair[0] = 0;
+ } else {
+ pair[0] = $headerCellsNoPersist.length - 1;
+ }
+ }
+
+ // TODO just blindly hiding the previous column and showing the next column can result in
+ // column content overflow
+ maintainWidths();
+ hideColumn($headerCellsNoPersist.get(pair[0]));
+ tblsaw.updateColspanCells(classes.hiddenCol, $headerCellsNoPersist.get(pair[0]), false);
+
+ showColumn($headerCellsNoPersist.get(pair[1]));
+ tblsaw.updateColspanCells(classes.hiddenCol, $headerCellsNoPersist.get(pair[1]), true);
+
+ $table.trigger("tablesawcolumns");
+ }
+ }
+
+ $prevBtn.add($nextBtn).on("click", function(e) {
+ advance(!!$(e.target).closest($nextBtn).length);
+ e.preventDefault();
+ });
+
+ function getCoord(event, key) {
+ return (event.touches || event.originalEvent.touches)[0][key];
+ }
+
+ if (!$table.is("[" + attrs.disableTouchEvents + "]")) {
+ $table.on("touchstart.swipetoggle", function(e) {
+ var originX = getCoord(e, "pageX");
+ var originY = getCoord(e, "pageY");
+ var x;
+ var y;
+ var scrollTop = window.pageYOffset;
+
+ $(window).off(Tablesaw.events.resize, fakeBreakpoints);
+
+ $(this)
+ .on("touchmove.swipetoggle", function(e) {
+ x = getCoord(e, "pageX");
+ y = getCoord(e, "pageY");
+ })
+ .on("touchend.swipetoggle", function() {
+ var cfg = tbl.getConfig({
+ swipeHorizontalThreshold: 30,
+ swipeVerticalThreshold: 30
+ });
+
+ // This config code is a little awkward because shoestring doesn’t support deep $.extend
+ // Trying to work around when devs only override one of (not both) horizontalThreshold or
+ // verticalThreshold in their TablesawConfig.
+ // @TODO major version bump: remove cfg.swipe, move to just use the swipePrefix keys
+ var verticalThreshold = cfg.swipe
+ ? cfg.swipe.verticalThreshold
+ : cfg.swipeVerticalThreshold;
+ var horizontalThreshold = cfg.swipe
+ ? cfg.swipe.horizontalThreshold
+ : cfg.swipeHorizontalThreshold;
+
+ var isPageScrolled = Math.abs(window.pageYOffset - scrollTop) >= verticalThreshold;
+ var isVerticalSwipe = Math.abs(y - originY) >= verticalThreshold;
+
+ if (!isVerticalSwipe && !isPageScrolled) {
+ if (x - originX < -1 * horizontalThreshold) {
+ advance(true);
+ }
+ if (x - originX > horizontalThreshold) {
+ advance(false);
+ }
+ }
+
+ window.setTimeout(function() {
+ $(window).on(Tablesaw.events.resize, fakeBreakpoints);
+ }, 300);
+
+ $(this).off("touchmove.swipetoggle touchend.swipetoggle");
+ });
+ });
+ }
+
+ $table
+ .on("tablesawcolumns.swipetoggle", function() {
+ var canGoPrev = canAdvance(getPrev());
+ var canGoNext = canAdvance(getNext());
+ $prevBtn[canGoPrev ? "removeClass" : "addClass"](classes.hideBtn);
+ $nextBtn[canGoNext ? "removeClass" : "addClass"](classes.hideBtn);
+
+ tblsaw.$toolbar[!canGoPrev && !canGoNext ? "addClass" : "removeClass"](
+ classes.allColumnsVisible
+ );
+ })
+ .on("tablesawnext.swipetoggle", function() {
+ advance(true);
+ })
+ .on("tablesawprev.swipetoggle", function() {
+ advance(false);
+ })
+ .on(Tablesaw.events.destroy + ".swipetoggle", function() {
+ var $t = $(this);
+
+ $t.removeClass("tablesaw-swipe");
+ tblsaw.$toolbar.find(".tablesaw-advance").remove();
+ $(window).off(Tablesaw.events.resize, fakeBreakpoints);
+
+ $t.off(".swipetoggle");
+ })
+ .on(Tablesaw.events.refresh, function() {
+ unmaintainWidths();
+ initMinHeaderWidths();
+ fakeBreakpoints();
+ });
+
+ fakeBreakpoints();
+ $(window).on(Tablesaw.events.resize, fakeBreakpoints);
+ }
+
+ // on tablecreate, init
+ $(document).on(Tablesaw.events.create, function(e, tablesaw) {
+ if (tablesaw.mode === "swipe") {
+ createSwipeTable(tablesaw, tablesaw.$table);
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
+
+(function() {
+ var MiniMap = {
+ attr: {
+ init: "data-tablesaw-minimap"
+ },
+ show: function(table) {
+ var mq = table.getAttribute(MiniMap.attr.init);
+
+ if (mq === "") {
+ // value-less but exists
+ return true;
+ } else if (mq && "matchMedia" in window) {
+ // has a mq value
+ return window.matchMedia(mq).matches;
+ }
+
+ return false;
+ }
+ };
+
+ function createMiniMap($table) {
+ var tblsaw = $table.data("tablesaw");
+ var $btns = $(' ');
+ var $dotNav = $('
").addClass(S.classes.main + " " + S.classes.toolbar);
+
+ var html = [
+ '");
+
+ $switcher.html(html.join(""));
+
+ var $otherToolbarItems = $toolbar.find(".tablesaw-advance").eq(0);
+ if ($otherToolbarItems.length) {
+ $switcher.insertBefore($otherToolbarItems);
+ } else {
+ $switcher.appendTo($toolbar);
+ }
+
+ $switcher.find(".tablesaw-btn").tablesawbtn();
+ $switcher.find("select").on("change", function(event) {
+ return S.onModeChange.call(table, event, $(this).val());
+ });
+ },
+ onModeChange: function(event, val) {
+ var $table = $(this);
+ var tblsaw = $table.data("tablesaw");
+ var $switcher = tblsaw.$toolbar.find("." + S.classes.main);
+
+ $switcher.remove();
+ tblsaw.destroy();
+
+ $table.attr("data-tablesaw-mode", val);
+ $table.tablesaw();
+ }
+ };
+
+ $(document).on(Tablesaw.events.create, function(e, Tablesaw) {
+ if (Tablesaw.$table.is(S.selectors.init)) {
+ S.init(Tablesaw.table);
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
+
+(function() {
+ var pluginName = "tablesawCheckAll";
+
+ function CheckAll(tablesaw) {
+ this.tablesaw = tablesaw;
+ this.$table = tablesaw.$table;
+
+ this.attr = "data-tablesaw-checkall";
+ this.checkAllSelector = "[" + this.attr + "]";
+ this.forceCheckedSelector = "[" + this.attr + "-checked]";
+ this.forceUncheckedSelector = "[" + this.attr + "-unchecked]";
+ this.checkboxSelector = 'input[type="checkbox"]';
+
+ this.$triggers = null;
+ this.$checkboxes = null;
+
+ if (this.$table.data(pluginName)) {
+ return;
+ }
+ this.$table.data(pluginName, this);
+ this.init();
+ }
+
+ CheckAll.prototype._filterCells = function($checkboxes) {
+ return $checkboxes
+ .filter(function() {
+ return !$(this)
+ .closest("tr")
+ .is("[data-tablesaw-subrow],[data-tablesaw-ignorerow]");
+ })
+ .find(this.checkboxSelector)
+ .not(this.checkAllSelector);
+ };
+
+ // With buttons you can use a scoping selector like: data-tablesaw-checkall="#my-scoped-id input[type='checkbox']"
+ CheckAll.prototype.getCheckboxesForButton = function(button) {
+ return this._filterCells($($(button).attr(this.attr)));
+ };
+
+ CheckAll.prototype.getCheckboxesForCheckbox = function(checkbox) {
+ return this._filterCells($($(checkbox).closest("th")[0].cells));
+ };
+
+ CheckAll.prototype.init = function() {
+ var self = this;
+ this.$table.find(this.checkAllSelector).each(function() {
+ var $trigger = $(this);
+ if ($trigger.is(self.checkboxSelector)) {
+ self.addCheckboxEvents(this);
+ } else {
+ self.addButtonEvents(this);
+ }
+ });
+ };
+
+ CheckAll.prototype.addButtonEvents = function(trigger) {
+ var self = this;
+
+ // Update body checkboxes when header checkbox is changed
+ $(trigger).on("click", function(event) {
+ event.preventDefault();
+
+ var $checkboxes = self.getCheckboxesForButton(this);
+
+ var allChecked = true;
+ $checkboxes.each(function() {
+ if (!this.checked) {
+ allChecked = false;
+ }
+ });
+
+ var setChecked;
+ if ($(this).is(self.forceCheckedSelector)) {
+ setChecked = true;
+ } else if ($(this).is(self.forceUncheckedSelector)) {
+ setChecked = false;
+ } else {
+ setChecked = allChecked ? false : true;
+ }
+
+ $checkboxes.each(function() {
+ this.checked = setChecked;
+
+ $(this).trigger("change." + pluginName);
+ });
+ });
+ };
+
+ CheckAll.prototype.addCheckboxEvents = function(trigger) {
+ var self = this;
+
+ // Update body checkboxes when header checkbox is changed
+ $(trigger).on("change", function() {
+ var setChecked = this.checked;
+
+ self.getCheckboxesForCheckbox(this).each(function() {
+ this.checked = setChecked;
+ });
+ });
+
+ var $checkboxes = self.getCheckboxesForCheckbox(trigger);
+
+ // Update header checkbox when body checkboxes are changed
+ $checkboxes.on("change." + pluginName, function() {
+ var checkedCount = 0;
+ $checkboxes.each(function() {
+ if (this.checked) {
+ checkedCount++;
+ }
+ });
+
+ var allSelected = checkedCount === $checkboxes.length;
+
+ trigger.checked = allSelected;
+
+ // only indeterminate if some are selected (not all and not none)
+ trigger.indeterminate = checkedCount !== 0 && !allSelected;
+ });
+ };
+
+ // on tablecreate, init
+ $(document).on(Tablesaw.events.create, function(e, tablesaw) {
+ new CheckAll(tablesaw);
+ });
+
+ Tablesaw.CheckAll = CheckAll;
+})();
+
+ return Tablesaw;
+}));
diff --git a/js/tablesaw/package.json b/js/tablesaw/package.json
new file mode 100755
index 0000000..0359de3
--- /dev/null
+++ b/js/tablesaw/package.json
@@ -0,0 +1,76 @@
+{
+ "name": "tablesaw",
+ "title": "Tablesaw",
+ "description": "A set of plugins for responsive tables.",
+ "version": "3.0.9",
+ "homepage": "https://github.com/filamentgroup/tablesaw",
+ "author": {
+ "name": "Zach Leatherman",
+ "company": "Filament Group",
+ "email": "zach@filamentgroup.com",
+ "url": "http://filamentgroup.com/"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/filamentgroup/tablesaw.git"
+ },
+ "bugs": "https://github.com/filamentgroup/tablesaw/issues",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ },
+ "scripts": {
+ "test": "./node_modules/.bin/grunt test",
+ "precommit": "lint-staged",
+ "prepush": "grunt test"
+ },
+ "lint-staged": {
+ "src/*.js": [
+ "prettier --write --print-width 100 --use-tabs",
+ "git add"
+ ]
+ },
+ "ava": {
+ "files": [
+ "test/**/*.test.js"
+ ],
+ "require": [
+ "babel-register",
+ "./test/helpers/setup-browser-env.js"
+ ],
+ "inherit": true,
+ "verbose": true
+ },
+ "main": "dist/tablesaw.js",
+ "dependencies": {
+ "shoestring": "^2.0.0"
+ },
+ "devDependencies": {
+ "ava": "^0.23.0",
+ "babel-register": "^6.26.0",
+ "browser-env": "^3.2.2",
+ "console-advanced": "^1.0.7",
+ "grunt": "^1.0.1",
+ "grunt-bytesize": "~0.2.0",
+ "grunt-contrib-clean": "~1.1.0",
+ "grunt-contrib-compress": "~1.4.3",
+ "grunt-contrib-concat": "~1.0.1",
+ "grunt-contrib-copy": "~1.0.0",
+ "grunt-contrib-cssmin": "^2.2.0",
+ "grunt-contrib-qunit": "^2.0.0",
+ "grunt-contrib-uglify": "^3.0.1",
+ "grunt-contrib-watch": "~1.0.0",
+ "grunt-gh-pages": "~2.0.0",
+ "grunt-myth": "~1.1.0",
+ "grunt-run": "^0.8.0",
+ "husky": "^0.14.3",
+ "jquery": "^3.1.0",
+ "lint-staged": "^4.0.1",
+ "matchdep": "~1.0.1",
+ "prettier": "^1.8.2",
+ "qunitjs": "^2.4.1"
+ },
+ "optionalDependencies": {
+ "javascript-natural-sort": "^0.7.1"
+ }
+}
diff --git a/js/tablesaw/src/lib/shoestring-custom.js b/js/tablesaw/src/lib/shoestring-custom.js
new file mode 100755
index 0000000..43552aa
--- /dev/null
+++ b/js/tablesaw/src/lib/shoestring-custom.js
@@ -0,0 +1,1706 @@
+/*! Shoestring - v2.0.0 - 2017-02-14
+* http://github.com/filamentgroup/shoestring/
+* Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */
+(function( factory ) {
+ if( typeof define === 'function' && define.amd ) {
+ // AMD. Register as an anonymous module.
+ define( [ 'shoestring' ], factory );
+ } else if (typeof module === 'object' && module.exports) {
+ // Node/CommonJS
+ module.exports = factory();
+ } else {
+ // Browser globals
+ factory();
+ }
+}(function () {
+ var win = typeof window !== "undefined" ? window : this;
+ var doc = win.document;
+
+
+ /**
+ * The shoestring object constructor.
+ *
+ * @param {string,object} prim The selector to find or element to wrap.
+ * @param {object} sec The context in which to match the `prim` selector.
+ * @returns shoestring
+ * @this window
+ */
+ function shoestring( prim, sec ){
+ var pType = typeof( prim ),
+ ret = [],
+ sel;
+
+ // return an empty shoestring object
+ if( !prim ){
+ return new Shoestring( ret );
+ }
+
+ // ready calls
+ if( prim.call ){
+ return shoestring.ready( prim );
+ }
+
+ // handle re-wrapping shoestring objects
+ if( prim.constructor === Shoestring && !sec ){
+ return prim;
+ }
+
+ // if string starting with <, make html
+ if( pType === "string" && prim.indexOf( "<" ) === 0 ){
+ var dfrag = doc.createElement( "div" );
+
+ dfrag.innerHTML = prim;
+
+ // TODO depends on children (circular)
+ return shoestring( dfrag ).children().each(function(){
+ dfrag.removeChild( this );
+ });
+ }
+
+ // if string, it's a selector, use qsa
+ if( pType === "string" ){
+ if( sec ){
+ return shoestring( sec ).find( prim );
+ }
+
+ sel = doc.querySelectorAll( prim );
+
+ return new Shoestring( sel, prim );
+ }
+
+ // array like objects or node lists
+ if( Object.prototype.toString.call( pType ) === '[object Array]' ||
+ (win.NodeList && prim instanceof win.NodeList) ){
+
+ return new Shoestring( prim, prim );
+ }
+
+ // if it's an array, use all the elements
+ if( prim.constructor === Array ){
+ return new Shoestring( prim, prim );
+ }
+
+ // otherwise assume it's an object the we want at an index
+ return new Shoestring( [prim], prim );
+ }
+
+ var Shoestring = function( ret, prim ) {
+ this.length = 0;
+ this.selector = prim;
+ shoestring.merge(this, ret);
+ };
+
+ // TODO only required for tests
+ Shoestring.prototype.reverse = [].reverse;
+
+ // For adding element set methods
+ shoestring.fn = Shoestring.prototype;
+
+ shoestring.Shoestring = Shoestring;
+
+ // For extending objects
+ // TODO move to separate module when we use prototypes
+ shoestring.extend = function( first, second ){
+ for( var i in second ){
+ if( second.hasOwnProperty( i ) ){
+ first[ i ] = second[ i ];
+ }
+ }
+
+ return first;
+ };
+
+ // taken directly from jQuery
+ shoestring.merge = function( first, second ) {
+ var len, j, i;
+
+ len = +second.length,
+ j = 0,
+ i = first.length;
+
+ for ( ; j < len; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ first.length = i;
+
+ return first;
+ };
+
+ // expose
+ win.shoestring = shoestring;
+
+
+
+ /**
+ * Iterates over `shoestring` collections.
+ *
+ * @param {function} callback The callback to be invoked on each element and index
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.each = function( callback ){
+ return shoestring.each( this, callback );
+ };
+
+ shoestring.each = function( collection, callback ) {
+ var val;
+ for( var i = 0, il = collection.length; i < il; i++ ){
+ val = callback.call( collection[i], i, collection[i] );
+ if( val === false ){
+ break;
+ }
+ }
+
+ return collection;
+ };
+
+
+
+ /**
+ * Check for array membership.
+ *
+ * @param {object} needle The thing to find.
+ * @param {object} haystack The thing to find the needle in.
+ * @return {boolean}
+ * @this window
+ */
+ shoestring.inArray = function( needle, haystack ){
+ var isin = -1;
+ for( var i = 0, il = haystack.length; i < il; i++ ){
+ if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){
+ isin = i;
+ }
+ }
+ return isin;
+ };
+
+
+
+ /**
+ * Bind callbacks to be run when the DOM is "ready".
+ *
+ * @param {function} fn The callback to be run
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.ready = function( fn ){
+ if( ready && fn ){
+ fn.call( doc );
+ }
+ else if( fn ){
+ readyQueue.push( fn );
+ }
+ else {
+ runReady();
+ }
+
+ return [doc];
+ };
+
+ // TODO necessary?
+ shoestring.fn.ready = function( fn ){
+ shoestring.ready( fn );
+ return this;
+ };
+
+ // Empty and exec the ready queue
+ var ready = false,
+ readyQueue = [],
+ runReady = function(){
+ if( !ready ){
+ while( readyQueue.length ){
+ readyQueue.shift().call( doc );
+ }
+ ready = true;
+ }
+ };
+
+ // If DOM is already ready at exec time, depends on the browser.
+ // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128
+ if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") {
+ runReady();
+ } else {
+ doc.addEventListener( "DOMContentLoaded", runReady, false );
+ doc.addEventListener( "readystatechange", runReady, false );
+ win.addEventListener( "load", runReady, false );
+ }
+
+
+
+ /**
+ * Checks the current set of elements against the selector, if one matches return `true`.
+ *
+ * @param {string} selector The selector to check.
+ * @return {boolean}
+ * @this {shoestring}
+ */
+ shoestring.fn.is = function( selector ){
+ var ret = false, self = this, parents, check;
+
+ // assume a dom element
+ if( typeof selector !== "string" ){
+ // array-like, ie shoestring objects or element arrays
+ if( selector.length && selector[0] ){
+ check = selector;
+ } else {
+ check = [selector];
+ }
+
+ return _checkElements(this, check);
+ }
+
+ parents = this.parent();
+
+ if( !parents.length ){
+ parents = shoestring( doc );
+ }
+
+ parents.each(function( i, e ) {
+ var children;
+
+ children = e.querySelectorAll( selector );
+
+ ret = _checkElements( self, children );
+ });
+
+ return ret;
+ };
+
+ function _checkElements(needles, haystack){
+ var ret = false;
+
+ needles.each(function() {
+ var j = 0;
+
+ while( j < haystack.length ){
+ if( this === haystack[j] ){
+ ret = true;
+ }
+
+ j++;
+ }
+ });
+
+ return ret;
+ }
+
+
+
+ /**
+ * Get data attached to the first element or set data values on all elements in the current set.
+ *
+ * @param {string} name The data attribute name.
+ * @param {any} value The value assigned to the data attribute.
+ * @return {any|shoestring}
+ * @this shoestring
+ */
+ shoestring.fn.data = function( name, value ){
+ if( name !== undefined ){
+ if( value !== undefined ){
+ return this.each(function(){
+ if( !this.shoestringData ){
+ this.shoestringData = {};
+ }
+
+ this.shoestringData[ name ] = value;
+ });
+ }
+ else {
+ if( this[ 0 ] ) {
+ if( this[ 0 ].shoestringData ) {
+ return this[ 0 ].shoestringData[ name ];
+ }
+ }
+ }
+ }
+ else {
+ return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined;
+ }
+ };
+
+
+ /**
+ * Remove data associated with `name` or all the data, for each element in the current set.
+ *
+ * @param {string} name The data attribute name.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.removeData = function( name ){
+ return this.each(function(){
+ if( name !== undefined && this.shoestringData ){
+ this.shoestringData[ name ] = undefined;
+ delete this.shoestringData[ name ];
+ } else {
+ this[ 0 ].shoestringData = {};
+ }
+ });
+ };
+
+
+
+ /**
+ * An alias for the `shoestring` constructor.
+ */
+ win.$ = shoestring;
+
+
+
+ /**
+ * Add a class to each DOM element in the set of elements.
+ *
+ * @param {string} className The name of the class to be added.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.addClass = function( className ){
+ var classes = className.replace(/^\s+|\s+$/g, '').split( " " );
+
+ return this.each(function(){
+ for( var i = 0, il = classes.length; i < il; i++ ){
+ if( this.className !== undefined &&
+ (this.className === "" ||
+ !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){
+ this.className += " " + classes[ i ];
+ }
+ }
+ });
+ };
+
+
+
+ /**
+ * Add elements matching the selector to the current set.
+ *
+ * @param {string} selector The selector for the elements to add from the DOM
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.add = function( selector ){
+ var ret = [];
+ this.each(function(){
+ ret.push( this );
+ });
+
+ shoestring( selector ).each(function(){
+ ret.push( this );
+ });
+
+ return shoestring( ret );
+ };
+
+
+
+ /**
+ * Insert an element or HTML string as the last child of each element in the set.
+ *
+ * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.append = function( fragment ){
+ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
+ fragment = shoestring( fragment );
+ }
+
+ return this.each(function( i ){
+ for( var j = 0, jl = fragment.length; j < jl; j++ ){
+ this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] );
+ }
+ });
+ };
+
+
+
+ /**
+ * Insert the current set as the last child of the elements matching the selector.
+ *
+ * @param {string} selector The selector after which to append the current set.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.appendTo = function( selector ){
+ return this.each(function(){
+ shoestring( selector ).append( this );
+ });
+ };
+
+
+
+ /**
+ * Get the value of the first element of the set or set the value of all the elements in the set.
+ *
+ * @param {string} name The attribute name.
+ * @param {string} value The new value for the attribute.
+ * @return {shoestring|string|undefined}
+ * @this {shoestring}
+ */
+ shoestring.fn.attr = function( name, value ){
+ var nameStr = typeof( name ) === "string";
+
+ if( value !== undefined || !nameStr ){
+ return this.each(function(){
+ if( nameStr ){
+ this.setAttribute( name, value );
+ } else {
+ for( var i in name ){
+ if( name.hasOwnProperty( i ) ){
+ this.setAttribute( i, name[ i ] );
+ }
+ }
+ }
+ });
+ } else {
+ return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined;
+ }
+ };
+
+
+
+ /**
+ * Insert an element or HTML string before each element in the current set.
+ *
+ * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.before = function( fragment ){
+ if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
+ fragment = shoestring( fragment );
+ }
+
+ return this.each(function( i ){
+ for( var j = 0, jl = fragment.length; j < jl; j++ ){
+ this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this );
+ }
+ });
+ };
+
+
+
+ /**
+ * Get the children of the current collection.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.children = function(){
+ var ret = [],
+ childs,
+ j;
+ this.each(function(){
+ childs = this.children;
+ j = -1;
+
+ while( j++ < childs.length-1 ){
+ if( shoestring.inArray( childs[ j ], ret ) === -1 ){
+ ret.push( childs[ j ] );
+ }
+ }
+ });
+ return shoestring(ret);
+ };
+
+
+
+ /**
+ * Find an element matching the selector in the set of the current element and its parents.
+ *
+ * @param {string} selector The selector used to identify the target element.
+ * @return shoestring
+ * @this shoestring
+ */
+ shoestring.fn.closest = function( selector ){
+ var ret = [];
+
+ if( !selector ){
+ return shoestring( ret );
+ }
+
+ this.each(function(){
+ var element, $self = shoestring( element = this );
+
+ if( $self.is(selector) ){
+ ret.push( this );
+ return;
+ }
+
+ while( element.parentElement ) {
+ if( shoestring(element.parentElement).is(selector) ){
+ ret.push( element.parentElement );
+ break;
+ }
+
+ element = element.parentElement;
+ }
+ });
+
+ return shoestring( ret );
+ };
+
+
+
+ shoestring.cssExceptions = {
+ 'float': [ 'cssFloat' ]
+ };
+
+
+
+ (function() {
+ var cssExceptions = shoestring.cssExceptions;
+
+ // IE8 uses marginRight instead of margin-right
+ function convertPropertyName( str ) {
+ return str.replace( /\-([A-Za-z])/g, function ( match, character ) {
+ return character.toUpperCase();
+ });
+ }
+
+ function _getStyle( element, property ) {
+ return win.getComputedStyle( element, null ).getPropertyValue( property );
+ }
+
+ var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ];
+
+ /**
+ * Private function for getting the computed style of an element.
+ *
+ * **NOTE** Please use the [css](../css.js.html) method instead.
+ *
+ * @method _getStyle
+ * @param {HTMLElement} element The element we want the style property for.
+ * @param {string} property The css property we want the style for.
+ */
+ shoestring._getStyle = function( element, property ) {
+ var convert, value, j, k;
+
+ if( cssExceptions[ property ] ) {
+ for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) {
+ value = _getStyle( element, cssExceptions[ property ][ j ] );
+
+ if( value ) {
+ return value;
+ }
+ }
+ }
+
+ for( j = 0, k = vendorPrefixes.length; j < k; j++ ) {
+ convert = convertPropertyName( vendorPrefixes[ j ] + property );
+
+ // VendorprefixKeyName || key-name
+ value = _getStyle( element, convert );
+
+ if( convert !== property ) {
+ value = value || _getStyle( element, property );
+ }
+
+ if( vendorPrefixes[ j ] ) {
+ // -vendorprefix-key-name
+ value = value || _getStyle( element, vendorPrefixes[ j ] + property );
+ }
+
+ if( value ) {
+ return value;
+ }
+ }
+
+ return undefined;
+ };
+ })();
+
+
+
+ (function() {
+ var cssExceptions = shoestring.cssExceptions;
+
+ // IE8 uses marginRight instead of margin-right
+ function convertPropertyName( str ) {
+ return str.replace( /\-([A-Za-z])/g, function ( match, character ) {
+ return character.toUpperCase();
+ });
+ }
+
+ /**
+ * Private function for setting the style of an element.
+ *
+ * **NOTE** Please use the [css](../css.js.html) method instead.
+ *
+ * @method _setStyle
+ * @param {HTMLElement} element The element we want to style.
+ * @param {string} property The property being used to style the element.
+ * @param {string} value The css value for the style property.
+ */
+ shoestring._setStyle = function( element, property, value ) {
+ var convertedProperty = convertPropertyName(property);
+
+ element.style[ property ] = value;
+
+ if( convertedProperty !== property ) {
+ element.style[ convertedProperty ] = value;
+ }
+
+ if( cssExceptions[ property ] ) {
+ for( var j = 0, k = cssExceptions[ property ].length; j inside of ?");
+ }
+
+ // assign an id if there is none
+ if (!this.$table.attr("id")) {
+ this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000));
+ }
+
+ this.createToolbar();
+
+ this._initCells();
+
+ this.$table.data(pluginName, this);
+
+ this.$table.trigger(events.create, [this]);
+ };
+
+ Table.prototype.getConfig = function(pluginSpecificConfig) {
+ // shoestring extend doesn’t support arbitrary args
+ var configs = $.extend(defaultConfig, pluginSpecificConfig || {});
+ return $.extend(configs, typeof TablesawConfig !== "undefined" ? TablesawConfig : {});
+ };
+
+ Table.prototype._getPrimaryHeaderRow = function() {
+ return this._getHeaderRows().eq(0);
+ };
+
+ Table.prototype._getHeaderRows = function() {
+ return this.$thead
+ .children()
+ .filter("tr")
+ .filter(function() {
+ return !$(this).is("[data-tablesaw-ignorerow]");
+ });
+ };
+
+ Table.prototype._getRowIndex = function($row) {
+ return $row.prevAll().length;
+ };
+
+ Table.prototype._getHeaderRowIndeces = function() {
+ var self = this;
+ var indeces = [];
+ this._getHeaderRows().each(function() {
+ indeces.push(self._getRowIndex($(this)));
+ });
+ return indeces;
+ };
+
+ Table.prototype._getPrimaryHeaderCells = function($row) {
+ return ($row || this._getPrimaryHeaderRow()).find("th");
+ };
+
+ Table.prototype._$getCells = function(th) {
+ var self = this;
+ return $(th)
+ .add(th.cells)
+ .filter(function() {
+ var $t = $(this);
+ var $row = $t.parent();
+ var hasColspan = $t.is("[colspan]");
+ // no subrows or ignored rows (keep cells in ignored rows that do not have a colspan)
+ return (
+ !$row.is("[" + self.attributes.subrow + "]") &&
+ (!$row.is("[" + self.attributes.ignorerow + "]") || !hasColspan)
+ );
+ });
+ };
+
+ Table.prototype._getVisibleColspan = function() {
+ var colspan = 0;
+ this._getPrimaryHeaderCells().each(function() {
+ var $t = $(this);
+ if ($t.css("display") !== "none") {
+ colspan += parseInt($t.attr("colspan"), 10) || 1;
+ }
+ });
+ return colspan;
+ };
+
+ Table.prototype.getColspanForCell = function($cell) {
+ var visibleColspan = this._getVisibleColspan();
+ var visibleSiblingColumns = 0;
+ if ($cell.closest("tr").data("tablesaw-rowspanned")) {
+ visibleSiblingColumns++;
+ }
+
+ $cell.siblings().each(function() {
+ var $t = $(this);
+ var colColspan = parseInt($t.attr("colspan"), 10) || 1;
+
+ if ($t.css("display") !== "none") {
+ visibleSiblingColumns += colColspan;
+ }
+ });
+ // console.log( $cell[ 0 ], visibleColspan, visibleSiblingColumns );
+
+ return visibleColspan - visibleSiblingColumns;
+ };
+
+ Table.prototype.isCellInColumn = function(header, cell) {
+ return $(header)
+ .add(header.cells)
+ .filter(function() {
+ return this === cell;
+ }).length;
+ };
+
+ Table.prototype.updateColspanCells = function(cls, header, userAction) {
+ var self = this;
+ var primaryHeaderRow = self._getPrimaryHeaderRow();
+
+ // find persistent column rowspans
+ this.$table.find("[rowspan][data-tablesaw-priority]").each(function() {
+ var $t = $(this);
+ if ($t.attr("data-tablesaw-priority") !== "persist") {
+ return;
+ }
+
+ var $row = $t.closest("tr");
+ var rowspan = parseInt($t.attr("rowspan"), 10);
+ if (rowspan > 1) {
+ $row = $row.next();
+
+ $row.data("tablesaw-rowspanned", true);
+
+ rowspan--;
+ }
+ });
+
+ this.$table
+ .find("[colspan],[data-tablesaw-maxcolspan]")
+ .filter(function() {
+ // is not in primary header row
+ return $(this).closest("tr")[0] !== primaryHeaderRow[0];
+ })
+ .each(function() {
+ var $cell = $(this);
+
+ if (userAction === undefined || self.isCellInColumn(header, this)) {
+ } else {
+ // if is not a user action AND the cell is not in the updating column, kill it
+ return;
+ }
+
+ var colspan = self.getColspanForCell($cell);
+
+ if (cls && userAction !== undefined) {
+ // console.log( colspan === 0 ? "addClass" : "removeClass", $cell );
+ $cell[colspan === 0 ? "addClass" : "removeClass"](cls);
+ }
+
+ // cache original colspan
+ var maxColspan = parseInt($cell.attr("data-tablesaw-maxcolspan"), 10);
+ if (!maxColspan) {
+ $cell.attr("data-tablesaw-maxcolspan", $cell.attr("colspan"));
+ } else if (colspan > maxColspan) {
+ colspan = maxColspan;
+ }
+
+ // console.log( this, "setting colspan to ", colspan );
+ $cell.attr("colspan", colspan);
+ });
+ };
+
+ Table.prototype._findPrimaryHeadersForCell = function(cell) {
+ var $headerRow = this._getPrimaryHeaderRow();
+ var $headers = this._getPrimaryHeaderCells($headerRow);
+ var headerRowIndex = this._getRowIndex($headerRow);
+ var results = [];
+
+ for (var rowNumber = 0; rowNumber < this.headerMapping.length; rowNumber++) {
+ if (rowNumber === headerRowIndex) {
+ continue;
+ }
+ for (var colNumber = 0; colNumber < this.headerMapping[rowNumber].length; colNumber++) {
+ if (this.headerMapping[rowNumber][colNumber] === cell) {
+ results.push($headers[colNumber]);
+ }
+ }
+ }
+ return results;
+ };
+
+ // used by init cells
+ Table.prototype.getRows = function() {
+ var self = this;
+ return this.$table.find("tr").filter(function() {
+ return $(this)
+ .closest("table")
+ .is(self.$table);
+ });
+ };
+
+ // used by sortable
+ Table.prototype.getBodyRows = function(tbody) {
+ return (tbody ? $(tbody) : this.$tbody).children().filter("tr");
+ };
+
+ Table.prototype.getHeaderCellIndex = function(cell) {
+ var lookup = this.headerMapping[0];
+ for (var colIndex = 0; colIndex < lookup.length; colIndex++) {
+ if (lookup[colIndex] === cell) {
+ return colIndex;
+ }
+ }
+
+ return -1;
+ };
+
+ Table.prototype._initCells = function() {
+ // re-establish original colspans
+ this.$table.find("[data-tablesaw-maxcolspan]").each(function() {
+ var $t = $(this);
+ $t.attr("colspan", $t.attr("data-tablesaw-maxcolspan"));
+ });
+
+ var $rows = this.getRows();
+ var columnLookup = [];
+
+ $rows.each(function(rowNumber) {
+ columnLookup[rowNumber] = [];
+ });
+
+ $rows.each(function(rowNumber) {
+ var coltally = 0;
+ var $t = $(this);
+ var children = $t.children();
+
+ children.each(function() {
+ var colspan = parseInt(
+ this.getAttribute("data-tablesaw-maxcolspan") || this.getAttribute("colspan"),
+ 10
+ );
+ var rowspan = parseInt(this.getAttribute("rowspan"), 10);
+
+ // set in a previous rowspan
+ while (columnLookup[rowNumber][coltally]) {
+ coltally++;
+ }
+
+ columnLookup[rowNumber][coltally] = this;
+
+ // TODO? both colspan and rowspan
+ if (colspan) {
+ for (var k = 0; k < colspan - 1; k++) {
+ coltally++;
+ columnLookup[rowNumber][coltally] = this;
+ }
+ }
+ if (rowspan) {
+ for (var j = 1; j < rowspan; j++) {
+ columnLookup[rowNumber + j][coltally] = this;
+ }
+ }
+
+ coltally++;
+ });
+ });
+
+ var headerRowIndeces = this._getHeaderRowIndeces();
+ for (var colNumber = 0; colNumber < columnLookup[0].length; colNumber++) {
+ for (var headerIndex = 0, k = headerRowIndeces.length; headerIndex < k; headerIndex++) {
+ var headerCol = columnLookup[headerRowIndeces[headerIndex]][colNumber];
+
+ var rowNumber = headerRowIndeces[headerIndex];
+ var rowCell;
+
+ if (!headerCol.cells) {
+ headerCol.cells = [];
+ }
+
+ while (rowNumber < columnLookup.length) {
+ rowCell = columnLookup[rowNumber][colNumber];
+
+ if (headerCol !== rowCell) {
+ headerCol.cells.push(rowCell);
+ }
+
+ rowNumber++;
+ }
+ }
+ }
+
+ this.headerMapping = columnLookup;
+ };
+
+ Table.prototype.refresh = function() {
+ this._initCells();
+
+ this.$table.trigger(events.refresh, [this]);
+ };
+
+ Table.prototype._getToolbarAnchor = function() {
+ var $parent = this.$table.parent();
+ if ($parent.is(".tablesaw-overflow")) {
+ return $parent;
+ }
+ return this.$table;
+ };
+
+ Table.prototype._getToolbar = function($anchor) {
+ if (!$anchor) {
+ $anchor = this._getToolbarAnchor();
+ }
+ return $anchor.prev().filter("." + classes.toolbar);
+ };
+
+ Table.prototype.createToolbar = function() {
+ // Insert the toolbar
+ // TODO move this into a separate component
+ var $anchor = this._getToolbarAnchor();
+ var $toolbar = this._getToolbar($anchor);
+ if (!$toolbar.length) {
+ $toolbar = $(" | ")
+ .addClass(classes.toolbar)
+ .insertBefore($anchor);
+ }
+ this.$toolbar = $toolbar;
+
+ if (this.mode) {
+ this.$toolbar.addClass("tablesaw-mode-" + this.mode);
+ }
+ };
+
+ Table.prototype.destroy = function() {
+ // Don’t remove the toolbar, just erase the classes on it.
+ // Some of the table features are not yet destroy-friendly.
+ this._getToolbar().each(function() {
+ this.className = this.className.replace(/\btablesaw-mode\-\w*\b/gi, "");
+ });
+
+ var tableId = this.$table.attr("id");
+ $(document).off("." + tableId);
+ $(window).off("." + tableId);
+
+ // other plugins
+ this.$table.trigger(events.destroy, [this]);
+
+ this.$table.removeData(pluginName);
+ };
+
+ // Collection method.
+ $.fn[pluginName] = function() {
+ return this.each(function() {
+ var $t = $(this);
+
+ if ($t.data(pluginName)) {
+ return;
+ }
+
+ new Table(this);
+ });
+ };
+
+ var $doc = $(document);
+ $doc.on("enhance.tablesaw", function(e) {
+ // Cut the mustard
+ if (Tablesaw.mustard) {
+ $(e.target)
+ .find(initSelector)
+ .filter(initFilterSelector)
+ [pluginName]();
+ }
+ });
+
+ // Avoid a resize during scroll:
+ // Some Mobile devices trigger a resize during scroll (sometimes when
+ // doing elastic stretch at the end of the document or from the
+ // location bar hide)
+ var isScrolling = false;
+ var scrollTimeout;
+ $doc.on("scroll.tablesaw", function() {
+ isScrolling = true;
+
+ window.clearTimeout(scrollTimeout);
+ scrollTimeout = window.setTimeout(function() {
+ isScrolling = false;
+ }, 300); // must be greater than the resize timeout below
+ });
+
+ var resizeTimeout;
+ $(window).on("resize", function() {
+ if (!isScrolling) {
+ window.clearTimeout(resizeTimeout);
+ resizeTimeout = window.setTimeout(function() {
+ $doc.trigger(events.resize);
+ }, 150); // must be less than the scrolling timeout above.
+ }
+ });
+
+ Tablesaw.Table = Table;
+})();
diff --git a/js/tablesaw/src/tables.minimap.css b/js/tablesaw/src/tables.minimap.css
new file mode 100755
index 0000000..deb41ef
--- /dev/null
+++ b/js/tablesaw/src/tables.minimap.css
@@ -0,0 +1,29 @@
+.tablesaw-advance {
+ float: right;
+}
+.tablesaw-advance.minimap {
+ margin-right: .4em;
+}
+.tablesaw-advance-dots {
+ float: left;
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+.tablesaw-advance-dots li {
+ display: table-cell;
+ margin: 0;
+ padding: .4em .2em;
+}
+.tablesaw-advance-dots li i {
+ width: .25em;
+ height: .25em;
+ background: #555;
+ border-radius: 100%;
+ display: inline-block;
+}
+.tablesaw-advance-dots-hide {
+ opacity: .25;
+ cursor: default;
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.minimap.js b/js/tablesaw/src/tables.minimap.js
new file mode 100755
index 0000000..876d837
--- /dev/null
+++ b/js/tablesaw/src/tables.minimap.js
@@ -0,0 +1,88 @@
+/*
+* tablesaw: A set of plugins for responsive tables
+* minimap: a set of dots to show which columns are currently visible.
+* Copyright (c) 2013 Filament Group, Inc.
+* MIT License
+*/
+
+(function() {
+ var MiniMap = {
+ attr: {
+ init: "data-tablesaw-minimap"
+ },
+ show: function(table) {
+ var mq = table.getAttribute(MiniMap.attr.init);
+
+ if (mq === "") {
+ // value-less but exists
+ return true;
+ } else if (mq && "matchMedia" in window) {
+ // has a mq value
+ return window.matchMedia(mq).matches;
+ }
+
+ return false;
+ }
+ };
+
+ function createMiniMap($table) {
+ var tblsaw = $table.data("tablesaw");
+ var $btns = $(' ');
+ var $dotNav = $('
").addClass(S.classes.main + " " + S.classes.toolbar);
+
+ var html = [
+ '");
+
+ $switcher.html(html.join(""));
+
+ var $otherToolbarItems = $toolbar.find(".tablesaw-advance").eq(0);
+ if ($otherToolbarItems.length) {
+ $switcher.insertBefore($otherToolbarItems);
+ } else {
+ $switcher.appendTo($toolbar);
+ }
+
+ $switcher.find(".tablesaw-btn").tablesawbtn();
+ $switcher.find("select").on("change", function(event) {
+ return S.onModeChange.call(table, event, $(this).val());
+ });
+ },
+ onModeChange: function(event, val) {
+ var $table = $(this);
+ var tblsaw = $table.data("tablesaw");
+ var $switcher = tblsaw.$toolbar.find("." + S.classes.main);
+
+ $switcher.remove();
+ tblsaw.destroy();
+
+ $table.attr("data-tablesaw-mode", val);
+ $table.tablesaw();
+ }
+ };
+
+ $(document).on(Tablesaw.events.create, function(e, Tablesaw) {
+ if (Tablesaw.$table.is(S.selectors.init)) {
+ S.init(Tablesaw.table);
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
diff --git a/js/tablesaw/src/tables.outro.js b/js/tablesaw/src/tables.outro.js
new file mode 100755
index 0000000..dd3746d
--- /dev/null
+++ b/js/tablesaw/src/tables.outro.js
@@ -0,0 +1,2 @@
+ return Tablesaw;
+}));
diff --git a/js/tablesaw/src/tables.skin.css b/js/tablesaw/src/tables.skin.css
new file mode 100755
index 0000000..dd61756
--- /dev/null
+++ b/js/tablesaw/src/tables.skin.css
@@ -0,0 +1,37 @@
+.tablesaw th,
+.tablesaw td {
+ padding: .5em .7em;
+ text-align: left;
+ vertical-align: middle;
+}
+.tablesaw-sortable-btn {
+ /* same as cell padding above */
+ padding: .5em .7em;
+}
+.tablesaw thead th {
+ text-align: left;
+}
+
+/* Table rows have a gray bottom stroke by default */
+.tablesaw-row-border tr {
+ border-bottom: 1px solid #dfdfdf;
+}
+/* Zebra striping */
+.tablesaw-row-zebra tr:nth-child(2n) {
+ background-color: #f8f8f8;
+}
+
+.tablesaw caption {
+ text-align: left;
+ margin: .59375em 0;
+}
+
+.tablesaw-swipe .tablesaw-swipe-cellpersist {
+ border-right: 2px solid #e4e1de;
+}
+.tablesaw-swipe-shadow .tablesaw-swipe-cellpersist {
+ border-right-width: 1px;
+}
+.tablesaw-swipe-shadow .tablesaw-swipe-cellpersist {
+ box-shadow: 3px 0 4px -1px #e4e1de;
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.sortable.css b/js/tablesaw/src/tables.sortable.css
new file mode 100755
index 0000000..7ddfd69
--- /dev/null
+++ b/js/tablesaw/src/tables.sortable.css
@@ -0,0 +1,34 @@
+.tablesaw-sortable-head {
+ position: relative;
+ vertical-align: top;
+}
+/* Override */
+.tablesaw .tablesaw-sortable-head {
+ padding: 0;
+}
+.tablesaw-sortable-btn {
+ min-width: 100%;
+ color: inherit;
+ background: transparent;
+ border: 0;
+ text-align: inherit;
+ font: inherit;
+ text-transform: inherit;
+}
+.tablesaw-sortable-arrow:after {
+ display: inline-block;
+ width: 10px;
+ height: 14px;
+ content: " ";
+ margin-left: .3125em;
+}
+.tablesaw-sortable-ascending .tablesaw-sortable-arrow:after,
+.tablesaw-sortable-descending .tablesaw-sortable-arrow:after {
+ content: "\0020";
+}
+.tablesaw-sortable-ascending .tablesaw-sortable-arrow:after {
+ content: "\2191";
+}
+.tablesaw-sortable-descending .tablesaw-sortable-arrow:after {
+ content: "\2193";
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.sortable.js b/js/tablesaw/src/tables.sortable.js
new file mode 100755
index 0000000..60112c6
--- /dev/null
+++ b/js/tablesaw/src/tables.sortable.js
@@ -0,0 +1,392 @@
+/*
+* tablesaw: A set of plugins for responsive tables
+* Sortable column headers
+* Copyright (c) 2013 Filament Group, Inc.
+* MIT License
+*/
+
+(function() {
+ function getSortValue(cell) {
+ var text = [];
+ $(cell.childNodes).each(function() {
+ var $el = $(this);
+ if ($el.is("input, select")) {
+ text.push($el.val());
+ } else if ($el.is(".tablesaw-cell-label")) {
+ } else {
+ text.push(($el.text() || "").replace(/^\s+|\s+$/g, ""));
+ }
+ });
+
+ return text.join("");
+ }
+
+ var pluginName = "tablesaw-sortable",
+ initSelector = "table[data-" + pluginName + "]",
+ sortableSwitchSelector = "[data-" + pluginName + "-switch]",
+ attrs = {
+ sortCol: "data-tablesaw-sortable-col",
+ defaultCol: "data-tablesaw-sortable-default-col",
+ numericCol: "data-tablesaw-sortable-numeric",
+ subRow: "data-tablesaw-subrow",
+ ignoreRow: "data-tablesaw-ignorerow"
+ },
+ classes = {
+ head: pluginName + "-head",
+ ascend: pluginName + "-ascending",
+ descend: pluginName + "-descending",
+ switcher: pluginName + "-switch",
+ tableToolbar: "tablesaw-bar-section",
+ sortButton: pluginName + "-btn"
+ },
+ methods = {
+ _create: function(o) {
+ return $(this).each(function() {
+ var init = $(this).data(pluginName + "-init");
+ if (init) {
+ return false;
+ }
+ $(this)
+ .data(pluginName + "-init", true)
+ .trigger("beforecreate." + pluginName)
+ [pluginName]("_init", o)
+ .trigger("create." + pluginName);
+ });
+ },
+ _init: function() {
+ var el = $(this);
+ var tblsaw = el.data("tablesaw");
+ var heads;
+ var $switcher;
+
+ function addClassToHeads(h) {
+ $.each(h, function(i, v) {
+ $(v).addClass(classes.head);
+ });
+ }
+
+ function makeHeadsActionable(h, fn) {
+ $.each(h, function(i, col) {
+ var b = $("");
+ b.on("click", { col: col }, fn);
+ $(col)
+ .wrapInner(b)
+ .find("button")
+ .append("");
+ });
+ }
+
+ function clearOthers(headcells) {
+ $.each(headcells, function(i, v) {
+ var col = $(v);
+ col.removeAttr(attrs.defaultCol);
+ col.removeClass(classes.ascend);
+ col.removeClass(classes.descend);
+ });
+ }
+
+ function headsOnAction(e) {
+ if ($(e.target).is("a[href]")) {
+ return;
+ }
+
+ e.stopPropagation();
+ var headCell = $(e.target).closest("[" + attrs.sortCol + "]"),
+ v = e.data.col,
+ newSortValue = heads.index(headCell[0]);
+
+ clearOthers(
+ headCell
+ .closest("thead")
+ .find("th")
+ .filter(function() {
+ return this !== headCell[0];
+ })
+ );
+ if (headCell.is("." + classes.descend) || !headCell.is("." + classes.ascend)) {
+ el[pluginName]("sortBy", v, true);
+ newSortValue += "_asc";
+ } else {
+ el[pluginName]("sortBy", v);
+ newSortValue += "_desc";
+ }
+ if ($switcher) {
+ $switcher
+ .find("select")
+ .val(newSortValue)
+ .trigger("refresh");
+ }
+
+ e.preventDefault();
+ }
+
+ function handleDefault(heads) {
+ $.each(heads, function(idx, el) {
+ var $el = $(el);
+ if ($el.is("[" + attrs.defaultCol + "]")) {
+ if (!$el.is("." + classes.descend)) {
+ $el.addClass(classes.ascend);
+ }
+ }
+ });
+ }
+
+ function addSwitcher(heads) {
+ $switcher = $(" ")
+ .addClass(classes.switcher)
+ .addClass(classes.tableToolbar);
+
+ var html = ["");
+
+ $switcher.html(html.join(""));
+
+ var $firstChild = tblsaw.$toolbar.children().eq(0);
+ if ($firstChild.length) {
+ $switcher.insertBefore($firstChild);
+ } else {
+ $switcher.appendTo(tblsaw.$toolbar);
+ }
+ $switcher.find(".tablesaw-btn").tablesawbtn();
+ $switcher.find("select").on("change", function() {
+ var val = $(this)
+ .val()
+ .split("_"),
+ head = heads.eq(val[0]);
+
+ clearOthers(head.siblings());
+ el[pluginName]("sortBy", head.get(0), val[1] === "asc");
+ });
+ }
+
+ el.addClass(pluginName);
+
+ heads = el
+ .children()
+ .filter("thead")
+ .find("th[" + attrs.sortCol + "]");
+
+ addClassToHeads(heads);
+ makeHeadsActionable(heads, headsOnAction);
+ handleDefault(heads);
+
+ if (el.is(sortableSwitchSelector)) {
+ addSwitcher(heads);
+ }
+ },
+ sortRows: function(rows, colNum, ascending, col, tbody) {
+ function convertCells(cellArr, belongingToTbody) {
+ var cells = [];
+ $.each(cellArr, function(i, cell) {
+ var row = cell.parentNode;
+ var $row = $(row);
+ // next row is a subrow
+ var subrows = [];
+ var $next = $row.next();
+ while ($next.is("[" + attrs.subRow + "]")) {
+ subrows.push($next[0]);
+ $next = $next.next();
+ }
+
+ var tbody = row.parentNode;
+
+ // current row is a subrow
+ if ($row.is("[" + attrs.subRow + "]")) {
+ } else if (tbody === belongingToTbody) {
+ cells.push({
+ element: cell,
+ cell: getSortValue(cell),
+ row: row,
+ subrows: subrows.length ? subrows : null,
+ ignored: $row.is("[" + attrs.ignoreRow + "]")
+ });
+ }
+ });
+ return cells;
+ }
+
+ function getSortFxn(ascending, forceNumeric) {
+ var fn,
+ regex = /[^\-\+\d\.]/g;
+ if (ascending) {
+ fn = function(a, b) {
+ if (a.ignored || b.ignored) {
+ return 0;
+ }
+ if (forceNumeric) {
+ return (
+ parseFloat(a.cell.replace(regex, "")) - parseFloat(b.cell.replace(regex, ""))
+ );
+ } else {
+ return a.cell.toLowerCase() > b.cell.toLowerCase() ? 1 : -1;
+ }
+ };
+ } else {
+ fn = function(a, b) {
+ if (a.ignored || b.ignored) {
+ return 0;
+ }
+ if (forceNumeric) {
+ return (
+ parseFloat(b.cell.replace(regex, "")) - parseFloat(a.cell.replace(regex, ""))
+ );
+ } else {
+ return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1;
+ }
+ };
+ }
+ return fn;
+ }
+
+ function convertToRows(sorted) {
+ var newRows = [],
+ i,
+ l;
+ for (i = 0, l = sorted.length; i < l; i++) {
+ newRows.push(sorted[i].row);
+ if (sorted[i].subrows) {
+ newRows.push(sorted[i].subrows);
+ }
+ }
+ return newRows;
+ }
+
+ var fn;
+ var sorted;
+ var cells = convertCells(col.cells, tbody);
+
+ var customFn = $(col).data("tablesaw-sort");
+
+ fn =
+ (customFn && typeof customFn === "function" ? customFn(ascending) : false) ||
+ getSortFxn(
+ ascending,
+ $(col).is("[" + attrs.numericCol + "]") &&
+ !$(col).is("[" + attrs.numericCol + '="false"]')
+ );
+
+ sorted = cells.sort(fn);
+
+ rows = convertToRows(sorted);
+
+ return rows;
+ },
+ makeColDefault: function(col, a) {
+ var c = $(col);
+ c.attr(attrs.defaultCol, "true");
+ if (a) {
+ c.removeClass(classes.descend);
+ c.addClass(classes.ascend);
+ } else {
+ c.removeClass(classes.ascend);
+ c.addClass(classes.descend);
+ }
+ },
+ sortBy: function(col, ascending) {
+ var el = $(this);
+ var colNum;
+ var tbl = el.data("tablesaw");
+ tbl.$tbody.each(function() {
+ var tbody = this;
+ var $tbody = $(this);
+ var rows = tbl.getBodyRows(tbody);
+ var sortedRows;
+ var map = tbl.headerMapping[0];
+ var j, k;
+
+ // find the column number that we’re sorting
+ for (j = 0, k = map.length; j < k; j++) {
+ if (map[j] === col) {
+ colNum = j;
+ break;
+ }
+ }
+
+ sortedRows = el[pluginName]("sortRows", rows, colNum, ascending, col, tbody);
+
+ // replace Table rows
+ for (j = 0, k = sortedRows.length; j < k; j++) {
+ $tbody.append(sortedRows[j]);
+ }
+ });
+
+ el[pluginName]("makeColDefault", col, ascending);
+
+ el.trigger("tablesaw-sorted");
+ }
+ };
+
+ // Collection method.
+ $.fn[pluginName] = function(arrg) {
+ var args = Array.prototype.slice.call(arguments, 1),
+ returnVal;
+
+ // if it's a method
+ if (arrg && typeof arrg === "string") {
+ returnVal = $.fn[pluginName].prototype[arrg].apply(this[0], args);
+ return typeof returnVal !== "undefined" ? returnVal : $(this);
+ }
+ // check init
+ if (!$(this).data(pluginName + "-active")) {
+ $(this).data(pluginName + "-active", true);
+ $.fn[pluginName].prototype._create.call(this, arrg);
+ }
+ return $(this);
+ };
+ // add methods
+ $.extend($.fn[pluginName].prototype, methods);
+
+ $(document).on(Tablesaw.events.create, function(e, Tablesaw) {
+ if (Tablesaw.$table.is(initSelector)) {
+ Tablesaw.$table[pluginName]();
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
diff --git a/js/tablesaw/src/tables.stack-default-breakpoint.css b/js/tablesaw/src/tables.stack-default-breakpoint.css
new file mode 100755
index 0000000..17e1619
--- /dev/null
+++ b/js/tablesaw/src/tables.stack-default-breakpoint.css
@@ -0,0 +1,57 @@
+@media (max-width: 39.9375em) {
+ /* Table rows have a gray bottom stroke by default */
+ .tablesaw-stack tbody tr {
+ display: block;
+ width: 100%;
+ border-bottom: 1px solid #dfdfdf;
+ }
+ .tablesaw-stack thead td,
+ .tablesaw-stack thead th {
+ display: none;
+ }
+ .tablesaw-stack tbody td,
+ .tablesaw-stack tbody th {
+ display: block;
+ float: left;
+ clear: left;
+ width: 100%;
+ }
+ .tablesaw-cell-label {
+ vertical-align: top;
+ }
+ .tablesaw-cell-content {
+ display: inline-block;
+ max-width: 67%;
+ }
+ .tablesaw-stack .tablesaw-stack-block .tablesaw-cell-label,
+ .tablesaw-stack .tablesaw-stack-block .tablesaw-cell-content {
+ display: block;
+ width: 100%;
+ max-width: 100%;
+ padding: 0;
+ }
+ .tablesaw-stack td:empty,
+ .tablesaw-stack th:empty {
+ display: none;
+ }
+}
+
+/* Media query to show as a standard table at 560px (35em x 16px) or wider */
+@media (min-width: 40em) {
+ .tablesaw-stack tr {
+ display: table-row;
+ }
+ /* Show the table header rows */
+ .tablesaw-stack td,
+ .tablesaw-stack th,
+ .tablesaw-stack thead td,
+ .tablesaw-stack thead th {
+ display: table-cell;
+ margin: 0;
+ }
+ /* Hide the labels in each cell */
+ .tablesaw-stack td .tablesaw-cell-label,
+ .tablesaw-stack th .tablesaw-cell-label {
+ display: none !important;
+ }
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.stack-mixin.scss b/js/tablesaw/src/tables.stack-mixin.scss
new file mode 100755
index 0000000..e4254a9
--- /dev/null
+++ b/js/tablesaw/src/tables.stack-mixin.scss
@@ -0,0 +1,71 @@
+/* Tablesaw Sass Mixins */
+
+@mixin tablesaw-stack-max {
+ /* Table rows have a gray bottom stroke by default */
+ .tablesaw-stack tbody tr {
+ display: block;
+ width: 100%;
+ border-bottom: 1px solid #dfdfdf;
+ }
+ .tablesaw-stack thead td,
+ .tablesaw-stack thead th {
+ display: none;
+ }
+ .tablesaw-stack tbody td,
+ .tablesaw-stack tbody th {
+ display: block;
+ float: left;
+ clear: left;
+ width: 100%;
+ }
+ .tablesaw-cell-label {
+ vertical-align: top;
+ }
+ .tablesaw-cell-content {
+ max-width: 67%;
+ display: inline-block;
+ }
+ .tablesaw-stack td:empty,
+ .tablesaw-stack th:empty {
+ display: none;
+ }
+}
+
+@mixin tablesaw-stack-min {
+ .tablesaw-stack tr {
+ display: table-row;
+ }
+ /* Show the table header rows */
+ .tablesaw-stack td,
+ .tablesaw-stack th,
+ .tablesaw-stack thead td,
+ .tablesaw-stack thead th {
+ display: table-cell;
+ margin: 0;
+ }
+ /* Hide the labels in each cell */
+ .tablesaw-stack td .tablesaw-cell-label,
+ .tablesaw-stack th .tablesaw-cell-label {
+ display: none !important;
+ }
+}
+
+$use-respond-mixins: false !default;
+@mixin tablesaw-stack( $breakpoint: 40em ) {
+ @if $use-respond-mixins {
+ @include respond-max($breakpoint - .0625) {
+ @include tablesaw-stack-max;
+ }
+ @include respond-min($breakpoint) {
+ @include tablesaw-stack-min;
+ }
+ }
+ @else {
+ @media (max-width: ($breakpoint - .0625)) {
+ @include tablesaw-stack-max;
+ }
+ @media (min-width: $breakpoint) {
+ @include tablesaw-stack-min;
+ }
+ }
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.stack.css b/js/tablesaw/src/tables.stack.css
new file mode 100755
index 0000000..7a3bf16
--- /dev/null
+++ b/js/tablesaw/src/tables.stack.css
@@ -0,0 +1,51 @@
+/*
+ Styles for the table stack mode
+*/
+
+.tablesaw-stack td .tablesaw-cell-label,
+.tablesaw-stack th .tablesaw-cell-label {
+ display: none;
+}
+
+/* Mobile first styles: Begin with the stacked presentation at narrow widths */
+/* Support note IE9+: @media only all */
+@media only all {
+ /* Show the table cells as a block level element */
+ .tablesaw-stack {
+ clear: both;
+ }
+ .tablesaw-stack td,
+ .tablesaw-stack th {
+ text-align: left;
+ display: block;
+ }
+ .tablesaw-stack tr {
+ clear: both;
+ display: table-row;
+ }
+ /* Make the label elements a percentage width */
+ .tablesaw-stack td .tablesaw-cell-label,
+ .tablesaw-stack th .tablesaw-cell-label {
+ display: inline-block;
+ padding: 0 .6em 0 0;
+ width: 30%;
+ }
+ /* For grouped headers, have a different style to visually separate the levels by classing the first label in each col group */
+ .tablesaw-stack th .tablesaw-cell-label-top,
+ .tablesaw-stack td .tablesaw-cell-label-top {
+ display: block;
+ padding: .4em 0;
+ margin: .4em 0;
+ }
+ .tablesaw-cell-label {
+ display: block;
+ }
+ /* Avoid double strokes when stacked */
+ .tablesaw-stack tbody th.group {
+ margin-top:-1px;
+ }
+ /* Avoid double strokes when stacked */
+ .tablesaw-stack th.group b.tablesaw-cell-label {
+ display: none !important;
+ }
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.stack.js b/js/tablesaw/src/tables.stack.js
new file mode 100755
index 0000000..0d6a41d
--- /dev/null
+++ b/js/tablesaw/src/tables.stack.js
@@ -0,0 +1,134 @@
+/*
+* tablesaw: A set of plugins for responsive tables
+* Stack: switches from column layout to rows with inline labels
+* Copyright (c) 2013 Filament Group, Inc.
+* MIT License
+*/
+
+(function() {
+ var classes = {
+ stackTable: "tablesaw-stack",
+ cellLabels: "tablesaw-cell-label",
+ cellContentLabels: "tablesaw-cell-content"
+ };
+
+ var data = {
+ key: "tablesaw-stack"
+ };
+
+ var attrs = {
+ labelless: "data-tablesaw-no-labels",
+ hideempty: "data-tablesaw-hide-empty"
+ };
+
+ var Stack = function(element, tablesaw) {
+ this.tablesaw = tablesaw;
+ this.$table = $(element);
+
+ this.labelless = this.$table.is("[" + attrs.labelless + "]");
+ this.hideempty = this.$table.is("[" + attrs.hideempty + "]");
+
+ this.$table.data(data.key, this);
+ };
+
+ Stack.prototype.init = function() {
+ this.$table.addClass(classes.stackTable);
+
+ if (this.labelless) {
+ return;
+ }
+
+ var self = this;
+
+ this.$table
+ .find("th, td")
+ .filter(function() {
+ return !$(this).closest("thead").length;
+ })
+ .filter(function() {
+ return (
+ !$(this)
+ .closest("tr")
+ .is("[" + attrs.labelless + "]") &&
+ (!self.hideempty || !!$(this).html())
+ );
+ })
+ .each(function() {
+ var $newHeader = $(document.createElement("b")).addClass(classes.cellLabels);
+ var $cell = $(this);
+
+ $(self.tablesaw._findPrimaryHeadersForCell(this)).each(function(index) {
+ var $header = $(this.cloneNode(true));
+ // TODO decouple from sortable better
+ // Changed from .text() in https://github.com/filamentgroup/tablesaw/commit/b9c12a8f893ec192830ec3ba2d75f062642f935b
+ // to preserve structural html in headers, like
+ var $sortableButton = $header.find(".tablesaw-sortable-btn");
+ $header.find(".tablesaw-sortable-arrow").remove();
+
+ // TODO decouple from checkall better
+ var $checkall = $header.find("[data-tablesaw-checkall]");
+ $checkall.closest("label").remove();
+ if ($checkall.length) {
+ $newHeader = $([]);
+ return;
+ }
+
+ if (index > 0) {
+ $newHeader.append(document.createTextNode(", "));
+ }
+
+ var parentNode = $sortableButton.length ? $sortableButton[0] : $header[0];
+ var el;
+ while ((el = parentNode.firstChild)) {
+ $newHeader[0].appendChild(el);
+ }
+ });
+
+ if ($newHeader.length && !$cell.find("." + classes.cellContentLabels).length) {
+ $cell.wrapInner("");
+ }
+
+ // Update if already exists.
+ var $label = $cell.find("." + classes.cellLabels);
+ if (!$label.length) {
+ $cell.prepend($newHeader);
+ } else {
+ // only if changed
+ $label.replaceWith($newHeader);
+ }
+ });
+ };
+
+ Stack.prototype.destroy = function() {
+ this.$table.removeClass(classes.stackTable);
+ this.$table.find("." + classes.cellLabels).remove();
+ this.$table.find("." + classes.cellContentLabels).each(function() {
+ $(this).replaceWith(this.childNodes);
+ });
+ };
+
+ // on tablecreate, init
+ $(document)
+ .on(Tablesaw.events.create, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ var table = new Stack(tablesaw.table, tablesaw);
+ table.init();
+ }
+ })
+ .on(Tablesaw.events.refresh, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ $(tablesaw.table)
+ .data(data.key)
+ .init();
+ }
+ })
+ .on(Tablesaw.events.destroy, function(e, tablesaw) {
+ if (tablesaw.mode === "stack") {
+ $(tablesaw.table)
+ .data(data.key)
+ .destroy();
+ }
+ });
+
+ Tablesaw.Stack = Stack;
+})();
diff --git a/js/tablesaw/src/tables.swipetoggle.css b/js/tablesaw/src/tables.swipetoggle.css
new file mode 100755
index 0000000..5b56423
--- /dev/null
+++ b/js/tablesaw/src/tables.swipetoggle.css
@@ -0,0 +1,10 @@
+.tablesaw-fix-persist {
+ table-layout: fixed;
+}
+@media only all {
+ /* Unchecked manually: Always hide */
+ .tablesaw-swipe th.tablesaw-swipe-cellhidden,
+ .tablesaw-swipe td.tablesaw-swipe-cellhidden {
+ display: none;
+ }
+}
\ No newline at end of file
diff --git a/js/tablesaw/src/tables.swipetoggle.js b/js/tablesaw/src/tables.swipetoggle.js
new file mode 100755
index 0000000..49f34d7
--- /dev/null
+++ b/js/tablesaw/src/tables.swipetoggle.js
@@ -0,0 +1,369 @@
+/*
+* tablesaw: A set of plugins for responsive tables
+* Swipe Toggle: swipe gesture (or buttons) to navigate which columns are shown.
+* Copyright (c) 2013 Filament Group, Inc.
+* MIT License
+*/
+
+(function() {
+ var classes = {
+ hideBtn: "disabled",
+ persistWidths: "tablesaw-fix-persist",
+ hiddenCol: "tablesaw-swipe-cellhidden",
+ persistCol: "tablesaw-swipe-cellpersist",
+ allColumnsVisible: "tablesaw-all-cols-visible"
+ };
+ var attrs = {
+ disableTouchEvents: "data-tablesaw-no-touch",
+ ignorerow: "data-tablesaw-ignorerow",
+ subrow: "data-tablesaw-subrow"
+ };
+
+ function createSwipeTable(tbl, $table) {
+ var tblsaw = $table.data("tablesaw");
+
+ var $btns = $("");
+ // TODO next major version: remove .btn
+ var $prevBtn = $(
+ "" +
+ Tablesaw.i18n.swipePreviousColumn +
+ ""
+ ).appendTo($btns);
+ // TODO next major version: remove .btn
+ var $nextBtn = $(
+ "" +
+ Tablesaw.i18n.swipeNextColumn +
+ ""
+ ).appendTo($btns);
+
+ var $headerCells = tbl._getPrimaryHeaderCells();
+ var $headerCellsNoPersist = $headerCells.not('[data-tablesaw-priority="persist"]');
+ var headerWidths = [];
+ var $head = $(document.head || "head");
+ var tableId = $table.attr("id");
+
+ if (!$headerCells.length) {
+ throw new Error("tablesaw swipe: no header cells found.");
+ }
+
+ $table.addClass("tablesaw-swipe");
+
+ function initMinHeaderWidths() {
+ $table.css({
+ width: "1px"
+ });
+
+ // remove any hidden columns
+ $table.find("." + classes.hiddenCol).removeClass(classes.hiddenCol);
+
+ headerWidths = [];
+ // Calculate initial widths
+ $headerCells.each(function() {
+ headerWidths.push(this.offsetWidth);
+ });
+
+ // reset props
+ $table.css({
+ width: ""
+ });
+ }
+
+ initMinHeaderWidths();
+
+ $btns.appendTo(tblsaw.$toolbar);
+
+ if (!tableId) {
+ tableId = "tableswipe-" + Math.round(Math.random() * 10000);
+ $table.attr("id", tableId);
+ }
+
+ function showColumn(headerCell) {
+ tblsaw._$getCells(headerCell).removeClass(classes.hiddenCol);
+ }
+
+ function hideColumn(headerCell) {
+ tblsaw._$getCells(headerCell).addClass(classes.hiddenCol);
+ }
+
+ function persistColumn(headerCell) {
+ tblsaw._$getCells(headerCell).addClass(classes.persistCol);
+ }
+
+ function isPersistent(headerCell) {
+ return $(headerCell).is('[data-tablesaw-priority="persist"]');
+ }
+
+ function unmaintainWidths() {
+ $table.removeClass(classes.persistWidths);
+ $("#" + tableId + "-persist").remove();
+ }
+
+ function maintainWidths() {
+ var prefix = "#" + tableId + ".tablesaw-swipe ",
+ styles = [],
+ tableWidth = $table.width(),
+ hash = [],
+ newHash;
+
+ // save persistent column widths (as long as they take up less than 75% of table width)
+ $headerCells.each(function(index) {
+ var width;
+ if (isPersistent(this)) {
+ width = this.offsetWidth;
+
+ if (width < tableWidth * 0.75) {
+ hash.push(index + "-" + width);
+ styles.push(
+ prefix +
+ " ." +
+ classes.persistCol +
+ ":nth-child(" +
+ (index + 1) +
+ ") { width: " +
+ width +
+ "px; }"
+ );
+ }
+ }
+ });
+ newHash = hash.join("_");
+
+ if (styles.length) {
+ $table.addClass(classes.persistWidths);
+ var $style = $("#" + tableId + "-persist");
+ // If style element not yet added OR if the widths have changed
+ if (!$style.length || $style.data("tablesaw-hash") !== newHash) {
+ // Remove existing
+ $style.remove();
+
+ $("")
+ .attr("id", tableId + "-persist")
+ .data("tablesaw-hash", newHash)
+ .appendTo($head);
+ }
+ }
+ }
+
+ function getNext() {
+ var next = [],
+ checkFound;
+
+ $headerCellsNoPersist.each(function(i) {
+ var $t = $(this),
+ isHidden = $t.css("display") === "none" || $t.is("." + classes.hiddenCol);
+
+ if (!isHidden && !checkFound) {
+ checkFound = true;
+ next[0] = i;
+ } else if (isHidden && checkFound) {
+ next[1] = i;
+
+ return false;
+ }
+ });
+
+ return next;
+ }
+
+ function getPrev() {
+ var next = getNext();
+ return [next[1] - 1, next[0] - 1];
+ }
+
+ function nextpair(fwd) {
+ return fwd ? getNext() : getPrev();
+ }
+
+ function canAdvance(pair) {
+ return pair[1] > -1 && pair[1] < $headerCellsNoPersist.length;
+ }
+
+ function matchesMedia() {
+ var matchMedia = $table.attr("data-tablesaw-swipe-media");
+ return !matchMedia || ("matchMedia" in window && window.matchMedia(matchMedia).matches);
+ }
+
+ function fakeBreakpoints() {
+ if (!matchesMedia()) {
+ return;
+ }
+
+ var containerWidth = $table.parent().width(),
+ persist = [],
+ sum = 0,
+ sums = [],
+ visibleNonPersistantCount = $headerCells.length;
+
+ $headerCells.each(function(index) {
+ var $t = $(this),
+ isPersist = $t.is('[data-tablesaw-priority="persist"]');
+
+ persist.push(isPersist);
+ sum += headerWidths[index];
+ sums.push(sum);
+
+ // is persistent or is hidden
+ if (isPersist || sum > containerWidth) {
+ visibleNonPersistantCount--;
+ }
+ });
+
+ // We need at least one column to swipe.
+ var needsNonPersistentColumn = visibleNonPersistantCount === 0;
+
+ $headerCells.each(function(index) {
+ if (sums[index] > containerWidth) {
+ hideColumn(this);
+ }
+ });
+
+ $headerCells.each(function(index) {
+ if (persist[index]) {
+ // for visual box-shadow
+ persistColumn(this);
+ return;
+ }
+
+ if (sums[index] <= containerWidth || needsNonPersistentColumn) {
+ needsNonPersistentColumn = false;
+ showColumn(this);
+ tblsaw.updateColspanCells(classes.hiddenCol, this, true);
+ }
+ });
+
+ unmaintainWidths();
+
+ $table.trigger("tablesawcolumns");
+ }
+
+ function advance(fwd) {
+ var pair = nextpair(fwd);
+ if (canAdvance(pair)) {
+ if (isNaN(pair[0])) {
+ if (fwd) {
+ pair[0] = 0;
+ } else {
+ pair[0] = $headerCellsNoPersist.length - 1;
+ }
+ }
+
+ // TODO just blindly hiding the previous column and showing the next column can result in
+ // column content overflow
+ maintainWidths();
+ hideColumn($headerCellsNoPersist.get(pair[0]));
+ tblsaw.updateColspanCells(classes.hiddenCol, $headerCellsNoPersist.get(pair[0]), false);
+
+ showColumn($headerCellsNoPersist.get(pair[1]));
+ tblsaw.updateColspanCells(classes.hiddenCol, $headerCellsNoPersist.get(pair[1]), true);
+
+ $table.trigger("tablesawcolumns");
+ }
+ }
+
+ $prevBtn.add($nextBtn).on("click", function(e) {
+ advance(!!$(e.target).closest($nextBtn).length);
+ e.preventDefault();
+ });
+
+ function getCoord(event, key) {
+ return (event.touches || event.originalEvent.touches)[0][key];
+ }
+
+ if (!$table.is("[" + attrs.disableTouchEvents + "]")) {
+ $table.on("touchstart.swipetoggle", function(e) {
+ var originX = getCoord(e, "pageX");
+ var originY = getCoord(e, "pageY");
+ var x;
+ var y;
+ var scrollTop = window.pageYOffset;
+
+ $(window).off(Tablesaw.events.resize, fakeBreakpoints);
+
+ $(this)
+ .on("touchmove.swipetoggle", function(e) {
+ x = getCoord(e, "pageX");
+ y = getCoord(e, "pageY");
+ })
+ .on("touchend.swipetoggle", function() {
+ var cfg = tbl.getConfig({
+ swipeHorizontalThreshold: 30,
+ swipeVerticalThreshold: 30
+ });
+
+ // This config code is a little awkward because shoestring doesn’t support deep $.extend
+ // Trying to work around when devs only override one of (not both) horizontalThreshold or
+ // verticalThreshold in their TablesawConfig.
+ // @TODO major version bump: remove cfg.swipe, move to just use the swipePrefix keys
+ var verticalThreshold = cfg.swipe
+ ? cfg.swipe.verticalThreshold
+ : cfg.swipeVerticalThreshold;
+ var horizontalThreshold = cfg.swipe
+ ? cfg.swipe.horizontalThreshold
+ : cfg.swipeHorizontalThreshold;
+
+ var isPageScrolled = Math.abs(window.pageYOffset - scrollTop) >= verticalThreshold;
+ var isVerticalSwipe = Math.abs(y - originY) >= verticalThreshold;
+
+ if (!isVerticalSwipe && !isPageScrolled) {
+ if (x - originX < -1 * horizontalThreshold) {
+ advance(true);
+ }
+ if (x - originX > horizontalThreshold) {
+ advance(false);
+ }
+ }
+
+ window.setTimeout(function() {
+ $(window).on(Tablesaw.events.resize, fakeBreakpoints);
+ }, 300);
+
+ $(this).off("touchmove.swipetoggle touchend.swipetoggle");
+ });
+ });
+ }
+
+ $table
+ .on("tablesawcolumns.swipetoggle", function() {
+ var canGoPrev = canAdvance(getPrev());
+ var canGoNext = canAdvance(getNext());
+ $prevBtn[canGoPrev ? "removeClass" : "addClass"](classes.hideBtn);
+ $nextBtn[canGoNext ? "removeClass" : "addClass"](classes.hideBtn);
+
+ tblsaw.$toolbar[!canGoPrev && !canGoNext ? "addClass" : "removeClass"](
+ classes.allColumnsVisible
+ );
+ })
+ .on("tablesawnext.swipetoggle", function() {
+ advance(true);
+ })
+ .on("tablesawprev.swipetoggle", function() {
+ advance(false);
+ })
+ .on(Tablesaw.events.destroy + ".swipetoggle", function() {
+ var $t = $(this);
+
+ $t.removeClass("tablesaw-swipe");
+ tblsaw.$toolbar.find(".tablesaw-advance").remove();
+ $(window).off(Tablesaw.events.resize, fakeBreakpoints);
+
+ $t.off(".swipetoggle");
+ })
+ .on(Tablesaw.events.refresh, function() {
+ unmaintainWidths();
+ initMinHeaderWidths();
+ fakeBreakpoints();
+ });
+
+ fakeBreakpoints();
+ $(window).on(Tablesaw.events.resize, fakeBreakpoints);
+ }
+
+ // on tablecreate, init
+ $(document).on(Tablesaw.events.create, function(e, tablesaw) {
+ if (tablesaw.mode === "swipe") {
+ createSwipeTable(tablesaw, tablesaw.$table);
+ }
+ });
+
+ // TODO OOP this and add to Tablesaw object
+})();
diff --git a/js/tablesaw/src/tables.toolbar.css b/js/tablesaw/src/tables.toolbar.css
new file mode 100755
index 0000000..dba82e7
--- /dev/null
+++ b/js/tablesaw/src/tables.toolbar.css
@@ -0,0 +1,273 @@
+/*
+* Button Markup
+* Custom-styled native inputs/buttons.
+*
+* Copyright (c) 2012 Filament Group, Inc.
+* Licensed under the MIT, GPL licenses.
+*/
+
+:root {
+ --button-text: #1c95d4;
+ --button-selected-background: #29abe2;
+ --button-checkbox-selected-background: #34a3de;
+ --button-selected-shadow: rgba(0,75,115,.45);
+ --button-interaction-shadow: #4faeef;
+}
+
+.tablesaw-btn {
+ border: 1px solid #ccc;
+ border-radius: .25em;
+ background: none;
+ box-shadow: 0 1px 0 rgba(255,255,255,1);
+ color: #4a4a4a;
+ cursor: pointer;
+ display: inline-block;
+ margin: 0;
+ padding: .5em .85em .4em .85em;
+ position: relative;
+ text-align: center;
+ text-decoration: none;
+ text-transform: capitalize;
+
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+}
+a.tablesaw-btn {
+ color: var(--button-text);
+}
+.tablesaw-btn:hover {
+ text-decoration: none;
+}
+
+/* Default radio/checkbox styling horizonal controlgroups. */
+.tablesaw-btn:active {
+ background-color: #ddd;
+}
+@supports ( box-shadow: none ) {
+ .tablesaw-btn:focus {
+ background-color: #fff;
+ outline: none;
+ }
+ .tablesaw-btn:focus {
+ box-shadow: 0 0 .35em var(--button-interaction-shadow) !important;
+ }
+}
+
+.tablesaw-btn-select select {
+ background: none;
+ border: none;
+ display:inline-block;
+ position: absolute;
+ left: 0;
+ top: 0;
+ margin: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 2;
+ min-height: 1em;
+ opacity: 0;
+ color: transparent;
+}
+.tablesaw-btn select option {
+ background: #fff;
+ color: #000;
+}
+.tablesaw-btn {
+ display: inline-block;
+ width: auto;
+ height: auto;
+ position: relative;
+ top: 0;
+}
+
+.tablesaw-btn.btn-small {
+ font-size: 1.0625em;
+ line-height: 19px;
+ padding: .3em 1em .3em 1em;
+}
+.tablesaw-btn.btn-micro {
+ font-size: .8125em;
+ padding: .4em .7em .25em .7em;
+}
+
+.tablesaw-btn-select {
+ padding-right: 1.5em;
+ text-align: left;
+ display: inline-block;
+ color:#4d4d4d;
+ padding-right: 2.5em;
+ min-width: 7.25em;
+}
+
+.tablesaw-btn-select:after {
+ content: " ";
+ position: absolute;
+ background: none;
+ background-repeat: no-repeat;
+ background-position: .25em .45em;
+ content: "\25bc";
+ font-size: .55em;
+ padding-top: 1.2em;
+ padding-left: 1em;
+ left:auto;
+ right: 0;
+ margin: 0;
+ top: 0;
+ bottom: 0;
+ width: 1.8em;
+}
+.tablesaw-btn-select.btn-small:after,
+.tablesaw-btn-select.btn-micro:after {
+ width: 1.2em;
+ font-size: .5em;
+ padding-top: 1em;
+ padding-right: .5em;
+ line-height: 1.65;
+ background: none;
+ box-shadow: none;
+ border-left-width: 0;
+}
+
+/* Column navigation buttons for swipe and columntoggle tables */
+.tablesaw-advance .tablesaw-btn {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ box-sizing: border-box;
+ text-shadow: 0 1px 0 #fff;
+ border-radius: .25em;
+}
+.tablesaw-advance .tablesaw-btn.btn-micro {
+ font-size: .8125em;
+ padding: .3em .7em .25em .7em;
+}
+.tablesaw-advance a.tablesaw-nav-btn:first-child {
+ margin-left: 0;
+}
+.tablesaw-advance a.tablesaw-nav-btn:last-child {
+ margin-right: 0;
+}
+.tablesaw-advance a.tablesaw-nav-btn {
+ display: inline-block;
+ overflow: hidden;
+ width: 1.8em;
+ height: 1.8em;
+ background-position: 50% 50%;
+ margin-left: .25em;
+ margin-right: .25em;
+ position: relative;
+ text-indent: -9999px;
+}
+.tablesaw-advance a.tablesaw-nav-btn.left:before,
+.tablesaw-advance a.tablesaw-nav-btn.right:before,
+.tablesaw-advance a.tablesaw-nav-btn.down:before,
+.tablesaw-advance a.tablesaw-nav-btn.up:before {
+ content: "\0020";
+ overflow: hidden;
+ width: 0;
+ height: 0;
+ position: absolute;
+}
+.tablesaw-advance a.tablesaw-nav-btn.down:before {
+ left: .5em;
+ top: .65em;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-top: 5px solid #808080;
+}
+.tablesaw-advance a.tablesaw-nav-btn.up:before {
+ left: .5em;
+ top: .65em;
+ border-left: 5px solid transparent;
+ border-right: 5px solid transparent;
+ border-bottom: 5px solid #808080;
+}
+.tablesaw-advance a.tablesaw-nav-btn.left:before,
+.tablesaw-advance a.tablesaw-nav-btn.right:before {
+ top: .45em;
+ border-top: 5px solid transparent;
+ border-bottom: 5px solid transparent;
+}
+.tablesaw-advance a.tablesaw-nav-btn.left:before {
+ left: .6em;
+ border-right: 5px solid #808080;
+}
+.tablesaw-advance a.tablesaw-nav-btn.right:before {
+ left: .7em;
+ border-left: 5px solid #808080;
+}
+.tablesaw-advance a.tablesaw-nav-btn.disabled {
+ opacity: .25;
+ cursor: default;
+ pointer-events: none;
+}
+
+/* Table Toolbar */
+.tablesaw-bar {
+ clear: both;
+}
+.tablesaw-bar * {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+.tablesaw-bar-section {
+ float: left;
+}
+.tablesaw-bar-section label {
+ font-size: .875em;
+ padding: .5em 0;
+ clear: both;
+ display: block;
+ color: #888;
+ margin-right: .5em;
+ text-transform:uppercase;
+}
+.tablesaw-btn,
+.tablesaw-enhanced .tablesaw-btn {
+ margin-top: .5em;
+ margin-bottom: .5em;
+}
+.tablesaw-btn-select,
+.tablesaw-enhanced .tablesaw-btn-select {
+ margin-bottom: 0;
+}
+/* TODO */
+.tablesaw-bar .tablesaw-bar-section .tablesaw-btn {
+ margin-left: .4em;
+ margin-top: 0;
+ text-transform:uppercase;
+ border: none;
+ box-shadow: none;
+ background: transparent;
+ font-size: 1em;
+ padding-left: .3em;
+}
+.tablesaw-bar .tablesaw-bar-section .btn-select {
+ min-width: 0;
+}
+.tablesaw-bar .tablesaw-bar-section .btn-select:after {
+ padding-top: .9em;
+}
+.tablesaw-bar .tablesaw-bar-section select {
+ color: #888;
+ text-transform: none;
+ background: transparent;
+}
+.tablesaw-bar-section ~ table {
+ clear: both;
+}
+.tablesaw-bar-section .abbreviated {
+ display: inline;
+}
+.tablesaw-bar-section .longform {
+ display: none;
+}
+@media (min-width: 24em) {
+ .tablesaw-bar-section .abbreviated {
+ display: none;
+ }
+ .tablesaw-bar-section .longform {
+ display: inline;
+ }
+}
diff --git a/tablesaw-js.html b/tablesaw-js.html
new file mode 100644
index 0000000..fa0e431
--- /dev/null
+++ b/tablesaw-js.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+ |
|---|