diff --git a/UI/Content/tablesorter.bootstrap.css b/UI/Content/tablesorter.bootstrap.css
deleted file mode 100644
index 5f199f591..000000000
--- a/UI/Content/tablesorter.bootstrap.css
+++ /dev/null
@@ -1,136 +0,0 @@
-/*************
-  Bootstrap theme
- *************/
-/* jQuery Bootstrap Theme */
-.tablesorter-bootstrap {
-    width: 100%;
-}
-
-.tablesorter-bootstrap .tablesorter-header,
-.tablesorter-bootstrap tfoot th,
-.tablesorter-bootstrap tfoot td {
-    font: bold 14px/20px Arial, Sans-serif;
-    position: relative;
-    padding: 8px;
-    margin: 0 0 18px;
-    list-style: none;
-    background-color: #fbfbfb;
-    background-image: -moz-linear-gradient(top, white, #efefef);
-    background-image: -ms-linear-gradient(top, white, #efefef);
-    background-image: -webkit-gradient(linear, 0 0, 0 100%, from(white), to(#efefef));
-    background-image: -webkit-linear-gradient(top, white, #efefef);
-    background-image: -o-linear-gradient(top, white, #efefef);
-    background-image: linear-gradient(to bottom, white, #efefef);
-    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#efefef', GradientType=0);
-    background-repeat: repeat-x;
-    -webkit-box-shadow: inset 0 1px 0 white;
-    -moz-box-shadow: inset 0 1px 0 #ffffff;
-    box-shadow: inset 0 1px 0 white;
-}
-
-.tablesorter-bootstrap .tablesorter-header {
-    cursor: pointer;
-}
-
-.tablesorter-bootstrap .tablesorter-header-inner {
-    position: relative;
-    padding: 4px 18px 4px 4px;
-}
-
-/* bootstrap uses <i> for icons */
-.tablesorter-bootstrap .tablesorter-header i {
-    position: absolute;
-    right: 2px;
-    top: 50%;
-    margin-top: -7px; /* half the icon height; older IE doesn't like this */
-    width: 14px;
-    height: 14px;
-    background-repeat: no-repeat;
-    line-height: 14px;
-    display: inline-block;
-}
-
-.tablesorter-bootstrap .bootstrap-icon-unsorted {
-    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAOCAYAAAD5YeaVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAWVJREFUeNqUUL9Lw2AUTGP8mqGlpBQkNeCSRcckEBcHq1jImMElToKuDvpHFMGhU0BQcHBwLji6CE1B4uB/INQsDi4d2jQ/fPeZxo764OV6915f7lLJ81xot9tCURXqdVEUr7IsO6ffH9Q5BlEUCaLwWxWqTcbYnaIoh0Dw4gAvcWlxq1qt9hqNxg6hUGAP+uIPUrGs0qXLer2+v/pTX6QpxLtkc2U2m53ACb8sSdIDXerSEms2m6+DweAICA4d89KGbduf9MpEVdXQ9/2LVqv1CASHjjn3iq/x1xKFfxQPqGnada1W86bT6SiO42OS3qk3KPStLMvbk8nkfjwen/LLuq6blFymMB0KdUPSGhAcOualjX6/f0bCiC7NaWGPQr0BwaFjzn0gYJqmLAiCA8/zni3LmhuGkQPBoWPOPwQeaPIqD4fDruu6L6Zp5kBw6IudchmdJAkLw3DXcZwnIPjy/FuAAQCiqqWWCAFKcwAAAABJRU5ErkJggg==);
-}
-
-/* since bootstrap (table-striped) uses nth-child(), we just use this to add a zebra stripe color */
-.tablesorter-bootstrap tr.odd td {
-    background-color: #f9f9f9;
-}
-
-.tablesorter-bootstrap tbody > .odd:hover > td,
-.tablesorter-bootstrap tbody > .even:hover > td {
-    background-color: #f5f5f5;
-}
-
-.tablesorter-bootstrap tr.even td {
-    background-color: #ffffff;
-}
-
-/* processing icon */
-.tablesorter-bootstrap .tablesorter-processing {
-    background-image: url('data:image/gif;base64,R0lGODlhFAAUAKEAAO7u7lpaWgAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgACACwAAAAAFAAUAAACQZRvoIDtu1wLQUAlqKTVxqwhXIiBnDg6Y4eyx4lKW5XK7wrLeK3vbq8J2W4T4e1nMhpWrZCTt3xKZ8kgsggdJmUFACH5BAEKAAIALAcAAAALAAcAAAIUVB6ii7jajgCAuUmtovxtXnmdUAAAIfkEAQoAAgAsDQACAAcACwAAAhRUIpmHy/3gUVQAQO9NetuugCFWAAAh+QQBCgACACwNAAcABwALAAACE5QVcZjKbVo6ck2AF95m5/6BSwEAIfkEAQoAAgAsBwANAAsABwAAAhOUH3kr6QaAcSrGWe1VQl+mMUIBACH5BAEKAAIALAIADQALAAcAAAIUlICmh7ncTAgqijkruDiv7n2YUAAAIfkEAQoAAgAsAAAHAAcACwAAAhQUIGmHyedehIoqFXLKfPOAaZdWAAAh+QQFCgACACwAAAIABwALAAACFJQFcJiXb15zLYRl7cla8OtlGGgUADs=');
-    position: absolute;
-    z-index: 1000;
-}
-
-/* filter widget */
-.tablesorter-bootstrap .tablesorter-filter-row .tablesorter-filter {
-    width: 98%;
-    height: inherit;
-    margin: 0 auto;
-    padding: 4px 6px;
-    background-color: #ffffff;
-    color: #333333;
-    -webkit-box-sizing: border-box;
-    -moz-box-sizing: border-box;
-    box-sizing: border-box;
-    -webkit-transition: height 0.1s ease;
-    -moz-transition: height 0.1s ease;
-    -o-transition: height 0.1s ease;
-    transition: height 0.1s ease;
-}
-
-.tablesorter-bootstrap .tablesorter-filter-row td {
-    background: #eeeeee;
-    line-height: normal;
-    text-align: center;
-    padding: 4px 6px;
-    vertical-align: middle;
-    -webkit-transition: line-height 0.1s ease;
-    -moz-transition: line-height 0.1s ease;
-    -o-transition: line-height 0.1s ease;
-    transition: line-height 0.1s ease;
-}
-
-/* hidden filter row */
-.tablesorter-bootstrap .tablesorter-filter-row.hideme td {
-    padding: 2px; /* change this to modify the thickness of the closed border row */
-    margin: 0;
-    line-height: 0;
-}
-
-.tablesorter-bootstrap .tablesorter-filter-row.hideme .tablesorter-filter {
-    height: 1px;
-    min-height: 0;
-    border: 0;
-    padding: 0;
-    margin: 0;
-    /* don't use visibility: hidden because it disables tabbing */
-    opacity: 0;
-    filter: alpha(opacity=0);
-}
-
-/* pager plugin */
-.tablesorter-bootstrap .tablesorter-pager select {
-    padding: 4px 6px;
-}
-
-.tablesorter-bootstrap .tablesorter-pager .pagedisplay {
-    border: 0;
-}
-
-th {
-    background-repeat: no-repeat;
-}
diff --git a/UI/Content/tablesorter.pager.css b/UI/Content/tablesorter.pager.css
deleted file mode 100644
index 3d94e9a02..000000000
--- a/UI/Content/tablesorter.pager.css
+++ /dev/null
@@ -1,17 +0,0 @@
-/*** css used when "updateArrows" option is true ***/
-/* the pager itself gets a disabled class when the number of rows is less than the size */
-.tablesorter-pager.disabled {
-    display: none;
-}
-
-/* hide or fade out pager arrows when the first or last row is visible */
-.tablesorter-pager .disabled {
-    /* visibility: hidden */
-    opacity: 0.5;
-    filter: alpha(opacity=50);
-    cursor: default;
-}
-
-.pagedisplay-container {
-    padding-top: 5px;
-}
\ No newline at end of file
diff --git a/UI/Index.html b/UI/Index.html
index 82b50d3e7..f27193482 100644
--- a/UI/Index.html
+++ b/UI/Index.html
@@ -12,8 +12,6 @@
     <link href="/content/fullcalendar.css" rel='stylesheet' type='text/css'>
     <link href="/content/backbone.backgrid.paginator.css" rel='stylesheet' type='text/css'/>
     <link href="/content/backbone.backgrid.filter.css" rel='stylesheet' type='text/css'/>
-    <link href="/content/tablesorter.bootstrap.css" rel='stylesheet' type='text/css'/>
-    <link href="/content/tablesorter.pager.css" rel='stylesheet' type='text/css'/>
     <link href="/content/bootstrap.toggle-switch.css" rel='stylesheet' type='text/css'/>
     <link href="/AddSeries/addseries.css" rel='stylesheet' type='text/css'/>
     <link href="/content/menu.css" rel='stylesheet' type='text/css'/>
@@ -92,9 +90,6 @@
 <script src="/JsLibraries/backbone.backgrid.js"></script>
 <script src="/JsLibraries/backbone.backgrid.paginator.js"></script>
 <script src="/JsLibraries/backbone.backgrid.filter.js"></script>
-<script src="/JsLibraries/jquery.tablesorter.js"></script>
-<script src="/JsLibraries/jquery.tablesorter.bootstrap.js"></script>
-<script src="/JsLibraries/jquery.tablesorter.pager.js"></script>
 <script src="/JsLibraries/jquery.backstretch.js"></script>
 <script src="/JsLibraries/sugar.js"></script>
 <script src="/JsLibraries/fullcalendar.js"></script>
@@ -105,7 +100,6 @@
 <script src="/Mixins/backbone.marionette.templates.js"></script>
 <script src="/Mixins/backbone.marionette.region.mixin.js"></script>
 <script src="/Mixins/backbone.ajax.js"></script>
-<script src="/Mixins/tablesorter.extensions.js"></script>
 <script src="/Mixins/spoon.js"></script>
 <script src="/Mixins/backbone.modelbinder.mixin.js"></script>
 <script src="/Mixins/backbone.backgrid.mixin.js"></script>
diff --git a/UI/JsLibraries/jquery.tablesorter.bootstrap.js b/UI/JsLibraries/jquery.tablesorter.bootstrap.js
deleted file mode 100644
index 5dae16a44..000000000
--- a/UI/JsLibraries/jquery.tablesorter.bootstrap.js
+++ /dev/null
@@ -1,981 +0,0 @@
-/*! tableSorter 2.4+ widgets - updated 1/29/2013
- *
- * Column Styles
- * Column Filters
- * Column Resizing
- * Sticky Header
- * UI Theme (generalized)
- * Save Sort
- * ["zebra", "uitheme", "stickyHeaders", "filter", "columns"]
- */
-/*jshint browser:true, jquery:true, unused:false, loopfunc:true */
-/*global jQuery: false, localStorage: false, navigator: false */
-;(function($){
-    "use strict";
-    $.tablesorter = $.tablesorter || {};
-
-    $.tablesorter.themes = {
-        "bootstrap" : {
-            table      : 'table table-bordered table-striped',
-            header     : 'bootstrap-header', // give the header a gradient background
-            footerRow  : '',
-            footerCells: '',
-            icons      : '', // add "icon-white" to make them white; this icon class is added to the <i> in the header
-            sortNone   : 'bootstrap-icon-unsorted',
-            sortAsc    : 'icon-chevron-up',
-            sortDesc   : 'icon-chevron-down',
-            active     : '', // applied when column is sorted
-            hover      : '', // use custom css here - bootstrap class may not override it
-            filterRow  : '', // filter row class
-            even       : '', // even row zebra striping
-            odd        : ''  // odd row zebra striping
-        },
-        "jui" : {
-            table      : 'ui-widget ui-widget-content ui-corner-all', // table classes
-            header     : 'ui-widget-header ui-corner-all ui-state-default', // header classes
-            footerRow  : '',
-            footerCells: '',
-            icons      : 'ui-icon', // icon class added to the <i> in the header
-            sortNone   : 'ui-icon-carat-2-n-s',
-            sortAsc    : 'ui-icon-carat-1-n',
-            sortDesc   : 'ui-icon-carat-1-s',
-            active     : 'ui-state-active', // applied when column is sorted
-            hover      : 'ui-state-hover',  // hover class
-            filterRow  : '',
-            even       : 'ui-widget-content', // even row zebra striping
-            odd        : 'ui-state-default'   // odd row zebra striping
-        }
-    };
-
-    // *** Store data in local storage, with a cookie fallback ***
-    /* IE7 needs JSON library for JSON.stringify - (http://caniuse.com/#search=json)
-     if you need it, then include https://github.com/douglascrockford/JSON-js
-
-     $.parseJSON is not available is jQuery versions older than 1.4.1, using older
-     versions will only allow storing information for one page at a time
-
-     // *** Save data (JSON format only) ***
-     // val must be valid JSON... use http://jsonlint.com/ to ensure it is valid
-     var val = { "mywidget" : "data1" }; // valid JSON uses double quotes
-     // $.tablesorter.storage(table, key, val);
-     $.tablesorter.storage(table, 'tablesorter-mywidget', val);
-
-     // *** Get data: $.tablesorter.storage(table, key); ***
-     v = $.tablesorter.storage(table, 'tablesorter-mywidget');
-     // val may be empty, so also check for your data
-     val = (v && v.hasOwnProperty('mywidget')) ? v.mywidget : '';
-     alert(val); // "data1" if saved, or "" if not
-     */
-    $.tablesorter.storage = function(table, key, val){
-        var d, k, ls = false, v = {},
-            id = table.id || $('.tablesorter').index( $(table) ),
-            url = window.location.pathname;
-        try { ls = !!(localStorage.getItem); } catch(e) {}
-        // *** get val ***
-        if ($.parseJSON){
-            if (ls){
-                v = $.parseJSON(localStorage[key]) || {};
-            } else {
-                k = document.cookie.split(/[;\s|=]/); // cookie
-                d = $.inArray(key, k) + 1; // add one to get from the key to the value
-                v = (d !== 0) ? $.parseJSON(k[d]) || {} : {};
-            }
-        }
-        // allow val to be an empty string to
-        if ((val || val === '') && window.JSON && JSON.hasOwnProperty('stringify')){
-            // add unique identifiers = url pathname > table ID/index on page > data
-            if (!v[url]) {
-                v[url] = {};
-            }
-            v[url][id] = val;
-            // *** set val ***
-            if (ls){
-                localStorage[key] = JSON.stringify(v);
-            } else {
-                d = new Date();
-                d.setTime(d.getTime() + (31536e+6)); // 365 days
-                document.cookie = key + '=' + (JSON.stringify(v)).replace(/\"/g,'\"') + '; expires=' + d.toGMTString() + '; path=/';
-            }
-        } else {
-            return v && v[url] ? v[url][id] : {};
-        }
-    };
-
-    // Widget: General UI theme
-    // "uitheme" option in "widgetOptions"
-    // **************************
-    $.tablesorter.addWidget({
-                                id: "uitheme",
-                                format: function(table){
-                                    var time, klass, $el, $tar,
-                                        t = $.tablesorter.themes,
-                                        $t = $(table),
-                                        c = table.config,
-                                        wo = c.widgetOptions,
-                                        theme = c.theme !== 'default' ? c.theme : wo.uitheme || 'jui', // default uitheme is 'jui'
-                                        o = t[ t[theme] ? theme : t[wo.uitheme] ? wo.uitheme : 'jui'],
-                                        $h = $(c.headerList),
-                                        sh = 'tr.' + (wo.stickyHeaders || 'tablesorter-stickyHeader'),
-                                        rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
-                                    if (c.debug) { time = new Date(); }
-                                    if (!$t.hasClass('tablesorter-' + theme) || c.theme === theme || !table.hasInitialized){
-                                        // update zebra stripes
-                                        if (o.even !== '') { wo.zebra[0] += ' ' + o.even; }
-                                        if (o.odd !== '') { wo.zebra[1] += ' ' + o.odd; }
-                                        // add table/footer class names
-                                        t = $t
-                                            // remove other selected themes; use widgetOptions.theme_remove
-                                            .removeClass( c.theme === '' ? '' : 'tablesorter-' + c.theme )
-                                            .addClass('tablesorter-' + theme + ' ' + o.table) // add theme widget class name
-                                            .find('tfoot');
-                                        if (t.length) {
-                                            t
-                                                .find('tr').addClass(o.footerRow)
-                                                .children('th, td').addClass(o.footerCells);
-                                        }
-                                        // update header classes
-                                        $h
-                                            .addClass(o.header)
-                                            .filter(':not(.sorter-false)')
-                                            .hover(function(){
-                                                       $(this).addClass(o.hover);
-                                                   }, function(){
-                                                       $(this).removeClass(o.hover);
-                                                   });
-                                        if (!$h.find('.tablesorter-wrapper').length) {
-                                            // Firefox needs this inner div to position the resizer correctly
-                                            $h.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
-                                        }
-                                        if (c.cssIcon){
-                                            // if c.cssIcon is '', then no <i> is added to the header
-                                            $h.find('.' + c.cssIcon).addClass(o.icons);
-                                        }
-                                        if ($t.hasClass('hasFilters')){
-                                            $h.find('.tablesorter-filter-row').addClass(o.filterRow);
-                                        }
-                                    }
-                                    $.each($h, function(i){
-                                        $el = $(this);
-                                        $tar = (c.cssIcon) ? $el.find('.' + c.cssIcon) : $el;
-                                        if (this.sortDisabled){
-                                            // no sort arrows for disabled columns!
-                                            $el.removeClass(rmv);
-                                            $tar.removeClass(rmv + ' tablesorter-icon ' + o.icons);
-                                        } else {
-                                            t = ($t.hasClass('hasStickyHeaders')) ? $t.find(sh).find('th').eq(i).add($el) : $el;
-                                            klass = ($el.hasClass(c.cssAsc)) ? o.sortAsc : ($el.hasClass(c.cssDesc)) ? o.sortDesc : $el.hasClass(c.cssHeader) ? o.sortNone : '';
-                                            $el[klass === o.sortNone ? 'removeClass' : 'addClass'](o.active);
-                                            $tar.removeClass(rmv).addClass(klass);
-                                        }
-                                    });
-                                    if (c.debug){
-                                        $.tablesorter.benchmark("Applying " + theme + " theme", time);
-                                    }
-                                },
-                                remove: function(table, c, wo){
-                                    var $t = $(table),
-                                        theme = typeof wo.uitheme === 'object' ? 'jui' : wo.uitheme || 'jui',
-                                        o = typeof wo.uitheme === 'object' ? wo.uitheme : $.tablesorter.themes[ $.tablesorter.themes.hasOwnProperty(theme) ? theme : 'jui'],
-                                        $h = $t.children('thead').children(),
-                                        rmv = o.sortNone + ' ' + o.sortDesc + ' ' + o.sortAsc;
-                                    $t
-                                        .removeClass('tablesorter-' + theme + ' ' + o.table)
-                                        .find(c.cssHeader).removeClass(o.header);
-                                    $h
-                                        .unbind('mouseenter mouseleave') // remove hover
-                                        .removeClass(o.hover + ' ' + rmv + ' ' + o.active)
-                                        .find('.tablesorter-filter-row').removeClass(o.filterRow);
-                                    $h.find('.tablesorter-icon').removeClass(o.icons);
-                                }
-                            });
-
-    // Widget: Column styles
-    // "columns", "columns_thead" (true) and
-    // "columns_tfoot" (true) options in "widgetOptions"
-    // **************************
-    $.tablesorter.addWidget({
-                                id: "columns",
-                                format: function(table){
-                                    var $tb, $tr, $td, $t, time, last, rmv, i, k, l,
-                                        $tbl = $(table),
-                                        c = table.config,
-                                        wo = c.widgetOptions,
-                                        b = c.$tbodies,
-                                        list = c.sortList,
-                                        len = list.length,
-                                        css = [ "primary", "secondary", "tertiary" ]; // default options
-                                    // keep backwards compatibility, for now
-                                    css = (c.widgetColumns && c.widgetColumns.hasOwnProperty('css')) ? c.widgetColumns.css || css :
-                                        (wo && wo.hasOwnProperty('columns')) ? wo.columns || css : css;
-                                    last = css.length-1;
-                                    rmv = css.join(' ');
-                                    if (c.debug){
-                                        time = new Date();
-                                    }
-                                    // check if there is a sort (on initialization there may not be one)
-                                    for (k = 0; k < b.length; k++ ){
-                                        $tb = $.tablesorter.processTbody(table, b.eq(k), true); // detach tbody
-                                        $tr = $tb.children('tr');
-                                        l = $tr.length;
-                                        // loop through the visible rows
-                                        $tr.each(function(){
-                                            $t = $(this);
-                                            if (this.style.display !== 'none'){
-                                                // remove all columns class names
-                                                $td = $t.children().removeClass(rmv);
-                                                // add appropriate column class names
-                                                if (list && list[0]){
-                                                    // primary sort column class
-                                                    $td.eq(list[0][0]).addClass(css[0]);
-                                                    if (len > 1){
-                                                        for (i = 1; i < len; i++){
-                                                            // secondary, tertiary, etc sort column classes
-                                                            $td.eq(list[i][0]).addClass( css[i] || css[last] );
-                                                        }
-                                                    }
-                                                }
-                                            }
-                                        });
-                                        $.tablesorter.processTbody(table, $tb, false);
-                                    }
-                                    // add classes to thead and tfoot
-                                    $tr = wo.columns_thead !== false ? 'thead tr' : '';
-                                    if (wo.columns_tfoot !== false) {
-                                        $tr += ($tr === '' ? '' : ',') + 'tfoot tr';
-                                    }
-                                    if ($tr.length) {
-                                        $t = $tbl.find($tr).children().removeClass(rmv);
-                                        if (list && list[0]){
-                                            // primary sort column class
-                                            $t.filter('[data-column="' + list[0][0] + '"]').addClass(css[0]);
-                                            if (len > 1){
-                                                for (i = 1; i < len; i++){
-                                                    // secondary, tertiary, etc sort column classes
-                                                    $t.filter('[data-column="' + list[i][0] + '"]').addClass(css[i] || css[last]);
-                                                }
-                                            }
-                                        }
-                                    }
-                                    if (c.debug){
-                                        $.tablesorter.benchmark("Applying Columns widget", time);
-                                    }
-                                },
-                                remove: function(table, c, wo){
-                                    var k, $tb,
-                                        b = c.$tbodies,
-                                        rmv = (c.widgetOptions.columns || [ "primary", "secondary", "tertiary" ]).join(' ');
-                                    c.$headers.removeClass(rmv);
-                                    $(table).children('tfoot').children('tr').children('th, td').removeClass(rmv);
-                                    for (k = 0; k < b.length; k++ ){
-                                        $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody
-                                        $tb.children('tr').each(function(){
-                                            $(this).children().removeClass(rmv);
-                                        });
-                                        $.tablesorter.processTbody(table, $tb, false); // restore tbody
-                                    }
-                                }
-                            });
-
-    /* Widget: filter
-     widgetOptions:
-     filter_childRows     : false  // if true, filter includes child row content in the search
-     filter_columnFilters : true   // if true, a filter will be added to the top of each table column
-     filter_cssFilter     : 'tablesorter-filter' // css class name added to the filter row & each input in the row
-     filter_functions     : null   // add custom filter functions using this option
-     filter_hideFilters   : false  // collapse filter row when mouse leaves the area
-     filter_ignoreCase    : true   // if true, make all searches case-insensitive
-     filter_reset         : null   // jQuery selector string of an element used to reset the filters
-     filter_searchDelay   : 300    // typing delay in milliseconds before starting a search
-     filter_startsWith    : false  // if true, filter start from the beginning of the cell contents
-     filter_useParsedData : false  // filter all data using parsed content
-     filter_serversideFiltering : false // if true, server-side filtering should be performed because client-side filtering will be disabled, but the ui and events will still be used.
-     **************************/
-    $.tablesorter.addWidget({
-                                id: "filter",
-                                format: function(table){
-                                    if (table.config.parsers && !$(table).hasClass('hasFilters')){
-                                        var i, j, k, l, val, ff, x, xi, st, sel, str,
-                                            ft, ft2, $th, rg, s, t, dis, col,
-                                            last = '', // save last filter search
-                                            ts = $.tablesorter,
-                                            c = table.config,
-                                            $ths = $(c.headerList),
-                                            wo = c.widgetOptions,
-                                            css = wo.filter_cssFilter || 'tablesorter-filter',
-                                            $t = $(table).addClass('hasFilters'),
-                                            b = c.$tbodies,
-                                            cols = c.parsers.length,
-                                            reg = [ // regex used in filter "check" functions
-                                                /^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/, // 0 = regex to test for regex
-                                                new RegExp(c.cssChildRow), // 1 = child row
-                                                /undefined|number/, // 2 = check type
-                                                /(^[\"|\'|=])|([\"|\'|=]$)/, // 3 = exact match
-                                                /[\"\'=]/g, // 4 = replace exact match flags
-                                                /[^\w,. \-()]/g, // 5 = replace non-digits (from digit & currency parser)
-                                                /[<>=]/g // 6 = replace operators
-                                            ],
-                                            parsed = $ths.map(function(i){
-                                                return (ts.getData) ? ts.getData($ths.filter('[data-column="' + i + '"]:last'), c.headers[i], 'filter') === 'parsed' : $(this).hasClass('filter-parsed');
-                                            }).get(),
-                                            time, timer,
-
-                                        // dig fer gold
-                                            checkFilters = function(filter){
-                                                var arry = $.isArray(filter),
-                                                    $inpts = $t.find('thead').eq(0).children('tr').find('select.' + css + ', input.' + css),
-                                                    v = (arry) ? filter : $inpts.map(function(){
-                                                        return $(this).val() || '';
-                                                    }).get(),
-                                                    cv = (v || []).join(''); // combined filter values
-                                                // add filter array back into inputs
-                                                if (arry) {
-                                                    $inpts.each(function(i,el){
-                                                        $(el).val(filter[i] || '');
-                                                    });
-                                                }
-                                                if (wo.filter_hideFilters === true){
-                                                    // show/hide filter row as needed
-                                                    $t.find('.tablesorter-filter-row').trigger( cv === '' ? 'mouseleave' : 'mouseenter' );
-                                                }
-                                                // return if the last search is the same; but filter === false when updating the search
-                                                // see example-widget-filter.html filter toggle buttons
-                                                if (last === cv && filter !== false) { return; }
-                                                $t.trigger('filterStart', [v]);
-                                                if (c.showProcessing) {
-                                                    // give it time for the processing icon to kick in
-                                                    setTimeout(function(){
-                                                        findRows(filter, v, cv);
-                                                        return false;
-                                                    }, 30);
-                                                } else {
-                                                    findRows(filter, v, cv);
-                                                    return false;
-                                                }
-                                            },
-                                            findRows = function(filter, v, cv){
-                                                var $tb, $tr, $td, cr, r, l, ff, time, arry;
-                                                if (c.debug) { time = new Date(); }
-
-                                                for (k = 0; k < b.length; k++ ){
-                                                    $tb = $.tablesorter.processTbody(table, b.eq(k), true);
-                                                    $tr = $tb.children('tr');
-                                                    l = $tr.length;
-                                                    if (cv === '' || wo.filter_serversideFiltering){
-                                                        $tr.show().removeClass('filtered');
-                                                    } else {
-                                                        // loop through the rows
-                                                        for (j = 0; j < l; j++){
-                                                            // skip child rows
-                                                            if (reg[1].test($tr[j].className)) { continue; }
-                                                            r = true;
-                                                            cr = $tr.eq(j).nextUntil('tr:not(.' + c.cssChildRow + ')');
-                                                            // so, if "table.config.widgetOptions.filter_childRows" is true and there is
-                                                            // a match anywhere in the child row, then it will make the row visible
-                                                            // checked here so the option can be changed dynamically
-                                                            t = (cr.length && (wo && wo.hasOwnProperty('filter_childRows') &&
-                                                                               typeof wo.filter_childRows !== 'undefined' ? wo.filter_childRows : true)) ? cr.text() : '';
-                                                            t = wo.filter_ignoreCase ? t.toLocaleLowerCase() : t;
-                                                            $td = $tr.eq(j).children('td');
-                                                            for (i = 0; i < cols; i++){
-                                                                // ignore if filter is empty or disabled
-                                                                if (v[i]){
-                                                                    // check if column data should be from the cell or from parsed data
-                                                                    if (wo.filter_useParsedData || parsed[i]){
-                                                                        x = c.cache[k].normalized[j][i];
-                                                                    } else {
-                                                                        // using older or original tablesorter
-                                                                        x = $.trim($td.eq(i).text());
-                                                                    }
-                                                                    xi = !reg[2].test(typeof x) && wo.filter_ignoreCase ? x.toLocaleLowerCase() : x;
-                                                                    ff = r; // if r is true, show that row
-                                                                    // val = case insensitive, v[i] = case sensitive
-                                                                    val = wo.filter_ignoreCase ? v[i].toLocaleLowerCase() : v[i];
-                                                                    if (wo.filter_functions && wo.filter_functions[i]){
-                                                                        if (wo.filter_functions[i] === true){
-                                                                            // default selector; no "filter-select" class
-                                                                            ff = ($ths.filter('[data-column="' + i + '"]:last').hasClass('filter-match')) ? xi.search(val) >= 0 : v[i] === x;
-                                                                        } else if (typeof wo.filter_functions[i] === 'function'){
-                                                                            // filter callback( exact cell content, parser normalized content, filter input value, column index )
-                                                                            ff = wo.filter_functions[i](x, c.cache[k].normalized[j][i], v[i], i);
-                                                                        } else if (typeof wo.filter_functions[i][v[i]] === 'function'){
-                                                                            // selector option function
-                                                                            ff = wo.filter_functions[i][v[i]](x, c.cache[k].normalized[j][i], v[i], i);
-                                                                        }
-                                                                        // Look for regex
-                                                                    } else if (reg[0].test(val)){
-                                                                        rg = reg[0].exec(val);
-                                                                        try {
-                                                                            ff = new RegExp(rg[1], rg[2]).test(xi);
-                                                                        } catch (err){
-                                                                            ff = false;
-                                                                        }
-                                                                        // Look for quotes or equals to get an exact match
-                                                                    } else if (reg[3].test(val) && xi === val.replace(reg[4], '')){
-                                                                        ff = true;
-                                                                        // Look for a not match
-                                                                    } else if (/^\!/.test(val)){
-                                                                        val = val.replace('!','');
-                                                                        s = xi.search($.trim(val));
-                                                                        ff = val === '' ? true : !(wo.filter_startsWith ? s === 0 : s >= 0);
-                                                                        // Look for operators >, >=, < or <=
-                                                                    } else if (/^[<>]=?/.test(val)){
-                                                                        // xi may be numeric - see issue #149
-                                                                        rg = isNaN(xi) ? $.tablesorter.formatFloat(xi.replace(reg[5], ''), table) : $.tablesorter.formatFloat(xi, table);
-                                                                        s = $.tablesorter.formatFloat(val.replace(reg[5], '').replace(reg[6],''), table);
-                                                                        if (/>/.test(val)) { ff = />=/.test(val) ? rg >= s : rg > s; }
-                                                                        if (/</.test(val)) { ff = /<=/.test(val) ? rg <= s : rg < s; }
-                                                                        // Look for wild card: ? = single, or * = multiple
-                                                                    } else if (/[\?|\*]/.test(val)){
-                                                                        ff = new RegExp( val.replace(/\?/g, '\\S{1}').replace(/\*/g, '\\S*') ).test(xi);
-                                                                        // Look for match, and add child row data for matching
-                                                                    } else {
-                                                                        x = (xi + t).indexOf(val);
-                                                                        ff  = ( (!wo.filter_startsWith && x >= 0) || (wo.filter_startsWith && x === 0) );
-                                                                    }
-                                                                    r = (ff) ? (r ? true : false) : false;
-                                                                }
-                                                            }
-                                                            $tr[j].style.display = (r ? '' : 'none');
-                                                            $tr.eq(j)[r ? 'removeClass' : 'addClass']('filtered');
-                                                            if (cr.length) { cr[r ? 'show' : 'hide'](); }
-                                                        }
-                                                    }
-                                                    $.tablesorter.processTbody(table, $tb, false);
-                                                }
-
-                                                last = cv; // save last search
-                                                if (c.debug){
-                                                    ts.benchmark("Completed filter widget search", time);
-                                                }
-                                                $t.trigger('applyWidgets'); // make sure zebra widget is applied
-                                                $t.trigger('filterEnd');
-                                            },
-                                            buildSelect = function(i, updating){
-                                                var o, arry = [];
-                                                i = parseInt(i, 10);
-                                                o = '<option value="">' + ($ths.filter('[data-column="' + i + '"]:last').attr('data-placeholder') || '') + '</option>';
-                                                for (k = 0; k < b.length; k++ ){
-                                                    l = c.cache[k].row.length;
-                                                    // loop through the rows
-                                                    for (j = 0; j < l; j++){
-                                                        // get non-normalized cell content
-                                                        if (wo.filter_useParsedData){
-                                                            arry.push( '' + c.cache[k].normalized[j][i] );
-                                                        } else {
-                                                            t = c.cache[k].row[j][0].cells[i];
-                                                            if (t){
-                                                                arry.push( $.trim(c.supportsTextContent ? t.textContent : $(t).text()) );
-                                                            }
-                                                        }
-                                                    }
-                                                }
-
-                                                // get unique elements and sort the list
-                                                // if $.tablesorter.sortText exists (not in the original tablesorter),
-                                                // then natural sort the list otherwise use a basic sort
-                                                arry = $.grep(arry, function(v, k){
-                                                    return $.inArray(v ,arry) === k;
-                                                });
-                                                arry = (ts.sortText) ? arry.sort(function(a,b){ return ts.sortText(table, a, b, i); }) : arry.sort(true);
-
-                                                // build option list
-                                                for (k = 0; k < arry.length; k++){
-                                                    o += '<option value="' + arry[k] + '">' + arry[k] + '</option>';
-                                                }
-                                                $t.find('thead').find('select.' + css + '[data-column="' + i + '"]')[ updating ? 'html' : 'append' ](o);
-                                            },
-                                            buildDefault = function(updating){
-                                                // build default select dropdown
-                                                for (i = 0; i < cols; i++){
-                                                    t = $ths.filter('[data-column="' + i + '"]:last');
-                                                    // look for the filter-select class; build/update it if found
-                                                    if ((t.hasClass('filter-select') || wo.filter_functions && wo.filter_functions[i] === true) && !t.hasClass('filter-false')){
-                                                        if (!wo.filter_functions) { wo.filter_functions = {}; }
-                                                        wo.filter_functions[i] = true; // make sure this select gets processed by filter_functions
-                                                        buildSelect(i, updating);
-                                                    }
-                                                }
-                                            };
-
-                                        if (c.debug){
-                                            time = new Date();
-                                        }
-                                        wo.filter_ignoreCase = wo.filter_ignoreCase !== false; // set default filter_ignoreCase to true
-                                        wo.filter_useParsedData = wo.filter_useParsedData === true; // default is false
-                                        // don't build filter row if columnFilters is false or all columns are set to "filter-false" - issue #156
-                                        if (wo.filter_columnFilters !== false && $ths.filter('.filter-false').length !== $ths.length){
-                                            t = '<tr class="tablesorter-filter-row">'; // build filter row
-                                            for (i = 0; i < cols; i++){
-                                                dis = false;
-                                                $th = $ths.filter('[data-column="' + i + '"]:last'); // assuming last cell of a column is the main column
-                                                sel = (wo.filter_functions && wo.filter_functions[i] && typeof wo.filter_functions[i] !== 'function') || $th.hasClass('filter-select');
-                                                t += '<td>';
-                                                if (sel){
-                                                    t += '<select data-column="' + i + '" class="' + css;
-                                                } else {
-                                                    t += '<input type="search" placeholder="' + ($th.attr('data-placeholder') || "") + '" data-column="' + i + '" class="' + css;
-                                                }
-                                                // use header option - headers: { 1: { filter: false } } OR add class="filter-false"
-                                                if (ts.getData){
-                                                    dis = ts.getData($th[0], c.headers[i], 'filter') === 'false';
-                                                    // get data from jQuery data, metadata, headers option or header class name
-                                                    t += dis ? ' disabled" disabled' : '"';
-                                                } else {
-                                                    dis = (c.headers[i] && c.headers[i].hasOwnProperty('filter') && c.headers[i].filter === false) || $th.hasClass('filter-false');
-                                                    // only class names and header options - keep this for compatibility with tablesorter v2.0.5
-                                                    t += (dis) ? ' disabled" disabled' : '"';
-                                                }
-                                                t += (sel ? '></select>' : '>') + '</td>';
-                                            }
-                                            $t.find('thead').eq(0).append(t += '</tr>');
-                                        }
-                                        $t
-                                            // add .tsfilter namespace to all BUT search
-                                            .bind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter '), function(e, filter){
-                                                      if (e.type !== 'search'){
-                                                          buildDefault(true);
-                                                      }
-                                                      checkFilters(e.type === 'search' ? filter : '');
-                                                      return false;
-                                                  })
-                                            .find('input.' + css).bind('keyup search', function(e, filter){
-                                                                           // ignore arrow and meta keys; allow backspace
-                                                                           if ((e.which < 32 && e.which !== 8) || (e.which >= 37 && e.which <=40)) { return; }
-                                                                           // skip delay
-                                                                           if (typeof filter !== 'undefined'){
-                                                                               checkFilters(filter);
-                                                                               return false;
-                                                                           }
-                                                                           // delay filtering
-                                                                           clearTimeout(timer);
-                                                                           timer = setTimeout(function(){
-                                                                               checkFilters();
-                                                                           }, wo.filter_searchDelay || 300);
-                                                                       });
-
-                                        // reset button/link
-                                        if (wo.filter_reset && $(wo.filter_reset).length){
-                                            $(wo.filter_reset).bind('click', function(){
-                                                $t.find('.' + css).val('');
-                                                checkFilters();
-                                                return false;
-                                            });
-                                        }
-                                        if (wo.filter_functions){
-                                            // i = column # (string)
-                                            for (col in wo.filter_functions){
-                                                if (wo.filter_functions.hasOwnProperty(col) && typeof col === 'string'){
-                                                    t = $ths.filter('[data-column="' + col + '"]:last');
-                                                    ff = '';
-                                                    if (wo.filter_functions[col] === true && !t.hasClass('filter-false')){
-                                                        buildSelect(col);
-                                                    } else if (typeof col === 'string' && !t.hasClass('filter-false')){
-                                                        // add custom drop down list
-                                                        for (str in wo.filter_functions[col]){
-                                                            if (typeof str === 'string'){
-                                                                ff += ff === '' ? '<option value="">' + (t.attr('data-placeholder') || '') + '</option>' : '';
-                                                                ff += '<option value="' + str + '">' + str + '</option>';
-                                                            }
-                                                        }
-                                                        $t.find('thead').find('select.' + css + '[data-column="' + col + '"]').append(ff);
-                                                    }
-                                                }
-                                            }
-                                        }
-                                        // not really updating, but if the column has both the "filter-select" class & filter_functions set to true,
-                                        // it would append the same options twice.
-                                        buildDefault(true);
-
-                                        $t.find('select.' + css).bind('change search', function(){
-                                            checkFilters();
-                                        });
-
-                                        if (wo.filter_hideFilters === true){
-                                            $t
-                                                .find('.tablesorter-filter-row')
-                                                .addClass('hideme')
-                                                .bind('mouseenter mouseleave', function(e){
-                                                          // save event object - http://bugs.jquery.com/ticket/12140
-                                                          var all, evt = e;
-                                                          ft = $(this);
-                                                          clearTimeout(st);
-                                                          st = setTimeout(function(){
-                                                              if (/enter|over/.test(evt.type)){
-                                                                  ft.removeClass('hideme');
-                                                              } else {
-                                                                  // don't hide if input has focus
-                                                                  // $(':focus') needs jQuery 1.6+
-                                                                  if ($(document.activeElement).closest('tr')[0] !== ft[0]){
-                                                                      // get all filter values
-                                                                      all = $t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){
-                                                                          return $(this).val() || '';
-                                                                      }).get().join('');
-                                                                      // don't hide row if any filter has a value
-                                                                      if (all === ''){
-                                                                          ft.addClass('hideme');
-                                                                      }
-                                                                  }
-                                                              }
-                                                          }, 200);
-                                                      })
-                                                .find('input, select').bind('focus blur', function(e){
-                                                                                ft2 = $(this).closest('tr');
-                                                                                clearTimeout(st);
-                                                                                st = setTimeout(function(){
-                                                                                    // don't hide row if any filter has a value
-                                                                                    if ($t.find('.' + (wo.filter_cssFilter || 'tablesorter-filter')).map(function(){ return $(this).val() || ''; }).get().join('') === ''){
-                                                                                        ft2[ e.type === 'focus' ? 'removeClass' : 'addClass']('hideme');
-                                                                                    }
-                                                                                }, 200);
-                                                                            });
-                                        }
-
-                                        // show processing icon
-                                        if (c.showProcessing) {
-                                            $t.bind('filterStart filterEnd', function(e, v) {
-                                                var fc = (v) ? $t.find('.' + c.cssHeader).filter('[data-column]').filter(function(){
-                                                    return v[$(this).data('column')] !== '';
-                                                }) : '';
-                                                ts.isProcessing($t[0], e.type === 'filterStart', v ? fc : '');
-                                            });
-                                        }
-
-                                        if (c.debug){
-                                            ts.benchmark("Applying Filter widget", time);
-                                        }
-                                        // filter widget initialized
-                                        $t.trigger('filterInit');
-                                    }
-                                },
-                                remove: function(table, c, wo){
-                                    var k, $tb,
-                                        $t = $(table),
-                                        b = c.$tbodies;
-                                    $t
-                                        .removeClass('hasFilters')
-                                        // add .tsfilter namespace to all BUT search
-                                        .unbind('addRows updateCell update appendCache search'.split(' ').join('.tsfilter'))
-                                        .find('.tablesorter-filter-row').remove();
-                                    for (k = 0; k < b.length; k++ ){
-                                        $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody
-                                        $tb.children().removeClass('filtered').show();
-                                        $.tablesorter.processTbody(table, $tb, false); // restore tbody
-                                    }
-                                    if (wo.filterreset) { $(wo.filter_reset).unbind('click'); }
-                                }
-                            });
-
-    // Widget: Sticky headers
-    // based on this awesome article:
-    // http://css-tricks.com/13465-persistent-headers/
-    // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech
-    // **************************
-    $.tablesorter.addWidget({
-                                id: "stickyHeaders",
-                                format: function(table){
-                                    if ($(table).hasClass('hasStickyHeaders')) { return; }
-                                    var $table = $(table).addClass('hasStickyHeaders'),
-                                        c = table.config,
-                                        wo = c.widgetOptions,
-                                        win = $(window),
-                                        header = $(table).children('thead:first'), //.add( $(table).find('caption') ),
-                                        hdrCells = header.children('tr:not(.sticky-false)').children(),
-                                        css = wo.stickyHeaders || 'tablesorter-stickyHeader',
-                                        innr = '.tablesorter-header-inner',
-                                        firstRow = hdrCells.eq(0).parent(),
-                                        tfoot = $table.find('tfoot'),
-                                        t2 = wo.$sticky = $table.clone(), // clone table, but don't remove id... the table might be styled by css
-                                    // clone the entire thead - seems to work in IE8+
-                                        stkyHdr = t2.children('thead:first')
-                                            .addClass(css)
-                                            .css({
-                                                     width      : header.outerWidth(true),
-                                                     position   : 'fixed',
-                                                     margin     : 0,
-                                                     top        : 0,
-                                                     visibility : 'hidden',
-                                                     zIndex     : 1
-                                                 }),
-                                        stkyCells = stkyHdr.children('tr:not(.sticky-false)').children(), // issue #172
-                                        laststate = '',
-                                        spacing = 0,
-                                        resizeHdr = function(){
-                                            var bwsr = navigator.userAgent;
-                                            spacing = 0;
-                                            // yes, I dislike browser sniffing, but it really is needed here :(
-                                            // webkit automatically compensates for border spacing
-                                            if ($table.css('border-collapse') !== 'collapse' && !/(webkit|msie)/i.test(bwsr)) {
-                                                // Firefox & Opera use the border-spacing
-                                                // update border-spacing here because of demos that switch themes
-                                                spacing = parseInt(hdrCells.eq(0).css('border-left-width'), 10) * 2;
-                                            }
-                                            stkyHdr.css({
-                                                            left : header.offset().left - win.scrollLeft() - spacing,
-                                                            width: header.outerWidth()
-                                                        });
-                                            stkyCells
-                                                .each(function(i){
-                                                          var $h = hdrCells.eq(i);
-                                                          $(this).css({
-                                                                          width: $h.width() - spacing,
-                                                                          height: $h.height()
-                                                                      });
-                                                      })
-                                                .find(innr).each(function(i){
-                                                                     var hi = hdrCells.eq(i).find(innr),
-                                                                         w = hi.width(); // - ( parseInt(hi.css('padding-left'), 10) + parseInt(hi.css('padding-right'), 10) );
-                                                                     $(this).width(w);
-                                                                 });
-                                        };
-                                    // clear out cloned table, except for sticky header
-                                    t2.find('thead:gt(0),tr.sticky-false,tbody,tfoot,caption').remove();
-                                    t2.css({ height:0, width:0, padding:0, margin:0, border:0 });
-                                    // remove rows you don't want to be sticky
-                                    stkyHdr.find('tr.sticky-false').remove();
-                                    // remove resizable block
-                                    stkyCells.find('.tablesorter-resizer').remove();
-                                    // update sticky header class names to match real header after sorting
-                                    $table
-                                        .bind('sortEnd.tsSticky', function(){
-                                                  hdrCells.each(function(i){
-                                                      var t = stkyCells.eq(i);
-                                                      t.attr('class', $(this).attr('class'));
-                                                      if (c.cssIcon){
-                                                          t
-                                                              .find('.' + c.cssIcon)
-                                                              .attr('class', $(this).find('.' + c.cssIcon).attr('class'));
-                                                      }
-                                                  });
-                                              })
-                                        .bind('pagerComplete.tsSticky', function(){
-                                                  resizeHdr();
-                                              });
-                                    // set sticky header cell width and link clicks to real header
-                                    hdrCells.find('*').andSelf().filter(c.selectorSort).each(function(i){
-                                        var t = $(this);
-                                        stkyCells.eq(i)
-                                            // clicking on sticky will trigger sort
-                                            .bind('mouseup', function(e){
-                                                      t.trigger(e, true); // external mouseup flag (click timer is ignored)
-                                                  })
-                                            // prevent sticky header text selection
-                                            .bind('mousedown', function(){
-                                                      this.onselectstart = function(){ return false; };
-                                                      return false;
-                                                  });
-                                    });
-                                    // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned.
-                                    $table.after( t2 );
-                                    // make it sticky!
-                                    win
-                                        .bind('scroll.tsSticky', function(){
-                                                  var offset = firstRow.offset(),
-                                                      sTop = win.scrollTop(),
-                                                      tableHt = $table.height() - (stkyHdr.height() + (tfoot.height() || 0)),
-                                                      vis = (sTop > offset.top) && (sTop < offset.top + tableHt) ? 'visible' : 'hidden';
-                                                  stkyHdr
-                                                      .css({
-                                                               // adjust when scrolling horizontally - fixes issue #143
-                                                               left : header.offset().left - win.scrollLeft() - spacing,
-                                                               visibility : vis
-                                                           });
-                                                  if (vis !== laststate){
-                                                      // make sure the column widths match
-                                                      resizeHdr();
-                                                      laststate = vis;
-                                                  }
-                                              })
-                                        .bind('resize.tsSticky', function(){
-                                                  resizeHdr();
-                                              });
-                                },
-                                remove: function(table, c, wo){
-                                    var $t = $(table),
-                                        css = wo.stickyHeaders || 'tablesorter-stickyHeader';
-                                    $t
-                                        .removeClass('hasStickyHeaders')
-                                        .unbind('sortEnd.tsSticky pagerComplete.tsSticky')
-                                        .find('.' + css).remove();
-                                    if (wo.$sticky) { wo.$sticky.remove(); } // remove cloned thead
-                                    $(window).unbind('scroll.tsSticky resize.tsSticky');
-                                }
-                            });
-
-    // Add Column resizing widget
-    // this widget saves the column widths if
-    // $.tablesorter.storage function is included
-    // **************************
-    $.tablesorter.addWidget({
-                                id: "resizable",
-                                format: function(table){
-                                    if ($(table).hasClass('hasResizable')) { return; }
-                                    $(table).addClass('hasResizable');
-                                    var $t, t, i, j, s, $c, $cols, w, tw,
-                                        $tbl = $(table),
-                                        c = table.config,
-                                        wo = c.widgetOptions,
-                                        position = 0,
-                                        $target = null,
-                                        $next = null,
-                                        fullWidth = Math.abs($tbl.parent().width() - $tbl.width()) < 20,
-                                        stopResize = function(){
-                                            if ($.tablesorter.storage && $target){
-                                                s[$target.index()] = $target.width();
-                                                s[$next.index()] = $next.width();
-                                                $target.width( s[$target.index()] );
-                                                $next.width( s[$next.index()] );
-                                                if (wo.resizable !== false){
-                                                    $.tablesorter.storage(table, 'tablesorter-resizable', s);
-                                                }
-                                            }
-                                            position = 0;
-                                            $target = $next = null;
-                                            $(window).trigger('resize'); // will update stickyHeaders, just in case
-                                        };
-                                    s = ($.tablesorter.storage && wo.resizable !== false) ? $.tablesorter.storage(table, 'tablesorter-resizable') : {};
-                                    // process only if table ID or url match
-                                    if (s){
-                                        for (j in s){
-                                            if (!isNaN(j) && j < c.headerList.length){
-                                                $(c.headerList[j]).width(s[j]); // set saved resizable widths
-                                            }
-                                        }
-                                    }
-                                    $t = $tbl.children('thead:first').children('tr');
-                                    // add resizable-false class name to headers (across rows as needed)
-                                    $t.children().each(function(){
-                                        t = $(this);
-                                        i = t.attr('data-column');
-                                        j = $.tablesorter.getData( t, c.headers[i], 'resizable') === "false";
-                                        $t.children().filter('[data-column="' + i + '"]').toggleClass('resizable-false', j);
-                                    });
-                                    // add wrapper inside each cell to allow for positioning of the resizable target block
-                                    $t.each(function(){
-                                        $c = $(this).children(':not(.resizable-false)');
-                                        if (!$(this).find('.tablesorter-wrapper').length) {
-                                            // Firefox needs this inner div to position the resizer correctly
-                                            $c.wrapInner('<div class="tablesorter-wrapper" style="position:relative;height:100%;width:100%"></div>');
-                                        }
-                                        $c = $c.slice(0,-1); // don't include the last column of the row
-                                        $cols = $cols ? $cols.add($c) : $c;
-                                    });
-                                    $cols
-                                        .each(function(){
-                                                  $t = $(this);
-                                                  j = parseInt($t.css('padding-right'), 10) + 10; // 8 is 1/2 of the 16px wide resizer grip
-                                                  t = '<div class="tablesorter-resizer" style="cursor:w-resize;position:absolute;z-index:1;right:-' + j +
-                                                      'px;top:0;height:100%;width:20px;"></div>';
-                                                  $t
-                                                      .find('.tablesorter-wrapper')
-                                                      .append(t);
-                                              })
-                                        .bind('mousemove.tsresize', function(e){
-                                                  // ignore mousemove if no mousedown
-                                                  if (position === 0 || !$target) { return; }
-                                                  // resize columns
-                                                  w = e.pageX - position;
-                                                  tw = $target.width();
-                                                  $target.width( tw + w );
-                                                  if ($target.width() !== tw && fullWidth){
-                                                      $next.width( $next.width() - w );
-                                                  }
-                                                  position = e.pageX;
-                                              })
-                                        .bind('mouseup.tsresize', function(){
-                                                  stopResize();
-                                              })
-                                        .find('.tablesorter-resizer,.tablesorter-resizer-grip')
-                                        .bind('mousedown', function(e){
-                                                  // save header cell and mouse position; closest() not supported by jQuery v1.2.6
-                                                  $target = $(e.target).closest('th');
-                                                  t = c.$headers.filter('[data-column="' + $target.attr('data-column') + '"]');
-                                                  if (t.length > 1) { $target = $target.add(t); }
-                                                  // if table is not as wide as it's parent, then resize the table
-                                                  $next = e.shiftKey ? $target.parent().find('th:not(.resizable-false)').filter(':last') : $target.nextAll(':not(.resizable-false)').eq(0);
-                                                  position = e.pageX;
-                                              });
-                                    $tbl.find('thead:first')
-                                        .bind('mouseup.tsresize mouseleave.tsresize', function(e){
-                                                  stopResize();
-                                              })
-                                        // right click to reset columns to default widths
-                                        .bind('contextmenu.tsresize', function(){
-                                                  $.tablesorter.resizableReset(table);
-                                                  // $.isEmptyObject() needs jQuery 1.4+
-                                                  var rtn = $.isEmptyObject ? $.isEmptyObject(s) : s === {}; // allow right click if already reset
-                                                  s = {};
-                                                  return rtn;
-                                              });
-                                },
-                                remove: function(table, c, wo){
-                                    $(table)
-                                        .removeClass('hasResizable')
-                                        .find('thead')
-                                        .unbind('mouseup.tsresize mouseleave.tsresize contextmenu.tsresize')
-                                        .find('tr').children()
-                                        .unbind('mousemove.tsresize mouseup.tsresize')
-                                        // don't remove "tablesorter-wrapper" as uitheme uses it too
-                                        .find('.tablesorter-resizer,.tablesorter-resizer-grip').remove();
-                                    $.tablesorter.resizableReset(table);
-                                }
-                            });
-    $.tablesorter.resizableReset = function(table){
-        $(table.config.headerList).filter(':not(.resizable-false)').css('width','');
-        if ($.tablesorter.storage) { $.tablesorter.storage(table, 'tablesorter-resizable', {}); }
-    };
-
-    // Save table sort widget
-    // this widget saves the last sort only if the
-    // saveSort widget option is true AND the
-    // $.tablesorter.storage function is included
-    // **************************
-    $.tablesorter.addWidget({
-                                id: 'saveSort',
-                                init: function(table, thisWidget){
-                                    // run widget format before all other widgets are applied to the table
-                                    thisWidget.format(table, true);
-                                },
-                                format: function(table, init){
-                                    var sl, time, c = table.config,
-                                        wo = c.widgetOptions,
-                                        ss = wo.saveSort !== false, // make saveSort active/inactive; default to true
-                                        sortList = { "sortList" : c.sortList };
-                                    if (c.debug){
-                                        time = new Date();
-                                    }
-                                    if ($(table).hasClass('hasSaveSort')){
-                                        if (ss && table.hasInitialized && $.tablesorter.storage){
-                                            $.tablesorter.storage( table, 'tablesorter-savesort', sortList );
-                                            if (c.debug){
-                                                $.tablesorter.benchmark('saveSort widget: Saving last sort: ' + c.sortList, time);
-                                            }
-                                        }
-                                    } else {
-                                        // set table sort on initial run of the widget
-                                        $(table).addClass('hasSaveSort');
-                                        sortList = '';
-                                        // get data
-                                        if ($.tablesorter.storage){
-                                            sl = $.tablesorter.storage( table, 'tablesorter-savesort' );
-                                            sortList = (sl && sl.hasOwnProperty('sortList') && $.isArray(sl.sortList)) ? sl.sortList : '';
-                                            if (c.debug){
-                                                $.tablesorter.benchmark('saveSort: Last sort loaded: "' + sortList + '"', time);
-                                            }
-                                        }
-                                        // init is true when widget init is run, this will run this widget before all other widgets have initialized
-                                        // this method allows using this widget in the original tablesorter plugin; but then it will run all widgets twice.
-                                        if (init && sortList && sortList.length > 0){
-                                            c.sortList = sortList;
-                                        } else if (table.hasInitialized && sortList && sortList.length > 0){
-                                            // update sort change
-                                            $(table).trigger('sorton', [sortList]);
-                                        }
-                                    }
-                                },
-                                remove: function(table, c, wo){
-                                    // clear storage
-                                    if ($.tablesorter.storage) { $.tablesorter.storage( table, 'tablesorter-savesort', '' ); }
-                                }
-                            });
-
-})(jQuery);
\ No newline at end of file
diff --git a/UI/JsLibraries/jquery.tablesorter.js b/UI/JsLibraries/jquery.tablesorter.js
deleted file mode 100644
index 7820a3e66..000000000
--- a/UI/JsLibraries/jquery.tablesorter.js
+++ /dev/null
@@ -1,1383 +0,0 @@
-/*!
- * TableSorter 2.7.12 - Client-side table sorting with ease!
- * @requires jQuery v1.2.6+
- *
- * Copyright (c) 2007 Christian Bach
- * Examples and docs at: http://tablesorter.com
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * @type jQuery
- * @name tablesorter
- * @cat Plugins/Tablesorter
- * @author Christian Bach/christian.bach@polyester.se
- * @contributor Rob Garrison/https://github.com/Mottie/tablesorter
- */
-/*jshint browser:true, jquery:true, unused:false, expr: true */
-/*global console:false, alert:false */
-!(function($) {
-    "use strict";
-    $.extend({
-        /*jshint supernew:true */
-        tablesorter: new function() {
-
-            var ts = this;
-
-            ts.version = "2.7.12";
-
-            ts.parsers = [];
-            ts.widgets = [];
-            ts.defaults = {
-
-                // *** appearance
-                theme            : 'default',  // adds tablesorter-{theme} to the table for styling
-                widthFixed       : false,      // adds colgroup to fix widths of columns
-                showProcessing   : false,      // show an indeterminate timer icon in the header when the table is sorted or filtered.
-
-                headerTemplate   : '{content}',// header layout template (HTML ok); {content} = innerHTML, {icon} = <i/> (class from cssIcon)
-                onRenderTemplate : null,       // function(index, template){ return template; }, (template is a string)
-                onRenderHeader   : null,       // function(index){}, (nothing to return)
-
-                // *** functionality
-                cancelSelection  : true,       // prevent text selection in the header
-                dateFormat       : 'mmddyyyy', // other options: "ddmmyyy" or "yyyymmdd"
-                sortMultiSortKey : 'shiftKey', // key used to select additional columns
-                sortResetKey     : 'ctrlKey',  // key used to remove sorting on a column
-                usNumberFormat   : true,       // false for German "1.234.567,89" or French "1 234 567,89"
-                delayInit        : false,      // if false, the parsed table contents will not update until the first sort
-                serverSideSorting: false,      // if true, server-side sorting should be performed because client-side sorting will be disabled, but the ui and events will still be used.
-
-                // *** sort options
-                headers          : {},         // set sorter, string, empty, locked order, sortInitialOrder, filter, etc.
-                ignoreCase       : true,       // ignore case while sorting
-                sortForce        : null,       // column(s) first sorted; always applied
-                sortList         : [],         // Initial sort order; applied initially; updated when manually sorted
-                sortAppend       : null,       // column(s) sorted last; always applied
-
-                sortInitialOrder : 'asc',      // sort direction on first click
-                sortLocaleCompare: false,      // replace equivalent character (accented characters)
-                sortReset        : false,      // third click on the header will reset column to default - unsorted
-                sortRestart      : false,      // restart sort to "sortInitialOrder" when clicking on previously unsorted columns
-
-                emptyTo          : 'bottom',   // sort empty cell to bottom, top, none, zero
-                stringTo         : 'max',      // sort strings in numerical column as max, min, top, bottom, zero
-                textExtraction   : 'simple',   // text extraction method/function - function(node, table, cellIndex){}
-                textSorter       : null,       // use custom text sorter - function(a,b){ return a.sort(b); } // basic sort
-
-                // *** widget options
-                widgets: [],                   // method to add widgets, e.g. widgets: ['zebra']
-                widgetOptions    : {
-                    zebra : [ 'even', 'odd' ]    // zebra widget alternating row class names
-                },
-                initWidgets      : true,       // apply widgets on tablesorter initialization
-
-                // *** callbacks
-                initialized      : null,       // function(table){},
-
-                // *** css class names
-                tableClass       : 'tablesorter',
-                cssAsc           : 'tablesorter-headerAsc',
-                cssChildRow      : 'tablesorter-childRow', // previously "expand-child"
-                cssDesc          : 'tablesorter-headerDesc',
-                cssHeader        : 'tablesorter-header',
-                cssHeaderRow     : 'tablesorter-headerRow',
-                cssIcon          : 'tablesorter-icon', //  if this class exists, a <i> will be added to the header automatically
-                cssInfoBlock     : 'tablesorter-infoOnly', // don't sort tbody with this class name
-                cssProcessing    : 'tablesorter-processing', // processing icon applied to header during sort/filter
-
-                // *** selectors
-                selectorHeaders  : '> thead th, > thead td',
-                selectorSort     : 'th, td',   // jQuery selector of content within selectorHeaders that is clickable to trigger a sort
-                selectorRemove   : '.remove-me',
-
-                // *** advanced
-                debug            : false,
-
-                // *** Internal variables
-                headerList: [],
-                empties: {},
-                strings: {},
-                parsers: []
-
-                // deprecated; but retained for backwards compatibility
-                // widgetZebra: { css: ["even", "odd"] }
-
-            };
-
-            /* debuging utils */
-            function log(s) {
-                if (typeof console !== "undefined" && typeof console.log !== "undefined") {
-                    console.log(s);
-                } else {
-                    alert(s);
-                }
-            }
-
-            function benchmark(s, d) {
-                log(s + " (" + (new Date().getTime() - d.getTime()) + "ms)");
-            }
-
-            ts.benchmark = benchmark;
-
-            function getElementText(table, node, cellIndex) {
-                if (!node) { return ""; }
-                var c = table.config,
-                    t = c.textExtraction, text = "";
-                if (t === "simple") {
-                    if (c.supportsTextContent) {
-                        text = node.textContent; // newer browsers support this
-                    } else {
-                        text = $(node).text();
-                    }
-                } else {
-                    if (typeof(t) === "function") {
-                        text = t(node, table, cellIndex);
-                    } else if (typeof(t) === "object" && t.hasOwnProperty(cellIndex)) {
-                        text = t[cellIndex](node, table, cellIndex);
-                    } else {
-                        text = c.supportsTextContent ? node.textContent : $(node).text();
-                    }
-                }
-                return $.trim(text);
-            }
-
-            function detectParserForColumn(table, rows, rowIndex, cellIndex) {
-                var i, l = ts.parsers.length,
-                    node = false,
-                    nodeValue = '',
-                    keepLooking = true;
-                while (nodeValue === '' && keepLooking) {
-                    rowIndex++;
-                    if (rows[rowIndex]) {
-                        node = rows[rowIndex].cells[cellIndex];
-                        nodeValue = getElementText(table, node, cellIndex);
-                        if (table.config.debug) {
-                            log('Checking if value was empty on row ' + rowIndex + ', column: ' + cellIndex + ': ' + nodeValue);
-                        }
-                    } else {
-                        keepLooking = false;
-                    }
-                }
-                for (i = 1; i < l; i++) {
-                    if (ts.parsers[i].is && ts.parsers[i].is(nodeValue, table, node)) {
-                        return ts.parsers[i];
-                    }
-                }
-                // 0 is always the generic parser (text)
-                return ts.parsers[0];
-            }
-
-            function buildParserCache(table) {
-                var c = table.config,
-                // update table bodies in case we start with an empty table
-                    tb = c.$tbodies = c.$table.children('tbody:not(.' + c.cssInfoBlock + ')'),
-                    rows, list, l, i, h, ch, p, parsersDebug = "";
-                if ( tb.length === 0) {
-                    return c.debug ? log('*Empty table!* Not building a parser cache') : '';
-                }
-                rows = tb[0].rows;
-                if (rows[0]) {
-                    list = [];
-                    l = rows[0].cells.length;
-                    for (i = 0; i < l; i++) {
-                        // tons of thanks to AnthonyM1229 for working out the following selector (issue #74) to make this work in IE8!
-                        // More fixes to this selector to work properly in iOS and jQuery 1.8+ (issue #132 & #174)
-                        h = c.$headers.filter(':not([colspan])');
-                        h = h.add( c.$headers.filter('[colspan="1"]') ) // ie8 fix
-                            .filter('[data-column="' + i + '"]:last');
-                        ch = c.headers[i];
-                        // get column parser
-                        p = ts.getParserById( ts.getData(h, ch, 'sorter') );
-                        // empty cells behaviour - keeping emptyToBottom for backwards compatibility
-                        c.empties[i] = ts.getData(h, ch, 'empty') || c.emptyTo || (c.emptyToBottom ? 'bottom' : 'top' );
-                        // text strings behaviour in numerical sorts
-                        c.strings[i] = ts.getData(h, ch, 'string') || c.stringTo || 'max';
-                        if (!p) {
-                            p = detectParserForColumn(table, rows, -1, i);
-                        }
-                        if (c.debug) {
-                            parsersDebug += "column:" + i + "; parser:" + p.id + "; string:" + c.strings[i] + '; empty: ' + c.empties[i] + "\n";
-                        }
-                        list.push(p);
-                    }
-                }
-                if (c.debug) {
-                    log(parsersDebug);
-                }
-                return list;
-            }
-
-            /* utils */
-            function buildCache(table) {
-                var b = table.tBodies,
-                    tc = table.config,
-                    totalRows,
-                    totalCells,
-                    parsers = tc.parsers,
-                    t, v, i, j, k, c, cols, cacheTime, colMax = [];
-                tc.cache = {};
-                // if no parsers found, return - it's an empty table.
-                if (!parsers) {
-                    return tc.debug ? log('*Empty table!* Not building a cache') : '';
-                }
-                if (tc.debug) {
-                    cacheTime = new Date();
-                }
-                // processing icon
-                if (tc.showProcessing) {
-                    ts.isProcessing(table, true);
-                }
-                for (k = 0; k < b.length; k++) {
-                    tc.cache[k] = { row: [], normalized: [] };
-                    // ignore tbodies with class name from css.cssInfoBlock
-                    if (!$(b[k]).hasClass(tc.cssInfoBlock)) {
-                        totalRows = (b[k] && b[k].rows.length) || 0;
-                        totalCells = (b[k].rows[0] && b[k].rows[0].cells.length) || 0;
-                        for (i = 0; i < totalRows; ++i) {
-                            /** Add the table data to main data array */
-                            c = $(b[k].rows[i]);
-                            cols = [];
-                            // if this is a child row, add it to the last row's children and continue to the next row
-                            if (c.hasClass(tc.cssChildRow)) {
-                                tc.cache[k].row[tc.cache[k].row.length - 1] = tc.cache[k].row[tc.cache[k].row.length - 1].add(c);
-                                // go to the next for loop
-                                continue;
-                            }
-                            tc.cache[k].row.push(c);
-                            for (j = 0; j < totalCells; ++j) {
-                                t = getElementText(table, c[0].cells[j], j);
-                                // allow parsing if the string is empty, previously parsing would change it to zero,
-                                // in case the parser needs to extract data from the table cell attributes
-                                v = parsers[j].format(t, table, c[0].cells[j], j);
-                                cols.push(v);
-                                if ((parsers[j].type || '').toLowerCase() === "numeric") {
-                                    colMax[j] = Math.max(Math.abs(v), colMax[j] || 0); // determine column max value (ignore sign)
-                                }
-                            }
-                            cols.push(tc.cache[k].normalized.length); // add position for rowCache
-                            tc.cache[k].normalized.push(cols);
-                        }
-                        tc.cache[k].colMax = colMax;
-                    }
-                }
-                if (tc.showProcessing) {
-                    ts.isProcessing(table); // remove processing icon
-                }
-                if (tc.debug) {
-                    benchmark("Building cache for " + totalRows + " rows", cacheTime);
-                }
-            }
-
-            // init flag (true) used by pager plugin to prevent widget application
-            function appendToTable(table, init) {
-                var c = table.config,
-                    b = table.tBodies,
-                    rows = [],
-                    c2 = c.cache,
-                    r, n, totalRows, checkCell, $bk, $tb,
-                    i, j, k, l, pos, appendTime;
-                if (!c2[0]) { return; } // empty table - fixes #206
-                if (c.debug) {
-                    appendTime = new Date();
-                }
-                for (k = 0; k < b.length; k++) {
-                    $bk = $(b[k]);
-                    if (!$bk.hasClass(c.cssInfoBlock)) {
-                        // get tbody
-                        $tb = ts.processTbody(table, $bk, true);
-                        r = c2[k].row;
-                        n = c2[k].normalized;
-                        totalRows = n.length;
-                        checkCell = totalRows ? (n[0].length - 1) : 0;
-                        for (i = 0; i < totalRows; i++) {
-                            pos = n[i][checkCell];
-                            rows.push(r[pos]);
-                            // removeRows used by the pager plugin
-                            if (!c.appender || !c.removeRows) {
-                                l = r[pos].length;
-                                for (j = 0; j < l; j++) {
-                                    $tb.append(r[pos][j]);
-                                }
-                            }
-                        }
-                        // restore tbody
-                        ts.processTbody(table, $tb, false);
-                    }
-                }
-                if (c.appender) {
-                    c.appender(table, rows);
-                }
-                if (c.debug) {
-                    benchmark("Rebuilt table", appendTime);
-                }
-                // apply table widgets
-                if (!init) { ts.applyWidget(table); }
-                // trigger sortend
-                $(table).trigger("sortEnd", table);
-            }
-
-            // computeTableHeaderCellIndexes from:
-            // http://www.javascripttoolbox.com/lib/table/examples.php
-            // http://www.javascripttoolbox.com/temp/table_cellindex.html
-            function computeThIndexes(t) {
-                var matrix = [],
-                    lookup = {},
-                    cols = 0, // determine the number of columns
-                    trs = $(t).find('thead:eq(0), tfoot').children('tr'), // children tr in tfoot - see issue #196
-                    i, j, k, l, c, cells, rowIndex, cellId, rowSpan, colSpan, firstAvailCol, matrixrow;
-                for (i = 0; i < trs.length; i++) {
-                    cells = trs[i].cells;
-                    for (j = 0; j < cells.length; j++) {
-                        c = cells[j];
-                        rowIndex = c.parentNode.rowIndex;
-                        cellId = rowIndex + "-" + c.cellIndex;
-                        rowSpan = c.rowSpan || 1;
-                        colSpan = c.colSpan || 1;
-                        if (typeof(matrix[rowIndex]) === "undefined") {
-                            matrix[rowIndex] = [];
-                        }
-                        // Find first available column in the first row
-                        for (k = 0; k < matrix[rowIndex].length + 1; k++) {
-                            if (typeof(matrix[rowIndex][k]) === "undefined") {
-                                firstAvailCol = k;
-                                break;
-                            }
-                        }
-                        lookup[cellId] = firstAvailCol;
-                        cols = Math.max(firstAvailCol, cols);
-                        // add data-column
-                        $(c).attr({ 'data-column' : firstAvailCol }); // 'data-row' : rowIndex
-                        for (k = rowIndex; k < rowIndex + rowSpan; k++) {
-                            if (typeof(matrix[k]) === "undefined") {
-                                matrix[k] = [];
-                            }
-                            matrixrow = matrix[k];
-                            for (l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
-                                matrixrow[l] = "x";
-                            }
-                        }
-                    }
-                }
-                t.config.columns = cols; // may not be accurate if # header columns !== # tbody columns
-                return lookup;
-            }
-
-            function formatSortingOrder(v) {
-                // look for "d" in "desc" order; return true
-                return (/^d/i.test(v) || v === 1);
-            }
-
-            function buildHeaders(table) {
-                var header_index = computeThIndexes(table), ch, $t,
-                    h, i, t, lock, time, $tableHeaders, c = table.config;
-                c.headerList = [], c.headerContent = [];
-                if (c.debug) {
-                    time = new Date();
-                }
-                i = c.cssIcon ? '<i class="' + c.cssIcon + '"></i>' : ''; // add icon if cssIcon option exists
-                $tableHeaders = $(table).find(c.selectorHeaders).each(function(index) {
-                    $t = $(this);
-                    ch = c.headers[index];
-                    c.headerContent[index] = this.innerHTML; // save original header content
-                    // set up header template
-                    t = c.headerTemplate.replace(/\{content\}/g, this.innerHTML).replace(/\{icon\}/g, i);
-                    if (c.onRenderTemplate) {
-                        h = c.onRenderTemplate.apply($t, [index, t]);
-                        if (h && typeof h === 'string') { t = h; } // only change t if something is returned
-                    }
-                    this.innerHTML = '<div class="tablesorter-header-inner">' + t + '</div>'; // faster than wrapInner
-
-                    if (c.onRenderHeader) { c.onRenderHeader.apply($t, [index]); }
-
-                    this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
-                    this.order = formatSortingOrder( ts.getData($t, ch, 'sortInitialOrder') || c.sortInitialOrder ) ? [1,0,2] : [0,1,2];
-                    this.count = -1; // set to -1 because clicking on the header automatically adds one
-                    if (ts.getData($t, ch, 'sorter') === 'false') {
-                        this.sortDisabled = true;
-                        $t.addClass('sorter-false');
-                    } else {
-                        $t.removeClass('sorter-false');
-                    }
-                    this.lockedOrder = false;
-                    lock = ts.getData($t, ch, 'lockedOrder') || false;
-                    if (typeof(lock) !== 'undefined' && lock !== false) {
-                        this.order = this.lockedOrder = formatSortingOrder(lock) ? [1,1,1] : [0,0,0];
-                    }
-                    $t.addClass( (this.sortDisabled ? 'sorter-false ' : ' ') + c.cssHeader );
-                    // add cell to headerList
-                    c.headerList[index] = this;
-                    // add to parent in case there are multiple rows
-                    $t.parent().addClass(c.cssHeaderRow);
-                });
-                if (table.config.debug) {
-                    benchmark("Built headers:", time);
-                    log($tableHeaders);
-                }
-                return $tableHeaders;
-            }
-
-            function setHeadersCss(table) {
-                var f, i, j, l,
-                    c = table.config,
-                    list = c.sortList,
-                    css = [c.cssAsc, c.cssDesc],
-                // find the footer
-                    $t = $(table).find('tfoot tr').children().removeClass(css.join(' '));
-                // remove all header information
-                c.$headers.removeClass(css.join(' '));
-                l = list.length;
-                for (i = 0; i < l; i++) {
-                    // direction = 2 means reset!
-                    if (list[i][1] !== 2) {
-                        // multicolumn sorting updating - choose the :last in case there are nested columns
-                        f = c.$headers.not('.sorter-false').filter('[data-column="' + list[i][0] + '"]' + (l === 1 ? ':last' : '') );
-                        if (f.length) {
-                            for (j = 0; j < f.length; j++) {
-                                if (!f[j].sortDisabled) {
-                                    f.eq(j).addClass(css[list[i][1]]);
-                                    // add sorted class to footer, if it exists
-                                    if ($t.length) {
-                                        $t.filter('[data-column="' + list[i][0] + '"]').eq(j).addClass(css[list[i][1]]);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-
-            // automatically add col group, and column sizes if set
-            function fixColumnWidth(table) {
-                if (table.config.widthFixed && $(table).find('colgroup').length === 0) {
-                    var colgroup = $('<colgroup>'),
-                        overallWidth = $(table).width();
-                    $(table.tBodies[0]).find("tr:first").children("td").each(function() {
-                        colgroup.append($('<col>').css('width', parseInt(($(this).width()/overallWidth)*1000, 10)/10 + '%'));
-                    });
-                    $(table).prepend(colgroup);
-                }
-            }
-
-            function updateHeaderSortCount(table, list) {
-                var s, t, o, c = table.config,
-                    sl = list || c.sortList;
-                c.sortList = [];
-                $.each(sl, function(i,v){
-                    // ensure all sortList values are numeric - fixes #127
-                    s = [ parseInt(v[0], 10), parseInt(v[1], 10) ];
-                    // make sure header exists
-                    o = c.headerList[s[0]];
-                    if (o) { // prevents error if sorton array is wrong
-                        c.sortList.push(s);
-                        t = $.inArray(s[1], o.order); // fixes issue #167
-                        o.count = t >= 0 ? t : s[1] % (c.sortReset ? 3 : 2);
-                    }
-                });
-            }
-
-            function getCachedSortType(parsers, i) {
-                return (parsers && parsers[i]) ? parsers[i].type || '' : '';
-            }
-
-            // sort multiple columns
-            function multisort(table) { /*jshint loopfunc:true */
-                var dynamicExp, sortWrapper, col, mx = 0, dir = 0, tc = table.config,
-                    sortList = tc.sortList, l = sortList.length, bl = table.tBodies.length,
-                    sortTime, i, j, k, c, colMax, cache, lc, s, e, order, orgOrderCol;
-                if (tc.serverSideSorting || !tc.cache[0]) { // empty table - fixes #206
-                    return;
-                }
-                if (tc.debug) { sortTime = new Date(); }
-                for (k = 0; k < bl; k++) {
-                    colMax = tc.cache[k].colMax;
-                    cache = tc.cache[k].normalized;
-                    lc = cache.length;
-                    orgOrderCol = (cache && cache[0]) ? cache[0].length - 1 : 0;
-                    cache.sort(function(a, b) {
-                        // cache is undefined here in IE, so don't use it!
-                        for (i = 0; i < l; i++) {
-                            c = sortList[i][0];
-                            order = sortList[i][1];
-                            // fallback to natural sort since it is more robust
-                            s = /n/i.test(getCachedSortType(tc.parsers, c)) ? "Numeric" : "Text";
-                            s += order === 0 ? "" : "Desc";
-                            if (/Numeric/.test(s) && tc.strings[c]) {
-                                // sort strings in numerical columns
-                                if (typeof (tc.string[tc.strings[c]]) === 'boolean') {
-                                    dir = (order === 0 ? 1 : -1) * (tc.string[tc.strings[c]] ? -1 : 1);
-                                } else {
-                                    dir = (tc.strings[c]) ? tc.string[tc.strings[c]] || 0 : 0;
-                                }
-                            }
-                            var sort = $.tablesorter["sort" + s](table, a[c], b[c], c, colMax[c], dir);
-                            if (sort) { return sort; }
-                        }
-                        return a[orgOrderCol] - b[orgOrderCol];
-                    });
-                }
-                if (tc.debug) { benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time", sortTime); }
-            }
-
-            function resortComplete($table, callback){
-                $table.trigger('updateComplete');
-                if (typeof callback === "function") {
-                    callback($table[0]);
-                }
-            }
-
-            function checkResort($table, flag, callback) {
-                if (flag !== false) {
-                    $table.trigger("sorton", [$table[0].config.sortList, function(){
-                        resortComplete($table, callback);
-                    }]);
-                } else {
-                    resortComplete($table, callback);
-                }
-            }
-
-            /* public methods */
-            ts.construct = function(settings) {
-                return this.each(function() {
-                    // if no thead or tbody, or tablesorter is already present, quit
-                    if (!this.tHead || this.tBodies.length === 0 || this.hasInitialized === true) {
-                        return (this.config && this.config.debug) ? log('stopping initialization! No thead, tbody or tablesorter has already been initialized') : '';
-                    }
-                    // declare
-                    var $cell, $this = $(this), $t0 = this,
-                        c, i, j, k = '', a, s, o, downTime,
-                        m = $.metadata;
-                    // initialization flag
-                    $t0.hasInitialized = false;
-                    // new blank config object
-                    $t0.config = {};
-                    // merge and extend
-                    c = $.extend(true, $t0.config, ts.defaults, settings);
-                    // save the settings where they read
-                    $.data($t0, "tablesorter", c);
-                    if (c.debug) { $.data( $t0, 'startoveralltimer', new Date()); }
-                    // constants
-                    c.supportsTextContent = $('<span>x</span>')[0].textContent === 'x';
-                    c.supportsDataObject = parseFloat($.fn.jquery) >= 1.4;
-                    // digit sort text location; keeping max+/- for backwards compatibility
-                    c.string = { 'max': 1, 'min': -1, 'max+': 1, 'max-': -1, 'zero': 0, 'none': 0, 'null': 0, 'top': true, 'bottom': false };
-                    // add table theme class only if there isn't already one there
-                    if (!/tablesorter\-/.test($this.attr('class'))) {
-                        k = (c.theme !== '' ? ' tablesorter-' + c.theme : '');
-                    }
-                    c.$table = $this.addClass(c.tableClass + k);
-                    c.$tbodies = $this.children('tbody:not(.' + c.cssInfoBlock + ')');
-                    // build headers
-                    c.$headers = buildHeaders($t0);
-                    // fixate columns if the users supplies the fixedWidth option
-                    // do this after theme has been applied
-                    fixColumnWidth($t0);
-                    // try to auto detect column type, and store in tables config
-                    c.parsers = buildParserCache($t0);
-                    // build the cache for the tbody cells
-                    // delayInit will delay building the cache until the user starts a sort
-                    if (!c.delayInit) { buildCache($t0); }
-                    // apply event handling to headers
-                    // this is to big, perhaps break it out?
-                    c.$headers
-                        // http://stackoverflow.com/questions/5312849/jquery-find-self; andSelf() deprecated in jQuery 1.8
-                        .find('*')[ $.fn.addBack ? 'addBack': 'andSelf' ]().filter(c.selectorSort)
-                        .unbind('mousedown.tablesorter mouseup.tablesorter')
-                        .bind('mousedown.tablesorter mouseup.tablesorter', function(e, external) {
-                            // jQuery v1.2.6 doesn't have closest()
-                            var $cell = this.tagName.match('TH|TD') ? $(this) : $(this).parents('th, td').filter(':last'), cell = $cell[0];
-                            // only recognize left clicks
-                            if ((e.which || e.button) !== 1) { return false; }
-                            // set timer on mousedown
-                            if (e.type === 'mousedown') {
-                                downTime = new Date().getTime();
-                                return e.target.tagName === "INPUT" ? '' : !c.cancelSelection;
-                            }
-                            // ignore long clicks (prevents resizable widget from initializing a sort)
-                            if (external !== true && (new Date().getTime() - downTime > 250)) { return false; }
-                            if (c.delayInit && !c.cache) { buildCache($t0); }
-                            if (!cell.sortDisabled) {
-                                // Only call sortStart if sorting is enabled
-                                $this.trigger("sortStart", $t0);
-                                // store exp, for speed
-                                // $cell = $(this);
-                                k = !e[c.sortMultiSortKey];
-                                // get current column sort order
-                                cell.count = e[c.sortResetKey] ? 2 : (cell.count + 1) % (c.sortReset ? 3 : 2);
-                                // reset all sorts on non-current column - issue #30
-                                if (c.sortRestart) {
-                                    i = cell;
-                                    c.$headers.each(function() {
-                                        // only reset counts on columns that weren't just clicked on and if not included in a multisort
-                                        if (this !== i && (k || !$(this).is('.' + c.cssDesc + ',.' + c.cssAsc))) {
-                                            this.count = -1;
-                                        }
-                                    });
-                                }
-                                // get current column index
-                                i = cell.column;
-                                // user only wants to sort on one column
-                                if (k) {
-                                    // flush the sort list
-                                    c.sortList = [];
-                                    if (c.sortForce !== null) {
-                                        a = c.sortForce;
-                                        for (j = 0; j < a.length; j++) {
-                                            if (a[j][0] !== i) {
-                                                c.sortList.push(a[j]);
-                                            }
-                                        }
-                                    }
-                                    // add column to sort list
-                                    o = cell.order[cell.count];
-                                    if (o < 2) {
-                                        c.sortList.push([i, o]);
-                                        // add other columns if header spans across multiple
-                                        if (cell.colSpan > 1) {
-                                            for (j = 1; j < cell.colSpan; j++) {
-                                                c.sortList.push([i + j, o]);
-                                            }
-                                        }
-                                    }
-                                    // multi column sorting
-                                } else {
-                                    // get rid of the sortAppend before adding more - fixes issue #115
-                                    if (c.sortAppend && c.sortList.length > 1) {
-                                        if (ts.isValueInArray(c.sortAppend[0][0], c.sortList)) {
-                                            c.sortList.pop();
-                                        }
-                                    }
-                                    // the user has clicked on an already sorted column
-                                    if (ts.isValueInArray(i, c.sortList)) {
-                                        // reverse the sorting direction for all tables
-                                        for (j = 0; j < c.sortList.length; j++) {
-                                            s = c.sortList[j];
-                                            o = c.headerList[s[0]];
-                                            if (s[0] === i) {
-                                                s[1] = o.order[o.count];
-                                                if (s[1] === 2) {
-                                                    c.sortList.splice(j,1);
-                                                    o.count = -1;
-                                                }
-                                            }
-                                        }
-                                    } else {
-                                        // add column to sort list array
-                                        o = cell.order[cell.count];
-                                        if (o < 2) {
-                                            c.sortList.push([i, o]);
-                                            // add other columns if header spans across multiple
-                                            if (cell.colSpan > 1) {
-                                                for (j = 1; j < cell.colSpan; j++) {
-                                                    c.sortList.push([i + j, o]);
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
-                                if (c.sortAppend !== null) {
-                                    a = c.sortAppend;
-                                    for (j = 0; j < a.length; j++) {
-                                        if (a[j][0] !== i) {
-                                            c.sortList.push(a[j]);
-                                        }
-                                    }
-                                }
-                                // sortBegin event triggered immediately before the sort
-                                $this.trigger("sortBegin", $t0);
-                                // setTimeout needed so the processing icon shows up
-                                setTimeout(function(){
-                                    // set css for headers
-                                    setHeadersCss($t0);
-                                    multisort($t0);
-                                    appendToTable($t0);
-                                }, 1);
-                            }
-                        });
-                    if (c.cancelSelection) {
-                        // cancel selection
-                        c.$headers.each(function() {
-                            this.onselectstart = function() {
-                                return false;
-                            };
-                        });
-                    }
-                    // apply easy methods that trigger boundd events
-                    $this
-                        .unbind('sortReset update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave '.split(' ').join('.tablesorter '))
-                        .bind("sortReset.tablesorter", function(){
-                            c.sortList = [];
-                            setHeadersCss($t0);
-                            multisort($t0);
-                            appendToTable($t0);
-                        })
-                        .bind("update.tablesorter updateRows.tablesorter", function(e, resort, callback) {
-                            // remove rows/elements before update
-                            $this.find(c.selectorRemove).remove();
-                            // rebuild parsers
-                            c.parsers = buildParserCache($t0);
-                            // rebuild the cache map
-                            buildCache($t0);
-                            checkResort($this, resort, callback);
-                        })
-                        .bind("updateCell.tablesorter", function(e, cell, resort, callback) {
-                            $this.find(c.selectorRemove).remove();
-                            // get position from the dom
-                            var l, row, icell,
-                                $tb = $this.find('tbody'),
-                            // update cache - format: function(s, table, cell, cellIndex)
-                            // no closest in jQuery v1.2.6 - tbdy = $tb.index( $(cell).closest('tbody') ),$row = $(cell).closest('tr');
-                                tbdy = $tb.index( $(cell).parents('tbody').filter(':last') ),
-                                $row = $(cell).parents('tr').filter(':last');
-                            cell = $(cell)[0]; // in case cell is a jQuery object
-                            // tbody may not exist if update is initialized while tbody is removed for processing
-                            if ($tb.length && tbdy >= 0) {
-                                row = $tb.eq(tbdy).find('tr').index( $row );
-                                icell = cell.cellIndex;
-                                l = c.cache[tbdy].normalized[row].length - 1;
-                                c.cache[tbdy].row[$t0.config.cache[tbdy].normalized[row][l]] = $row;
-                                c.cache[tbdy].normalized[row][icell] = c.parsers[icell].format( getElementText($t0, cell, icell), $t0, cell, icell );
-                                checkResort($this, resort, callback);
-                            }
-                        })
-                        .bind("addRows.tablesorter", function(e, $row, resort, callback) {
-                            var i, rows = $row.filter('tr').length,
-                                dat = [], l = $row[0].cells.length,
-                                tbdy = $this.find('tbody').index( $row.closest('tbody') );
-                            // fixes adding rows to an empty table - see issue #179
-                            if (!c.parsers) {
-                                c.parsers = buildParserCache($t0);
-                            }
-                            // add each row
-                            for (i = 0; i < rows; i++) {
-                                // add each cell
-                                for (j = 0; j < l; j++) {
-                                    dat[j] = c.parsers[j].format( getElementText($t0, $row[i].cells[j], j), $t0, $row[i].cells[j], j );
-                                }
-                                // add the row index to the end
-                                dat.push(c.cache[tbdy].row.length);
-                                // update cache
-                                c.cache[tbdy].row.push([$row[i]]);
-                                c.cache[tbdy].normalized.push(dat);
-                                dat = [];
-                            }
-                            // resort using current settings
-                            checkResort($this, resort, callback);
-                        })
-                        .bind("sorton.tablesorter", function(e, list, callback, init) {
-                            $this.trigger("sortStart", this);
-                            // update header count index
-                            updateHeaderSortCount($t0, list);
-                            // set css for headers
-                            setHeadersCss($t0);
-                            // sort the table and append it to the dom
-                            multisort($t0);
-                            appendToTable($t0, init);
-                            if (typeof callback === "function") {
-                                callback($t0);
-                            }
-                        })
-                        .bind("appendCache.tablesorter", function(e, callback, init) {
-                            appendToTable($t0, init);
-                            if (typeof callback === "function") {
-                                callback($t0);
-                            }
-                        })
-                        .bind("applyWidgetId.tablesorter", function(e, id) {
-                            ts.getWidgetById(id).format($t0, c, c.widgetOptions);
-                        })
-                        .bind("applyWidgets.tablesorter", function(e, init) {
-                            // apply widgets
-                            ts.applyWidget($t0, init);
-                        })
-                        .bind("refreshWidgets.tablesorter", function(e, all, dontapply){
-                            ts.refreshWidgets($t0, all, dontapply);
-                        })
-                        .bind("destroy.tablesorter", function(e, c, cb){
-                            ts.destroy($t0, c, cb);
-                        });
-
-                    // get sort list from jQuery data or metadata
-                    // in jQuery < 1.4, an error occurs when calling $this.data()
-                    if (c.supportsDataObject && typeof $this.data().sortlist !== 'undefined') {
-                        c.sortList = $this.data().sortlist;
-                    } else if (m && ($this.metadata() && $this.metadata().sortlist)) {
-                        c.sortList = $this.metadata().sortlist;
-                    }
-                    // apply widget init code
-                    ts.applyWidget($t0, true);
-                    // if user has supplied a sort list to constructor
-                    if (c.sortList.length > 0) {
-                        $this.trigger("sorton", [c.sortList, {}, !c.initWidgets]);
-                    } else if (c.initWidgets) {
-                        // apply widget format
-                        ts.applyWidget($t0);
-                    }
-
-                    // show processesing icon
-                    if (c.showProcessing) {
-                        $this
-                            .unbind('sortBegin.tablesorter sortEnd.tablesorter')
-                            .bind('sortBegin.tablesorter sortEnd.tablesorter', function(e) {
-                                ts.isProcessing($t0, e.type === 'sortBegin');
-                            });
-                    }
-
-                    // initialized
-                    $t0.hasInitialized = true;
-                    if (c.debug) {
-                        ts.benchmark("Overall initialization time", $.data( $t0, 'startoveralltimer'));
-                    }
-                    $this.trigger('tablesorter-initialized', $t0);
-                    if (typeof c.initialized === 'function') { c.initialized($t0); }
-                });
-            };
-
-            // *** Process table ***
-            // add processing indicator
-            ts.isProcessing = function(table, toggle, $ths) {
-                var c = table.config,
-                // default to all headers
-                    $h = $ths || $(table).find('.' + c.cssHeader);
-                if (toggle) {
-                    if (c.sortList.length > 0) {
-                        // get headers from the sortList
-                        $h = $h.filter(function(){
-                            // get data-column from attr to keep  compatibility with jQuery 1.2.6
-                            return this.sortDisabled ? false : ts.isValueInArray( parseFloat($(this).attr('data-column')), c.sortList);
-                        });
-                    }
-                    $h.addClass(c.cssProcessing);
-                } else {
-                    $h.removeClass(c.cssProcessing);
-                }
-            };
-
-            // detach tbody but save the position
-            // don't use tbody because there are portions that look for a tbody index (updateCell)
-            ts.processTbody = function(table, $tb, getIt){
-                var t, holdr;
-                if (getIt) {
-                    $tb.before('<span class="tablesorter-savemyplace"/>');
-                    holdr = ($.fn.detach) ? $tb.detach() : $tb.remove();
-                    return holdr;
-                }
-                holdr = $(table).find('span.tablesorter-savemyplace');
-                $tb.insertAfter( holdr );
-                holdr.remove();
-            };
-
-            ts.clearTableBody = function(table) {
-                table.config.$tbodies.empty();
-            };
-
-            ts.destroy = function(table, removeClasses, callback){
-                if (!table.hasInitialized) { return; }
-                // remove all widgets
-                ts.refreshWidgets(table, true, true);
-                var $t = $(table), c = table.config,
-                    $h = $t.find('thead:first'),
-                    $r = $h.find('tr.' + c.cssHeaderRow).removeClass(c.cssHeaderRow),
-                    $f = $t.find('tfoot:first > tr').children('th, td');
-                // remove widget added rows, just in case
-                $h.find('tr').not($r).remove();
-                // disable tablesorter
-                $t
-                    .removeData('tablesorter')
-                    .unbind('sortReset update updateCell addRows sorton appendCache applyWidgetId applyWidgets refreshWidgets destroy mouseup mouseleave sortBegin sortEnd '.split(' ').join('.tablesorter '));
-                c.$headers.add($f)
-                    .removeClass(c.cssHeader + ' ' + c.cssAsc + ' ' + c.cssDesc)
-                    .removeAttr('data-column');
-                $r.find(c.selectorSort).unbind('mousedown.tablesorter mouseup.tablesorter');
-                // restore headers
-                $r.children().each(function(i){
-                    $(this).html( c.headerContent[i] );
-                });
-                if (removeClasses !== false) {
-                    $t.removeClass(c.tableClass + ' tablesorter-' + c.theme);
-                }
-                // clear flag in case the plugin is initialized again
-                table.hasInitialized = false;
-                if (typeof callback === 'function') {
-                    callback(table);
-                }
-            };
-
-            // *** sort functions ***
-            // regex used in natural sort
-            ts.regex = [
-                /(^([+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?)?$|^0x[0-9a-f]+$|\d+)/gi, // chunk/tokenize numbers & letters
-                /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/, //date
-                /^0x[0-9a-f]+$/i // hex
-            ];
-
-            // Natural sort - https://github.com/overset/javascript-natural-sort
-            ts.sortText = function(table, a, b, col) {
-                if (a === b) { return 0; }
-                var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ],
-                    r = ts.regex, xN, xD, yN, yD, xF, yF, i, mx;
-                if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; }
-                if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; }
-                if (typeof c.textSorter === 'function') { return c.textSorter(a, b, table, col); }
-                // chunk/tokenize
-                xN = a.replace(r[0], '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
-                yN = b.replace(r[0], '\\0$1\\0').replace(/\\0$/, '').replace(/^\\0/, '').split('\\0');
-                // numeric, hex or date detection
-                xD = parseInt(a.match(r[2]),16) || (xN.length !== 1 && a.match(r[1]) && Date.parse(a));
-                yD = parseInt(b.match(r[2]),16) || (xD && b.match(r[1]) && Date.parse(b)) || null;
-                // first try and sort Hex codes or Dates
-                if (yD) {
-                    if ( xD < yD ) { return -1; }
-                    if ( xD > yD ) { return 1; }
-                }
-                mx = Math.max(xN.length, yN.length);
-                // natural sorting through split numeric strings and default strings
-                for (i = 0; i < mx; i++) {
-                    // find floats not starting with '0', string or 0 if not defined
-                    xF = isNaN(xN[i]) ? xN[i] || 0 : parseFloat(xN[i]) || 0;
-                    yF = isNaN(yN[i]) ? yN[i] || 0 : parseFloat(yN[i]) || 0;
-                    // handle numeric vs string comparison - number < string - (Kyle Adams)
-                    if (isNaN(xF) !== isNaN(yF)) { return (isNaN(xF)) ? 1 : -1; }
-                    // rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
-                    if (typeof xF !== typeof yF) {
-                        xF += '';
-                        yF += '';
-                    }
-                    if (xF < yF) { return -1; }
-                    if (xF > yF) { return 1; }
-                }
-                return 0;
-            };
-
-            ts.sortTextDesc = function(table, a, b, col) {
-                if (a === b) { return 0; }
-                var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ];
-                if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; }
-                if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; }
-                if (typeof c.textSorter === 'function') { return c.textSorter(b, a, table, col); }
-                return ts.sortText(table, b, a);
-            };
-
-            // return text string value by adding up ascii value
-            // so the text is somewhat sorted when using a digital sort
-            // this is NOT an alphanumeric sort
-            ts.getTextValue = function(a, mx, d) {
-                if (mx) {
-                    // make sure the text value is greater than the max numerical value (mx)
-                    var i, l = a ? a.length : 0, n = mx + d;
-                    for (i = 0; i < l; i++) {
-                        n += a.charCodeAt(i);
-                    }
-                    return d * n;
-                }
-                return 0;
-            };
-
-            ts.sortNumeric = function(table, a, b, col, mx, d) {
-                if (a === b) { return 0; }
-                var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ];
-                if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : -e || -1; }
-                if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : e || 1; }
-                if (isNaN(a)) { a = ts.getTextValue(a, mx, d); }
-                if (isNaN(b)) { b = ts.getTextValue(b, mx, d); }
-                return a - b;
-            };
-
-            ts.sortNumericDesc = function(table, a, b, col, mx, d) {
-                if (a === b) { return 0; }
-                var c = table.config, e = c.string[ (c.empties[col] || c.emptyTo ) ];
-                if (a === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? -1 : 1) : e || 1; }
-                if (b === '' && e !== 0) { return (typeof(e) === 'boolean') ? (e ? 1 : -1) : -e || -1; }
-                if (isNaN(a)) { a = ts.getTextValue(a, mx, d); }
-                if (isNaN(b)) { b = ts.getTextValue(b, mx, d); }
-                return b - a;
-            };
-
-            // used when replacing accented characters during sorting
-            ts.characterEquivalents = {
-                "a" : "\u00e1\u00e0\u00e2\u00e3\u00e4\u0105\u00e5", // áàâãäąå
-                "A" : "\u00c1\u00c0\u00c2\u00c3\u00c4\u0104\u00c5", // ÁÀÂÃÄĄÅ
-                "c" : "\u00e7\u0107\u010d", // çćč
-                "C" : "\u00c7\u0106\u010c", // ÇĆČ
-                "e" : "\u00e9\u00e8\u00ea\u00eb\u011b\u0119", // éèêëěę
-                "E" : "\u00c9\u00c8\u00ca\u00cb\u011a\u0118", // ÉÈÊËĚĘ
-                "i" : "\u00ed\u00ec\u0130\u00ee\u00ef\u0131", // íìİîïı
-                "I" : "\u00cd\u00cc\u0130\u00ce\u00cf", // ÍÌİÎÏ
-                "o" : "\u00f3\u00f2\u00f4\u00f5\u00f6", // óòôõö
-                "O" : "\u00d3\u00d2\u00d4\u00d5\u00d6", // ÓÒÔÕÖ
-                "ss": "\u00df", // ß (s sharp)
-                "SS": "\u1e9e", // ẞ (Capital sharp s)
-                "u" : "\u00fa\u00f9\u00fb\u00fc\u016f", // úùûüů
-                "U" : "\u00da\u00d9\u00db\u00dc\u016e" // ÚÙÛÜŮ
-            };
-            ts.replaceAccents = function(s) {
-                var a, acc = '[', eq = ts.characterEquivalents;
-                if (!ts.characterRegex) {
-                    ts.characterRegexArray = {};
-                    for (a in eq) {
-                        if (typeof a === 'string') {
-                            acc += eq[a];
-                            ts.characterRegexArray[a] = new RegExp('[' + eq[a] + ']', 'g');
-                        }
-                    }
-                    ts.characterRegex = new RegExp(acc + ']');
-                }
-                if (ts.characterRegex.test(s)) {
-                    for (a in eq) {
-                        if (typeof a === 'string') {
-                            s = s.replace( ts.characterRegexArray[a], a );
-                        }
-                    }
-                }
-                return s;
-            };
-
-            // *** utilities ***
-            ts.isValueInArray = function(v, a) {
-                var i, l = a.length;
-                for (i = 0; i < l; i++) {
-                    if (a[i][0] === v) {
-                        return true;
-                    }
-                }
-                return false;
-            };
-
-            ts.addParser = function(parser) {
-                var i, l = ts.parsers.length, a = true;
-                for (i = 0; i < l; i++) {
-                    if (ts.parsers[i].id.toLowerCase() === parser.id.toLowerCase()) {
-                        a = false;
-                    }
-                }
-                if (a) {
-                    ts.parsers.push(parser);
-                }
-            };
-
-            ts.getParserById = function(name) {
-                var i, l = ts.parsers.length;
-                for (i = 0; i < l; i++) {
-                    if (ts.parsers[i].id.toLowerCase() === (name.toString()).toLowerCase()) {
-                        return ts.parsers[i];
-                    }
-                }
-                return false;
-            };
-
-            ts.addWidget = function(widget) {
-                ts.widgets.push(widget);
-            };
-
-            ts.getWidgetById = function(name) {
-                var i, w, l = ts.widgets.length;
-                for (i = 0; i < l; i++) {
-                    w = ts.widgets[i];
-                    if (w && w.hasOwnProperty('id') && w.id.toLowerCase() === name.toLowerCase()) {
-                        return w;
-                    }
-                }
-            };
-
-            ts.applyWidget = function(table, init) {
-                var c = table.config,
-                    wo = c.widgetOptions,
-                    ws = c.widgets.sort().reverse(), // ensure that widgets are always applied in a certain order
-                    time, i, w, l = ws.length;
-                // make zebra last
-                i = $.inArray('zebra', c.widgets);
-                if (i >= 0) {
-                    c.widgets.splice(i,1);
-                    c.widgets.push('zebra');
-                }
-                if (c.debug) {
-                    time = new Date();
-                }
-                // add selected widgets
-                for (i = 0; i < l; i++) {
-                    w = ts.getWidgetById(ws[i]);
-                    if ( w ) {
-                        if (init === true && w.hasOwnProperty('init')) {
-                            w.init(table, w, c, wo);
-                        } else if (!init && w.hasOwnProperty('format')) {
-                            w.format(table, c, wo);
-                        }
-                    }
-                }
-                if (c.debug) {
-                    benchmark("Completed " + (init === true ? "initializing" : "applying") + " widgets", time);
-                }
-            };
-
-            ts.refreshWidgets = function(table, doAll, dontapply) {
-                var i, c = table.config,
-                    cw = c.widgets,
-                    w = ts.widgets, l = w.length;
-                // remove previous widgets
-                for (i = 0; i < l; i++){
-                    if ( w[i] && w[i].id && (doAll || $.inArray( w[i].id, cw ) < 0) ) {
-                        if (c.debug) { log( 'Refeshing widgets: Removing ' + w[i].id  ); }
-                        if (w[i].hasOwnProperty('remove')) { w[i].remove(table, c, c.widgetOptions); }
-                    }
-                }
-                if (dontapply !== true) {
-                    ts.applyWidget(table, doAll);
-                }
-            };
-
-            // get sorter, string, empty, etc options for each column from
-            // jQuery data, metadata, header option or header class name ("sorter-false")
-            // priority = jQuery data > meta > headers option > header class name
-            ts.getData = function(h, ch, key) {
-                var val = '', $h = $(h), m, cl;
-                if (!$h.length) { return ''; }
-                m = $.metadata ? $h.metadata() : false;
-                cl = ' ' + ($h.attr('class') || '');
-                if (typeof $h.data(key) !== 'undefined' || typeof $h.data(key.toLowerCase()) !== 'undefined'){
-                    // "data-lockedOrder" is assigned to "lockedorder"; but "data-locked-order" is assigned to "lockedOrder"
-                    // "data-sort-initial-order" is assigned to "sortInitialOrder"
-                    val += $h.data(key) || $h.data(key.toLowerCase());
-                } else if (m && typeof m[key] !== 'undefined') {
-                    val += m[key];
-                } else if (ch && typeof ch[key] !== 'undefined') {
-                    val += ch[key];
-                } else if (cl !== ' ' && cl.match(' ' + key + '-')) {
-                    // include sorter class name "sorter-text", etc
-                    val = cl.match( new RegExp(' ' + key + '-(\\w+)') )[1] || '';
-                }
-                return $.trim(val);
-            };
-
-            ts.formatFloat = function(s, table) {
-                if (typeof(s) !== 'string' || s === '') { return s; }
-                // allow using formatFloat without a table; defaults to US number format
-                var i,
-                    t = table && table.config ? table.config.usNumberFormat !== false :
-                        typeof table !== "undefined" ? table : true;
-                if (t) {
-                    // US Format - 1,234,567.89 -> 1234567.89
-                    s = s.replace(/,/g,'');
-                } else {
-                    // German Format = 1.234.567,89 -> 1234567.89
-                    // French Format = 1 234 567,89 -> 1234567.89
-                    s = s.replace(/[\s|\.]/g,'').replace(/,/g,'.');
-                }
-                if(/^\s*\([.\d]+\)/.test(s)) {
-                    // make (#) into a negative number -> (10) = -10
-                    s = s.replace(/^\s*\(/,'-').replace(/\)/,'');
-                }
-                i = parseFloat(s);
-                // return the text instead of zero
-                return isNaN(i) ? $.trim(s) : i;
-            };
-
-            ts.isDigit = function(s) {
-                // replace all unwanted chars and match
-                return isNaN(s) ? (/^[\-+(]?\d+[)]?$/).test(s.toString().replace(/[,.'"\s]/g, '')) : true;
-            };
-
-        }()
-    });
-
-    // make shortcut
-    var ts = $.tablesorter;
-
-    // extend plugin scope
-    $.fn.extend({
-        tablesorter: ts.construct
-    });
-
-    // add default parsers
-    ts.addParser({
-        id: "text",
-        is: function(s, table, node) {
-            return true;
-        },
-        format: function(s, table, cell, cellIndex) {
-            var c = table.config;
-            s = $.trim( c.ignoreCase ? s.toLocaleLowerCase() : s );
-            return c.sortLocaleCompare ? ts.replaceAccents(s) : s;
-        },
-        type: "text"
-    });
-
-    ts.addParser({
-        id: "currency",
-        is: function(s) {
-            return (/^\(?\d+[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]|[\u00a3$\u20ac\u00a4\u00a5\u00a2?.]\d+\)?$/).test((s || '').replace(/[,. ]/g,'')); // £$€¤¥¢
-        },
-        format: function(s, table) {
-            return ts.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "ipAddress",
-        is: function(s) {
-            return (/^\d{1,3}[\.]\d{1,3}[\.]\d{1,3}[\.]\d{1,3}$/).test(s);
-        },
-        format: function(s, table) {
-            var i, a = s.split("."),
-                r = "",
-                l = a.length;
-            for (i = 0; i < l; i++) {
-                r += ("00" + a[i]).slice(-3);
-            }
-            return ts.formatFloat(r, table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "url",
-        is: function(s) {
-            return (/^(https?|ftp|file):\/\//).test(s);
-        },
-        format: function(s) {
-            return $.trim(s.replace(/(https?|ftp|file):\/\//, ''));
-        },
-        type: "text"
-    });
-
-    ts.addParser({
-        id: "isoDate",
-        is: function(s) {
-            return (/^\d{4}[\/\-]\d{1,2}[\/\-]\d{1,2}/).test(s);
-        },
-        format: function(s, table) {
-            return ts.formatFloat((s !== "") ? (new Date(s.replace(/-/g, "/")).getTime() || "") : "", table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "percent",
-        is: function(s) {
-            return (/(\d\s?%|%\s?\d)/).test(s);
-        },
-        format: function(s, table) {
-            return ts.formatFloat(s.replace(/%/g, ""), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "usLongDate",
-        is: function(s) {
-            // two digit years are not allowed cross-browser
-            // Jan 01, 2013 12:34:56 PM or 01 Jan 2013
-            return (/^[A-Z]{3,10}\.?\s+\d{1,2},?\s+(\d{4})(\s+\d{1,2}:\d{2}(:\d{2})?(\s+[AP]M)?)?$/i).test(s) || (/^\d{1,2}\s+[A-Z]{3,10}\s+\d{4}/i).test(s);
-        },
-        format: function(s, table) {
-            return ts.formatFloat( (new Date(s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ''), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "shortDate", // "mmddyyyy", "ddmmyyyy" or "yyyymmdd"
-        is: function(s) {
-            // testing for ####-##-####, so it's not perfect
-            return (/^(\d{1,2}|\d{4})[\/\-\,\.\s+]\d{1,2}[\/\-\.\,\s+](\d{1,2}|\d{4})$/).test(s);
-        },
-        format: function(s, table, cell, cellIndex) {
-            var c = table.config, ci = c.headerList[cellIndex],
-                format = ci.shortDateFormat;
-            if (typeof format === 'undefined') {
-                // cache header formatting so it doesn't getData for every cell in the column
-                format = ci.shortDateFormat = ts.getData( ci, c.headers[cellIndex], 'dateFormat') || c.dateFormat;
-            }
-            s = s.replace(/\s+/g," ").replace(/[\-|\.|\,]/g, "/");
-            if (format === "mmddyyyy") {
-                s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$1/$2");
-            } else if (format === "ddmmyyyy") {
-                s = s.replace(/(\d{1,2})[\/\s](\d{1,2})[\/\s](\d{4})/, "$3/$2/$1");
-            } else if (format === "yyyymmdd") {
-                s = s.replace(/(\d{4})[\/\s](\d{1,2})[\/\s](\d{1,2})/, "$1/$2/$3");
-            }
-            return ts.formatFloat( (new Date(s).getTime() || ''), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "time",
-        is: function(s) {
-            return (/^(([0-2]?\d:[0-5]\d)|([0-1]?\d:[0-5]\d\s?([AP]M)))$/i).test(s);
-        },
-        format: function(s, table) {
-            return ts.formatFloat( (new Date("2000/01/01 " + s.replace(/(\S)([AP]M)$/i, "$1 $2")).getTime() || ""), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "digit",
-        is: function(s) {
-            return ts.isDigit(s);
-        },
-        format: function(s, table) {
-            return ts.formatFloat(s.replace(/[^\w,. \-()]/g, ""), table);
-        },
-        type: "numeric"
-    });
-
-    ts.addParser({
-        id: "metadata",
-        is: function(s) {
-            return false;
-        },
-        format: function(s, table, cell) {
-            var c = table.config,
-                p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
-            return $(cell).metadata()[p];
-        },
-        type: "numeric"
-    });
-
-    // add default widgets
-    ts.addWidget({
-        id: "zebra",
-        format: function(table, c, wo) {
-            var $tb, $tv, $tr, row, even, time, k, l,
-                child = new RegExp(c.cssChildRow, 'i'),
-                b = c.$tbodies;
-            if (c.debug) {
-                time = new Date();
-            }
-            for (k = 0; k < b.length; k++ ) {
-                // loop through the visible rows
-                $tb = b.eq(k);
-                l = $tb.children('tr').length;
-                if (l > 1) {
-                    row = 0;
-                    $tv = $tb.children('tr:visible');
-                    // revered back to using jQuery each - strangely it's the fastest method
-                    /*jshint loopfunc:true */
-                    $tv.each(function(){
-                        $tr = $(this);
-                        // style children rows the same way the parent row was styled
-                        if (!child.test(this.className)) { row++; }
-                        even = (row % 2 === 0);
-                        $tr.removeClass(wo.zebra[even ? 1 : 0]).addClass(wo.zebra[even ? 0 : 1]);
-                    });
-                }
-            }
-            if (c.debug) {
-                ts.benchmark("Applying Zebra widget", time);
-            }
-        },
-        remove: function(table, c, wo){
-            var k, $tb,
-                b = c.$tbodies,
-                rmv = (c.widgetOptions.zebra || [ "even", "odd" ]).join(' ');
-            for (k = 0; k < b.length; k++ ){
-                $tb = $.tablesorter.processTbody(table, b.eq(k), true); // remove tbody
-                $tb.children().removeClass(rmv);
-                $.tablesorter.processTbody(table, $tb, false); // restore tbody
-            }
-        }
-    });
-
-})(jQuery);
\ No newline at end of file
diff --git a/UI/JsLibraries/jquery.tablesorter.pager.js b/UI/JsLibraries/jquery.tablesorter.pager.js
deleted file mode 100644
index 99081c8bf..000000000
--- a/UI/JsLibraries/jquery.tablesorter.pager.js
+++ /dev/null
@@ -1,575 +0,0 @@
-/*!
- * tablesorter pager plugin
- * updated 2/20/2013
- */
-/*jshint browser:true, jquery:true, unused:false */
-;(function($) {
-    "use strict";
-    /*jshint supernew:true */
-    $.extend({ tablesorterPager: new function() {
-
-        this.defaults = {
-            // target the pager markup
-            container: null,
-
-            // use this format: "http://mydatabase.com?page={page}&size={size}&{sortList:col}&{filterList:fcol}"
-            // where {page} is replaced by the page number, {size} is replaced by the number of records to show,
-            // {sortList:col} adds the sortList to the url into a "col" array, and {filterList:fcol} adds
-            // the filterList to the url into an "fcol" array.
-            // So a sortList = [[2,0],[3,0]] becomes "&col[2]=0&col[3]=0" in the url
-            // and a filterList = [[2,Blue],[3,13]] becomes "&fcol[2]=Blue&fcol[3]=13" in the url
-            ajaxUrl: null,
-
-            // process ajax so that the following information is returned:
-            // [ total_rows (number), rows (array of arrays), headers (array; optional) ]
-            // example:
-            // [
-            //   100,  // total rows
-            //   [
-            //     [ "row1cell1", "row1cell2", ... "row1cellN" ],
-            //     [ "row2cell1", "row2cell2", ... "row2cellN" ],
-            //     ...
-            //     [ "rowNcell1", "rowNcell2", ... "rowNcellN" ]
-            //   ],
-            //   [ "header1", "header2", ... "headerN" ] // optional
-            // ]
-            ajaxProcessing: function(ajax){ return [ 0, [], null ]; },
-
-            // output default: '{page}/{totalPages}'
-            // possible variables: {page}, {totalPages}, {filteredPages}, {startRow}, {endRow}, {filteredRows} and {totalRows}
-            output: '{startRow} to {endRow} of {totalRows} rows', // '{page}/{totalPages}'
-
-            // apply disabled classname to the pager arrows when the rows at either extreme is visible
-            updateArrows: true,
-
-            // starting page of the pager (zero based index)
-            page: 0,
-
-            // Number of visible rows
-            size: 10,
-
-            // if true, the table will remain the same height no matter how many records are displayed. The space is made up by an empty
-            // table row set to a height to compensate; default is false
-            fixedHeight: false,
-
-            // remove rows from the table to speed up the sort of large tables.
-            // setting this to false, only hides the non-visible rows; needed if you plan to add/remove rows with the pager enabled.
-            removeRows: false, // removing rows in larger tables speeds up the sort
-
-            // css class names of pager arrows
-            cssFirst: '.first', // go to first page arrow
-            cssPrev: '.prev', // previous page arrow
-            cssNext: '.next', // next page arrow
-            cssLast: '.last', // go to last page arrow
-            cssGoto: '.gotoPage', // go to page selector - select dropdown that sets the current page
-            cssPageDisplay: '.pagedisplay', // location of where the "output" is displayed
-            cssPageSize: '.pagesize', // page size selector - select dropdown that sets the "size" option
-            cssErrorRow: 'tablesorter-errorRow', // error information row
-
-            // class added to arrows when at the extremes (i.e. prev/first arrows are "disabled" when on the first page)
-            cssDisabled: 'disabled', // Note there is no period "." in front of this class name
-
-            // stuff not set by the user
-            totalRows: 0,
-            totalPages: 0,
-            filteredRows: 0,
-            filteredPages: 0
-
-        };
-
-        var $this = this,
-
-        // hide arrows at extremes
-            pagerArrows = function(c, disable) {
-                var a = 'addClass',
-                    r = 'removeClass',
-                    d = c.cssDisabled,
-                    dis = !!disable,
-                    tp = Math.min( c.totalPages, c.filteredPages );
-                if ( c.updateArrows ) {
-                    $(c.cssFirst + ',' + c.cssPrev, c.container)[ ( dis || c.page === 0 ) ? a : r ](d);
-                    $(c.cssNext + ',' + c.cssLast, c.container)[ ( dis || c.page === tp - 1 ) ? a : r ](d);
-                }
-            },
-
-            updatePageDisplay = function(table, c) {
-                var i, p, s, t, out, f = $(table).hasClass('hasFilters') && !c.ajaxUrl;
-                c.filteredRows = (f) ? table.config.$tbodies.children('tr:not(.filtered,.remove-me)').length : c.totalRows;
-                c.filteredPages = (f) ? Math.ceil( c.filteredRows / c.size ) : c.totalPages;
-                if ( Math.min( c.totalPages, c.filteredPages ) > 0 ) {
-                    t = (c.size * c.page > c.filteredRows);
-                    c.startRow = (t) ? 1 : ( c.size * c.page ) + 1;
-                    c.page = (t) ? 0 : c.page;
-                    c.endRow = Math.min( c.filteredRows, c.totalRows, c.size * ( c.page + 1 ) );
-                    out = $(c.cssPageDisplay, c.container);
-                    // form the output string
-                    s = c.output.replace(/\{(page|filteredRows|filteredPages|totalPages|startRow|endRow|totalRows)\}/gi, function(m){
-                        return {
-                            '{page}'            : c.page + 1,
-                            '{filteredRows}'    : c.filteredRows,
-                            '{filteredPages}'   : c.filteredPages,
-                            '{totalPages}'      : c.totalPages,
-                            '{startRow}'        : c.startRow,
-                            '{endRow}'          : c.endRow,
-                            '{totalRows}'       : c.totalRows
-                        }[m];
-                    });
-                    if (out[0]) {
-                        out[ (out[0].tagName === 'INPUT') ? 'val' : 'html' ](s);
-                        if ( $(c.cssGoto, c.container).length ) {
-                            t = '';
-                            p = Math.min( c.totalPages, c.filteredPages );
-                            for ( i = 1; i <= p; i++ ) {
-                                t += '<option>' + i + '</option>';
-                            }
-                            $(c.cssGoto, c.container).html(t).val(c.page + 1);
-                        }
-                    }
-                }
-                pagerArrows(c);
-                if (c.initialized) { $(table).trigger('pagerComplete', c); }
-            },
-
-            fixHeight = function(table, c) {
-                var d, h, $b = $(table.tBodies[0]);
-                if (c.fixedHeight) {
-                    $b.find('tr.pagerSavedHeightSpacer').remove();
-                    h = $.data(table, 'pagerSavedHeight');
-                    if (h) {
-                        d = h - $b.height();
-                        if ( d > 5 && $.data(table, 'pagerLastSize') === c.size && $b.children('tr:visible').length < c.size ) {
-                            $b.append('<tr class="pagerSavedHeightSpacer ' + table.config.selectorRemove.replace(/(tr)?\./g,'') + '" style="height:' + d + 'px;"></tr>');
-                        }
-                    }
-                }
-            },
-
-            changeHeight = function(table, c) {
-                var $b = $(table.tBodies[0]);
-                $b.find('tr.pagerSavedHeightSpacer').remove();
-                $.data(table, 'pagerSavedHeight', $b.height());
-                fixHeight(table, c);
-                $.data(table, 'pagerLastSize', c.size);
-            },
-
-            hideRows = function(table, c){
-                if (!c.ajaxUrl) {
-                    var i,
-                        rows = $(table.tBodies).children('tr:not(.' + table.config.cssChildRow + ')'),
-                        l = rows.length,
-                        s = ( c.page * c.size ),
-                        e =  s + c.size,
-                        j = 0; // size counter
-                    for ( i = 0; i < l; i++ ){
-                        if (!/filtered/.test(rows[i].className)) {
-                            rows[i].style.display = ( j >= s && j < e ) ? '' : 'none';
-                            j++;
-                        }
-                    }
-                }
-            },
-
-            hideRowsSetup = function(table, c){
-                c.size = parseInt( $(c.cssPageSize, c.container).find('option[selected]').val(), 10 ) || c.size;
-                $.data(table, 'pagerLastSize', c.size);
-                pagerArrows(c);
-                if ( !c.removeRows ) {
-                    hideRows(table, c);
-                    $(table).bind('sortEnd.pager filterEnd.pager', function(){
-                        hideRows(table, c);
-                    });
-                }
-            },
-
-            renderAjax = function(data, table, c, exception){
-                // process data
-                if ( typeof(c.ajaxProcessing) === "function" ) {
-                    // ajaxProcessing result: [ total, rows, headers ]
-                    var i, j, hsh, $f, $sh,
-                        $t = $(table),
-                        tc = table.config,
-                        hl = $t.find('thead th').length, tds = '',
-                        err = '<tr class="' + c.cssErrorRow + ' ' + tc.selectorRemove.replace(/(tr)?\./g,'') + '"><td style="text-align: center;" colspan="' + hl + '">' +
-                            (exception ? exception.message + ' (' + exception.name + ')' : 'No rows found') + '</td></tr>',
-                        result = c.ajaxProcessing(data) || [ 0, [] ],
-                        d = result[1] || [],
-                        l = d.length,
-                        th = result[2];
-                    if ( l > 0 ) {
-                        for ( i = 0; i < l; i++ ) {
-                            tds += '<tr>';
-                            for ( j = 0; j < d[i].length; j++ ) {
-                                // build tbody cells
-                                tds += '<td>' + d[i][j] + '</td>';
-                            }
-                            tds += '</tr>';
-                        }
-                    }
-                    // only add new header text if the length matches
-                    if ( th && th.length === hl ) {
-                        hsh = $t.hasClass('hasStickyHeaders');
-                        $sh = $t.find('.' + ((tc.widgetOptions && tc.widgetOptions.stickyHeaders) || 'tablesorter-stickyheader'));
-                        $f = $t.find('tfoot tr:first').children();
-                        $t.find('th.' + tc.cssHeader).each(function(j){
-                            var $t = $(this), icn;
-                            // add new test within the first span it finds, or just in the header
-                            if ( $t.find('.' + tc.cssIcon).length ) {
-                                icn = $t.find('.' + tc.cssIcon).clone(true);
-                                $t.find('.tablesorter-header-inner').html( th[j] ).append(icn);
-                                if ( hsh && $sh.length ) {
-                                    icn = $sh.find('th').eq(j).find('.' + tc.cssIcon).clone(true);
-                                    $sh.find('th').eq(j).find('.tablesorter-header-inner').html( th[j] ).append(icn);
-                                }
-                            } else {
-                                $t.find('.tablesorter-header-inner').html( th[j] );
-                                $sh.find('th').eq(j).find('.tablesorter-header-inner').html( th[j] );
-                            }
-                            $f.eq(j).html( th[j] );
-                        });
-                    }
-
-                    $t.find('thead tr.' + c.cssErrorRow).remove(); // Clean up any previous error.
-                    if ( exception ) {
-                        // add error row to thead instead of tbody, or clicking on the header will result in a parser error
-                        $t.find('thead').append(err);
-                    } else {
-                        $(table.tBodies[0]).html( tds ); // add rows to first tbody
-                    }
-                    if (tc.showProcessing) {
-                        $.tablesorter.isProcessing(table); // remove loading icon
-                    }
-                    $t.trigger('update');
-                    c.totalRows = result[0] || 0;
-                    c.totalPages = Math.ceil( c.totalRows / c.size );
-                    updatePageDisplay(table, c);
-                    fixHeight(table, c);
-                    if (c.initialized) { $t.trigger('pagerChange', c); }
-                }
-                if (!c.initialized) {
-                    c.initialized = true;
-                    $(table).trigger('pagerInitialized', c);
-                }
-            },
-
-            getAjax = function(table, c){
-                var url = getAjaxUrl(table, c),
-                    tc = table.config;
-                if ( url !== '' ) {
-                    if (tc.showProcessing) {
-                        $.tablesorter.isProcessing(table, true); // show loading icon
-                    }
-                    $(document).bind('ajaxError.pager', function(e, xhr, settings, exception) {
-                        if (settings.url === url) {
-                            renderAjax(null, table, c, exception);
-                            $(document).unbind('ajaxError.pager');
-                        }
-                    });
-                    $.getJSON(url, function(data) {
-                        renderAjax(data, table, c);
-                        $(document).unbind('ajaxError.pager');
-                    });
-                }
-            },
-
-            getAjaxUrl = function(table, c) {
-                var url = (c.ajaxUrl) ? c.ajaxUrl.replace(/\{page\}/g, c.page).replace(/\{size\}/g, c.size) : '',
-                    sl = table.config.sortList,
-                    fl = c.currentFilters || [],
-                    sortCol = url.match(/\{sortList[\s+]?:[\s+]?([^}]*)\}/),
-                    filterCol = url.match(/\{filterList[\s+]?:[\s+]?([^}]*)\}/),
-                    arry = [];
-                if (sortCol) {
-                    sortCol = sortCol[1];
-                    $.each(sl, function(i,v){
-                        arry.push(sortCol + '[' + v[0] + ']=' + v[1]);
-                    });
-                    // if the arry is empty, just add the col parameter... "&{sortList:col}" becomes "&col"
-                    url = url.replace(/\{sortList[\s+]?:[\s+]?([^\}]*)\}/g, arry.length ? arry.join('&') : sortCol );
-                }
-                if (filterCol) {
-                    filterCol = filterCol[1];
-                    $.each(fl, function(i,v){
-                        if (v) {
-                            arry.push(filterCol + '[' + i + ']=' + encodeURIComponent(v));
-                        }
-                    });
-                    // if the arry is empty, just add the fcol parameter... "&{filterList:fcol}" becomes "&fcol"
-                    url = url.replace(/\{filterList[\s+]?:[\s+]?([^\}]*)\}/g, arry.length ? arry.join('&') : filterCol );
-                }
-
-                return url;
-            },
-
-            renderTable = function(table, rows, c) {
-                c.isDisabled = false; // needed because sorting will change the page and re-enable the pager
-                var i, j, o,
-                    f = document.createDocumentFragment(),
-                    l = rows.length,
-                    s = ( c.page * c.size ),
-                    e = ( s + c.size );
-                if ( l < 1 ) { return; } // empty table, abort!
-                if (c.initialized) { $(table).trigger('pagerChange', c); }
-                if ( !c.removeRows ) {
-                    hideRows(table, c);
-                } else {
-                    if ( e > rows.length ) {
-                        e = rows.length;
-                    }
-                    $(table.tBodies[0]).addClass('tablesorter-hidden');
-                    $.tablesorter.clearTableBody(table);
-                    for ( i = s; i < e; i++ ) {
-                        o = rows[i];
-                        l = o.length;
-                        for ( j = 0; j < l; j++ ) {
-                            f.appendChild(o[j]);
-                        }
-                    }
-                    table.tBodies[0].appendChild(f);
-                    $(table.tBodies[0]).removeClass('tablesorter-hidden');
-                }
-                if ( c.page >= c.totalPages ) {
-                    moveToLastPage(table, c);
-                }
-                updatePageDisplay(table, c);
-                if ( !c.isDisabled ) { fixHeight(table, c); }
-                $(table).trigger('applyWidgets');
-            },
-
-            showAllRows = function(table, c){
-                if ( c.ajax ) {
-                    pagerArrows(c, true);
-                } else {
-                    c.isDisabled = true;
-                    $.data(table, 'pagerLastPage', c.page);
-                    $.data(table, 'pagerLastSize', c.size);
-                    c.page = 0;
-                    c.size = c.totalRows;
-                    c.totalPages = 1;
-                    $('tr.pagerSavedHeightSpacer', table.tBodies[0]).remove();
-                    renderTable(table, table.config.rowsCopy, c);
-                }
-                // disable size selector
-                $(c.container).find(c.cssPageSize + ',' + c.cssGoto).each(function(){
-                    $(this).addClass(c.cssDisabled)[0].disabled = true;
-                });
-            },
-
-            moveToPage = function(table, c) {
-                if ( c.isDisabled ) { return; }
-                var p = Math.min( c.totalPages, c.filteredPages );
-                if ( c.page < 0 ) { c.page = 0; }
-                if ( c.page > ( p - 1 ) && p !== 0 ) { c.page = p - 1; }
-                if (c.ajax) {
-                    getAjax(table, c);
-                } else if (!c.ajax) {
-                    renderTable(table, table.config.rowsCopy, c);
-                }
-                $.data(table, 'pagerLastPage', c.page);
-                $.data(table, 'pagerUpdateTriggered', true);
-                if (c.initialized) { $(table).trigger('pageMoved', c); }
-            },
-
-            setPageSize = function(table, size, c) {
-                c.size = size;
-                $.data(table, 'pagerLastPage', c.page);
-                $.data(table, 'pagerLastSize', c.size);
-                c.totalPages = Math.ceil( c.totalRows / c.size );
-                moveToPage(table, c);
-            },
-
-            moveToFirstPage = function(table, c) {
-                c.page = 0;
-                moveToPage(table, c);
-            },
-
-            moveToLastPage = function(table, c) {
-                c.page = ( Math.min( c.totalPages, c.filteredPages ) - 1 );
-                moveToPage(table, c);
-            },
-
-            moveToNextPage = function(table, c) {
-                c.page++;
-                if ( c.page >= ( Math.min( c.totalPages, c.filteredPages ) - 1 ) ) {
-                    c.page = ( Math.min( c.totalPages, c.filteredPages ) - 1 );
-                }
-                moveToPage(table, c);
-            },
-
-            moveToPrevPage = function(table, c) {
-                c.page--;
-                if ( c.page <= 0 ) {
-                    c.page = 0;
-                }
-                moveToPage(table, c);
-            },
-
-            destroyPager = function(table, c){
-                showAllRows(table, c);
-                $(c.container).hide(); // hide pager
-                table.config.appender = null; // remove pager appender function
-                $(table).unbind('destroy.pager sortEnd.pager filterEnd.pager enable.pager disable.pager');
-            },
-
-            enablePager = function(table, c, triggered){
-                var p = $(c.cssPageSize, c.container).removeClass(c.cssDisabled).removeAttr('disabled');
-                $(c.container).find(c.cssGoto).removeClass(c.cssDisabled).removeAttr('disabled');
-                c.isDisabled = false;
-                c.page = $.data(table, 'pagerLastPage') || c.page || 0;
-                c.size = $.data(table, 'pagerLastSize') || parseInt(p.find('option[selected]').val(), 10) || c.size;
-                p.val(c.size); // set page size
-                c.totalPages = Math.ceil( Math.min( c.totalPages, c.filteredPages ) / c.size);
-                if ( triggered ) {
-                    $(table).trigger('update');
-                    setPageSize(table, c.size, c);
-                    hideRowsSetup(table, c);
-                    fixHeight(table, c);
-                }
-            };
-
-        $this.appender = function(table, rows) {
-            var c = table.config.pager;
-            if ( !c.ajax ) {
-                table.config.rowsCopy = rows;
-                c.totalRows = rows.length;
-                c.size = $.data(table, 'pagerLastSize') || c.size;
-                c.totalPages = Math.ceil(c.totalRows / c.size);
-                renderTable(table, rows, c);
-            }
-        };
-
-        $this.construct = function(settings) {
-            return this.each(function() {
-                // check if tablesorter has initialized
-                if (!(this.config && this.hasInitialized)) { return; }
-                var t, ctrls, fxn,
-                    config = this.config,
-                    c = config.pager = $.extend( {}, $.tablesorterPager.defaults, settings ),
-                    table = this,
-                    tc = table.config,
-                    $t = $(table),
-                    pager = $(c.container).addClass('tablesorter-pager').show(); // added in case the pager is reinitialized after being destroyed.
-                config.appender = $this.appender;
-
-                $t
-                    .unbind('filterStart.pager filterEnd.pager sortEnd.pager disable.pager enable.pager destroy.pager update.pager pageSize.pager')
-                    .bind('filterStart.pager', function(e, filters) {
-                        $.data(table, 'pagerUpdateTriggered', false);
-                        c.currentFilters = filters;
-                    })
-                    // update pager after filter widget completes
-                    .bind('filterEnd.pager sortEnd.pager', function(e) {
-                        //Prevent infinite event loops from occuring by setting this in all moveToPage calls and catching it here.
-                        if ($.data(table, 'pagerUpdateTriggered')) {
-                            $.data(table, 'pagerUpdateTriggered', false);
-                            return;
-                        }
-                        if (e.type === 'filterEnd') { c.page = 0; }
-                        moveToPage(table, c);
-                        updatePageDisplay(table, c);
-                        fixHeight(table, c);
-                    })
-                    .bind('disable.pager', function(){
-                        showAllRows(table, c);
-                    })
-                    .bind('enable.pager', function(){
-                        enablePager(table, c, true);
-                    })
-                    .bind('destroy.pager', function(){
-                        destroyPager(table, c);
-                    })
-                    .bind('update.pager', function(){
-                        hideRows(table, c);
-                    })
-                    .bind('pageSize.pager', function(e,v){
-                        c.size = parseInt(v, 10) || 10;
-                        hideRows(table, c);
-                        updatePageDisplay(table, c);
-                    })
-                    .bind('pageSet.pager', function(e,v){
-                        c.page = (parseInt(v, 10) || 1) - 1;
-                        moveToPage(table, c);
-                        updatePageDisplay(table, c);
-                    });
-
-                // clicked controls
-                ctrls = [c.cssFirst, c.cssPrev, c.cssNext, c.cssLast];
-                fxn = [ moveToFirstPage, moveToPrevPage, moveToNextPage, moveToLastPage ];
-
-                pager.find(ctrls.join(','))
-                    .unbind('click.pager')
-                    .bind('click.pager', function(e){
-                        var i, $this = $(this), l = ctrls.length;
-                        if ( !$this.hasClass(c.cssDisabled) ) {
-                            for (i = 0; i < l; i++) {
-                                if ($this.is(ctrls[i])) {
-                                    fxn[i](table, c);
-                                    break;
-                                }
-                            }
-                        }
-                        return false;
-                    });
-
-                // goto selector
-                if ( pager.find(c.cssGoto).length ) {
-                    pager.find(c.cssGoto)
-                        .unbind('change')
-                        .bind('change', function(){
-                            c.page = $(this).val() - 1;
-                            moveToPage(table, c);
-                        });
-                    updatePageDisplay(table, c);
-                }
-
-                // page size selector
-                t = pager.find(c.cssPageSize);
-                if ( t.length ) {
-                    t.unbind('change.pager').bind('change.pager', function() {
-                        t.val( $(this).val() ); // in case there are more than one pagers
-                        if ( !$(this).hasClass(c.cssDisabled) ) {
-                            setPageSize(table, parseInt( $(this).val(), 10 ), c);
-                            changeHeight(table, c);
-                        }
-                        return false;
-                    });
-                }
-
-                // clear initialized flag
-                c.initialized = false;
-                // before initialization event
-                $t.trigger('pagerBeforeInitialized', c);
-
-                enablePager(table, c, false);
-
-                if ( typeof(c.ajaxUrl) === 'string' ) {
-                    // ajax pager; interact with database
-                    c.ajax = true;
-                    //When filtering with ajax, allow only custom filtering function, disable default filtering since it will be done server side.
-                    tc.widgetOptions.filter_serversideFiltering = true;
-                    tc.serverSideSorting = true;
-                    moveToPage(table, c);
-                } else {
-                    c.ajax = false;
-                    // Regular pager; all rows stored in memory
-                    $(this).trigger("appendCache", true);
-                    hideRowsSetup(table, c);
-                }
-
-                changeHeight(table, c);
-
-                // pager initialized
-                if (!c.ajax) {
-                    c.initialized = true;
-                    $(table).trigger('pagerInitialized', c);
-                }
-            });
-        };
-
-    }()
-    });
-// extend plugin scope
-    $.fn.extend({
-        tablesorterPager: $.tablesorterPager.construct
-    });
-
-})(jQuery);
\ No newline at end of file
diff --git a/UI/Mixins/tablesorter.extensions.js b/UI/Mixins/tablesorter.extensions.js
deleted file mode 100644
index 7ecbe5f9d..000000000
--- a/UI/Mixins/tablesorter.extensions.js
+++ /dev/null
@@ -1,50 +0,0 @@
-$.tablesorter.addParser({
-    // set a unique id 
-    id    : 'title',
-    is    : function () {
-        // return false so this parser is not auto detected 
-        return false;
-    },
-    format: function (s) {
-        // format your data for normalization
-        return s.match(/title="(.*?)"/)[1].toLowerCase();
-    },
-    // set type, either numeric or text 
-    type  : 'text'
-});
-
-$.tablesorter.addParser({
-    // set a unique id 
-    id    : 'date',
-    is    : function () {
-        // return false so this parser is not auto detected 
-        return false;
-    },
-    format: function (s) {
-        // format your data for normalization
-        var match = s.match(/data-date="(.*?)"/)[1];
-
-        if (match === '') {
-            return Date.create().addYears(100).format(Date.ISO8601_DATETIME);
-        }
-
-        return match;
-    },
-    // set type, either numeric or text 
-    type  : 'text'
-});
-
-$.tablesorter.addParser({
-    // set a unique id 
-    id    : 'innerHtml',
-    is    : function () {
-        // return false so this parser is not auto detected 
-        return false;
-    },
-    format: function (s) {
-        // format your data for normalization
-        return $(s).get(0).innerHTML;
-    },
-    // set type, either numeric or text 
-    type  : 'text'
-});