/*! Tablesaw - v2.0.2 - 2015-10-28
* https://github.com/filamentgroup/tablesaw
* Copyright (c) 2015 Filament Group; Licensed  */
/*
* tablesaw: A set of plugins for responsive tables
* Stack and Column Toggle tables
* Copyright (c) 2013 Filament Group, Inc.
* MIT License
*/

if (typeof Tablesaw === "undefined") {
    Tablesaw = {
        i18n: {
            modes: ['Stack', 'Swipe', 'Toggle'],
            columns: 'Col<span class=\"a11y-sm\">umn</span>s',
            columnBtnText: 'Columns',
            columnsDialogError: 'No eligible columns.',
            sort: 'Sort'
        },
        // cut the mustard
        mustard: 'querySelector' in document &&
            (!window.blackberry || window.WebKitPoint) &&
            !window.operamini
    };
}
if (!Tablesaw.config) {
    Tablesaw.config = {};
}
if (Tablesaw.mustard) {
    jQuery(document.documentElement).addClass('tablesaw-enhanced');
}

;(function ($) {
    var pluginName = "table",
        classes = {
            toolbar: "tablesaw-bar"
        },
        events = {
            create: "tablesawcreate",
            destroy: "tablesawdestroy",
            refresh: "tablesawrefresh"
        },
        defaultMode = "stack",
        initSelector = "table[data-tablesaw-mode],table[data-tablesaw-sortable]";

    var Table = function (element) {
        if (!element) {
            throw new Error("Tablesaw requires an element.");
        }

        this.table = element;
        this.$table = $(element);

        this.mode = this.$table.attr("data-tablesaw-mode") || defaultMode;

        this.init();
    };

    Table.prototype.init = function () {
        // assign an id if there is none
        if (!this.$table.attr("id")) {
            this.$table.attr("id", pluginName + "-" + Math.round(Math.random() * 10000));
        }

        this.createToolbar();

        var colstart = this._initCells();

        this.$table.trigger(events.create, [this, colstart]);
    };

    Table.prototype._initCells = function () {
        var colstart,
            thrs = this.table.querySelectorAll("thead tr"),
            self = this;

        $(thrs).each(function () {
            var coltally = 0;

            $(this).children().each(function () {
                var span = parseInt(this.getAttribute("colspan"), 10),
                    sel = ":nth-child(" + (coltally + 1) + ")";

                colstart = coltally + 1;

                if (span) {
                    for (var k = 0; k < span - 1; k++) {
                        coltally++;
                        sel += ", :nth-child(" + (coltally + 1) + ")";
                    }
                }

                // Store "cells" data on header as a reference to all cells in the same column as this TH
                this.cells = self.$table.find("tr").not(thrs[0]).not(this).children().filter(sel);
                coltally++;
            });
        });

        return colstart;
    };

    Table.prototype.refresh = function () {
        this._initCells();

        this.$table.trigger(events.refresh);
    };

    Table.prototype.createToolbar = function () {
        // Insert the toolbar
        // TODO move this into a separate component
        var $toolbar = this.$table.prev().filter('.' + classes.toolbar);
        if (!$toolbar.length) {
            $toolbar = $('<div>')
                .addClass(classes.toolbar)
                .insertBefore(this.$table);
        }
        this.$toolbar = $toolbar;

        if (this.mode) {
            this.$toolbar.addClass('mode-' + this.mode);
        }
    };

    Table.prototype.destroy = function () {
        // Don’t remove the toolbar. Some of the table features are not yet destroy-friendly.
        this.$table.prev().filter('.' + classes.toolbar).each(function () {
            this.className = this.className.replace(/\bmode\-\w*\b/gi, '');
        });

        var tableId = this.$table.attr('id');
        $(document).unbind("." + tableId);
        $(window).unbind("." + tableId);

        // other plugins
        this.$table.trigger(events.destroy, [this]);

        this.$table.removeAttr('data-tablesaw-mode');

        this.$table.removeData(pluginName);
    };

    // Collection method.
    $.fn[pluginName] = function () {
        return this.each(function () {
            var $t = $(this);

            if ($t.data(pluginName)) {
                return;
            }

            var table = new Table(this);
            $t.data(pluginName, table);
        });
    };

    $(document).on("enhance.tablesaw", function (e) {
        // Cut the mustard
        if (Tablesaw.mustard) {
            $(e.target).find(initSelector)[pluginName]();
        }
    });

}(jQuery));

;(function (win, $, undefined) {

    var classes = {
        stackTable: 'tablesaw-stack',
        cellLabels: 'tablesaw-cell-label',
        cellContentLabels: 'tablesaw-cell-content'
    };

    var data = {
        obj: 'tablesaw-stack'
    };

    var attrs = {
        labelless: 'data-tablesaw-no-labels',
        hideempty: 'data-tablesaw-hide-empty'
    };

    var Stack = function (element) {

        this.$table = $(element);

        this.labelless = this.$table.is('[' + attrs.labelless + ']');
        this.hideempty = this.$table.is('[' + attrs.hideempty + ']');

        if (!this.labelless) {
            // allHeaders references headers, plus all THs in the thead, which may include several rows, or not
            this.allHeaders = this.$table.find("th");
        }

        this.$table.data(data.obj, this);
    };

    Stack.prototype.init = function (colstart) {
        this.$table.addClass(classes.stackTable);

        if (this.labelless) {
            return;
        }

        // get headers in reverse order so that top-level headers are appended last
        var reverseHeaders = $(this.allHeaders);
        var hideempty = this.hideempty;

        // create the hide/show toggles
        reverseHeaders.each(function () {
            var $t = $(this),
                $cells = $(this.cells).filter(function () {
                    return !$(this).parent().is("[" + attrs.labelless + "]") && (!hideempty || !$(this).is(":empty"));
                }),
                hierarchyClass = $cells.not(this).filter("thead th").length && " tablesaw-cell-label-top",
                // TODO reduce coupling with sortable
                $sortableButton = $t.find(".tablesaw-sortable-btn"),
                html = $sortableButton.length ? $sortableButton.html() : $t.html();

            if (html !== "") {
                if (hierarchyClass) {
                    var iteration = parseInt($(this).attr("colspan"), 10),
                        filter = "";

                    if (iteration) {
                        filter = "td:nth-child(" + iteration + "n + " + (colstart) + ")";
                    }
                    $cells.filter(filter).prepend("<b class='" + classes.cellLabels + hierarchyClass + "'>" + html + "</b>");
                } else {
                    $cells.wrapInner("<span class='" + classes.cellContentLabels + "'></span>");
                    $cells.prepend("<b class='" + classes.cellLabels + "'>" + html + "</b>");
                }
            }
        });
    };

    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("tablesawcreate", function (e, Tablesaw, colstart) {
        if (Tablesaw.mode === 'stack') {
            var table = new Stack(Tablesaw.table);
            table.init(colstart);
        }

    });

    $(document).on("tablesawdestroy", function (e, Tablesaw) {

        if (Tablesaw.mode === 'stack') {
            $(Tablesaw.table).data(data.obj).destroy();
        }

    });

}(this, jQuery));
;(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) {
                    $(this)
                        .addClass("btn-select")
                        [pluginName]("_select", sel);
                }
                return oEl;
            },
            _select: function (sel) {
                var update = function (oEl, sel) {
                    var opts = $(sel).find("option"),
                        label, el, children;

                    opts.each(function () {
                        var opt = this;
                        if (opt.selected) {
                            label = document.createTextNode(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.nodeType === 3) {
                                oEl.replaceChild(label, el);
                            }
                        }
                    }
                };

                update(this, sel);
                $(this).bind("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);
            }

            // otherwise, init

            $(this).data(pluginName + "active", true);
            $.fn[pluginName].prototype._create.call(this);
        });
    };

    // add methods
    $.extend($.fn[pluginName].prototype, methods);

}(jQuery));
;(function (win, $, undefined) {

    var ColumnToggle = function (element) {

        this.$table = $(element);

        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-',
            // TODO duplicate class, also in tables.js
            toolbar: 'tablesaw-bar'
        };

        // Expose headers and allHeaders properties on the widget
        // headers references the THs within the first TR in the table
        this.headers = this.$table.find('tr:first > th');

        this.$table.data('tablesaw-coltoggle', this);
    };

    ColumnToggle.prototype.init = function () {

        var tableId,
            id,
            $menuButton,
            $popup,
            $menu,
            $btnContain,
            self = this;

        this.$table.addClass(this.classes.columnToggleTable);

        tableId = this.$table.attr("id");
        id = tableId + "-popup";
        $btnContain = $("<div class='" + this.classes.columnBtnContain + "'></div>");
        $menuButton = $("<a href='#" + id + "' class='btn btn-micro " + this.classes.columnBtn + "' data-popup-link>" +
            "<span>" + Tablesaw.i18n.columnBtnText + "</span></a>");
        $popup = $("<div class='dialog-table-coltoggle " + this.classes.popup + "' id='" + id + "'></div>");
        $menu = $("<div class='btn-group d-block'></div>");

        var hasNonPersistentHeaders = false;
        $(this.headers).not("td").each(function () {
            var $this = $(this),
                priority = $this.attr("data-tablesaw-priority"),
                $cells = self.$getCells(this);

            if (priority && priority !== "persist") {
                $cells.addClass(self.classes.priorityPrefix + priority);

                $("<label><input type='checkbox' checked>" + $this.text() + "</label>")
                    .appendTo($menu)
                    .children(0)
                    .data("tablesaw-header", this);

                hasNonPersistentHeaders = true;
            }
        });

        if (!hasNonPersistentHeaders) {
            $menu.append('<label>' + Tablesaw.i18n.columnsDialogError + '</label>');
        }

        $menu.appendTo($popup);

        // bind change event listeners to inputs - TODO: move to a private method?
        $menu.find('input[type="checkbox"]').on("change", function (e) {
            var checked = e.target.checked;

            self.$getCellsFromCheckbox(e.target)
                .toggleClass("tablesaw-cell-hidden", !checked)
                .toggleClass("tablesaw-cell-visible", checked);

            self.$table.trigger('tablesawcolumns');
        });

        $menuButton.appendTo($btnContain);
        $btnContain.appendTo(this.$table.prev().filter('.' + this.classes.toolbar));

        var closeTimeout;

        function openPopup() {
            $btnContain.addClass('visible');
            $menuButton.removeClass('down').addClass('up');

            $(document).unbind('click.' + tableId, closePopup);

            window.clearTimeout(closeTimeout);
            closeTimeout = window.setTimeout(function () {
                $(document).one('click.' + tableId, closePopup);
            }, 15);
        }

        function closePopup(event) {
            // Click came from inside the popup, ignore.
            if (event && $(event.target).closest("." + self.classes.popup).length) {
                return;
            }

            $(document).unbind('click.' + tableId);
            $menuButton.removeClass('up').addClass('down');
            $btnContain.removeClass('visible');
        }

        $menuButton.on("click.tablesaw", function (event) {
            event.preventDefault();

            if (!$btnContain.is(".visible")) {
                openPopup();
            } else {
                closePopup();
            }
        });

        $popup.appendTo($btnContain);

        this.$menu = $menu;

        $(window).on("resize." + tableId, function () {
            self.refreshToggle();
        });

        this.refreshToggle();
    };

    ColumnToggle.prototype.$getCells = function (th) {
        return $(th).add(th.cells);
    };

    ColumnToggle.prototype.$getCellsFromCheckbox = function (checkbox) {
        var th = $(checkbox).data("tablesaw-header");
        return this.$getCells(th);
    };

    ColumnToggle.prototype.refreshToggle = function () {
        var self = this;
        this.$menu.find("input").each(function () {
            this.checked = self.$getCellsFromCheckbox(this).eq(0).css("display") === "table-cell";
        });
    };

    ColumnToggle.prototype.refreshPriority = function () {
        var self = this;
        $(this.headers).not("td").each(function () {
            var $this = $(this),
                priority = $this.attr("data-tablesaw-priority"),
                $cells = $this.add(this.cells);

            if (priority && priority !== "persist") {
                $cells.addClass(self.classes.priorityPrefix + priority);
            }
        });
    };

    ColumnToggle.prototype.destroy = function () {
        // table toolbars, document and window .tableId events
        // removed in parent tables.js destroy method

        this.$table.removeClass(this.classes.columnToggleTable);
        this.$table.find('th, td').each(function () {
            var $cell = $(this);
            $cell.removeClass('tablesaw-cell-hidden')
                .removeClass('tablesaw-cell-visible');

            this.className = this.className.replace(/\bui\-table\-priority\-\d\b/g, '');
        });
    };

    // on tablecreate, init
    $(document).on("tablesawcreate", function (e, Tablesaw) {

        if (Tablesaw.mode === 'columntoggle') {
            var table = new ColumnToggle(Tablesaw.table);
            table.init();
        }

    });

    $(document).on("tablesawdestroy", function (e, Tablesaw) {
        if (Tablesaw.mode === 'columntoggle') {
            $(Tablesaw.table).data('tablesaw-coltoggle').destroy();
        }
    });

}(this, jQuery));
;(function (win, $, undefined) {

    $.extend(Tablesaw.config, {
        swipe: {
            horizontalThreshold: 15,
            verticalThreshold: 30
        }
    });

    function isIE8() {
        var div = document.createElement('div'),
            all = div.getElementsByTagName('i');

        div.innerHTML = '<!--[if lte IE 8]><i></i><![endif]-->';

        return !!all.length;
    }

    function createSwipeTable($table) {

        var $btns = $("<div class='tablesaw-advance'></div>"),
            $prevBtn = $("<a href='#' class='tablesaw-nav-btn btn btn-micro left' title='Previous Column'></a>").appendTo($btns),
            $nextBtn = $("<a href='#' class='tablesaw-nav-btn btn btn-micro right' title='Next Column'></a>").appendTo($btns),
            hideBtn = 'disabled',
            persistWidths = 'tablesaw-fix-persist',
            $headerCells = $table.find("thead th"),
            $headerCellsNoPersist = $headerCells.not('[data-tablesaw-priority="persist"]'),
            headerWidths = [],
            $head = $(document.head || 'head'),
            tableId = $table.attr('id'),
            // TODO switch this to an nth-child feature test
            supportsNthChild = !isIE8();

        if (!$headerCells.length) {
            throw new Error("tablesaw swipe: no header cells found. Are you using <th> inside of <thead>?");
        }

        // Calculate initial widths
        $table.css('width', 'auto');
        $headerCells.each(function () {
            headerWidths.push($(this).outerWidth());
        });
        $table.css('width', '');

        $btns.appendTo($table.prev().filter('.tablesaw-bar'));

        $table.addClass("tablesaw-swipe");

        if (!tableId) {
            tableId = 'tableswipe-' + Math.round(Math.random() * 10000);
            $table.attr('id', tableId);
        }

        function $getCells(headerCell) {
            return $(headerCell.cells).add(headerCell);
        }

        function showColumn(headerCell) {
            $getCells(headerCell).removeClass('tablesaw-cell-hidden');
        }

        function hideColumn(headerCell) {
            $getCells(headerCell).addClass('tablesaw-cell-hidden');
        }

        function persistColumn(headerCell) {
            $getCells(headerCell).addClass('tablesaw-cell-persist');
        }

        function isPersistent(headerCell) {
            return $(headerCell).is('[data-tablesaw-priority="persist"]');
        }

        function unmaintainWidths() {
            $table.removeClass(persistWidths);
            $('#' + tableId + '-persist').remove();
        }

        function maintainWidths() {
            var prefix = '#' + tableId + '.tablesaw-swipe ',
                styles = [],
                tableWidth = $table.width(),
                hash = [],
                newHash;

            $headerCells.each(function (index) {
                var width;
                if (isPersistent(this)) {
                    width = $(this).outerWidth();

                    // Only save width on non-greedy columns (take up less than 75% of table width)
                    if (width < tableWidth * 0.75) {
                        hash.push(index + '-' + width);
                        styles.push(prefix + ' .tablesaw-cell-persist:nth-child(' + (index + 1) + ') { width: ' + width + 'px; }');
                    }
                }
            });
            newHash = hash.join('_');

            $table.addClass(persistWidths);

            var $style = $('#' + tableId + '-persist');
            // If style element not yet added OR if the widths have changed
            if (!$style.length || $style.data('hash') !== newHash) {
                // Remove existing
                $style.remove();

                if (styles.length) {
                    $('<style>' + styles.join("\n") + '</style>')
                        .attr('id', tableId + '-persist')
                        .data('hash', newHash)
                        .appendTo($head);
                }
            }
        }

        function getNext() {
            var next = [],
                checkFound;

            $headerCellsNoPersist.each(function (i) {
                var $t = $(this),
                    isHidden = $t.css("display") === "none" || $t.is(".tablesaw-cell-hidden");

                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 win) && win.matchMedia(matchMedia).matches;
        }

        function fakeBreakpoints() {
            if (!matchesMedia()) {
                return;
            }

            var extraPaddingPixels = 20,
                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] + (isPersist ? 0 : extraPaddingPixels);
                sums.push(sum);

                // is persistent or is hidden
                if (isPersist || sum > containerWidth) {
                    visibleNonPersistantCount--;
                }
            });

            var needsNonPersistentColumn = visibleNonPersistantCount === 0;

            $headerCells.each(function (index) {
                if (persist[index]) {

                    // for visual box-shadow
                    persistColumn(this);
                    return;
                }

                if (sums[index] <= containerWidth || needsNonPersistentColumn) {
                    needsNonPersistentColumn = false;
                    showColumn(this);
                } else {
                    hideColumn(this);
                }
            });

            if (supportsNthChild) {
                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;
                    }
                }

                if (supportsNthChild) {
                    maintainWidths();
                }

                hideColumn($headerCellsNoPersist.get(pair[0]));
                showColumn($headerCellsNoPersist.get(pair[1]));

                $table.trigger('tablesawcolumns');
            }
        }

        $prevBtn.add($nextBtn).click(function (e) {
            advance(!!$(e.target).closest($nextBtn).length);
            e.preventDefault();
        });

        function getCoord(event, key) {
            return (event.touches || event.originalEvent.touches)[0][key];
        }

        $table
            .bind("touchstart.swipetoggle", function (e) {
                var originX = getCoord(e, 'pageX'),
                    originY = getCoord(e, 'pageY'),
                    x,
                    y;

                $(win).off("resize", fakeBreakpoints);

                $(this)
                    .bind("touchmove", function (e) {
                        x = getCoord(e, 'pageX');
                        y = getCoord(e, 'pageY');
                        var cfg = Tablesaw.config.swipe;
                        if (Math.abs(x - originX) > cfg.horizontalThreshold && Math.abs(y - originY) < cfg.verticalThreshold) {
                            e.preventDefault();
                        }
                    })
                    .bind("touchend.swipetoggle", function () {
                        var cfg = Tablesaw.config.swipe;
                        if (Math.abs(y - originY) < cfg.verticalThreshold) {
                            if (x - originX < -1 * cfg.horizontalThreshold) {
                                advance(true);
                            }
                            if (x - originX > cfg.horizontalThreshold) {
                                advance(false);
                            }
                        }

                        window.setTimeout(function () {
                            $(win).on("resize", fakeBreakpoints);
                        }, 300);
                        $(this).unbind("touchmove touchend");
                    });

            })
            .bind("tablesawcolumns.swipetoggle", function () {
                $prevBtn[canAdvance(getPrev()) ? "removeClass" : "addClass"](hideBtn);
                $nextBtn[canAdvance(getNext()) ? "removeClass" : "addClass"](hideBtn);
            })
            .bind("tablesawnext.swipetoggle", function () {
                advance(true);
            })
            .bind("tablesawprev.swipetoggle", function () {
                advance(false);
            })
            .bind("tablesawdestroy.swipetoggle", function () {
                var $t = $(this);

                $t.removeClass('tablesaw-swipe');
                $t.prev().filter('.tablesaw-bar').find('.tablesaw-advance').remove();
                $(win).off("resize", fakeBreakpoints);

                $t.unbind(".swipetoggle");
            });

        fakeBreakpoints();
        $(win).on("resize", fakeBreakpoints);
    }


    // on tablecreate, init
    $(document).on("tablesawcreate", function (e, Tablesaw) {

        if (Tablesaw.mode === 'swipe') {
            createSwipeTable(Tablesaw.$table);
        }

    });

}(this, jQuery));

;(function ($) {
    function getSortValue(cell) {
        return $.map(cell.childNodes, function (el) {
            var $el = $(el);
            if ($el.is('input, select')) {
                return $el.val();
            } else if ($el.hasClass('tablesaw-cell-label')) {
                return;
            }
            return $.trim($el.text());
        }).join('');
    }

    var pluginName = "tablesaw-sortable",
        initSelector = "table[data-" + pluginName + "]",
        sortableSwitchSelector = "[data-" + pluginName + "-switch]",
        attrs = {
            defaultCol: "data-tablesaw-sortable-default-col",
            numericCol: "data-tablesaw-sortable-numeric"
        },
        classes = {
            head: pluginName + "-head",
            ascend: pluginName + "-ascending",
            descend: pluginName + "-descending",
            switcher: pluginName + "-switch",
            tableToolbar: 'tablesaw-toolbar',
            sortButton: pluginName + "-btn"
        },
        methods = {
            _create: function (o) {
                return $(this).each(function () {
                    var init = $(this).data("init" + pluginName);
                    if (init) {
                        return false;
                    }
                    $(this)
                        .data("init" + pluginName, true)
                        .trigger("beforecreate." + pluginName)
                        [pluginName]("_init", o)
                        .trigger("create." + pluginName);
                });
            },
            _init: function () {
                var el = $(this),
                    heads,
                    $switcher;

                var addClassToTable = function () {
                        el.addClass(pluginName);
                    },
                    addClassToHeads = function (h) {
                        $.each(h, function (i, v) {
                            $(v).addClass(classes.head);
                        });
                    },
                    makeHeadsActionable = function (h, fn) {
                        $.each(h, function (i, v) {
                            var b = $("<button class='" + classes.sortButton + "'/>");
                            b.bind("click", {col: v}, fn);
                            $(v).wrapInner(b);
                        });
                    },
                    clearOthers = function (sibs) {
                        $.each(sibs, function (i, v) {
                            var col = $(v);
                            col.removeAttr(attrs.defaultCol);
                            col.removeClass(classes.ascend);
                            col.removeClass(classes.descend);
                        });
                    },
                    headsOnAction = function (e) {
                        if ($(e.target).is('a[href]')) {
                            return;
                        }

                        e.stopPropagation();
                        var head = $(this).parent(),
                            v = e.data.col,
                            newSortValue = heads.index(head);

                        clearOthers(head.siblings());
                        if (head.hasClass(classes.descend)) {
                            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();
                    },
                    handleDefault = function (heads) {
                        $.each(heads, function (idx, el) {
                            var $el = $(el);
                            if ($el.is("[" + attrs.defaultCol + "]")) {
                                if (!$el.hasClass(classes.descend)) {
                                    $el.addClass(classes.ascend);
                                }
                            }
                        });
                    },
                    addSwitcher = function (heads) {
                        $switcher = $('<div>').addClass(classes.switcher).addClass(classes.tableToolbar).html(function () {
                            var html = ['<label>' + Tablesaw.i18n.sort + ':'];

                            html.push('<span class="btn btn-small">&#160;<select>');
                            heads.each(function (j) {
                                var $t = $(this);
                                var isDefaultCol = $t.is("[" + attrs.defaultCol + "]");
                                var isDescending = $t.hasClass(classes.descend);

                                var hasNumericAttribute = $t.is('[data-sortable-numeric]');
                                var numericCount = 0;
                                // Check only the first four rows to see if the column is numbers.
                                var numericCountMax = 5;

                                $(this.cells).slice(0, numericCountMax).each(function () {
                                    if (!isNaN(parseInt(getSortValue(this), 10))) {
                                        numericCount++;
                                    }
                                });
                                var isNumeric = numericCount === numericCountMax;
                                if (!hasNumericAttribute) {
                                    $t.attr("data-sortable-numeric", isNumeric ? "" : "false");
                                }

                                html.push('<option' + (isDefaultCol && !isDescending ? ' selected' : '') + ' value="' + j + '_asc">' + $t.text() + ' ' + (isNumeric ? '&#x2191;' : '(A-Z)') + '</option>');
                                html.push('<option' + (isDefaultCol && isDescending ? ' selected' : '') + ' value="' + j + '_desc">' + $t.text() + ' ' + (isNumeric ? '&#x2193;' : '(Z-A)') + '</option>');
                            });
                            html.push('</select></span></label>');

                            return html.join('');
                        });

                        var $toolbar = el.prev().filter('.tablesaw-bar'),
                            $firstChild = $toolbar.children().eq(0);

                        if ($firstChild.length) {
                            $switcher.insertBefore($firstChild);
                        } else {
                            $switcher.appendTo($toolbar);
                        }
                        $switcher.find('.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');
                        });
                    };

                addClassToTable();
                heads = el.find("thead th[data-" + pluginName + "-col]");
                addClassToHeads(heads);
                makeHeadsActionable(heads, headsOnAction);
                handleDefault(heads);

                if (el.is(sortableSwitchSelector)) {
                    addSwitcher(heads, el.find('tbody tr:nth-child(-n+3)'));
                }
            },
            getColumnNumber: function (col) {
                return $(col).prevAll().length;
            },
            getTableRows: function () {
                return $(this).find("tbody tr");
            },
            sortRows: function (rows, colNum, ascending, col) {
                var cells, fn, sorted;
                var getCells = function (rows) {
                        var cells = [];
                        $.each(rows, function (i, r) {
                            var element = $(r).children().get(colNum);
                            cells.push({
                                element: element,
                                cell: getSortValue(element),
                                rowNum: i
                            });
                        });
                        return cells;
                    },
                    getSortFxn = function (ascending, forceNumeric) {
                        var fn,
                            regex = /[^\-\+\d\.]/g;
                        if (ascending) {
                            fn = function (a, b) {
                                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 (forceNumeric) {
                                    return parseFloat(b.cell.replace(regex, '')) - parseFloat(a.cell.replace(regex, ''));
                                } else {
                                    return a.cell.toLowerCase() < b.cell.toLowerCase() ? 1 : -1;
                                }
                            };
                        }
                        return fn;
                    },
                    applyToRows = function (sorted, rows) {
                        var newRows = [], i, l, cur;
                        for (i = 0, l = sorted.length; i < l; i++) {
                            cur = sorted[i].rowNum;
                            newRows.push(rows[cur]);
                        }
                        return newRows;
                    };

                cells = getCells(rows);
                var customFn = $(col).data('tablesaw-sort');
                fn = (customFn && typeof customFn === "function" ? customFn(ascending) : false) ||
                    getSortFxn(ascending, $(col).is('[data-sortable-numeric]') && !$(col).is('[data-sortable-numeric="false"]'));
                sorted = cells.sort(fn);
                rows = applyToRows(sorted, rows);
                return rows;
            },
            replaceTableRows: function (rows) {
                var el = $(this),
                    body = el.find("tbody");
                body.html(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), colNum, rows;

                colNum = el[pluginName]("getColumnNumber", col);
                rows = el[pluginName]("getTableRows");
                rows = el[pluginName]("sortRows", rows, colNum, ascending, col);
                el[pluginName]("replaceTableRows", rows);
                el[pluginName]("makeColDefault", col, ascending);
            }
        };

    // 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 + "data")) {
            $(this).data(pluginName + "active", true);
            $.fn[pluginName].prototype._create.call(this, arrg);
        }
        return $(this);
    };
    // add methods
    $.extend($.fn[pluginName].prototype, methods);

    $(document).on("tablesawcreate", function (e, Tablesaw) {
        if (Tablesaw.$table.is(initSelector)) {
            Tablesaw.$table[pluginName]();
        }
    });

}(jQuery));

;(function (win, $, undefined) {

    var MM = {
        attr: {
            init: 'data-tablesaw-minimap'
        }
    };

    function createMiniMap($table) {

        var $btns = $('<div class="tablesaw-advance minimap">'),
            $dotNav = $('<ul class="tablesaw-advance-dots">').appendTo($btns),
            hideDot = 'tablesaw-advance-dots-hide',
            $headerCells = $table.find('thead th');

        // populate dots
        $headerCells.each(function () {
            $dotNav.append('<li><i></i></li>');
        });

        $btns.appendTo($table.prev().filter('.tablesaw-bar'));

        function showMinimap($table) {
            var mq = $table.attr(MM.attr.init);
            return !mq || win.matchMedia && win.matchMedia(mq).matches;
        }

        function showHideNav() {
            if (!showMinimap($table)) {
                $btns.hide();
                return;
            }
            $btns.show();

            // show/hide dots
            var dots = $dotNav.find("li").removeClass(hideDot);
            $table.find("thead th").each(function (i) {
                if ($(this).css("display") === "none") {
                    dots.eq(i).addClass(hideDot);
                }
            });
        }

        // run on init and resize
        showHideNav();
        $(win).on("resize", showHideNav);


        $table
            .bind("tablesawcolumns.minimap", function () {
                showHideNav();
            })
            .bind("tablesawdestroy.minimap", function () {
                var $t = $(this);

                $t.prev().filter('.tablesaw-bar').find('.tablesaw-advance').remove();
                $(win).off("resize", showHideNav);

                $t.unbind(".minimap");
            });
    }


    // on tablecreate, init
    $(document).on("tablesawcreate", function (e, Tablesaw) {

        if ((Tablesaw.mode === 'swipe' || Tablesaw.mode === 'columntoggle') && Tablesaw.$table.is('[ ' + MM.attr.init + ']')) {
            createMiniMap(Tablesaw.$table);
        }

    });

}(this, jQuery));

;(function (win, $) {

    var S = {
        selectors: {
            init: 'table[data-tablesaw-mode-switch]'
        },
        attributes: {
            excludeMode: 'data-tablesaw-mode-exclude'
        },
        classes: {
            main: 'tablesaw-modeswitch',
            toolbar: 'tablesaw-toolbar'
        },
        modes: ['stack', 'swipe', 'columntoggle'],
        init: function (table) {
            var $table = $(table),
                ignoreMode = $table.attr(S.attributes.excludeMode),
                $toolbar = $table.prev().filter('.tablesaw-bar'),
                modeVal = '',
                $switcher = $('<div>').addClass(S.classes.main + ' ' + S.classes.toolbar).html(function () {
                    var html = ['<label>' + Tablesaw.i18n.columns + ':'],
                        dataMode = $table.attr('data-tablesaw-mode'),
                        isSelected;

                    html.push('<span class="btn btn-small">&#160;<select>');
                    for (var j = 0, k = S.modes.length; j < k; j++) {
                        if (ignoreMode && ignoreMode.toLowerCase() === S.modes[j]) {
                            continue;
                        }

                        isSelected = dataMode === S.modes[j];

                        if (isSelected) {
                            modeVal = S.modes[j];
                        }

                        html.push('<option' +
                            (isSelected ? ' selected' : '') +
                            ' value="' + S.modes[j] + '">' + Tablesaw.i18n.modes[j] + '</option>');
                    }
                    html.push('</select></span></label>');

                    return html.join('');
                });

            var $otherToolbarItems = $toolbar.find('.tablesaw-advance').eq(0);
            if ($otherToolbarItems.length) {
                $switcher.insertBefore($otherToolbarItems);
            } else {
                $switcher.appendTo($toolbar);
            }

            $switcher.find('.btn').tablesawbtn();
            $switcher.find('select').bind('change', S.onModeChange);
        },
        onModeChange: function () {
            var $t = $(this),
                $switcher = $t.closest('.' + S.classes.main),
                $table = $t.closest('.tablesaw-bar').nextUntil($table).eq(0),
                val = $t.val();

            $switcher.remove();
            $table.data('table').destroy();

            $table.attr('data-tablesaw-mode', val);
            $table.table();
        }
    };

    $(win.document).on("tablesawcreate", function (e, Tablesaw) {
        if (Tablesaw.$table.is(S.selectors.init)) {
            S.init(Tablesaw.table);
        }
    });

})(this, jQuery);
