diff options
Diffstat (limited to 'jquery.tablesorter.widgets.js')
-rw-r--r-- | jquery.tablesorter.widgets.js | 3182 |
1 files changed, 3177 insertions, 5 deletions
diff --git a/jquery.tablesorter.widgets.js b/jquery.tablesorter.widgets.js index 85477a2..5955f8c 100644 --- a/jquery.tablesorter.widgets.js +++ b/jquery.tablesorter.widgets.js @@ -1,10 +1,3182 @@ /*** This file is dynamically generated *** █████▄ ▄████▄ █████▄ ▄████▄ ██████ ███████▄ ▄████▄ █████▄ ██ ██████ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀▀██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▄▄ ██▄▄██ +██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██▀▀ ▀▀▀██ █████▀ ▀████▀ ██ ██ ▀████▀ ██ ██ ██ ██ ▀████▀ █████▀ ██ ██ █████▀ */ -/*! tablesorter (FORK) - updated 05-17-2015 (v2.22.1)*/ +/*! tablesorter (FORK) - updated 2018-11-20 (v2.31.1)*/ /* Includes widgets ( storage,uitheme,columns,filter,stickyHeaders,resizable,saveSort ) */ -!function(e){"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&"object"==typeof module.exports?module.exports=e(require("jquery")):e(jQuery)}(function(e){return function(e,t,r){"use strict";var i=e.tablesorter||{};i.storage=function(a,l,s,n){a=e(a)[0];var o,c,d,f=!1,h={},u=a.config,p=u&&u.widgetOptions,g=n&&n.useSessionStorage||p&&p.storage_useSessionStorage?"sessionStorage":"localStorage",m=e(a),b=n&&n.id||m.attr(n&&n.group||p&&p.storage_group||"data-table-group")||p&&p.storage_tableId||a.id||e(".tablesorter").index(m),y=n&&n.url||m.attr(n&&n.page||p&&p.storage_page||"data-table-page")||p&&p.storage_fixedUrl||u&&u.fixedUrl||t.location.pathname;if(g in t)try{t[g].setItem("_tmptest","temp"),f=!0,t[g].removeItem("_tmptest")}catch(_){u&&u.debug&&i.log(g+" is not supported in this browser")}return e.parseJSON&&(f?h=e.parseJSON(t[g][l]||"null")||{}:(c=r.cookie.split(/[;\s|=]/),o=e.inArray(l,c)+1,h=0!==o?e.parseJSON(c[o]||"null")||{}:{})),(s||""===s)&&t.JSON&&JSON.hasOwnProperty("stringify")?(h[y]||(h[y]={}),h[y][b]=s,f?t[g][l]=JSON.stringify(h):(d=new Date,d.setTime(d.getTime()+31536e6),r.cookie=l+"="+JSON.stringify(h).replace(/\"/g,'"')+"; expires="+d.toGMTString()+"; path=/"),void 0):h&&h[y]?h[y][b]:""}}(jQuery,window,document),function(e){"use strict";var t=e.tablesorter||{};t.themes={bootstrap:{table:"table table-bordered table-striped",caption:"caption",header:"bootstrap-header",sortNone:"",sortAsc:"",sortDesc:"",active:"",hover:"",icons:"",iconSortNone:"bootstrap-icon-unsorted",iconSortAsc:"icon-chevron-up glyphicon glyphicon-chevron-up",iconSortDesc:"icon-chevron-down glyphicon glyphicon-chevron-down",filterRow:"",footerRow:"",footerCells:"",even:"",odd:""},jui:{table:"ui-widget ui-widget-content ui-corner-all",caption:"ui-widget-content",header:"ui-widget-header ui-corner-all ui-state-default",sortNone:"",sortAsc:"",sortDesc:"",active:"ui-state-active",hover:"ui-state-hover",icons:"ui-icon",iconSortNone:"ui-icon-carat-2-n-s",iconSortAsc:"ui-icon-carat-1-n",iconSortDesc:"ui-icon-carat-1-s",filterRow:"",footerRow:"",footerCells:"",even:"ui-widget-content",odd:"ui-state-default"}},e.extend(t.css,{wrapper:"tablesorter-wrapper"}),t.addWidget({id:"uitheme",priority:10,format:function(r,i,a){var l,s,n,o,c,d,f,h,u,p,g,m,b=t.themes,y=i.$table.add(e(i.namespace+"_extra_table")),_=i.$headers.add(e(i.namespace+"_extra_headers")),v=i.theme||"jui",w=b[v]||{},x=e.trim([w.sortNone,w.sortDesc,w.sortAsc,w.active].join(" ")),C=e.trim([w.iconSortNone,w.iconSortDesc,w.iconSortAsc].join(" "));for(i.debug&&(o=new Date),y.hasClass("tablesorter-"+v)&&i.theme===i.appliedTheme&&a.uitheme_applied||(a.uitheme_applied=!0,u=b[i.appliedTheme]||{},m=!e.isEmptyObject(u),p=m?[u.sortNone,u.sortDesc,u.sortAsc,u.active].join(" "):"",g=m?[u.iconSortNone,u.iconSortDesc,u.iconSortAsc].join(" "):"",m&&(a.zebra[0]=e.trim(" "+a.zebra[0].replace(" "+u.even,"")),a.zebra[1]=e.trim(" "+a.zebra[1].replace(" "+u.odd,"")),i.$tbodies.children().removeClass([u.even,u.odd].join(" "))),w.even&&(a.zebra[0]+=" "+w.even),w.odd&&(a.zebra[1]+=" "+w.odd),y.children("caption").removeClass(u.caption||"").addClass(w.caption),f=y.removeClass((i.appliedTheme?"tablesorter-"+(i.appliedTheme||""):"")+" "+(u.table||"")).addClass("tablesorter-"+v+" "+(w.table||"")).children("tfoot"),i.appliedTheme=i.theme,f.length&&f.children("tr").removeClass(u.footerRow||"").addClass(w.footerRow).children("th, td").removeClass(u.footerCells||"").addClass(w.footerCells),_.removeClass((m?[u.header,u.hover,p].join(" "):"")||"").addClass(w.header).not(".sorter-false").unbind("mouseenter.tsuitheme mouseleave.tsuitheme").bind("mouseenter.tsuitheme mouseleave.tsuitheme",function(t){e(this)["mouseenter"===t.type?"addClass":"removeClass"](w.hover||"")}),_.each(function(){var r=e(this);r.find("."+t.css.wrapper).length||r.wrapInner('<div class="'+t.css.wrapper+'" style="position:relative;height:100%;width:100%"></div>')}),i.cssIcon&&_.find("."+t.css.icon).removeClass(m?[u.icons,g].join(" "):"").addClass(w.icons||""),y.hasClass("hasFilters")&&y.children("thead").children("."+t.css.filterRow).removeClass(m?u.filterRow||"":"").addClass(w.filterRow||"")),l=0;l<i.columns;l++)c=i.$headers.add(e(i.namespace+"_extra_headers")).not(".sorter-false").filter('[data-column="'+l+'"]'),d=t.css.icon?c.find("."+t.css.icon):e(),h=_.not(".sorter-false").filter('[data-column="'+l+'"]:last'),h.length&&(c.removeClass(x),d.removeClass(C),h[0].sortDisabled?d.removeClass(w.icons||""):(s=w.sortNone,n=w.iconSortNone,h.hasClass(t.css.sortAsc)?(s=[w.sortAsc,w.active].join(" "),n=w.iconSortAsc):h.hasClass(t.css.sortDesc)&&(s=[w.sortDesc,w.active].join(" "),n=w.iconSortDesc),c.addClass(s),d.addClass(n||"")));i.debug&&t.benchmark("Applying "+v+" theme",o)},remove:function(e,r,i,a){if(i.uitheme_applied){var l=r.$table,s=r.appliedTheme||"jui",n=t.themes[s]||t.themes.jui,o=l.children("thead").children(),c=n.sortNone+" "+n.sortDesc+" "+n.sortAsc,d=n.iconSortNone+" "+n.iconSortDesc+" "+n.iconSortAsc;l.removeClass("tablesorter-"+s+" "+n.table),i.uitheme_applied=!1,a||(l.find(t.css.header).removeClass(n.header),o.unbind("mouseenter.tsuitheme mouseleave.tsuitheme").removeClass(n.hover+" "+c+" "+n.active).filter("."+t.css.filterRow).removeClass(n.filterRow),o.find("."+t.css.icon).removeClass(n.icons+" "+d))}}})}(jQuery),function(e){"use strict";var t=e.tablesorter||{};t.addWidget({id:"columns",priority:30,options:{columns:["primary","secondary","tertiary"]},format:function(r,i,a){var l,s,n,o,c,d,f,h,u=i.$table,p=i.$tbodies,g=i.sortList,m=g.length,b=a&&a.columns||["primary","secondary","tertiary"],y=b.length-1;for(f=b.join(" "),s=0;s<p.length;s++)l=t.processTbody(r,p.eq(s),!0),n=l.children("tr"),n.each(function(){if(c=e(this),"none"!==this.style.display&&(d=c.children().removeClass(f),g&&g[0]&&(d.eq(g[0][0]).addClass(b[0]),m>1)))for(h=1;m>h;h++)d.eq(g[h][0]).addClass(b[h]||b[y])}),t.processTbody(r,l,!1);if(o=a.columns_thead!==!1?["thead tr"]:[],a.columns_tfoot!==!1&&o.push("tfoot tr"),o.length&&(n=u.find(o.join(",")).children().removeClass(f),m))for(h=0;m>h;h++)n.filter('[data-column="'+g[h][0]+'"]').addClass(b[h]||b[y])},remove:function(r,i,a){var l,s,n=i.$tbodies,o=(a.columns||["primary","secondary","tertiary"]).join(" ");for(i.$headers.removeClass(o),i.$table.children("tfoot").children("tr").children("th, td").removeClass(o),l=0;l<n.length;l++)s=t.processTbody(r,n.eq(l),!0),s.children("tr").each(function(){e(this).children().removeClass(o)}),t.processTbody(r,s,!1)}})}(jQuery),function(e){"use strict";var t=e.tablesorter||{},r=t.css;e.extend(r,{filterRow:"tablesorter-filter-row",filter:"tablesorter-filter",filterDisabled:"disabled",filterRowHide:"hideme"}),t.addWidget({id:"filter",priority:50,options:{filter_childRows:!1,filter_childByColumn:!1,filter_columnFilters:!0,filter_columnAnyMatch:!0,filter_cellFilter:"",filter_cssFilter:"",filter_defaultFilter:{},filter_excludeFilter:{},filter_external:"",filter_filteredRow:"filtered",filter_formatter:null,filter_functions:null,filter_hideEmpty:!0,filter_hideFilters:!1,filter_ignoreCase:!0,filter_liveSearch:!0,filter_onlyAvail:"filter-onlyAvail",filter_placeholder:{search:"",select:""},filter_reset:null,filter_saveFilters:!1,filter_searchDelay:300,filter_searchFiltered:!0,filter_selectSource:null,filter_startsWith:!1,filter_useParsedData:!1,filter_serversideFiltering:!1,filter_defaultAttrib:"data-value",filter_selectSourceSeparator:"|"},format:function(e,r,i){r.$table.hasClass("hasFilters")||t.filter.init(e,r,i)},remove:function(i,a,l,s){var n,o,c=a.$table,d=a.$tbodies,f="addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ".split(" ").join(a.namespace+"filter ");if(c.removeClass("hasFilters").unbind(f.replace(/\s+/g," ")).find("."+r.filterRow).remove(),!s){for(n=0;n<d.length;n++)o=t.processTbody(i,d.eq(n),!0),o.children().removeClass(l.filter_filteredRow).show(),t.processTbody(i,o,!1);l.filter_reset&&e(document).undelegate(l.filter_reset,"click.tsfilter")}}}),t.filter={regex:{regex:/^\/((?:\\\/|[^\/])+)\/([mig]{0,3})?$/,child:/tablesorter-childRow/,filtered:/filtered/,type:/undefined|number/,exact:/(^[\"\'=]+)|([\"\'=]+$)/g,nondigit:/[^\w,. \-()]/g,operators:/[<>=]/g,query:"(q|query)"},types:{regex:function(e,r){if(t.filter.regex.regex.test(r.filter)){var i,a=r.filter_regexCache[r.index]||t.filter.regex.regex.exec(r.filter),l=a instanceof RegExp;try{l||(r.filter_regexCache[r.index]=a=new RegExp(a[1],a[2])),i=a.test(r.exact)}catch(s){i=!1}return i}return null},operators:function(r,i){if(/^[<>]=?/.test(i.iFilter)&&""!==i.iExact){var a,l,s,n=r.table,o=i.index,c=i.parsed[o],d=t.formatFloat(i.iFilter.replace(t.filter.regex.operators,""),n),f=r.parsers[o],h=d;return(c||"numeric"===f.type)&&(s=e.trim(""+i.iFilter.replace(t.filter.regex.operators,"")),l=t.filter.parseFilter(r,s,o,!0),d="number"!=typeof l||""===l||isNaN(l)?d:l),!c&&"numeric"!==f.type||isNaN(d)||"undefined"==typeof i.cache?(s=isNaN(i.iExact)?i.iExact.replace(t.filter.regex.nondigit,""):i.iExact,a=t.formatFloat(s,n)):a=i.cache,/>/.test(i.iFilter)?l=/>=/.test(i.iFilter)?a>=d:a>d:/</.test(i.iFilter)&&(l=/<=/.test(i.iFilter)?d>=a:d>a),l||""!==h||(l=!0),l}return null},notMatch:function(r,i){if(/^\!/.test(i.iFilter)){var a,l=i.iFilter.replace("!",""),s=t.filter.parseFilter(r,l,i.index,i.parsed[i.index])||"";return t.filter.regex.exact.test(s)?(s=s.replace(t.filter.regex.exact,""),""===s?!0:e.trim(s)!==i.iExact):(a=i.iExact.search(e.trim(s)),""===s?!0:!(r.widgetOptions.filter_startsWith?0===a:a>=0))}return null},exact:function(r,i){if(t.filter.regex.exact.test(i.iFilter)){var a=i.iFilter.replace(t.filter.regex.exact,""),l=t.filter.parseFilter(r,a,i.index,i.parsed[i.index])||"";return i.anyMatch?e.inArray(l,i.rowArray)>=0:l==i.iExact}return null},and:function(r,i){if(t.filter.regex.andTest.test(i.filter)){for(var a=i.index,l=i.parsed[a],s=i.iFilter.split(t.filter.regex.andSplit),n=i.iExact.search(e.trim(t.filter.parseFilter(r,s[0],a,l)))>=0,o=s.length-1;n&&o;)n=n&&i.iExact.search(e.trim(t.filter.parseFilter(r,s[o],a,l)))>=0,o--;return n}return null},range:function(e,r){if(t.filter.regex.toTest.test(r.iFilter)){var i,a,l,s,n=e.table,o=r.index,c=r.parsed[o],d=r.iFilter.split(t.filter.regex.toSplit);return a=d[0].replace(t.filter.regex.nondigit,"")||"",l=t.formatFloat(t.filter.parseFilter(e,a,o,c),n),a=d[1].replace(t.filter.regex.nondigit,"")||"",s=t.formatFloat(t.filter.parseFilter(e,a,o,c),n),(c||"numeric"===e.parsers[o].type)&&(i=e.parsers[o].format(""+d[0],n,e.$headers.eq(o),o),l=""===i||isNaN(i)?l:i,i=e.parsers[o].format(""+d[1],n,e.$headers.eq(o),o),s=""===i||isNaN(i)?s:i),!c&&"numeric"!==e.parsers[o].type||isNaN(l)||isNaN(s)?(a=isNaN(r.iExact)?r.iExact.replace(t.filter.regex.nondigit,""):r.iExact,i=t.formatFloat(a,n)):i=r.cache,l>s&&(a=l,l=s,s=a),i>=l&&s>=i||""===l||""===s}return null},wild:function(r,i){if(/[\?\*\|]/.test(i.iFilter)||t.filter.regex.orReplace.test(i.filter)){var a=i.index,l=i.parsed[a],s=i.iFilter.replace(t.filter.regex.orReplace,"|"),n=""+(t.filter.parseFilter(r,s,a,l)||"");return!r.$headerIndexed[a].hasClass("filter-match")&&/\|/.test(n)&&("|"===n[n.length-1]&&(n+="*"),n=i.anyMatch&&e.isArray(i.rowArray)?"("+n+")":"^("+n+")$"),new RegExp(n.replace(/\?/g,"\\S{1}").replace(/\*/g,"\\S*")).test(i.iExact)}return null},fuzzy:function(e,r){if(/^~/.test(r.iFilter)){var i,a=0,l=r.iExact.length,s=r.iFilter.slice(1),n=t.filter.parseFilter(e,s,r.index,r.parsed[r.index])||"";for(i=0;l>i;i++)r.iExact[i]===n[a]&&(a+=1);return a===n.length?!0:!1}return null}},init:function(i,a,l){t.language=e.extend(!0,{},{to:"to",or:"or",and:"and"},t.language);var s,n,o,c,d,f,h,u,p,g=t.filter.regex;if(a.$table.addClass("hasFilters"),l.searchTimer=null,l.filter_initTimer=null,l.filter_formatterCount=0,l.filter_formatterInit=[],l.filter_anyColumnSelector='[data-column="all"],[data-column="any"]',l.filter_multipleColumnSelector='[data-column*="-"],[data-column*=","]',h="\\{"+t.filter.regex.query+"\\}",e.extend(g,{child:new RegExp(a.cssChildRow),filtered:new RegExp(l.filter_filteredRow),alreadyFiltered:new RegExp("(\\s+("+t.language.or+"|-|"+t.language.to+")\\s+)","i"),toTest:new RegExp("\\s+(-|"+t.language.to+")\\s+","i"),toSplit:new RegExp("(?:\\s+(?:-|"+t.language.to+")\\s+)","gi"),andTest:new RegExp("\\s+("+t.language.and+"|&&)\\s+","i"),andSplit:new RegExp("(?:\\s+(?:"+t.language.and+"|&&)\\s+)","gi"),orReplace:new RegExp("\\s+("+t.language.or+")\\s+","gi"),iQuery:new RegExp(h,"i"),igQuery:new RegExp(h,"ig")}),h=a.$headers.filter(".filter-false, .parser-false").length,l.filter_columnFilters!==!1&&h!==a.$headers.length&&t.filter.buildRow(i,a,l),o="addRows updateCell update updateRows updateComplete appendCache filterReset filterEnd search ".split(" ").join(a.namespace+"filter "),a.$table.bind(o,function(s,n){return h=l.filter_hideEmpty&&e.isEmptyObject(a.cache)&&!(a.delayInit&&"appendCache"===s.type),a.$table.find("."+r.filterRow).toggleClass(l.filter_filteredRow,h),/(search|filter)/.test(s.type)||(s.stopPropagation(),t.filter.buildDefault(i,!0)),"filterReset"===s.type?(a.$table.find("."+r.filter).add(l.filter_$externalFilters).val(""),t.filter.searching(i,[])):"filterEnd"===s.type?t.filter.buildDefault(i,!0):(n="search"===s.type?n:"updateComplete"===s.type?a.$table.data("lastSearch"):"",/(update|add)/.test(s.type)&&"updateComplete"!==s.type&&(a.lastCombinedFilter=null,a.lastSearch=[]),t.filter.searching(i,n,!0)),!1}),l.filter_reset&&(l.filter_reset instanceof e?l.filter_reset.click(function(){a.$table.trigger("filterReset")}):e(l.filter_reset).length&&e(document).undelegate(l.filter_reset,"click.tsfilter").delegate(l.filter_reset,"click.tsfilter",function(){a.$table.trigger("filterReset")})),l.filter_functions)for(d=0;d<a.columns;d++)if(u=t.getColumnData(i,l.filter_functions,d))if(c=a.$headerIndexed[d].removeClass("filter-select"),p=!(c.hasClass("filter-false")||c.hasClass("parser-false")),s="",u===!0&&p)t.filter.buildSelect(i,d);else if("object"==typeof u&&p){for(n in u)"string"==typeof n&&(s+=""===s?'<option value="">'+(c.data("placeholder")||c.attr("data-placeholder")||l.filter_placeholder.select||"")+"</option>":"",h=n,o=n,n.indexOf(l.filter_selectSourceSeparator)>=0&&(h=n.split(l.filter_selectSourceSeparator),o=h[1],h=h[0]),s+="<option "+(o===h?"":'data-function-name="'+n+'" ')+'value="'+h+'">'+o+"</option>");a.$table.find("thead").find("select."+r.filter+'[data-column="'+d+'"]').append(s),o=l.filter_selectSource,u=e.isFunction(o)?!0:t.getColumnData(i,o,d),u&&t.filter.buildSelect(a.table,d,"",!0,c.hasClass(l.filter_onlyAvail))}t.filter.buildDefault(i,!0),t.filter.bindSearch(i,a.$table.find("."+r.filter),!0),l.filter_external&&t.filter.bindSearch(i,l.filter_external),l.filter_hideFilters&&t.filter.hideFilters(i,a),a.showProcessing&&(o="filterStart filterEnd ".split(" ").join(a.namespace+"filter "),a.$table.unbind(o.replace(/\s+/g," ")).bind(o,function(l,s){c=s?a.$table.find("."+r.header).filter("[data-column]").filter(function(){return""!==s[e(this).data("column")]}):"",t.isProcessing(i,"filterStart"===l.type,s?c:"")})),a.filteredRows=a.totalRows,o="tablesorter-initialized pagerBeforeInitialized ".split(" ").join(a.namespace+"filter "),a.$table.unbind(o.replace(/\s+/g," ")).bind(o,function(){var e=this.config.widgetOptions;f=t.filter.setDefaults(i,a,e)||[],f.length&&(a.delayInit&&""===f.join("")||t.setFilters(i,f,!0)),a.$table.trigger("filterFomatterUpdate"),setTimeout(function(){e.filter_initialized||t.filter.filterInitComplete(a)},100)}),a.pager&&a.pager.initialized&&!l.filter_initialized&&(a.$table.trigger("filterFomatterUpdate"),setTimeout(function(){t.filter.filterInitComplete(a)},100))},formatterUpdated:function(e,t){var r=e.closest("table")[0].config.widgetOptions;r.filter_initialized||(r.filter_formatterInit[t]=1)},filterInitComplete:function(r){var i,a,l=r.widgetOptions,s=0,n=function(){l.filter_initialized=!0,r.$table.trigger("filterInit",r),t.filter.findRows(r.table,r.$table.data("lastSearch")||[])};if(e.isEmptyObject(l.filter_formatter))n();else{for(a=l.filter_formatterInit.length,i=0;a>i;i++)1===l.filter_formatterInit[i]&&s++;clearTimeout(l.filter_initTimer),l.filter_initialized||s!==l.filter_formatterCount?l.filter_initialized||(l.filter_initTimer=setTimeout(function(){n()},500)):n()}},setDefaults:function(r,i,a){var l,s,n,o,c,d=t.getFilters(r)||[];if(a.filter_saveFilters&&t.storage&&(s=t.storage(r,"tablesorter-filters")||[],l=e.isArray(s),l&&""===s.join("")||!l||(d=s)),""===d.join(""))for(c=i.$headers.add(a.filter_$externalFilters).filter("["+a.filter_defaultAttrib+"]"),n=0;n<=i.columns;n++)o=n===i.columns?"all":n,d[n]=c.filter('[data-column="'+o+'"]').attr(a.filter_defaultAttrib)||d[n]||"";return i.$table.data("lastSearch",d),d},parseFilter:function(e,t,r,i){return i?e.parsers[r].format(t,e.table,[],r):t},buildRow:function(i,a,l){var s,n,o,c,d,f,h,u,p=l.filter_cellFilter,g=a.columns,m=e.isArray(p),b='<tr role="row" class="'+r.filterRow+" "+a.cssIgnoreRow+'">';for(n=0;g>n;n++)b+="<td",b+=m?p[n]?' class="'+p[n]+'"':"":""!==p?' class="'+p+'"':"",b+="></td>";for(a.$filters=e(b+="</tr>").appendTo(a.$table.children("thead").eq(0)).find("td"),n=0;g>n;n++)d=!1,o=a.$headerIndexed[n],h=t.getColumnData(i,l.filter_functions,n),c=l.filter_functions&&h&&"function"!=typeof h||o.hasClass("filter-select"),s=t.getColumnData(i,a.headers,n),d="false"===t.getData(o[0],s,"filter")||"false"===t.getData(o[0],s,"parser"),c?b=e("<select>").appendTo(a.$filters.eq(n)):(h=t.getColumnData(i,l.filter_formatter,n),h?(l.filter_formatterCount++,b=h(a.$filters.eq(n),n),b&&0===b.length&&(b=a.$filters.eq(n).children("input")),b&&(0===b.parent().length||b.parent().length&&b.parent()[0]!==a.$filters[n])&&a.$filters.eq(n).append(b)):b=e('<input type="search">').appendTo(a.$filters.eq(n)),b&&(u=o.data("placeholder")||o.attr("data-placeholder")||l.filter_placeholder.search||"",b.attr("placeholder",u))),b&&(f=(e.isArray(l.filter_cssFilter)?"undefined"!=typeof l.filter_cssFilter[n]?l.filter_cssFilter[n]||"":"":l.filter_cssFilter)||"",b.addClass(r.filter+" "+f).attr("data-column",n),d&&(b.attr("placeholder","").addClass(r.filterDisabled)[0].disabled=!0))},bindSearch:function(r,i,a){if(r=e(r)[0],i=e(i),i.length){var l,s=r.config,n=s.widgetOptions,o=s.namespace+"filter",c=n.filter_$externalFilters;a!==!0&&(l=n.filter_anyColumnSelector+","+n.filter_multipleColumnSelector,n.filter_$anyMatch=i.filter(l),n.filter_$externalFilters=c&&c.length?n.filter_$externalFilters.add(i):i,t.setFilters(r,s.$table.data("lastSearch")||[],a===!1)),l="keypress keyup search change ".split(" ").join(o+" "),i.attr("data-lastSearchTime",(new Date).getTime()).unbind(l.replace(/\s+/g," ")).bind("keyup"+o,function(i){if(e(this).attr("data-lastSearchTime",(new Date).getTime()),27===i.which)this.value="";else{if(n.filter_liveSearch===!1)return;if(""!==this.value&&("number"==typeof n.filter_liveSearch&&this.value.length<n.filter_liveSearch||13!==i.which&&8!==i.which&&(i.which<32||i.which>=37&&i.which<=40)))return}t.filter.searching(r,!0,!0)}).bind("search change keypress ".split(" ").join(o+" "),function(i){var a=e(this).data("column");(13===i.which||"search"===i.type||"change"===i.type&&this.value!==s.lastSearch[a])&&(i.preventDefault(),e(this).attr("data-lastSearchTime",(new Date).getTime()),t.filter.searching(r,!1,!0))})}},searching:function(e,r,i){var a=e.config.widgetOptions;clearTimeout(a.searchTimer),"undefined"==typeof r||r===!0?a.searchTimer=setTimeout(function(){t.filter.checkFilters(e,r,i)},a.filter_liveSearch?a.filter_searchDelay:10):t.filter.checkFilters(e,r,i)},checkFilters:function(i,a,l){var s=i.config,n=s.widgetOptions,o=e.isArray(a),c=o?a:t.getFilters(i,!0),d=(c||[]).join("");return e.isEmptyObject(s.cache)?void(s.delayInit&&s.pager&&s.pager.initialized&&s.$table.trigger("updateCache",[function(){t.filter.checkFilters(i,!1,l)}])):(o&&(t.setFilters(i,c,!1,l!==!0),n.filter_initialized||(s.lastCombinedFilter="")),n.filter_hideFilters&&s.$table.find("."+r.filterRow).trigger(""===d?"mouseleave":"mouseenter"),s.lastCombinedFilter!==d||a===!1?(a===!1&&(s.lastCombinedFilter=null,s.lastSearch=[]),n.filter_initialized&&s.$table.trigger("filterStart",[c]),s.showProcessing?void setTimeout(function(){return t.filter.findRows(i,c,d),!1},30):(t.filter.findRows(i,c,d),!1)):void 0)},hideFilters:function(i,a){var l,s,n;e(i).find("."+r.filterRow).addClass(r.filterRowHide).bind("mouseenter mouseleave",function(t){var i=t;l=e(this),clearTimeout(n),n=setTimeout(function(){/enter|over/.test(i.type)?l.removeClass(r.filterRowHide):e(document.activeElement).closest("tr")[0]!==l[0]&&""===a.lastCombinedFilter&&l.addClass(r.filterRowHide)},200)}).find("input, select").bind("focus blur",function(i){s=e(this).closest("tr"),clearTimeout(n);var l=i;n=setTimeout(function(){""===t.getFilters(a.$table).join("")&&s.toggleClass(r.filterRowHide,"focus"===l.type)},200)})},defaultFilter:function(r,i){if(""===r)return r;var a=t.filter.regex.iQuery,l=i.match(t.filter.regex.igQuery).length,s=l>1?e.trim(r).split(/\s/):[e.trim(r)],n=s.length-1,o=0,c=i;for(1>n&&l>1&&(s[1]=s[0]);a.test(c);)c=c.replace(a,s[o++]||""),a.test(c)&&n>o&&""!==(s[o]||"")&&(c=i.replace(a,c));return c},getLatestSearch:function(t){return t?t.sort(function(t,r){return e(r).attr("data-lastSearchTime")-e(t).attr("data-lastSearchTime")}):e()},multipleColumns:function(r,i){var a,l,s,n,o,c,d,f,h,u=r.widgetOptions,p=u.filter_initialized||!i.filter(u.filter_anyColumnSelector).length,g=[],m=e.trim(t.filter.getLatestSearch(i).attr("data-column")||"");if(p&&/-/.test(m))for(l=m.match(/(\d+)\s*-\s*(\d+)/g),h=l.length,f=0;h>f;f++){for(s=l[f].split(/\s*-\s*/),n=parseInt(s[0],10)||0,o=parseInt(s[1],10)||r.columns-1,n>o&&(a=n,n=o,o=a),o>=r.columns&&(o=r.columns-1);o>=n;n++)g.push(n);m=m.replace(l[f],"")}if(p&&/,/.test(m))for(c=m.split(/\s*,\s*/),h=c.length,d=0;h>d;d++)""!==c[d]&&(f=parseInt(c[d],10),f<r.columns&&g.push(f));if(!g.length)for(f=0;f<r.columns;f++)g.push(f);return g},processRow:function(r,i,a){var l,s,n,o,c,d,f,h,u,p,g,m=t.filter.regex,b=r.widgetOptions,y=!0;if(i.$cells=i.$row.children(),i.anyMatchFlag){s=t.filter.multipleColumns(r,b.filter_$anyMatch),i.anyMatch=!0,i.rowArray=i.$cells.map(function(a){return e.inArray(a,s)>-1?(i.parsed[a]?g=i.cacheArray[a]:(g=i.rawArray[a],g=e.trim(b.filter_ignoreCase?g.toLowerCase():g),r.sortLocaleCompare&&(g=t.replaceAccents(g))),g):void 0}).get(),i.filter=i.anyMatchFilter,i.iFilter=i.iAnyMatchFilter,i.exact=i.rowArray.join(" "),i.iExact=b.filter_ignoreCase?i.exact.toLowerCase():i.exact,i.cache=i.cacheArray.slice(0,-1).join(" "),f=null,o=null;for(p in t.filter.types)e.inArray(p,a.noAnyMatch)<0&&null===o&&(o=t.filter.types[p](r,i),null!==o&&(f=o));if(null!==f)y=f;else if(b.filter_startsWith)for(y=!1,s=r.columns;!y&&s>0;)s--,y=y||0===i.rowArray[s].indexOf(i.iFilter);else y=(i.iExact+i.childRowText).indexOf(i.iFilter)>=0;if(i.anyMatch=!1,i.filters.join("")===i.filter)return y}for(s=0;s<r.columns;s++)if(i.filter=i.filters[s],i.index=s,h=a.excludeFilter[s],i.filter){if(i.cache=i.cacheArray[s],b.filter_useParsedData||i.parsed[s]?i.exact=i.cache:(c=i.rawArray[s]||"",i.exact=r.sortLocaleCompare?t.replaceAccents(c):c),i.iExact=!m.type.test(typeof i.exact)&&b.filter_ignoreCase?i.exact.toLowerCase():i.exact,c=y,p=b.filter_columnFilters?r.$filters.add(r.$externalFilters).filter('[data-column="'+s+'"]').find("select option:selected").attr("data-function-name")||"":"",r.sortLocaleCompare&&(i.filter=t.replaceAccents(i.filter)),d=!0,b.filter_defaultFilter&&m.iQuery.test(a.defaultColFilter[s])&&(i.filter=t.filter.defaultFilter(i.filter,a.defaultColFilter[s]),d=!1),i.iFilter=b.filter_ignoreCase?(i.filter||"").toLowerCase():i.filter,u=a.functions[s],l=r.$headerIndexed[s],n=l.hasClass("filter-select"),f=null,(u||n&&d)&&(u===!0||n?f=l.hasClass("filter-match")?i.iExact.search(i.iFilter)>=0:i.filter===i.exact:"function"==typeof u?f=u(i.exact,i.cache,i.filter,s,i.$row,r,i):"function"==typeof u[p||i.filter]&&(g=p||i.filter,f=u[g](i.exact,i.cache,i.filter,s,i.$row,r,i))),null===f){o=null;for(p in t.filter.types)e.inArray(p,h)<0&&null===o&&(o=t.filter.types[p](r,i),null!==o&&(f=o));null!==f?c=f:(g=(i.iExact+i.childRowText).indexOf(t.filter.parseFilter(r,i.iFilter,s,i.parsed[s])),c=!b.filter_startsWith&&g>=0||b.filter_startsWith&&0===g)}else c=f;y=c?y:!1}return y},findRows:function(r,i,a){if(r.config.lastCombinedFilter!==a&&r.config.widgetOptions.filter_initialized){var l,s,n,o,c,d,f,h,u,p,g,m,b,y,_,v,w,x,C,$,z,S,F=e.extend([],i),R=t.filter.regex,k=r.config,T=k.widgetOptions,A={anyMatch:!1,filters:i,filter_regexCache:[]},H={noAnyMatch:["range","notMatch","operators"],functions:[],excludeFilter:[],defaultColFilter:[],defaultAnyFilter:t.getColumnData(r,T.filter_defaultFilter,k.columns,!0)||""};for(A.parsed=k.$headers.map(function(i){return k.parsers&&k.parsers[i]&&k.parsers[i].parsed||t.getData&&"parsed"===t.getData(k.$headerIndexed[i],t.getColumnData(r,k.headers,i),"filter")||e(this).hasClass("filter-parsed")}).get(),h=0;h<k.columns;h++)H.functions[h]=t.getColumnData(r,T.filter_functions,h),H.defaultColFilter[h]=t.getColumnData(r,T.filter_defaultFilter,h)||"",H.excludeFilter[h]=(t.getColumnData(r,T.filter_excludeFilter,h,!0)||"").split(/\s+/);for(k.debug&&(t.log("Filter: Starting filter widget search",i),b=new Date),k.filteredRows=0,k.totalRows=0,a=(F||[]).join(""),d=0;d<k.$tbodies.length;d++){if(f=t.processTbody(r,k.$tbodies.eq(d),!0),h=k.columns,s=k.cache[d].normalized,o=e(e.map(s,function(e){return e[h].$row.get()})),""===a||T.filter_serversideFiltering)o.removeClass(T.filter_filteredRow).not("."+k.cssChildRow).css("display","");else{if(o=o.not("."+k.cssChildRow),l=o.length,(T.filter_$anyMatch&&T.filter_$anyMatch.length||"undefined"!=typeof i[k.columns])&&(A.anyMatchFlag=!0,A.anyMatchFilter=""+(i[k.columns]||T.filter_$anyMatch&&t.filter.getLatestSearch(T.filter_$anyMatch).val()||""),T.filter_columnAnyMatch)){for(x=A.anyMatchFilter.split(R.andSplit),C=!1,_=0;_<x.length;_++)$=x[_].split(":"),$.length>1&&(z=parseInt($[0],10)-1,z>=0&&z<k.columns&&(i[z]=$[1],x.splice(_,1),_--,C=!0));C&&(A.anyMatchFilter=x.join(" && "))}if(w=T.filter_searchFiltered,g=k.lastSearch||k.$table.data("lastSearch")||[],w)for(_=0;h+1>_;_++)y=i[_]||"",w||(_=h),w=!(!w||!g.length||0!==y.indexOf(g[_]||"")||R.alreadyFiltered.test(y)||/[=\"\|!]/.test(y)||/(>=?\s*-\d)/.test(y)||/(<=?\s*\d)/.test(y)||""!==y&&k.$filters&&k.$filters.eq(_).find("select").length&&!k.$headerIndexed[_].hasClass("filter-match"));for(v=o.not("."+T.filter_filteredRow).length,w&&0===v&&(w=!1),k.debug&&t.log("Filter: Searching through "+(w&&l>v?v:"all")+" rows"),A.anyMatchFlag&&(k.sortLocaleCompare&&(A.anyMatchFilter=t.replaceAccents(A.anyMatchFilter)),T.filter_defaultFilter&&R.iQuery.test(H.defaultAnyFilter)&&(A.anyMatchFilter=t.filter.defaultFilter(A.anyMatchFilter,H.defaultAnyFilter),w=!1),A.iAnyMatchFilter=T.filter_ignoreCase&&k.ignoreCase?A.anyMatchFilter.toLowerCase():A.anyMatchFilter),c=0;l>c;c++)if(S=o[c].className,u=c&&R.child.test(S),!(u||w&&R.filtered.test(S))){if(A.$row=o.eq(c),A.cacheArray=s[c],n=A.cacheArray[k.columns],A.rawArray=n.raw,A.childRowText="",!T.filter_childByColumn){for(S="",p=n.child,_=0;_<p.length;_++)S+=" "+p[_].join("")||"";A.childRowText=T.filter_childRows?T.filter_ignoreCase?S.toLowerCase():S:""}if(m=t.filter.processRow(k,A,H),p=n.$row.filter(":gt( 0 )"),T.filter_childRows&&p.length){if(T.filter_childByColumn)for(_=0;_<p.length;_++)A.$row=p.eq(_),A.cacheArray=n.child[_],A.rawArray=A.cacheArray,m=m||t.filter.processRow(k,A,H);p.toggleClass(T.filter_filteredRow,!m)}n.$row.toggleClass(T.filter_filteredRow,!m)[0].display=m?"":"none"}}k.filteredRows+=o.not("."+T.filter_filteredRow).length,k.totalRows+=o.length,t.processTbody(r,f,!1)}k.lastCombinedFilter=a,k.lastSearch=F,k.$table.data("lastSearch",F),T.filter_saveFilters&&t.storage&&t.storage(r,"tablesorter-filters",F),k.debug&&t.benchmark("Completed filter widget search",b),T.filter_initialized&&k.$table.trigger("filterEnd",k),setTimeout(function(){k.$table.trigger("applyWidgets")},0)}},getOptionSource:function(r,i,a){r=e(r)[0];var l,s,n,o=r.config,c=o.widgetOptions,d=[],f=!1,h=c.filter_selectSource,u=o.$table.data("lastSearch")||[],p=e.isFunction(h)?!0:t.getColumnData(r,h,i);if(a&&""!==u[i]&&(a=!1),p===!0)f=h(r,i,a);else{if(p instanceof e||"string"===e.type(p)&&p.indexOf("</option>")>=0)return p;e.isArray(p)?f=p:"object"===e.type(h)&&p&&(f=p(r,i,a))}if(f===!1&&(f=t.filter.getOptions(r,i,a)),f=e.grep(f,function(t,r){return e.inArray(t,f)===r}),o.$headerIndexed[i].hasClass("filter-select-nosort"))return f;for(n=f.length,s=0;n>s;s++)d.push({t:f[s],p:o.parsers&&o.parsers[i].format(f[s],r,[],i)});for(l=o.textSorter||"",d.sort(function(a,s){var n=a.p.toString(),o=s.p.toString();return e.isFunction(l)?l(n,o,!0,i,r):"object"==typeof l&&l.hasOwnProperty(i)?l[i](n,o,!0,i,r):t.sortNatural?t.sortNatural(n,o):!0}),f=[],n=d.length,s=0;n>s;s++)f.push(d[s].t);return f},getOptions:function(t,r,i){t=e(t)[0];var a,l,s,n,o,c=t.config,d=c.widgetOptions,f=[];for(l=0;l<c.$tbodies.length;l++)for(o=c.cache[l],s=c.cache[l].normalized.length,a=0;s>a;a++)n=o.row?o.row[a]:o.normalized[a][c.columns].$row[0],i&&n.className.match(d.filter_filteredRow)||f.push(d.filter_useParsedData||c.parsers[r].parsed||c.$headerIndexed[r].hasClass("filter-parsed")?""+o.normalized[a][r]:o.normalized[a][c.columns].raw[r]);return f},buildSelect:function(i,a,l,s,n){if(i=e(i)[0],a=parseInt(a,10),i.config.cache&&!e.isEmptyObject(i.config.cache)){var o,c,d,f,h,u,p=i.config,g=p.widgetOptions,m=p.$headerIndexed[a],b='<option value="">'+(m.data("placeholder")||m.attr("data-placeholder")||g.filter_placeholder.select||"")+"</option>",y=p.$table.find("thead").find("select."+r.filter+'[data-column="'+a+'"]').val();if(("undefined"==typeof l||""===l)&&(l=t.filter.getOptionSource(i,a,n)),e.isArray(l)){for(o=0;o<l.length;o++)d=l[o]=(""+l[o]).replace(/\"/g,"""),c=d,d.indexOf(g.filter_selectSourceSeparator)>=0&&(f=d.split(g.filter_selectSourceSeparator),c=f[0],d=f[1]),b+=""!==l[o]?"<option "+(c===d?"":'data-function-name="'+l[o]+'" ')+'value="'+c+'">'+d+"</option>":"";l=[]}h=(p.$filters?p.$filters:p.$table.children("thead")).find("."+r.filter),g.filter_$externalFilters&&(h=h&&h.length?h.add(g.filter_$externalFilters):g.filter_$externalFilters),u=h.filter('select[data-column="'+a+'"]'),u.length&&(u[s?"html":"append"](b),e.isArray(l)||u.append(l).val(y),u.val(y))}},buildDefault:function(e,r){var i,a,l,s=e.config,n=s.widgetOptions,o=s.columns;for(i=0;o>i;i++)a=s.$headerIndexed[i],l=!(a.hasClass("filter-false")||a.hasClass("parser-false")),(a.hasClass("filter-select")||t.getColumnData(e,n.filter_functions,i)===!0)&&l&&t.filter.buildSelect(e,i,"",r,a.hasClass(n.filter_onlyAvail))}},t.getFilters=function(i,a,l,s){var n,o,c,d,f=!1,h=i?e(i)[0].config:"",u=h?h.widgetOptions:"";if(a!==!0&&u&&!u.filter_columnFilters||e.isArray(l)&&l.join("")===h.lastCombinedFilter)return e(i).data("lastSearch");if(h&&(h.$filters&&(o=h.$filters.find("."+r.filter)),u.filter_$externalFilters&&(o=o&&o.length?o.add(u.filter_$externalFilters):u.filter_$externalFilters),o&&o.length))for(f=l||[],n=0;n<h.columns+1;n++)d=n===h.columns?u.filter_anyColumnSelector+","+u.filter_multipleColumnSelector:'[data-column="'+n+'"]',c=o.filter(d),c.length&&(c=t.filter.getLatestSearch(c),e.isArray(l)?(s&&c.slice(1),n===h.columns&&(d=c.filter(u.filter_anyColumnSelector),c=d.length?d:c),c.val(l[n]).trigger("change.tsfilter")):(f[n]=c.val()||"",n===h.columns?c.slice(1).filter('[data-column*="'+c.attr("data-column")+'"]').val(f[n]):c.slice(1).val(f[n])),n===h.columns&&c.length&&(u.filter_$anyMatch=c));return 0===f.length&&(f=!1),f},t.setFilters=function(r,i,a,l){var s=r?e(r)[0].config:"",n=t.getFilters(r,!0,i,l);return s&&a&&(s.lastCombinedFilter=null,s.lastSearch=[],t.filter.searching(s.table,i,l),s.$table.trigger("filterFomatterUpdate")),!!n}}(jQuery),function(e,t){"use strict";var r=e.tablesorter||{};e.extend(r.css,{sticky:"tablesorter-stickyHeader",stickyVis:"tablesorter-sticky-visible",stickyHide:"tablesorter-sticky-hidden",stickyWrap:"tablesorter-sticky-wrapper"}),r.addHeaderResizeEvent=function(t,r,i){t=e(t)[0]; -var a,l={timer:250},s=e.extend({},l,i),n=t.config,o=n.widgetOptions,c=function(t){o.resize_flag=!0,a=[],n.$headers.each(function(){var t=e(this),r=t.data("savedSizes")||[0,0],i=this.offsetWidth,l=this.offsetHeight;(i!==r[0]||l!==r[1])&&(t.data("savedSizes",[i,l]),a.push(this))}),a.length&&t!==!1&&n.$table.trigger("resize",[a]),o.resize_flag=!1};return c(!1),clearInterval(o.resize_timer),r?(o.resize_flag=!1,!1):void(o.resize_timer=setInterval(function(){o.resize_flag||c()},s.timer))},r.addWidget({id:"stickyHeaders",priority:60,options:{stickyHeaders:"",stickyHeaders_attachTo:null,stickyHeaders_xScroll:null,stickyHeaders_yScroll:null,stickyHeaders_offset:0,stickyHeaders_filteredToTop:!0,stickyHeaders_cloneId:"-sticky",stickyHeaders_addResizeEvent:!0,stickyHeaders_includeCaption:!0,stickyHeaders_zIndex:2},format:function(i,a,l){if(!(a.$table.hasClass("hasStickyHeaders")||e.inArray("filter",a.widgets)>=0&&!a.$table.hasClass("hasFilters"))){var s,n=a.$table,o=e(l.stickyHeaders_attachTo),c=a.namespace+"stickyheaders ",d=e(l.stickyHeaders_yScroll||l.stickyHeaders_attachTo||t),f=e(l.stickyHeaders_xScroll||l.stickyHeaders_attachTo||t),h=n.children("thead:first"),u=h.children("tr").not(".sticky-false").children(),p=n.children("tfoot"),g=isNaN(l.stickyHeaders_offset)?e(l.stickyHeaders_offset):"",m=g.length?g.height()||0:parseInt(l.stickyHeaders_offset,10)||0,b=n.parent().closest("."+r.css.table).hasClass("hasStickyHeaders")?n.parent().closest("table.tablesorter")[0].config.widgetOptions.$sticky.parent():[],y=b.length?b.height():0,_=l.$sticky=n.clone().addClass("containsStickyHeaders "+r.css.sticky+" "+l.stickyHeaders+" "+a.namespace.slice(1)+"_extra_table").wrap('<div class="'+r.css.stickyWrap+'">'),v=_.parent().addClass(r.css.stickyHide).css({position:o.length?"absolute":"fixed",padding:parseInt(_.parent().parent().css("padding-left"),10),top:m+y,left:0,visibility:"hidden",zIndex:l.stickyHeaders_zIndex||2}),w=_.children("thead:first"),x="",C=0,$=function(r,i){r.filter(":visible").each(function(r){var a,l,s=i.filter(":visible").eq(r),n=e(this);"border-box"===n.css("box-sizing")?a=n.outerWidth():"collapse"===s.css("border-collapse")?t.getComputedStyle?a=parseFloat(t.getComputedStyle(this,null).width):(l=parseFloat(n.css("border-width")),a=n.outerWidth()-parseFloat(n.css("padding-left"))-parseFloat(n.css("padding-right"))-l):a=n.width(),s.css({"min-width":a,"max-width":a})})},z=function(){m=g.length?g.height()||0:parseInt(l.stickyHeaders_offset,10)||0,C=0,v.css({left:o.length?parseInt(o.css("padding-left"),10)||0:n.offset().left-parseInt(n.css("margin-left"),10)-f.scrollLeft()-C,width:n.outerWidth()}),$(n,_),$(u,s)};o.length&&!o.css("position")&&o.css("position","relative"),_.attr("id")&&(_[0].id+=l.stickyHeaders_cloneId),_.find("thead:gt(0), tr.sticky-false").hide(),_.find("tbody, tfoot").remove(),_.find("caption").toggle(l.stickyHeaders_includeCaption),s=w.children().children(),_.css({height:0,width:0,margin:0}),s.find("."+r.css.resizer).remove(),n.addClass("hasStickyHeaders").bind("pagerComplete"+c,function(){z()}),r.bindEvents(i,w.children().children("."+r.css.header)),n.after(v),a.onRenderHeader&&w.children("tr").children().each(function(t){a.onRenderHeader.apply(e(this),[t,a,_])}),f.add(d).unbind("scroll resize ".split(" ").join(c).replace(/\s+/g," ")).bind("scroll resize ".split(" ").join(c),function(t){if(n.is(":visible")){y=b.length?b.offset().top-d.scrollTop()+b.height():0;var i=n.offset(),a=e.isWindow(d[0]),l=e.isWindow(f[0]),s=(o.length?a?d.scrollTop():d.offset().top:d.scrollTop())+m+y,c=n.height()-(v.height()+(p.height()||0)),h=s>i.top&&s<i.top+c?"visible":"hidden",u={visibility:h};o.length&&(u.top=a?s-o.offset().top:o.scrollTop()),l&&(u.left=n.offset().left-parseInt(n.css("margin-left"),10)-f.scrollLeft()-C),b.length&&(u.top=(u.top||0)+m+y),v.removeClass(r.css.stickyVis+" "+r.css.stickyHide).addClass("visible"===h?r.css.stickyVis:r.css.stickyHide).css(u),(h!==x||"resize"===t.type)&&(z(),x=h)}}),l.stickyHeaders_addResizeEvent&&r.addHeaderResizeEvent(i),n.hasClass("hasFilters")&&l.filter_columnFilters&&(n.bind("filterEnd"+c,function(){var i=e(document.activeElement).closest("td"),s=i.parent().children().index(i);v.hasClass(r.css.stickyVis)&&l.stickyHeaders_filteredToTop&&(t.scrollTo(0,n.position().top),s>=0&&a.$filters&&a.$filters.eq(s).find("a, select, input").filter(":visible").focus())}),r.filter.bindSearch(n,s.find("."+r.css.filter)),l.filter_hideFilters&&r.filter.hideFilters(_,a)),n.trigger("stickyHeadersInit")}},remove:function(i,a,l){var s=a.namespace+"stickyheaders ";a.$table.removeClass("hasStickyHeaders").unbind("pagerComplete filterEnd ".split(" ").join(s).replace(/\s+/g," ")).next("."+r.css.stickyWrap).remove(),l.$sticky&&l.$sticky.length&&l.$sticky.remove(),e(t).add(l.stickyHeaders_xScroll).add(l.stickyHeaders_yScroll).add(l.stickyHeaders_attachTo).unbind("scroll resize ".split(" ").join(s).replace(/\s+/g," ")),r.addHeaderResizeEvent(i,!1)}})}(jQuery,window),function(e,t){"use strict";var r=e.tablesorter||{};e.extend(r.css,{resizableContainer:"tablesorter-resizable-container",resizableHandle:"tablesorter-resizable-handle",resizableNoSelect:"tablesorter-disableSelection",resizableStorage:"tablesorter-resizable"}),e(function(){var t="<style>body."+r.css.resizableNoSelect+" { -ms-user-select: none; -moz-user-select: -moz-none;-khtml-user-select: none; -webkit-user-select: none; user-select: none; }."+r.css.resizableContainer+" { position: relative; height: 1px; }."+r.css.resizableHandle+" { position: absolute; display: inline-block; width: 8px; top: 1px;cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }</style>";e(t).appendTo("body")}),r.resizable={init:function(t,i){if(!t.$table.hasClass("hasResizable")){t.$table.addClass("hasResizable"),r.resizableReset(t.table,!0),i.resizable_={$wrap:t.$table.parent(),mouseXPosition:0,$target:null,$next:null,overflow:"auto"===t.$table.parent().css("overflow"),fullWidth:Math.abs(t.$table.parent().width()-t.$table.width())<20,storedSizes:[]};var a,l,s,n,o=parseInt(t.$table.css("margin-top"),10);for(i.resizable_.storedSizes=n=(r.storage&&i.resizable!==!1?r.storage(t.table,r.css.resizableStorage):[])||[],r.resizable.setWidths(t,i,n),i.$resizable_container=e('<div class="'+r.css.resizableContainer+'">').css({top:o}).insertBefore(t.$table),s=0;s<t.columns;s++)l=t.$headerIndexed[s],a="false"===r.getData(l,r.getColumnData(t.table,t.headers,s),"resizable"),a||e('<div class="'+r.css.resizableHandle+'">').appendTo(i.$resizable_container).attr({"data-column":s,unselectable:"on"}).data("header",l).bind("selectstart",!1);t.$table.one("tablesorter-initialized",function(){r.resizable.setHandlePosition(t,i),r.resizable.bindings(this.config,this.config.widgetOptions)})}},setWidth:function(e,t){e.css({width:t,"min-width":"","max-width":""})},setWidths:function(t,i,a){var l,s=e(t.namespace+"_extra_headers"),n=t.$table.children("colgroup").children("col");if(a=a||i.resizable_.storedSizes||[],a.length){for(l=0;l<t.columns;l++)t.$headerIndexed[l].width(a[l]),s.length&&r.resizable.setWidth(s.eq(l).add(n.eq(l)),a[l]);e(t.namespace+"_extra_table").length&&!r.hasWidget(t.table,"scroller")&&r.resizable.setWidth(e(t.namespace+"_extra_table"),t.$table.outerWidth())}},setHandlePosition:function(t,i){var a,l=r.hasWidget(t.table,"scroller"),s=t.$table.height(),n=i.$resizable_container.children(),o=Math.floor(n.width()/2);l&&(s=0,t.$table.closest("."+r.css.scrollerWrap).children().each(function(){var t=e(this);s+=t.filter('[style*="height"]').length?t.height():t.children("table").height()})),a=t.$table.position().left,n.each(function(){var r=e(this),l=parseInt(r.attr("data-column"),10),n=t.columns-1,c=r.data("header");c&&(c.is(":visible")?(n>l||l===n&&i.resizable_addLastColumn)&&r.css({display:"inline-block",height:s,left:c.position().left-a+c.outerWidth()-o}):r.hide())})},toggleTextSelection:function(t,i){var a=t.namespace+"tsresize";t.widgetOptions.resizable_.disabled=i,e("body").toggleClass(r.css.resizableNoSelect,i),i?e("body").attr("unselectable","on").bind("selectstart"+a,!1):e("body").removeAttr("unselectable").unbind("selectstart"+a)},bindings:function(i,a){var l=i.namespace+"tsresize";a.$resizable_container.children().bind("mousedown",function(t){var l,s,n=a.resizable_,o=e(i.namespace+"_extra_headers"),c=e(t.target).data("header");for(l=parseInt(c.attr("data-column"),10),n.$target=c=c.add(o.filter('[data-column="'+l+'"]')),n.target=l,n.$next=t.shiftKey||a.resizable_targetLast?c.parent().children().not(".resizable-false").filter(":last"):c.nextAll(":not(.resizable-false)").eq(0),l=parseInt(n.$next.attr("data-column"),10),n.$next=n.$next.add(o.filter('[data-column="'+l+'"]')),n.next=l,n.mouseXPosition=t.pageX,n.storedSizes=[],l=0;l<i.columns;l++)s=i.$headerIndexed[l],n.storedSizes[l]=s.is(":visible")?s.width():0;r.resizable.toggleTextSelection(i,!0)}),e(document).bind("mousemove"+l,function(e){var t=a.resizable_;t.disabled&&0!==t.mouseXPosition&&t.$target&&(a.resizable_throttle?(clearTimeout(t.timer),t.timer=setTimeout(function(){r.resizable.mouseMove(i,a,e)},isNaN(a.resizable_throttle)?5:a.resizable_throttle)):r.resizable.mouseMove(i,a,e))}).bind("mouseup"+l,function(){a.resizable_.disabled&&(r.resizable.toggleTextSelection(i,!1),r.resizable.stopResize(i,a),r.resizable.setHandlePosition(i,a))}),e(t).bind("resize"+l+" resizeEnd"+l,function(){r.resizable.setHandlePosition(i,a)}),i.$table.bind("columnUpdate"+l,function(){r.resizable.setHandlePosition(i,a)}).find("thead:first").add(e(i.namespace+"_extra_table").find("thead:first")).bind("contextmenu"+l,function(){var e=0===a.resizable_.storedSizes.length;return r.resizableReset(i.table),r.resizable.setHandlePosition(i,a),a.resizable_.storedSizes=[],e})},mouseMove:function(t,i,a){if(0!==i.resizable_.mouseXPosition&&i.resizable_.$target){var l=i.resizable_,s=l.$next,n=a.pageX-l.mouseXPosition;l.fullWidth?(l.storedSizes[l.target]+=n,l.storedSizes[l.next]-=n,r.resizable.setWidths(t,i)):l.overflow?(t.$table.add(e(t.namespace+"_extra_table")).width(function(e,t){return t+n}),s.length||(l.$wrap[0].scrollLeft=t.$table.width())):(l.storedSizes[l.target]+=n,r.resizable.setWidths(t,i)),l.mouseXPosition=a.pageX}},stopResize:function(i,a){var l,s,n=a.resizable_;if(n.storedSizes=[],r.storage){for(n.storedSizes=[],s=0;s<i.columns;s++)l=i.$headerIndexed[s],n.storedSizes[s]=l.is(":visible")?l.width():0;a.resizable!==!1&&r.storage(i.table,r.css.resizableStorage,n.storedSizes)}n.mouseXPosition=0,n.$target=n.$next=null,e(t).trigger("resize")}},r.addWidget({id:"resizable",priority:40,options:{resizable:!0,resizable_addLastColumn:!1,resizable_widths:[],resizable_throttle:!1,resizable_targetLast:!1},init:function(e,t,i,a){r.resizable.init(i,a)},remove:function(t,i,a,l){if(a.$resizable_container){var s=i.namespace+"tsresize";i.$table.add(e(i.namespace+"_extra_table")).removeClass("hasResizable").children("thead").unbind("contextmenu"+s),a.$resizable_container.remove(),r.resizable.toggleTextSelection(i,!1),r.resizableReset(t,l),e(document).unbind("mousemove"+s+" mouseup"+s)}}}),r.resizableReset=function(i,a){e(i).each(function(){var l,s,n=this.config,o=n&&n.widgetOptions;if(i&&n&&n.$headerIndexed.length){for(l=0;l<n.columns;l++)s=n.$headerIndexed[l],o.resizable_widths&&o.resizable_widths[l]?s.css("width",o.resizable_widths[l]):s.hasClass("resizable-false")||s.css("width","");e(t).trigger("resize"),r.storage&&!a&&r.storage(this,r.css.resizableStorage,{})}})}}(jQuery,window),function(e){"use strict";var t=e.tablesorter||{};t.addWidget({id:"saveSort",priority:20,options:{saveSort:!0},init:function(e,t,r,i){t.format(e,r,i,!0)},format:function(r,i,a,l){var s,n,o=i.$table,c=a.saveSort!==!1,d={sortList:i.sortList};i.debug&&(n=new Date),o.hasClass("hasSaveSort")?c&&r.hasInitialized&&t.storage&&(t.storage(r,"tablesorter-savesort",d),i.debug&&t.benchmark("saveSort widget: Saving last sort: "+i.sortList,n)):(o.addClass("hasSaveSort"),d="",t.storage&&(s=t.storage(r,"tablesorter-savesort"),d=s&&s.hasOwnProperty("sortList")&&e.isArray(s.sortList)?s.sortList:"",i.debug&&t.benchmark('saveSort: Last sort loaded: "'+d+'"',n),o.bind("saveSortReset",function(e){e.stopPropagation(),t.storage(r,"tablesorter-savesort","")})),l&&d&&d.length>0?i.sortList=d:r.hasInitialized&&d&&d.length>0&&o.trigger("sorton",[d]))},remove:function(e,r){r.$table.removeClass("hasSaveSort"),t.storage&&t.storage(e,"tablesorter-savesort","")}})}(jQuery),e.tablesorter}); +(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery) { +/*! Widget: storage - updated 2018-03-18 (v2.30.0) */ +/*global JSON:false */ +;(function ($, window, document) { + 'use strict'; + + var ts = $.tablesorter || {}; + + // update defaults for validator; these values must be falsy! + $.extend(true, ts.defaults, { + fixedUrl: '', + widgetOptions: { + storage_fixedUrl: '', + storage_group: '', + storage_page: '', + storage_storageType: '', + storage_tableId: '', + storage_useSessionStorage: '' + } + }); + + // *** 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 + */ + ts.storage = function(table, key, value, options) { + table = $(table)[0]; + var cookieIndex, cookies, date, + hasStorage = false, + values = {}, + c = table.config, + wo = c && c.widgetOptions, + debug = ts.debug(c, 'storage'), + storageType = ( + ( options && options.storageType ) || ( wo && wo.storage_storageType ) + ).toString().charAt(0).toLowerCase(), + // deprecating "useSessionStorage"; any storageType setting overrides it + session = storageType ? '' : + ( options && options.useSessionStorage ) || ( wo && wo.storage_useSessionStorage ), + $table = $(table), + // id from (1) options ID, (2) table 'data-table-group' attribute, (3) widgetOptions.storage_tableId, + // (4) table ID, then (5) table index + id = options && options.id || + $table.attr( options && options.group || wo && wo.storage_group || 'data-table-group') || + wo && wo.storage_tableId || table.id || $('.tablesorter').index( $table ), + // url from (1) options url, (2) table 'data-table-page' attribute, (3) widgetOptions.storage_fixedUrl, + // (4) table.config.fixedUrl (deprecated), then (5) window location path + url = options && options.url || + $table.attr(options && options.page || wo && wo.storage_page || 'data-table-page') || + wo && wo.storage_fixedUrl || c && c.fixedUrl || window.location.pathname; + + // skip if using cookies + if (storageType !== 'c') { + storageType = (storageType === 's' || session) ? 'sessionStorage' : 'localStorage'; + // https://gist.github.com/paulirish/5558557 + if (storageType in window) { + try { + window[storageType].setItem('_tmptest', 'temp'); + hasStorage = true; + window[storageType].removeItem('_tmptest'); + } catch (error) { + console.warn( storageType + ' is not supported in this browser' ); + } + } + } + if (debug) { + console.log('Storage >> Using', hasStorage ? storageType : 'cookies'); + } + // *** get value *** + if ($.parseJSON) { + if (hasStorage) { + values = $.parseJSON( window[storageType][key] || 'null' ) || {}; + } else { + // old browser, using cookies + cookies = document.cookie.split(/[;\s|=]/); + // add one to get from the key to the value + cookieIndex = $.inArray(key, cookies) + 1; + values = (cookieIndex !== 0) ? $.parseJSON(cookies[cookieIndex] || 'null') || {} : {}; + } + } + // allow value to be an empty string too + if (typeof value !== 'undefined' && window.JSON && JSON.hasOwnProperty('stringify')) { + // add unique identifiers = url pathname > table ID/index on page > data + if (!values[url]) { + values[url] = {}; + } + values[url][id] = value; + // *** set value *** + if (hasStorage) { + window[storageType][key] = JSON.stringify(values); + } else { + date = new Date(); + date.setTime(date.getTime() + (31536e+6)); // 365 days + document.cookie = key + '=' + (JSON.stringify(values)).replace(/\"/g, '\"') + '; expires=' + date.toGMTString() + '; path=/'; + } + } else { + return values && values[url] ? values[url][id] : ''; + } + }; + +})(jQuery, window, document); + +/*! Widget: uitheme - updated 2018-03-18 (v2.30.0) */ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + ts.themes = { + 'bootstrap' : { + table : 'table table-bordered table-striped', + caption : 'caption', + // header class names + header : 'bootstrap-header', // give the header a gradient background (theme.bootstrap_2.css) + sortNone : '', + sortAsc : '', + sortDesc : '', + active : '', // applied when column is sorted + hover : '', // custom css required - a defined bootstrap style may not override other classes + // icon class names + icons : '', // add 'bootstrap-icon-white' to make them white; this icon class is added to the <i> in the header + iconSortNone : 'bootstrap-icon-unsorted', // class name added to icon when column is not sorted + iconSortAsc : 'glyphicon glyphicon-chevron-up', // class name added to icon when column has ascending sort + iconSortDesc : 'glyphicon glyphicon-chevron-down', // class name added to icon when column has descending sort + filterRow : '', // filter row class + footerRow : '', + footerCells : '', + even : '', // even row zebra striping + odd : '' // odd row zebra striping + }, + 'jui' : { + table : 'ui-widget ui-widget-content ui-corner-all', // table classes + caption : 'ui-widget-content', + // header class names + header : 'ui-widget-header ui-corner-all ui-state-default', // header classes + sortNone : '', + sortAsc : '', + sortDesc : '', + active : 'ui-state-active', // applied when column is sorted + hover : 'ui-state-hover', // hover class + // icon class names + icons : 'ui-icon', // icon class added to the <i> in the header + iconSortNone : 'ui-icon-carat-2-n-s ui-icon-caret-2-n-s', // class name added to icon when column is not sorted + iconSortAsc : 'ui-icon-carat-1-n ui-icon-caret-1-n', // class name added to icon when column has ascending sort + iconSortDesc : 'ui-icon-carat-1-s ui-icon-caret-1-s', // class name added to icon when column has descending sort + filterRow : '', + footerRow : '', + footerCells : '', + even : 'ui-widget-content', // even row zebra striping + odd : 'ui-state-default' // odd row zebra striping + } + }; + + $.extend(ts.css, { + wrapper : 'tablesorter-wrapper' // ui theme & resizable + }); + + ts.addWidget({ + id: 'uitheme', + priority: 10, + format: function(table, c, wo) { + var i, tmp, hdr, icon, time, $header, $icon, $tfoot, $h, oldtheme, oldremove, oldIconRmv, hasOldTheme, + themesAll = ts.themes, + $table = c.$table.add( $( c.namespace + '_extra_table' ) ), + $headers = c.$headers.add( $( c.namespace + '_extra_headers' ) ), + theme = c.theme || 'jui', + themes = themesAll[theme] || {}, + remove = $.trim( [ themes.sortNone, themes.sortDesc, themes.sortAsc, themes.active ].join( ' ' ) ), + iconRmv = $.trim( [ themes.iconSortNone, themes.iconSortDesc, themes.iconSortAsc ].join( ' ' ) ), + debug = ts.debug(c, 'uitheme'); + if (debug) { time = new Date(); } + // initialization code - run once + if (!$table.hasClass('tablesorter-' + theme) || c.theme !== c.appliedTheme || !wo.uitheme_applied) { + wo.uitheme_applied = true; + oldtheme = themesAll[c.appliedTheme] || {}; + hasOldTheme = !$.isEmptyObject(oldtheme); + oldremove = hasOldTheme ? [ oldtheme.sortNone, oldtheme.sortDesc, oldtheme.sortAsc, oldtheme.active ].join( ' ' ) : ''; + oldIconRmv = hasOldTheme ? [ oldtheme.iconSortNone, oldtheme.iconSortDesc, oldtheme.iconSortAsc ].join( ' ' ) : ''; + if (hasOldTheme) { + wo.zebra[0] = $.trim( ' ' + wo.zebra[0].replace(' ' + oldtheme.even, '') ); + wo.zebra[1] = $.trim( ' ' + wo.zebra[1].replace(' ' + oldtheme.odd, '') ); + c.$tbodies.children().removeClass( [ oldtheme.even, oldtheme.odd ].join(' ') ); + } + // update zebra stripes + if (themes.even) { wo.zebra[0] += ' ' + themes.even; } + if (themes.odd) { wo.zebra[1] += ' ' + themes.odd; } + // add caption style + $table.children('caption') + .removeClass(oldtheme.caption || '') + .addClass(themes.caption); + // add table/footer class names + $tfoot = $table + // remove other selected themes + .removeClass( (c.appliedTheme ? 'tablesorter-' + (c.appliedTheme || '') : '') + ' ' + (oldtheme.table || '') ) + .addClass('tablesorter-' + theme + ' ' + (themes.table || '')) // add theme widget class name + .children('tfoot'); + c.appliedTheme = c.theme; + + if ($tfoot.length) { + $tfoot + // if oldtheme.footerRow or oldtheme.footerCells are undefined, all class names are removed + .children('tr').removeClass(oldtheme.footerRow || '').addClass(themes.footerRow) + .children('th, td').removeClass(oldtheme.footerCells || '').addClass(themes.footerCells); + } + // update header classes + $headers + .removeClass( (hasOldTheme ? [ oldtheme.header, oldtheme.hover, oldremove ].join(' ') : '') || '' ) + .addClass(themes.header) + .not('.sorter-false') + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') + .bind('mouseenter.tsuitheme mouseleave.tsuitheme', function(event) { + // toggleClass with switch added in jQuery 1.3 + $(this)[ event.type === 'mouseenter' ? 'addClass' : 'removeClass' ](themes.hover || ''); + }); + + $headers.each(function() { + var $this = $(this); + if (!$this.find('.' + ts.css.wrapper).length) { + // Firefox needs this inner div to position the icon & resizer correctly + $this.wrapInner('<div class="' + ts.css.wrapper + '" style="position:relative;height:100%;width:100%"></div>'); + } + }); + if (c.cssIcon) { + // if c.cssIcon is '', then no <i> is added to the header + $headers + .find('.' + ts.css.icon) + .removeClass(hasOldTheme ? [ oldtheme.icons, oldIconRmv ].join(' ') : '') + .addClass(themes.icons || ''); + } + // filter widget initializes after uitheme + if (ts.hasWidget( c.table, 'filter' )) { + tmp = function() { + $table.children('thead').children('.' + ts.css.filterRow) + .removeClass(hasOldTheme ? oldtheme.filterRow || '' : '') + .addClass(themes.filterRow || ''); + }; + if (wo.filter_initialized) { + tmp(); + } else { + $table.one('filterInit', function() { + tmp(); + }); + } + } + } + for (i = 0; i < c.columns; i++) { + $header = c.$headers + .add($(c.namespace + '_extra_headers')) + .not('.sorter-false') + .filter('[data-column="' + i + '"]'); + $icon = (ts.css.icon) ? $header.find('.' + ts.css.icon) : $(); + $h = $headers.not('.sorter-false').filter('[data-column="' + i + '"]:last'); + if ($h.length) { + $header.removeClass(remove); + $icon.removeClass(iconRmv); + if ($h[0].sortDisabled) { + // no sort arrows for disabled columns! + $icon.removeClass(themes.icons || ''); + } else { + hdr = themes.sortNone; + icon = themes.iconSortNone; + if ($h.hasClass(ts.css.sortAsc)) { + hdr = [ themes.sortAsc, themes.active ].join(' '); + icon = themes.iconSortAsc; + } else if ($h.hasClass(ts.css.sortDesc)) { + hdr = [ themes.sortDesc, themes.active ].join(' '); + icon = themes.iconSortDesc; + } + $header.addClass(hdr); + $icon.addClass(icon || ''); + } + } + } + if (debug) { + console.log('uitheme >> Applied ' + theme + ' theme' + ts.benchmark(time)); + } + }, + remove: function(table, c, wo, refreshing) { + if (!wo.uitheme_applied) { return; } + var $table = c.$table, + theme = c.appliedTheme || 'jui', + themes = ts.themes[ theme ] || ts.themes.jui, + $headers = $table.children('thead').children(), + remove = themes.sortNone + ' ' + themes.sortDesc + ' ' + themes.sortAsc, + iconRmv = themes.iconSortNone + ' ' + themes.iconSortDesc + ' ' + themes.iconSortAsc; + $table.removeClass('tablesorter-' + theme + ' ' + themes.table); + wo.uitheme_applied = false; + if (refreshing) { return; } + $table.find(ts.css.header).removeClass(themes.header); + $headers + .unbind('mouseenter.tsuitheme mouseleave.tsuitheme') // remove hover + .removeClass(themes.hover + ' ' + remove + ' ' + themes.active) + .filter('.' + ts.css.filterRow) + .removeClass(themes.filterRow); + $headers.find('.' + ts.css.icon).removeClass(themes.icons + ' ' + iconRmv); + } + }); + +})(jQuery); + +/*! Widget: columns - updated 5/24/2017 (v2.28.11) */ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + ts.addWidget({ + id: 'columns', + priority: 65, + options : { + columns : [ 'primary', 'secondary', 'tertiary' ] + }, + format: function(table, c, wo) { + var $tbody, tbodyIndex, $rows, rows, $row, $cells, remove, indx, + $table = c.$table, + $tbodies = c.$tbodies, + sortList = c.sortList, + len = sortList.length, + // removed c.widgetColumns support + css = wo && wo.columns || [ 'primary', 'secondary', 'tertiary' ], + last = css.length - 1; + remove = css.join(' '); + // check if there is a sort (on initialization there may not be one) + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // detach tbody + $rows = $tbody.children('tr'); + // loop through the visible rows + $rows.each(function() { + $row = $(this); + if (this.style.display !== 'none') { + // remove all columns class names + $cells = $row.children().removeClass(remove); + // add appropriate column class names + if (sortList && sortList[0]) { + // primary sort column class + $cells.eq(sortList[0][0]).addClass(css[0]); + if (len > 1) { + for (indx = 1; indx < len; indx++) { + // secondary, tertiary, etc sort column classes + $cells.eq(sortList[indx][0]).addClass( css[indx] || css[last] ); + } + } + } + } + }); + ts.processTbody(table, $tbody, false); + } + // add classes to thead and tfoot + rows = wo.columns_thead !== false ? [ 'thead tr' ] : []; + if (wo.columns_tfoot !== false) { + rows.push('tfoot tr'); + } + if (rows.length) { + $rows = $table.find( rows.join(',') ).children().removeClass(remove); + if (len) { + for (indx = 0; indx < len; indx++) { + // add primary. secondary, tertiary, etc sort column classes + $rows.filter('[data-column="' + sortList[indx][0] + '"]').addClass(css[indx] || css[last]); + } + } + } + }, + remove: function(table, c, wo) { + var tbodyIndex, $tbody, + $tbodies = c.$tbodies, + remove = (wo.columns || [ 'primary', 'secondary', 'tertiary' ]).join(' '); + c.$headers.removeClass(remove); + c.$table.children('tfoot').children('tr').children('th, td').removeClass(remove); + for (tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody(table, $tbodies.eq(tbodyIndex), true); // remove tbody + $tbody.children('tr').each(function() { + $(this).children().removeClass(remove); + }); + ts.processTbody(table, $tbody, false); // restore tbody + } + } + }); + +})(jQuery); + +/*! Widget: filter - updated 2018-03-18 (v2.30.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.7+ + * by Rob Garrison + */ +;( function ( $ ) { + 'use strict'; + var tsf, tsfRegex, + ts = $.tablesorter || {}, + tscss = ts.css, + tskeyCodes = ts.keyCodes; + + $.extend( tscss, { + filterRow : 'tablesorter-filter-row', + filter : 'tablesorter-filter', + filterDisabled : 'disabled', + filterRowHide : 'hideme' + }); + + $.extend( tskeyCodes, { + backSpace : 8, + escape : 27, + space : 32, + left : 37, + down : 40 + }); + + ts.addWidget({ + id: 'filter', + priority: 50, + options : { + filter_cellFilter : '', // css class name added to the filter cell ( string or array ) + filter_childRows : false, // if true, filter includes child row content in the search + filter_childByColumn : false, // ( filter_childRows must be true ) if true = search child rows by column; false = search all child row text grouped + filter_childWithSibs : true, // if true, include matching child row siblings + filter_columnAnyMatch: true, // if true, allows using '#:{query}' in AnyMatch searches ( column:query ) + filter_columnFilters : true, // if true, a filter will be added to the top of each table column + filter_cssFilter : '', // css class name added to the filter row & each input in the row ( tablesorter-filter is ALWAYS added ) + filter_defaultAttrib : 'data-value', // data attribute in the header cell that contains the default filter value + filter_defaultFilter : {}, // add a default column filter type '~{query}' to make fuzzy searches default; '{q1} AND {q2}' to make all searches use a logical AND. + filter_excludeFilter : {}, // filters to exclude, per column + filter_external : '', // jQuery selector string ( or jQuery object ) of external filters + filter_filteredRow : 'filtered', // class added to filtered rows; define in css with "display:none" to hide the filtered-out rows + filter_filterLabel : 'Filter "{{label}}" column by...', // Aria-label added to filter input/select; see #1495 + filter_formatter : null, // add custom filter elements to the filter row + filter_functions : null, // add custom filter functions using this option + filter_hideEmpty : true, // hide filter row when table is empty + filter_hideFilters : false, // collapse filter row when mouse leaves the area + filter_ignoreCase : true, // if true, make all searches case-insensitive + filter_liveSearch : true, // if true, search column content while the user types ( with a delay ) + filter_matchType : { 'input': 'exact', 'select': 'exact' }, // global query settings ('exact' or 'match'); overridden by "filter-match" or "filter-exact" class + filter_onlyAvail : 'filter-onlyAvail', // a header with a select dropdown & this class name will only show available ( visible ) options within the drop down + filter_placeholder : { search : '', select : '' }, // default placeholder text ( overridden by any header 'data-placeholder' setting ) + filter_reset : null, // jQuery selector string of an element used to reset the filters + filter_resetOnEsc : true, // Reset filter input when the user presses escape - normalized across browsers + filter_saveFilters : false, // Use the $.tablesorter.storage utility to save the most recent filters + filter_searchDelay : 300, // typing delay in milliseconds before starting a search + filter_searchFiltered: true, // allow searching through already filtered rows in special circumstances; will speed up searching in large tables if true + filter_selectSource : null, // include a function to return an array of values to be added to the column filter select + filter_selectSourceSeparator : '|', // filter_selectSource array text left of the separator is added to the option value, right into the option text + filter_serversideFiltering : false, // if true, must perform server-side filtering b/c client-side filtering is disabled, but the ui and events will still be used. + filter_startsWith : false, // if true, filter start from the beginning of the cell contents + filter_useParsedData : false // filter all data using parsed content + }, + format: function( table, c, wo ) { + if ( !c.$table.hasClass( 'hasFilters' ) ) { + tsf.init( table, c, wo ); + } + }, + remove: function( table, c, wo, refreshing ) { + var tbodyIndex, $tbody, + $table = c.$table, + $tbodies = c.$tbodies, + events = ( + 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterFomatterUpdate filterEnd search stickyHeadersInit ' + ).split( ' ' ).join( c.namespace + 'filter ' ); + $table + .removeClass( 'hasFilters' ) + // add filter namespace to all BUT search + .unbind( events.replace( ts.regex.spaces, ' ' ) ) + // remove the filter row even if refreshing, because the column might have been moved + .find( '.' + tscss.filterRow ).remove(); + wo.filter_initialized = false; + if ( refreshing ) { return; } + for ( tbodyIndex = 0; tbodyIndex < $tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, $tbodies.eq( tbodyIndex ), true ); // remove tbody + $tbody.children().removeClass( wo.filter_filteredRow ).show(); + ts.processTbody( table, $tbody, false ); // restore tbody + } + if ( wo.filter_reset ) { + $( document ).undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ); + } + } + }); + + tsf = ts.filter = { + + // regex used in filter 'check' functions - not for general use and not documented + regex: { + regex : /^\/((?:\\\/|[^\/])+)\/([migyu]{0,5})?$/, // regex to test for regex + child : /tablesorter-childRow/, // child row class name; this gets updated in the script + filtered : /filtered/, // filtered (hidden) row class name; updated in the script + type : /undefined|number/, // check type + exact : /(^[\"\'=]+)|([\"\'=]+$)/g, // exact match (allow '==') + operators : /[<>=]/g, // replace operators + query : '(q|query)', // replace filter queries + wild01 : /\?/g, // wild card match 0 or 1 + wild0More : /\*/g, // wild care match 0 or more + quote : /\"/g, + isNeg1 : /(>=?\s*-\d)/, + isNeg2 : /(<=?\s*\d)/ + }, + // function( c, data ) { } + // c = table.config + // data.$row = jQuery object of the row currently being processed + // data.$cells = jQuery object of all cells within the current row + // data.filters = array of filters for all columns ( some may be undefined ) + // data.filter = filter for the current column + // data.iFilter = same as data.filter, except lowercase ( if wo.filter_ignoreCase is true ) + // data.exact = table cell text ( or parsed data if column parser enabled; may be a number & not a string ) + // data.iExact = same as data.exact, except lowercase ( if wo.filter_ignoreCase is true; may be a number & not a string ) + // data.cache = table cell text from cache, so it has been parsed ( & in all lower case if c.ignoreCase is true ) + // data.cacheArray = An array of parsed content from each table cell in the row being processed + // data.index = column index; table = table element ( DOM ) + // data.parsed = array ( by column ) of boolean values ( from filter_useParsedData or 'filter-parsed' class ) + types: { + or : function( c, data, vars ) { + // look for "|", but not if it is inside of a regular expression + if ( ( tsfRegex.orTest.test( data.iFilter ) || tsfRegex.orSplit.test( data.filter ) ) && + // this test for regex has potential to slow down the overall search + !tsfRegex.regex.test( data.filter ) ) { + var indx, filterMatched, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + filter = data.filter.split( tsfRegex.orSplit ), + iFilter = data.iFilter.split( tsfRegex.orSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')'; + try { + // use try/catch, because query may not be a valid regex if "|" is contained within a partial regex search, + // e.g "/(Alex|Aar" -> Uncaught SyntaxError: Invalid regular expression: /(/(Alex)/: Unterminated group + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // filterMatched = data2.filter === '' && indx > 0 ? true + // look for an exact match with the 'or' unless the 'filter-match' class is found + filterMatched = regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ); + if ( filterMatched ) { + return filterMatched; + } + } catch ( error ) { + return null; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for an AND or && operator ( logical and ) + and : function( c, data, vars ) { + if ( tsfRegex.andTest.test( data.filter ) ) { + var indx, filterMatched, result, query, regex, + // duplicate data but split filter + data2 = $.extend( {}, data ), + filter = data.filter.split( tsfRegex.andSplit ), + iFilter = data.iFilter.split( tsfRegex.andSplit ), + len = filter.length; + for ( indx = 0; indx < len; indx++ ) { + data2.nestedFilters = true; + data2.filter = '' + ( tsf.parseFilter( c, filter[ indx ], data ) || '' ); + data2.iFilter = '' + ( tsf.parseFilter( c, iFilter[ indx ], data ) || '' ); + query = ( '(' + ( tsf.parseFilter( c, data2.filter, data ) || '' ) + ')' ) + // replace wild cards since /(a*)/i will match anything + .replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ); + try { + // use try/catch just in case RegExp is invalid + regex = new RegExp( data.isMatch ? query : '^' + query + '$', c.widgetOptions.filter_ignoreCase ? 'i' : '' ); + // look for an exact match with the 'and' unless the 'filter-match' class is found + result = ( regex.test( data2.exact ) || tsf.processTypes( c, data2, vars ) ); + if ( indx === 0 ) { + filterMatched = result; + } else { + filterMatched = filterMatched && result; + } + } catch ( error ) { + return null; + } + } + // may be null from processing types + return filterMatched || false; + } + return null; + }, + // Look for regex + regex: function( c, data ) { + if ( tsfRegex.regex.test( data.filter ) ) { + var matches, + // cache regex per column for optimal speed + regex = data.filter_regexCache[ data.index ] || tsfRegex.regex.exec( data.filter ), + isRegex = regex instanceof RegExp; + try { + if ( !isRegex ) { + // force case insensitive search if ignoreCase option set? + // if ( c.ignoreCase && !regex[2] ) { regex[2] = 'i'; } + data.filter_regexCache[ data.index ] = regex = new RegExp( regex[1], regex[2] ); + } + matches = regex.test( data.exact ); + } catch ( error ) { + matches = false; + } + return matches; + } + return null; + }, + // Look for operators >, >=, < or <= + operators: function( c, data ) { + // ignore empty strings... because '' < 10 is true + if ( tsfRegex.operTest.test( data.iFilter ) && data.iExact !== '' ) { + var cachedValue, result, txt, + table = c.table, + parsed = data.parsed[ data.index ], + query = ts.formatFloat( data.iFilter.replace( tsfRegex.operators, '' ), table ), + parser = c.parsers[ data.index ] || {}, + savedSearch = query; + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || parser.type === 'numeric' ) { + txt = $.trim( '' + data.iFilter.replace( tsfRegex.operators, '' ) ); + result = tsf.parseFilter( c, txt, data, true ); + query = ( typeof result === 'number' && result !== '' && !isNaN( result ) ) ? result : query; + } + // iExact may be numeric - see issue #149; + // check if cached is defined, because sometimes j goes out of range? ( numeric columns ) + if ( ( parsed || parser.type === 'numeric' ) && !isNaN( query ) && + typeof data.cache !== 'undefined' ) { + cachedValue = data.cache; + } else { + txt = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; + cachedValue = ts.formatFloat( txt, table ); + } + if ( tsfRegex.gtTest.test( data.iFilter ) ) { + result = tsfRegex.gteTest.test( data.iFilter ) ? cachedValue >= query : cachedValue > query; + } else if ( tsfRegex.ltTest.test( data.iFilter ) ) { + result = tsfRegex.lteTest.test( data.iFilter ) ? cachedValue <= query : cachedValue < query; + } + // keep showing all rows if nothing follows the operator + if ( !result && savedSearch === '' ) { + result = true; + } + return result; + } + return null; + }, + // Look for a not match + notMatch: function( c, data ) { + if ( tsfRegex.notTest.test( data.iFilter ) ) { + var indx, + txt = data.iFilter.replace( '!', '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; + if ( tsfRegex.exact.test( filter ) ) { + // look for exact not matches - see #628 + filter = filter.replace( tsfRegex.exact, '' ); + return filter === '' ? true : $.trim( filter ) !== data.iExact; + } else { + indx = data.iExact.search( $.trim( filter ) ); + return filter === '' ? true : + // return true if not found + data.anyMatch ? indx < 0 : + // return false if found + !( c.widgetOptions.filter_startsWith ? indx === 0 : indx >= 0 ); + } + } + return null; + }, + // Look for quotes or equals to get an exact match; ignore type since iExact could be numeric + exact: function( c, data ) { + /*jshint eqeqeq:false */ + if ( tsfRegex.exact.test( data.iFilter ) ) { + var txt = data.iFilter.replace( tsfRegex.exact, '' ), + filter = tsf.parseFilter( c, txt, data ) || ''; + // eslint-disable-next-line eqeqeq + return data.anyMatch ? $.inArray( filter, data.rowArray ) >= 0 : filter == data.iExact; + } + return null; + }, + // Look for a range ( using ' to ' or ' - ' ) - see issue #166; thanks matzhu! + range : function( c, data ) { + if ( tsfRegex.toTest.test( data.iFilter ) ) { + var result, tmp, range1, range2, + table = c.table, + index = data.index, + parsed = data.parsed[index], + // make sure the dash is for a range and not indicating a negative number + query = data.iFilter.split( tsfRegex.toSplit ); + + tmp = query[0].replace( ts.regex.nondigit, '' ) || ''; + range1 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); + tmp = query[1].replace( ts.regex.nondigit, '' ) || ''; + range2 = ts.formatFloat( tsf.parseFilter( c, tmp, data ), table ); + // parse filter value in case we're comparing numbers ( dates ) + if ( parsed || c.parsers[ index ].type === 'numeric' ) { + result = c.parsers[ index ].format( '' + query[0], table, c.$headers.eq( index ), index ); + range1 = ( result !== '' && !isNaN( result ) ) ? result : range1; + result = c.parsers[ index ].format( '' + query[1], table, c.$headers.eq( index ), index ); + range2 = ( result !== '' && !isNaN( result ) ) ? result : range2; + } + if ( ( parsed || c.parsers[ index ].type === 'numeric' ) && !isNaN( range1 ) && !isNaN( range2 ) ) { + result = data.cache; + } else { + tmp = isNaN( data.iExact ) ? data.iExact.replace( ts.regex.nondigit, '' ) : data.iExact; + result = ts.formatFloat( tmp, table ); + } + if ( range1 > range2 ) { + tmp = range1; range1 = range2; range2 = tmp; // swap + } + return ( result >= range1 && result <= range2 ) || ( range1 === '' || range2 === '' ); + } + return null; + }, + // Look for wild card: ? = single, * = multiple, or | = logical OR + wild : function( c, data ) { + if ( tsfRegex.wildOrTest.test( data.iFilter ) ) { + var query = '' + ( tsf.parseFilter( c, data.iFilter, data ) || '' ); + // look for an exact match with the 'or' unless the 'filter-match' class is found + if ( !tsfRegex.wildTest.test( query ) && data.nestedFilters ) { + query = data.isMatch ? query : '^(' + query + ')$'; + } + // parsing the filter may not work properly when using wildcards =/ + try { + return new RegExp( + query.replace( tsfRegex.wild01, '\\S{1}' ).replace( tsfRegex.wild0More, '\\S*' ), + c.widgetOptions.filter_ignoreCase ? 'i' : '' + ) + .test( data.exact ); + } catch ( error ) { + return null; + } + } + return null; + }, + // fuzzy text search; modified from https://github.com/mattyork/fuzzy ( MIT license ) + fuzzy: function( c, data ) { + if ( tsfRegex.fuzzyTest.test( data.iFilter ) ) { + var indx, + patternIndx = 0, + len = data.iExact.length, + txt = data.iFilter.slice( 1 ), + pattern = tsf.parseFilter( c, txt, data ) || ''; + for ( indx = 0; indx < len; indx++ ) { + if ( data.iExact[ indx ] === pattern[ patternIndx ] ) { + patternIndx += 1; + } + } + return patternIndx === pattern.length; + } + return null; + } + }, + init: function( table ) { + // filter language options + ts.language = $.extend( true, {}, { + to : 'to', + or : 'or', + and : 'and' + }, ts.language ); + + var options, string, txt, $header, column, val, fxn, noSelect, + c = table.config, + wo = c.widgetOptions, + processStr = function(prefix, str, suffix) { + str = str.trim(); + // don't include prefix/suffix if str is empty + return str === '' ? '' : (prefix || '') + str + (suffix || ''); + }; + c.$table.addClass( 'hasFilters' ); + c.lastSearch = []; + + // define timers so using clearTimeout won't cause an undefined error + wo.filter_searchTimer = null; + wo.filter_initTimer = null; + wo.filter_formatterCount = 0; + wo.filter_formatterInit = []; + wo.filter_anyColumnSelector = '[data-column="all"],[data-column="any"]'; + wo.filter_multipleColumnSelector = '[data-column*="-"],[data-column*=","]'; + + val = '\\{' + tsfRegex.query + '\\}'; + $.extend( tsfRegex, { + child : new RegExp( c.cssChildRow ), + filtered : new RegExp( wo.filter_filteredRow ), + alreadyFiltered : new RegExp( '(\\s+(-' + processStr('|', ts.language.or) + processStr('|', ts.language.to) + ')\\s+)', 'i' ), + toTest : new RegExp( '\\s+(-' + processStr('|', ts.language.to) + ')\\s+', 'i' ), + toSplit : new RegExp( '(?:\\s+(?:-' + processStr('|', ts.language.to) + ')\\s+)', 'gi' ), + andTest : new RegExp( '\\s+(' + processStr('', ts.language.and, '|') + '&&)\\s+', 'i' ), + andSplit : new RegExp( '(?:\\s+(?:' + processStr('', ts.language.and, '|') + '&&)\\s+)', 'gi' ), + orTest : new RegExp( '(\\|' + processStr('|\\s+', ts.language.or, '\\s+') + ')', 'i' ), + orSplit : new RegExp( '(?:\\|' + processStr('|\\s+(?:', ts.language.or, ')\\s+') + ')', 'gi' ), + iQuery : new RegExp( val, 'i' ), + igQuery : new RegExp( val, 'ig' ), + operTest : /^[<>]=?/, + gtTest : />/, + gteTest : />=/, + ltTest : /</, + lteTest : /<=/, + notTest : /^\!/, + wildOrTest : /[\?\*\|]/, + wildTest : /\?\*/, + fuzzyTest : /^~/, + exactTest : /[=\"\|!]/ + }); + + // don't build filter row if columnFilters is false or all columns are set to 'filter-false' + // see issue #156 + val = c.$headers.filter( '.filter-false, .parser-false' ).length; + if ( wo.filter_columnFilters !== false && val !== c.$headers.length ) { + // build filter row + tsf.buildRow( table, c, wo ); + } + + txt = 'addRows updateCell update updateRows updateComplete appendCache filterReset ' + + 'filterAndSortReset filterResetSaved filterEnd search '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table.bind( txt, function( event, filter ) { + val = wo.filter_hideEmpty && + $.isEmptyObject( c.cache ) && + !( c.delayInit && event.type === 'appendCache' ); + // hide filter row using the 'filtered' class name + c.$table.find( '.' + tscss.filterRow ).toggleClass( wo.filter_filteredRow, val ); // fixes #450 + if ( !/(search|filter)/.test( event.type ) ) { + event.stopPropagation(); + tsf.buildDefault( table, true ); + } + // Add filterAndSortReset - see #1361 + if ( event.type === 'filterReset' || event.type === 'filterAndSortReset' ) { + c.$table.find( '.' + tscss.filter ).add( wo.filter_$externalFilters ).val( '' ); + if ( event.type === 'filterAndSortReset' ) { + ts.sortReset( this.config, function() { + tsf.searching( table, [] ); + }); + } else { + tsf.searching( table, [] ); + } + } else if ( event.type === 'filterResetSaved' ) { + ts.storage( table, 'tablesorter-filters', '' ); + } else if ( event.type === 'filterEnd' ) { + tsf.buildDefault( table, true ); + } else { + // send false argument to force a new search; otherwise if the filter hasn't changed, + // it will return + filter = event.type === 'search' ? filter : + event.type === 'updateComplete' ? c.$table.data( 'lastSearch' ) : ''; + if ( /(update|add)/.test( event.type ) && event.type !== 'updateComplete' ) { + // force a new search since content has changed + c.lastCombinedFilter = null; + c.lastSearch = []; + // update filterFormatters after update (& small delay) - Fixes #1237 + setTimeout(function() { + c.$table.triggerHandler( 'filterFomatterUpdate' ); + }, 100); + } + // pass true ( skipFirst ) to prevent the tablesorter.setFilters function from skipping the first + // input ensures all inputs are updated when a search is triggered on the table + // $( 'table' ).trigger( 'search', [...] ); + tsf.searching( table, filter, true ); + } + return false; + }); + + // reset button/link + if ( wo.filter_reset ) { + if ( wo.filter_reset instanceof $ ) { + // reset contains a jQuery object, bind to it + wo.filter_reset.click( function() { + c.$table.triggerHandler( 'filterReset' ); + }); + } else if ( $( wo.filter_reset ).length ) { + // reset is a jQuery selector, use event delegation + $( document ) + .undelegate( wo.filter_reset, 'click' + c.namespace + 'filter' ) + .delegate( wo.filter_reset, 'click' + c.namespace + 'filter', function() { + // trigger a reset event, so other functions ( filter_formatter ) know when to reset + c.$table.triggerHandler( 'filterReset' ); + }); + } + } + if ( wo.filter_functions ) { + for ( column = 0; column < c.columns; column++ ) { + fxn = ts.getColumnData( table, wo.filter_functions, column ); + if ( fxn ) { + // remove 'filter-select' from header otherwise the options added here are replaced with + // all options + $header = c.$headerIndexed[ column ].removeClass( 'filter-select' ); + // don't build select if 'filter-false' or 'parser-false' set + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + options = ''; + if ( fxn === true && noSelect ) { + tsf.buildSelect( table, column ); + } else if ( typeof fxn === 'object' && noSelect ) { + // add custom drop down list + for ( string in fxn ) { + if ( typeof string === 'string' ) { + options += options === '' ? + '<option value="">' + + ( $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || + '' + ) + + '</option>' : ''; + val = string; + txt = string; + if ( string.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + val = string.split( wo.filter_selectSourceSeparator ); + txt = val[1]; + val = val[0]; + } + options += '<option ' + + ( txt === val ? '' : 'data-function-name="' + string + '" ' ) + + 'value="' + val + '">' + txt + '</option>'; + } + } + c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .append( options ); + txt = wo.filter_selectSource; + fxn = typeof txt === 'function' ? true : ts.getColumnData( table, txt, column ); + if ( fxn ) { + // updating so the extra options are appended + tsf.buildSelect( c.table, column, '', true, $header.hasClass( wo.filter_onlyAvail ) ); + } + } + } + } + } + // 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. + tsf.buildDefault( table, true ); + + tsf.bindSearch( table, c.$table.find( '.' + tscss.filter ), true ); + if ( wo.filter_external ) { + tsf.bindSearch( table, wo.filter_external ); + } + + if ( wo.filter_hideFilters ) { + tsf.hideFilters( c ); + } + + // show processing icon + if ( c.showProcessing ) { + txt = 'filterStart filterEnd '.split( ' ' ).join( c.namespace + 'filter-sp ' ); + c.$table + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) + .bind( txt, function( event, columns ) { + // only add processing to certain columns to all columns + $header = ( columns ) ? + c.$table + .find( '.' + tscss.header ) + .filter( '[data-column]' ) + .filter( function() { + return columns[ $( this ).data( 'column' ) ] !== ''; + }) : ''; + ts.isProcessing( table, event.type === 'filterStart', columns ? $header : '' ); + }); + } + + // set filtered rows count ( intially unfiltered ) + c.filteredRows = c.totalRows; + + // add default values + txt = 'tablesorter-initialized pagerBeforeInitialized '.split( ' ' ).join( c.namespace + 'filter ' ); + c.$table + .unbind( txt.replace( ts.regex.spaces, ' ' ) ) + .bind( txt, function() { + tsf.completeInit( this ); + }); + // if filter widget is added after pager has initialized; then set filter init flag + if ( c.pager && c.pager.initialized && !wo.filter_initialized ) { + c.$table.triggerHandler( 'filterFomatterUpdate' ); + setTimeout( function() { + tsf.filterInitComplete( c ); + }, 100 ); + } else if ( !wo.filter_initialized ) { + tsf.completeInit( table ); + } + }, + completeInit: function( table ) { + // redefine 'c' & 'wo' so they update properly inside this callback + var c = table.config, + wo = c.widgetOptions, + filters = tsf.setDefaults( table, c, wo ) || []; + if ( filters.length ) { + // prevent delayInit from triggering a cache build if filters are empty + if ( !( c.delayInit && filters.join( '' ) === '' ) ) { + ts.setFilters( table, filters, true ); + } + } + c.$table.triggerHandler( 'filterFomatterUpdate' ); + // trigger init after setTimeout to prevent multiple filterStart/End/Init triggers + setTimeout( function() { + if ( !wo.filter_initialized ) { + tsf.filterInitComplete( c ); + } + }, 100 ); + }, + + // $cell parameter, but not the config, is passed to the filter_formatters, + // so we have to work with it instead + formatterUpdated: function( $cell, column ) { + // prevent error if $cell is undefined - see #1056 + var $table = $cell && $cell.closest( 'table' ); + var config = $table.length && $table[0].config, + wo = config && config.widgetOptions; + if ( wo && !wo.filter_initialized ) { + // add updates by column since this function + // may be called numerous times before initialization + wo.filter_formatterInit[ column ] = 1; + } + }, + filterInitComplete: function( c ) { + var indx, len, + wo = c.widgetOptions, + count = 0, + completed = function() { + wo.filter_initialized = true; + // update lastSearch - it gets cleared often + c.lastSearch = c.$table.data( 'lastSearch' ); + c.$table.triggerHandler( 'filterInit', c ); + tsf.findRows( c.table, c.lastSearch || [] ); + if (ts.debug(c, 'filter')) { + console.log('Filter >> Widget initialized'); + } + }; + if ( $.isEmptyObject( wo.filter_formatter ) ) { + completed(); + } else { + len = wo.filter_formatterInit.length; + for ( indx = 0; indx < len; indx++ ) { + if ( wo.filter_formatterInit[ indx ] === 1 ) { + count++; + } + } + clearTimeout( wo.filter_initTimer ); + if ( !wo.filter_initialized && count === wo.filter_formatterCount ) { + // filter widget initialized + completed(); + } else if ( !wo.filter_initialized ) { + // fall back in case a filter_formatter doesn't call + // $.tablesorter.filter.formatterUpdated( $cell, column ), and the count is off + wo.filter_initTimer = setTimeout( function() { + completed(); + }, 500 ); + } + } + }, + // encode or decode filters for storage; see #1026 + processFilters: function( filters, encode ) { + var indx, + // fixes #1237; previously returning an encoded "filters" value + result = [], + mode = encode ? encodeURIComponent : decodeURIComponent, + len = filters.length; + for ( indx = 0; indx < len; indx++ ) { + if ( filters[ indx ] ) { + result[ indx ] = mode( filters[ indx ] ); + } + } + return result; + }, + setDefaults: function( table, c, wo ) { + var isArray, saved, indx, col, $filters, + // get current ( default ) filters + filters = ts.getFilters( table ) || []; + if ( wo.filter_saveFilters && ts.storage ) { + saved = ts.storage( table, 'tablesorter-filters' ) || []; + isArray = $.isArray( saved ); + // make sure we're not just getting an empty array + if ( !( isArray && saved.join( '' ) === '' || !isArray ) ) { + filters = tsf.processFilters( saved ); + } + } + // if no filters saved, then check default settings + if ( filters.join( '' ) === '' ) { + // allow adding default setting to external filters + $filters = c.$headers.add( wo.filter_$externalFilters ) + .filter( '[' + wo.filter_defaultAttrib + ']' ); + for ( indx = 0; indx <= c.columns; indx++ ) { + // include data-column='all' external filters + col = indx === c.columns ? 'all' : indx; + filters[ indx ] = $filters + .filter( '[data-column="' + col + '"]' ) + .attr( wo.filter_defaultAttrib ) || filters[indx] || ''; + } + } + c.$table.data( 'lastSearch', filters ); + return filters; + }, + parseFilter: function( c, filter, data, parsed ) { + return parsed || data.parsed[ data.index ] ? + c.parsers[ data.index ].format( filter, c.table, [], data.index ) : + filter; + }, + buildRow: function( table, c, wo ) { + var $filter, col, column, $header, makeSelect, disabled, name, ffxn, tmp, + // c.columns defined in computeThIndexes() + cellFilter = wo.filter_cellFilter, + columns = c.columns, + arry = $.isArray( cellFilter ), + buildFilter = '<tr role="search" class="' + tscss.filterRow + ' ' + c.cssIgnoreRow + '">'; + for ( column = 0; column < columns; column++ ) { + if ( c.$headerIndexed[ column ].length ) { + // account for entire column set with colspan. See #1047 + tmp = c.$headerIndexed[ column ] && c.$headerIndexed[ column ][0].colSpan || 0; + if ( tmp > 1 ) { + buildFilter += '<td data-column="' + column + '-' + ( column + tmp - 1 ) + '" colspan="' + tmp + '"'; + } else { + buildFilter += '<td data-column="' + column + '"'; + } + if ( arry ) { + buildFilter += ( cellFilter[ column ] ? ' class="' + cellFilter[ column ] + '"' : '' ); + } else { + buildFilter += ( cellFilter !== '' ? ' class="' + cellFilter + '"' : '' ); + } + buildFilter += '></td>'; + } + } + c.$filters = $( buildFilter += '</tr>' ) + .appendTo( c.$table.children( 'thead' ).eq( 0 ) ) + .children( 'td' ); + // build each filter input + for ( column = 0; column < columns; column++ ) { + disabled = false; + // assuming last cell of a column is the main column + $header = c.$headerIndexed[ column ]; + if ( $header && $header.length ) { + // $filter = c.$filters.filter( '[data-column="' + column + '"]' ); + $filter = tsf.getColumnElm( c, c.$filters, column ); + ffxn = ts.getColumnData( table, wo.filter_functions, column ); + makeSelect = ( wo.filter_functions && ffxn && typeof ffxn !== 'function' ) || + $header.hasClass( 'filter-select' ); + // get data from jQuery data, metadata, headers option or header class name + col = ts.getColumnData( table, c.headers, column ); + disabled = ts.getData( $header[0], col, 'filter' ) === 'false' || + ts.getData( $header[0], col, 'parser' ) === 'false'; + + if ( makeSelect ) { + buildFilter = $( '<select>' ).appendTo( $filter ); + } else { + ffxn = ts.getColumnData( table, wo.filter_formatter, column ); + if ( ffxn ) { + wo.filter_formatterCount++; + buildFilter = ffxn( $filter, column ); + // no element returned, so lets go find it + if ( buildFilter && buildFilter.length === 0 ) { + buildFilter = $filter.children( 'input' ); + } + // element not in DOM, so lets attach it + if ( buildFilter && ( buildFilter.parent().length === 0 || + ( buildFilter.parent().length && buildFilter.parent()[0] !== $filter[0] ) ) ) { + $filter.append( buildFilter ); + } + } else { + buildFilter = $( '<input type="search">' ).appendTo( $filter ); + } + if ( buildFilter ) { + tmp = $header.data( 'placeholder' ) || + $header.attr( 'data-placeholder' ) || + wo.filter_placeholder.search || ''; + buildFilter.attr( 'placeholder', tmp ); + } + } + if ( buildFilter ) { + // add filter class name + name = ( $.isArray( wo.filter_cssFilter ) ? + ( typeof wo.filter_cssFilter[column] !== 'undefined' ? wo.filter_cssFilter[column] || '' : '' ) : + wo.filter_cssFilter ) || ''; + // copy data-column from table cell (it will include colspan) + buildFilter.addClass( tscss.filter + ' ' + name ); + name = wo.filter_filterLabel; + tmp = name.match(/{{([^}]+?)}}/g); + if (!tmp) { + tmp = [ '{{label}}' ]; + } + $.each(tmp, function(indx, attr) { + var regex = new RegExp(attr, 'g'), + data = $header.attr('data-' + attr.replace(/{{|}}/g, '')), + text = typeof data === 'undefined' ? $header.text() : data; + name = name.replace( regex, $.trim( text ) ); + }); + buildFilter.attr({ + 'data-column': $filter.attr( 'data-column' ), + 'aria-label': name + }); + if ( disabled ) { + buildFilter.attr( 'placeholder', '' ).addClass( tscss.filterDisabled )[0].disabled = true; + } + } + } + } + }, + bindSearch: function( table, $el, internal ) { + table = $( table )[0]; + $el = $( $el ); // allow passing a selector string + if ( !$el.length ) { return; } + var tmp, + c = table.config, + wo = c.widgetOptions, + namespace = c.namespace + 'filter', + $ext = wo.filter_$externalFilters; + if ( internal !== true ) { + // save anyMatch element + tmp = wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector; + wo.filter_$anyMatch = $el.filter( tmp ); + if ( $ext && $ext.length ) { + wo.filter_$externalFilters = wo.filter_$externalFilters.add( $el ); + } else { + wo.filter_$externalFilters = $el; + } + // update values ( external filters added after table initialization ) + ts.setFilters( table, c.$table.data( 'lastSearch' ) || [], internal === false ); + } + // unbind events + tmp = ( 'keypress keyup keydown search change input '.split( ' ' ).join( namespace + ' ' ) ); + $el + // use data attribute instead of jQuery data since the head is cloned without including + // the data/binding + .attr( 'data-lastSearchTime', new Date().getTime() ) + .unbind( tmp.replace( ts.regex.spaces, ' ' ) ) + .bind( 'keydown' + namespace, function( event ) { + if ( event.which === tskeyCodes.escape && !table.config.widgetOptions.filter_resetOnEsc ) { + // prevent keypress event + return false; + } + }) + .bind( 'keyup' + namespace, function( event ) { + wo = table.config.widgetOptions; // make sure "wo" isn't cached + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + // emulate what webkit does.... escape clears the filter + if ( event.which === tskeyCodes.escape ) { + // make sure to restore the last value on escape + this.value = wo.filter_resetOnEsc ? '' : c.lastSearch[column]; + // don't return if the search value is empty ( all rows need to be revealed ) + } else if ( this.value !== '' && ( + // liveSearch can contain a min value length; ignore arrow and meta keys, but allow backspace + ( typeof liveSearch === 'number' && this.value.length < liveSearch ) || + // let return & backspace continue on, but ignore arrows & non-valid characters + ( event.which !== tskeyCodes.enter && event.which !== tskeyCodes.backSpace && + ( event.which < tskeyCodes.space || ( event.which >= tskeyCodes.left && event.which <= tskeyCodes.down ) ) ) ) ) { + return; + // live search + } else if ( liveSearch === false ) { + if ( this.value !== '' && event.which !== tskeyCodes.enter ) { + return; + } + } + // change event = no delay; last true flag tells getFilters to skip newest timed input + tsf.searching( table, true, true, column ); + }) + // include change for select - fixes #473 + .bind( 'search change keypress input blur '.split( ' ' ).join( namespace + ' ' ), function( event ) { + // don't get cached data, in case data-column changes dynamically + var column = parseInt( $( this ).attr( 'data-column' ), 10 ), + eventType = event.type, + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( table.config.widgetOptions.filter_initialized && + // immediate search if user presses enter + ( event.which === tskeyCodes.enter || + // immediate search if a "search" or "blur" is triggered on the input + ( eventType === 'search' || eventType === 'blur' ) || + // change & input events must be ignored if liveSearch !== true + ( eventType === 'change' || eventType === 'input' ) && + // prevent search if liveSearch is a number + ( liveSearch === true || liveSearch !== true && event.target.nodeName !== 'INPUT' ) && + // don't allow 'change' or 'input' event to process if the input value + // is the same - fixes #685 + this.value !== c.lastSearch[column] + ) + ) { + event.preventDefault(); + // init search with no delay + $( this ).attr( 'data-lastSearchTime', new Date().getTime() ); + tsf.searching( table, eventType !== 'keypress', true, column ); + } + }); + }, + searching: function( table, filter, skipFirst, column ) { + var liveSearch, + wo = table.config.widgetOptions; + if (typeof column === 'undefined') { + // no delay + liveSearch = false; + } else { + liveSearch = typeof wo.filter_liveSearch === 'boolean' ? + wo.filter_liveSearch : + // get column setting, or set to fallback value, or default to false + ts.getColumnData( table, wo.filter_liveSearch, column ); + if ( typeof liveSearch === 'undefined' ) { + liveSearch = wo.filter_liveSearch.fallback || false; + } + } + clearTimeout( wo.filter_searchTimer ); + if ( typeof filter === 'undefined' || filter === true ) { + // delay filtering + wo.filter_searchTimer = setTimeout( function() { + tsf.checkFilters( table, filter, skipFirst ); + }, liveSearch ? wo.filter_searchDelay : 10 ); + } else { + // skip delay + tsf.checkFilters( table, filter, skipFirst ); + } + }, + equalFilters: function (c, filter1, filter2) { + var indx, + f1 = [], + f2 = [], + len = c.columns + 1; // add one to include anyMatch filter + filter1 = $.isArray(filter1) ? filter1 : []; + filter2 = $.isArray(filter2) ? filter2 : []; + for (indx = 0; indx < len; indx++) { + f1[indx] = filter1[indx] || ''; + f2[indx] = filter2[indx] || ''; + } + return f1.join(',') === f2.join(','); + }, + checkFilters: function( table, filter, skipFirst ) { + var c = table.config, + wo = c.widgetOptions, + filterArray = $.isArray( filter ), + filters = ( filterArray ) ? filter : ts.getFilters( table, true ), + currentFilters = filters || []; // current filter values + // prevent errors if delay init is set + if ( $.isEmptyObject( c.cache ) ) { + // update cache if delayInit set & pager has initialized ( after user initiates a search ) + if ( c.delayInit && ( !c.pager || c.pager && c.pager.initialized ) ) { + ts.updateCache( c, function() { + tsf.checkFilters( table, false, skipFirst ); + }); + } + return; + } + // add filter array back into inputs + if ( filterArray ) { + ts.setFilters( table, filters, false, skipFirst !== true ); + if ( !wo.filter_initialized ) { + c.lastSearch = []; + c.lastCombinedFilter = ''; + } + } + if ( wo.filter_hideFilters ) { + // show/hide filter row as needed + c.$table + .find( '.' + tscss.filterRow ) + .triggerHandler( tsf.hideFiltersCheck( c ) ? '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 ( tsf.equalFilters(c, c.lastSearch, currentFilters) && filter !== false ) { + return; + } else if ( filter === false ) { + // force filter refresh + c.lastCombinedFilter = ''; + c.lastSearch = []; + } + // define filter inside it is false + filters = filters || []; + // convert filters to strings - see #1070 + filters = Array.prototype.map ? + filters.map( String ) : + // for IE8 & older browsers - maybe not the best method + filters.join( '\ufffd' ).split( '\ufffd' ); + + if ( wo.filter_initialized ) { + c.$table.triggerHandler( 'filterStart', [ filters ] ); + } + if ( c.showProcessing ) { + // give it time for the processing icon to kick in + setTimeout( function() { + tsf.findRows( table, filters, currentFilters ); + return false; + }, 30 ); + } else { + tsf.findRows( table, filters, currentFilters ); + return false; + } + }, + hideFiltersCheck: function( c ) { + if (typeof c.widgetOptions.filter_hideFilters === 'function') { + var val = c.widgetOptions.filter_hideFilters( c ); + if (typeof val === 'boolean') { + return val; + } + } + return ts.getFilters( c.$table ).join( '' ) === ''; + }, + hideFilters: function( c, $table ) { + var timer; + ( $table || c.$table ) + .find( '.' + tscss.filterRow ) + .addClass( tscss.filterRowHide ) + .bind( 'mouseenter mouseleave', function( e ) { + // save event object - http://bugs.jquery.com/ticket/12140 + var event = e, + $row = $( this ); + clearTimeout( timer ); + timer = setTimeout( function() { + if ( /enter|over/.test( event.type ) ) { + $row.removeClass( tscss.filterRowHide ); + } else { + // don't hide if input has focus + // $( ':focus' ) needs jQuery 1.6+ + if ( $( document.activeElement ).closest( 'tr' )[0] !== $row[0] ) { + // don't hide row if any filter has a value + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) ); + } + } + }, 200 ); + }) + .find( 'input, select' ).bind( 'focus blur', function( e ) { + var event = e, + $row = $( this ).closest( 'tr' ); + clearTimeout( timer ); + timer = setTimeout( function() { + clearTimeout( timer ); + // don't hide row if any filter has a value + $row.toggleClass( tscss.filterRowHide, tsf.hideFiltersCheck( c ) && event.type !== 'focus' ); + }, 200 ); + }); + }, + defaultFilter: function( filter, mask ) { + if ( filter === '' ) { return filter; } + var regex = tsfRegex.iQuery, + maskLen = mask.match( tsfRegex.igQuery ).length, + query = maskLen > 1 ? $.trim( filter ).split( /\s/ ) : [ $.trim( filter ) ], + len = query.length - 1, + indx = 0, + val = mask; + if ( len < 1 && maskLen > 1 ) { + // only one 'word' in query but mask has >1 slots + query[1] = query[0]; + } + // replace all {query} with query words... + // if query = 'Bob', then convert mask from '!{query}' to '!Bob' + // if query = 'Bob Joe Frank', then convert mask '{q} OR {q}' to 'Bob OR Joe OR Frank' + while ( regex.test( val ) ) { + val = val.replace( regex, query[indx++] || '' ); + if ( regex.test( val ) && indx < len && ( query[indx] || '' ) !== '' ) { + val = mask.replace( regex, val ); + } + } + return val; + }, + getLatestSearch: function( $input ) { + if ( $input ) { + return $input.sort( function( a, b ) { + return $( b ).attr( 'data-lastSearchTime' ) - $( a ).attr( 'data-lastSearchTime' ); + }); + } + return $input || $(); + }, + findRange: function( c, val, ignoreRanges ) { + // look for multiple columns '1-3,4-6,8' in data-column + var temp, ranges, range, start, end, singles, i, indx, len, + columns = []; + if ( /^[0-9]+$/.test( val ) ) { + // always return an array + return [ parseInt( val, 10 ) ]; + } + // process column range + if ( !ignoreRanges && /-/.test( val ) ) { + ranges = val.match( /(\d+)\s*-\s*(\d+)/g ); + len = ranges ? ranges.length : 0; + for ( indx = 0; indx < len; indx++ ) { + range = ranges[indx].split( /\s*-\s*/ ); + start = parseInt( range[0], 10 ) || 0; + end = parseInt( range[1], 10 ) || ( c.columns - 1 ); + if ( start > end ) { + temp = start; start = end; end = temp; // swap + } + if ( end >= c.columns ) { + end = c.columns - 1; + } + for ( ; start <= end; start++ ) { + columns[ columns.length ] = start; + } + // remove processed range from val + val = val.replace( ranges[ indx ], '' ); + } + } + // process single columns + if ( !ignoreRanges && /,/.test( val ) ) { + singles = val.split( /\s*,\s*/ ); + len = singles.length; + for ( i = 0; i < len; i++ ) { + if ( singles[ i ] !== '' ) { + indx = parseInt( singles[ i ], 10 ); + if ( indx < c.columns ) { + columns[ columns.length ] = indx; + } + } + } + } + // return all columns + if ( !columns.length ) { + for ( indx = 0; indx < c.columns; indx++ ) { + columns[ columns.length ] = indx; + } + } + return columns; + }, + getColumnElm: function( c, $elements, column ) { + // data-column may contain multiple columns '1-3,5-6,8' + // replaces: c.$filters.filter( '[data-column="' + column + '"]' ); + return $elements.filter( function() { + var cols = tsf.findRange( c, $( this ).attr( 'data-column' ) ); + return $.inArray( column, cols ) > -1; + }); + }, + multipleColumns: function( c, $input ) { + // look for multiple columns '1-3,4-6,8' in data-column + var wo = c.widgetOptions, + // only target 'all' column inputs on initialization + // & don't target 'all' column inputs if they don't exist + targets = wo.filter_initialized || !$input.filter( wo.filter_anyColumnSelector ).length, + val = $.trim( tsf.getLatestSearch( $input ).attr( 'data-column' ) || '' ); + return tsf.findRange( c, val, !targets ); + }, + processTypes: function( c, data, vars ) { + var ffxn, + filterMatched = null, + matches = null; + for ( ffxn in tsf.types ) { + if ( $.inArray( ffxn, vars.excludeMatch ) < 0 && matches === null ) { + matches = tsf.types[ffxn]( c, data, vars ); + if ( matches !== null ) { + data.matchedOn = ffxn; + filterMatched = matches; + } + } + } + return filterMatched; + }, + matchType: function( c, columnIndex ) { + var isMatch, + wo = c.widgetOptions, + $el = c.$headerIndexed[ columnIndex ]; + // filter-exact > filter-match > filter_matchType for type + if ( $el.hasClass( 'filter-exact' ) ) { + isMatch = false; + } else if ( $el.hasClass( 'filter-match' ) ) { + isMatch = true; + } else { + // filter-select is not applied when filter_functions are used, so look for a select + if ( wo.filter_columnFilters ) { + $el = c.$filters + .find( '.' + tscss.filter ) + .add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ); + } else if ( wo.filter_$externalFilters ) { + $el = wo.filter_$externalFilters.filter( '[data-column="' + columnIndex + '"]' ); + } + isMatch = $el.length ? + c.widgetOptions.filter_matchType[ ( $el[ 0 ].nodeName || '' ).toLowerCase() ] === 'match' : + // default to exact, if no inputs found + false; + } + return isMatch; + }, + processRow: function( c, data, vars ) { + var result, filterMatched, + fxn, ffxn, txt, + wo = c.widgetOptions, + showRow = true, + hasAnyMatchInput = wo.filter_$anyMatch && wo.filter_$anyMatch.length, + + // if wo.filter_$anyMatch data-column attribute is changed dynamically + // we don't want to do an "anyMatch" search on one column using data + // for the entire row - see #998 + columnIndex = wo.filter_$anyMatch && wo.filter_$anyMatch.length ? + // look for multiple columns '1-3,4-6,8' + tsf.multipleColumns( c, wo.filter_$anyMatch ) : + []; + data.$cells = data.$row.children(); + data.matchedOn = null; + if ( data.anyMatchFlag && columnIndex.length > 1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { + data.anyMatch = true; + data.isMatch = true; + data.rowArray = data.$cells.map( function( i ) { + if ( $.inArray( i, columnIndex ) > -1 || ( data.anyMatchFilter && !hasAnyMatchInput ) ) { + if ( data.parsed[ i ] ) { + txt = data.cacheArray[ i ]; + } else { + txt = data.rawArray[ i ]; + txt = $.trim( wo.filter_ignoreCase ? txt.toLowerCase() : txt ); + if ( c.sortLocaleCompare ) { + txt = ts.replaceAccents( txt ); + } + } + return txt; + } + }).get(); + data.filter = data.anyMatchFilter; + data.iFilter = data.iAnyMatchFilter; + data.exact = data.rowArray.join( ' ' ); + data.iExact = wo.filter_ignoreCase ? data.exact.toLowerCase() : data.exact; + data.cache = data.cacheArray.slice( 0, -1 ).join( ' ' ); + vars.excludeMatch = vars.noAnyMatch; + filterMatched = tsf.processTypes( c, data, vars ); + if ( filterMatched !== null ) { + showRow = filterMatched; + } else { + if ( wo.filter_startsWith ) { + showRow = false; + // data.rowArray may not contain all columns + columnIndex = Math.min( c.columns, data.rowArray.length ); + while ( !showRow && columnIndex > 0 ) { + columnIndex--; + showRow = showRow || data.rowArray[ columnIndex ].indexOf( data.iFilter ) === 0; + } + } else { + showRow = ( data.iExact + data.childRowText ).indexOf( data.iFilter ) >= 0; + } + } + data.anyMatch = false; + // no other filters to process + if ( data.filters.join( '' ) === data.filter ) { + return showRow; + } + } + + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.filter = data.filters[ columnIndex ]; + data.index = columnIndex; + + // filter types to exclude, per column + vars.excludeMatch = vars.excludeFilter[ columnIndex ]; + + // ignore if filter is empty or disabled + if ( data.filter ) { + data.cache = data.cacheArray[ columnIndex ]; + result = data.parsed[ columnIndex ] ? data.cache : data.rawArray[ columnIndex ] || ''; + data.exact = c.sortLocaleCompare ? ts.replaceAccents( result ) : result; // issue #405 + data.iExact = !tsfRegex.type.test( typeof data.exact ) && wo.filter_ignoreCase ? + data.exact.toLowerCase() : data.exact; + data.isMatch = tsf.matchType( c, columnIndex ); + + result = showRow; // if showRow is true, show that row + + // in case select filter option has a different value vs text 'a - z|A through Z' + ffxn = wo.filter_columnFilters ? + c.$filters.add( wo.filter_$externalFilters ) + .filter( '[data-column="' + columnIndex + '"]' ) + .find( 'select option:selected' ) + .attr( 'data-function-name' ) || '' : ''; + // replace accents - see #357 + if ( c.sortLocaleCompare ) { + data.filter = ts.replaceAccents( data.filter ); + } + + // replace column specific default filters - see #1088 + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultColFilter[ columnIndex ] ) ) { + data.filter = tsf.defaultFilter( data.filter, vars.defaultColFilter[ columnIndex ] ); + } + + // data.iFilter = case insensitive ( if wo.filter_ignoreCase is true ), + // data.filter = case sensitive + data.iFilter = wo.filter_ignoreCase ? ( data.filter || '' ).toLowerCase() : data.filter; + fxn = vars.functions[ columnIndex ]; + filterMatched = null; + if ( fxn ) { + if ( typeof fxn === 'function' ) { + // filter callback( exact cell content, parser normalized content, + // filter input value, column index, jQuery row object ) + filterMatched = fxn( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } else if ( typeof fxn[ ffxn || data.filter ] === 'function' ) { + // selector option function + txt = ffxn || data.filter; + filterMatched = + fxn[ txt ]( data.exact, data.cache, data.filter, columnIndex, data.$row, c, data ); + } + } + if ( filterMatched === null ) { + // cycle through the different filters + // filters return a boolean or null if nothing matches + filterMatched = tsf.processTypes( c, data, vars ); + // select with exact match; ignore "and" or "or" within the text; fixes #1486 + txt = fxn === true && (data.matchedOn === 'and' || data.matchedOn === 'or'); + if ( filterMatched !== null && !txt) { + result = filterMatched; + // Look for match, and add child row data for matching + } else { + // check fxn (filter-select in header) after filter types are checked + // without this, the filter + jQuery UI selectmenu demo was breaking + if ( fxn === true ) { + // default selector uses exact match unless 'filter-match' class is found + result = data.isMatch ? + // data.iExact may be a number + ( '' + data.iExact ).search( data.iFilter ) >= 0 : + data.filter === data.exact; + } else { + txt = ( data.iExact + data.childRowText ).indexOf( tsf.parseFilter( c, data.iFilter, data ) ); + result = ( ( !wo.filter_startsWith && txt >= 0 ) || ( wo.filter_startsWith && txt === 0 ) ); + } + } + } else { + result = filterMatched; + } + showRow = ( result ) ? showRow : false; + } + } + return showRow; + }, + findRows: function( table, filters, currentFilters ) { + if ( + tsf.equalFilters(table.config, table.config.lastSearch, currentFilters) || + !table.config.widgetOptions.filter_initialized + ) { + return; + } + var len, norm_rows, rowData, $rows, $row, rowIndex, tbodyIndex, $tbody, columnIndex, + isChild, childRow, lastSearch, showRow, showParent, time, val, indx, + notFiltered, searchFiltered, query, injected, res, id, txt, + storedFilters = $.extend( [], filters ), + c = table.config, + wo = c.widgetOptions, + debug = ts.debug(c, 'filter'), + // data object passed to filters; anyMatch is a flag for the filters + data = { + anyMatch: false, + filters: filters, + // regex filter type cache + filter_regexCache : [] + }, + vars = { + // anyMatch really screws up with these types of filters + noAnyMatch: [ 'range', 'operators' ], + // cache filter variables that use ts.getColumnData in the main loop + functions : [], + excludeFilter : [], + defaultColFilter : [], + defaultAnyFilter : ts.getColumnData( table, wo.filter_defaultFilter, c.columns, true ) || '' + }; + // parse columns after formatter, in case the class is added at that point + data.parsed = []; + for ( columnIndex = 0; columnIndex < c.columns; columnIndex++ ) { + data.parsed[ columnIndex ] = wo.filter_useParsedData || + // parser has a "parsed" parameter + ( c.parsers && c.parsers[ columnIndex ] && c.parsers[ columnIndex ].parsed || + // getData may not return 'parsed' if other 'filter-' class names exist + // ( e.g. <th class="filter-select filter-parsed"> ) + ts.getData && ts.getData( c.$headerIndexed[ columnIndex ], + ts.getColumnData( table, c.headers, columnIndex ), 'filter' ) === 'parsed' || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-parsed' ) ); + + vars.functions[ columnIndex ] = + ts.getColumnData( table, wo.filter_functions, columnIndex ) || + c.$headerIndexed[ columnIndex ].hasClass( 'filter-select' ); + vars.defaultColFilter[ columnIndex ] = + ts.getColumnData( table, wo.filter_defaultFilter, columnIndex ) || ''; + vars.excludeFilter[ columnIndex ] = + ( ts.getColumnData( table, wo.filter_excludeFilter, columnIndex, true ) || '' ).split( /\s+/ ); + } + + if ( debug ) { + console.log( 'Filter >> Starting filter widget search', filters ); + time = new Date(); + } + // filtered rows count + c.filteredRows = 0; + c.totalRows = 0; + currentFilters = ( storedFilters || [] ); + + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + $tbody = ts.processTbody( table, c.$tbodies.eq( tbodyIndex ), true ); + // skip child rows & widget added ( removable ) rows - fixes #448 thanks to @hempel! + // $rows = $tbody.children( 'tr' ).not( c.selectorRemove ); + columnIndex = c.columns; + // convert stored rows into a jQuery object + norm_rows = c.cache[ tbodyIndex ].normalized; + $rows = $( $.map( norm_rows, function( el ) { + return el[ columnIndex ].$row.get(); + }) ); + + if ( currentFilters.join('') === '' || wo.filter_serversideFiltering ) { + $rows + .removeClass( wo.filter_filteredRow ) + .not( '.' + c.cssChildRow ) + .css( 'display', '' ); + } else { + // filter out child rows + $rows = $rows.not( '.' + c.cssChildRow ); + len = $rows.length; + + if ( ( wo.filter_$anyMatch && wo.filter_$anyMatch.length ) || + typeof filters[c.columns] !== 'undefined' ) { + data.anyMatchFlag = true; + data.anyMatchFilter = '' + ( + filters[ c.columns ] || + wo.filter_$anyMatch && tsf.getLatestSearch( wo.filter_$anyMatch ).val() || + '' + ); + if ( wo.filter_columnAnyMatch ) { + // specific columns search + query = data.anyMatchFilter.split( tsfRegex.andSplit ); + injected = false; + for ( indx = 0; indx < query.length; indx++ ) { + res = query[ indx ].split( ':' ); + if ( res.length > 1 ) { + // make the column a one-based index ( non-developers start counting from one :P ) + if ( isNaN( res[0] ) ) { + $.each( c.headerContent, function( i, txt ) { + // multiple matches are possible + if ( txt.toLowerCase().indexOf( res[0] ) > -1 ) { + id = i; + filters[ id ] = res[1]; + } + }); + } else { + id = parseInt( res[0], 10 ) - 1; + } + if ( id >= 0 && id < c.columns ) { // if id is an integer + filters[ id ] = res[1]; + query.splice( indx, 1 ); + indx--; + injected = true; + } + } + } + if ( injected ) { + data.anyMatchFilter = query.join( ' && ' ); + } + } + } + + // optimize searching only through already filtered rows - see #313 + searchFiltered = wo.filter_searchFiltered; + lastSearch = c.lastSearch || c.$table.data( 'lastSearch' ) || []; + if ( searchFiltered ) { + // cycle through all filters; include last ( columnIndex + 1 = match any column ). Fixes #669 + for ( indx = 0; indx < columnIndex + 1; indx++ ) { + val = filters[indx] || ''; + // break out of loop if we've already determined not to search filtered rows + if ( !searchFiltered ) { indx = columnIndex; } + // search already filtered rows if... + searchFiltered = searchFiltered && lastSearch.length && + // there are no changes from beginning of filter + val.indexOf( lastSearch[indx] || '' ) === 0 && + // if there is NOT a logical 'or', or range ( 'to' or '-' ) in the string + !tsfRegex.alreadyFiltered.test( val ) && + // if we are not doing exact matches, using '|' ( logical or ) or not '!' + !tsfRegex.exactTest.test( val ) && + // don't search only filtered if the value is negative + // ( '> -10' => '> -100' will ignore hidden rows ) + !( tsfRegex.isNeg1.test( val ) || tsfRegex.isNeg2.test( val ) ) && + // if filtering using a select without a 'filter-match' class ( exact match ) - fixes #593 + !( val !== '' && c.$filters && c.$filters.filter( '[data-column="' + indx + '"]' ).find( 'select' ).length && + !tsf.matchType( c, indx ) ); + } + } + notFiltered = $rows.not( '.' + wo.filter_filteredRow ).length; + // can't search when all rows are hidden - this happens when looking for exact matches + if ( searchFiltered && notFiltered === 0 ) { searchFiltered = false; } + if ( debug ) { + console.log( 'Filter >> Searching through ' + + ( searchFiltered && notFiltered < len ? notFiltered : 'all' ) + ' rows' ); + } + if ( data.anyMatchFlag ) { + if ( c.sortLocaleCompare ) { + // replace accents + data.anyMatchFilter = ts.replaceAccents( data.anyMatchFilter ); + } + if ( wo.filter_defaultFilter && tsfRegex.iQuery.test( vars.defaultAnyFilter ) ) { + data.anyMatchFilter = tsf.defaultFilter( data.anyMatchFilter, vars.defaultAnyFilter ); + // clear search filtered flag because default filters are not saved to the last search + searchFiltered = false; + } + // make iAnyMatchFilter lowercase unless both filter widget & core ignoreCase options are true + // when c.ignoreCase is true, the cache contains all lower case data + data.iAnyMatchFilter = !( wo.filter_ignoreCase && c.ignoreCase ) ? + data.anyMatchFilter : + data.anyMatchFilter.toLowerCase(); + } + + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + + txt = $rows[ rowIndex ].className; + // the first row can never be a child row + isChild = rowIndex && tsfRegex.child.test( txt ); + // skip child rows & already filtered rows + if ( isChild || ( searchFiltered && tsfRegex.filtered.test( txt ) ) ) { + continue; + } + + data.$row = $rows.eq( rowIndex ); + data.rowIndex = rowIndex; + data.cacheArray = norm_rows[ rowIndex ]; + rowData = data.cacheArray[ c.columns ]; + data.rawArray = rowData.raw; + data.childRowText = ''; + + if ( !wo.filter_childByColumn ) { + txt = ''; + // child row cached text + childRow = rowData.child; + // 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 + for ( indx = 0; indx < childRow.length; indx++ ) { + txt += ' ' + childRow[indx].join( ' ' ) || ''; + } + data.childRowText = wo.filter_childRows ? + ( wo.filter_ignoreCase ? txt.toLowerCase() : txt ) : + ''; + } + + showRow = false; + showParent = tsf.processRow( c, data, vars ); + $row = rowData.$row; + + // don't pass reference to val + val = showParent ? true : false; + childRow = rowData.$row.filter( ':gt(0)' ); + if ( wo.filter_childRows && childRow.length ) { + if ( wo.filter_childByColumn ) { + if ( !wo.filter_childWithSibs ) { + // hide all child rows + childRow.addClass( wo.filter_filteredRow ); + // if only showing resulting child row, only include parent + $row = $row.eq( 0 ); + } + // cycle through each child row + for ( indx = 0; indx < childRow.length; indx++ ) { + data.$row = childRow.eq( indx ); + data.cacheArray = rowData.child[ indx ]; + data.rawArray = data.cacheArray; + val = tsf.processRow( c, data, vars ); + // use OR comparison on child rows + showRow = showRow || val; + if ( !wo.filter_childWithSibs && val ) { + childRow.eq( indx ).removeClass( wo.filter_filteredRow ); + } + } + } + // keep parent row match even if no child matches... see #1020 + showRow = showRow || showParent; + } else { + showRow = val; + } + $row + .toggleClass( wo.filter_filteredRow, !showRow )[0] + .display = showRow ? '' : 'none'; + } + } + c.filteredRows += $rows.not( '.' + wo.filter_filteredRow ).length; + c.totalRows += $rows.length; + ts.processTbody( table, $tbody, false ); + } + // lastCombinedFilter is no longer used internally + c.lastCombinedFilter = storedFilters.join(''); // save last search + // don't save 'filters' directly since it may have altered ( AnyMatch column searches ) + c.lastSearch = storedFilters; + c.$table.data( 'lastSearch', storedFilters ); + if ( wo.filter_saveFilters && ts.storage ) { + ts.storage( table, 'tablesorter-filters', tsf.processFilters( storedFilters, true ) ); + } + if ( debug ) { + console.log( 'Filter >> Completed search' + ts.benchmark(time) ); + } + if ( wo.filter_initialized ) { + c.$table.triggerHandler( 'filterBeforeEnd', c ); + c.$table.triggerHandler( 'filterEnd', c ); + } + setTimeout( function() { + ts.applyWidget( c.table ); // make sure zebra widget is applied + }, 0 ); + }, + getOptionSource: function( table, column, onlyAvail ) { + table = $( table )[0]; + var c = table.config, + wo = c.widgetOptions, + arry = false, + source = wo.filter_selectSource, + last = c.$table.data( 'lastSearch' ) || [], + fxn = typeof source === 'function' ? true : ts.getColumnData( table, source, column ); + + if ( onlyAvail && last[column] !== '' ) { + onlyAvail = false; + } + + // filter select source option + if ( fxn === true ) { + // OVERALL source + arry = source( table, column, onlyAvail ); + } else if ( fxn instanceof $ || ( $.type( fxn ) === 'string' && fxn.indexOf( '</option>' ) >= 0 ) ) { + // selectSource is a jQuery object or string of options + return fxn; + } else if ( $.isArray( fxn ) ) { + arry = fxn; + } else if ( $.type( source ) === 'object' && fxn ) { + // custom select source function for a SPECIFIC COLUMN + arry = fxn( table, column, onlyAvail ); + // abort - updating the selects from an external method + if (arry === null) { + return null; + } + } + if ( arry === false ) { + // fall back to original method + arry = tsf.getOptions( table, column, onlyAvail ); + } + + return tsf.processOptions( table, column, arry ); + + }, + processOptions: function( table, column, arry ) { + if ( !$.isArray( arry ) ) { + return false; + } + table = $( table )[0]; + var cts, txt, indx, len, parsedTxt, str, + c = table.config, + validColumn = typeof column !== 'undefined' && column !== null && column >= 0 && column < c.columns, + direction = validColumn ? c.$headerIndexed[ column ].hasClass( 'filter-select-sort-desc' ) : false, + parsed = []; + // 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( value, indx ) { + if ( value.text ) { + return true; + } + return $.inArray( value, arry ) === indx; + }); + if ( validColumn && c.$headerIndexed[ column ].hasClass( 'filter-select-nosort' ) ) { + // unsorted select options + return arry; + } else { + len = arry.length; + // parse select option values + for ( indx = 0; indx < len; indx++ ) { + txt = arry[ indx ]; + // check for object + str = txt.text ? txt.text : txt; + // sortNatural breaks if you don't pass it strings + parsedTxt = ( validColumn && c.parsers && c.parsers.length && + c.parsers[ column ].format( str, table, [], column ) || str ).toString(); + parsedTxt = c.widgetOptions.filter_ignoreCase ? parsedTxt.toLowerCase() : parsedTxt; + // parse array data using set column parser; this DOES NOT pass the original + // table cell to the parser format function + if ( txt.text ) { + txt.parsed = parsedTxt; + parsed[ parsed.length ] = txt; + } else { + parsed[ parsed.length ] = { + text : txt, + // check parser length - fixes #934 + parsed : parsedTxt + }; + } + } + // sort parsed select options + cts = c.textSorter || ''; + parsed.sort( function( a, b ) { + var x = direction ? b.parsed : a.parsed, + y = direction ? a.parsed : b.parsed; + if ( validColumn && typeof cts === 'function' ) { + // custom OVERALL text sorter + return cts( x, y, true, column, table ); + } else if ( validColumn && typeof cts === 'object' && cts.hasOwnProperty( column ) ) { + // custom text sorter for a SPECIFIC COLUMN + return cts[column]( x, y, true, column, table ); + } else if ( ts.sortNatural ) { + // fall back to natural sort + return ts.sortNatural( x, y ); + } + // using an older version! do a basic sort + return true; + }); + // rebuild arry from sorted parsed data + arry = []; + len = parsed.length; + for ( indx = 0; indx < len; indx++ ) { + arry[ arry.length ] = parsed[indx]; + } + return arry; + } + }, + getOptions: function( table, column, onlyAvail ) { + table = $( table )[0]; + var rowIndex, tbodyIndex, len, row, cache, indx, child, childLen, + c = table.config, + wo = c.widgetOptions, + arry = []; + for ( tbodyIndex = 0; tbodyIndex < c.$tbodies.length; tbodyIndex++ ) { + cache = c.cache[tbodyIndex]; + len = c.cache[tbodyIndex].normalized.length; + // loop through the rows + for ( rowIndex = 0; rowIndex < len; rowIndex++ ) { + // get cached row from cache.row ( old ) or row data object + // ( new; last item in normalized array ) + row = cache.row ? + cache.row[ rowIndex ] : + cache.normalized[ rowIndex ][ c.columns ].$row[0]; + // check if has class filtered + if ( onlyAvail && row.className.match( wo.filter_filteredRow ) ) { + continue; + } + // get non-normalized cell content + if ( wo.filter_useParsedData || + c.parsers[column].parsed || + c.$headerIndexed[column].hasClass( 'filter-parsed' ) ) { + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ column ]; + // child row parsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length - 1; + for ( indx = 0; indx < childLen; indx++ ) { + arry[ arry.length ] = '' + cache.normalized[ rowIndex ][ c.columns ].child[ indx ][ column ]; + } + } + } else { + // get raw cached data instead of content directly from the cells + arry[ arry.length ] = cache.normalized[ rowIndex ][ c.columns ].raw[ column ]; + // child row unparsed data + if ( wo.filter_childRows && wo.filter_childByColumn ) { + childLen = cache.normalized[ rowIndex ][ c.columns ].$row.length; + for ( indx = 1; indx < childLen; indx++ ) { + child = cache.normalized[ rowIndex ][ c.columns ].$row.eq( indx ).children().eq( column ); + arry[ arry.length ] = '' + ts.getElementText( c, child, column ); + } + } + } + } + } + return arry; + }, + buildSelect: function( table, column, arry, updating, onlyAvail ) { + table = $( table )[0]; + column = parseInt( column, 10 ); + if ( !table.config.cache || $.isEmptyObject( table.config.cache ) ) { + return; + } + + var indx, val, txt, t, $filters, $filter, option, + c = table.config, + wo = c.widgetOptions, + node = c.$headerIndexed[ column ], + // t.data( 'placeholder' ) won't work in jQuery older than 1.4.3 + options = '<option value="">' + + ( node.data( 'placeholder' ) || + node.attr( 'data-placeholder' ) || + wo.filter_placeholder.select || '' + ) + '</option>', + // Get curent filter value + currentValue = c.$table + .find( 'thead' ) + .find( 'select.' + tscss.filter + '[data-column="' + column + '"]' ) + .val(); + + // nothing included in arry ( external source ), so get the options from + // filter_selectSource or column data + if ( typeof arry === 'undefined' || arry === '' ) { + arry = tsf.getOptionSource( table, column, onlyAvail ); + // abort, selects are updated by an external method + if (arry === null) { + return; + } + } + + if ( $.isArray( arry ) ) { + // build option list + for ( indx = 0; indx < arry.length; indx++ ) { + option = arry[ indx ]; + if ( option.text ) { + // OBJECT!! add data-function-name in case the value is set in filter_functions + option['data-function-name'] = typeof option.value === 'undefined' ? option.text : option.value; + + // support jQuery < v1.8, otherwise the below code could be shortened to + // options += $( '<option>', option )[ 0 ].outerHTML; + options += '<option'; + for ( val in option ) { + if ( option.hasOwnProperty( val ) && val !== 'text' ) { + options += ' ' + val + '="' + option[ val ].replace( tsfRegex.quote, '"' ) + '"'; + } + } + if ( !option.value ) { + options += ' value="' + option.text.replace( tsfRegex.quote, '"' ) + '"'; + } + options += '>' + option.text.replace( tsfRegex.quote, '"' ) + '</option>'; + // above code is needed in jQuery < v1.8 + + // make sure we don't turn an object into a string (objects without a "text" property) + } else if ( '' + option !== '[object Object]' ) { + txt = option = ( '' + option ).replace( tsfRegex.quote, '"' ); + val = txt; + // allow including a symbol in the selectSource array + // 'a-z|A through Z' so that 'a-z' becomes the option value + // and 'A through Z' becomes the option text + if ( txt.indexOf( wo.filter_selectSourceSeparator ) >= 0 ) { + t = txt.split( wo.filter_selectSourceSeparator ); + val = t[0]; + txt = t[1]; + } + // replace quotes - fixes #242 & ignore empty strings + // see http://stackoverflow.com/q/14990971/145346 + options += option !== '' ? + '<option ' + + ( val === txt ? '' : 'data-function-name="' + option + '" ' ) + + 'value="' + val + '">' + txt + + '</option>' : ''; + } + } + // clear arry so it doesn't get appended twice + arry = []; + } + + // update all selects in the same column ( clone thead in sticky headers & + // any external selects ) - fixes 473 + $filters = ( c.$filters ? c.$filters : c.$table.children( 'thead' ) ) + .find( '.' + tscss.filter ); + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + $filter = $filters.filter( 'select[data-column="' + column + '"]' ); + + // make sure there is a select there! + if ( $filter.length ) { + $filter[ updating ? 'html' : 'append' ]( options ); + if ( !$.isArray( arry ) ) { + // append options if arry is provided externally as a string or jQuery object + // options ( default value ) was already added + $filter.append( arry ).val( currentValue ); + } + $filter.val( currentValue ); + } + }, + buildDefault: function( table, updating ) { + var columnIndex, $header, noSelect, + c = table.config, + wo = c.widgetOptions, + columns = c.columns; + // build default select dropdown + for ( columnIndex = 0; columnIndex < columns; columnIndex++ ) { + $header = c.$headerIndexed[columnIndex]; + noSelect = !( $header.hasClass( 'filter-false' ) || $header.hasClass( 'parser-false' ) ); + // look for the filter-select class; build/update it if found + if ( ( $header.hasClass( 'filter-select' ) || + ts.getColumnData( table, wo.filter_functions, columnIndex ) === true ) && noSelect ) { + tsf.buildSelect( table, columnIndex, '', updating, $header.hasClass( wo.filter_onlyAvail ) ); + } + } + } + }; + + // filter regex variable + tsfRegex = tsf.regex; + + ts.getFilters = function( table, getRaw, setFilters, skipFirst ) { + var i, $filters, $column, cols, + filters = [], + c = table ? $( table )[0].config : '', + wo = c ? c.widgetOptions : ''; + if ( ( getRaw !== true && wo && !wo.filter_columnFilters ) || + // setFilters called, but last search is exactly the same as the current + // fixes issue #733 & #903 where calling update causes the input values to reset + ( $.isArray(setFilters) && tsf.equalFilters(c, setFilters, c.lastSearch) ) + ) { + return $( table ).data( 'lastSearch' ) || []; + } + if ( c ) { + if ( c.$filters ) { + $filters = c.$filters.find( '.' + tscss.filter ); + } + if ( wo.filter_$externalFilters ) { + $filters = $filters && $filters.length ? + $filters.add( wo.filter_$externalFilters ) : + wo.filter_$externalFilters; + } + if ( $filters && $filters.length ) { + filters = setFilters || []; + for ( i = 0; i < c.columns + 1; i++ ) { + cols = ( i === c.columns ? + // 'all' columns can now include a range or set of columms ( data-column='0-2,4,6-7' ) + wo.filter_anyColumnSelector + ',' + wo.filter_multipleColumnSelector : + '[data-column="' + i + '"]' ); + $column = $filters.filter( cols ); + if ( $column.length ) { + // move the latest search to the first slot in the array + $column = tsf.getLatestSearch( $column ); + if ( $.isArray( setFilters ) ) { + // skip first ( latest input ) to maintain cursor position while typing + if ( skipFirst && $column.length > 1 ) { + $column = $column.slice( 1 ); + } + if ( i === c.columns ) { + // prevent data-column='all' from filling data-column='0,1' ( etc ) + cols = $column.filter( wo.filter_anyColumnSelector ); + $column = cols.length ? cols : $column; + } + $column + .val( setFilters[ i ] ) + // must include a namespace here; but not c.namespace + 'filter'? + .trigger( 'change' + c.namespace ); + } else { + filters[i] = $column.val() || ''; + // don't change the first... it will move the cursor + if ( i === c.columns ) { + // don't update range columns from 'all' setting + $column + .slice( 1 ) + .filter( '[data-column*="' + $column.attr( 'data-column' ) + '"]' ) + .val( filters[ i ] ); + } else { + $column + .slice( 1 ) + .val( filters[ i ] ); + } + } + // save any match input dynamically + if ( i === c.columns && $column.length ) { + wo.filter_$anyMatch = $column; + } + } + } + } + } + return filters; + }; + + ts.setFilters = function( table, filter, apply, skipFirst ) { + var c = table ? $( table )[0].config : '', + valid = ts.getFilters( table, true, filter, skipFirst ); + // default apply to "true" + if ( typeof apply === 'undefined' ) { + apply = true; + } + if ( c && apply ) { + // ensure new set filters are applied, even if the search is the same + c.lastCombinedFilter = null; + c.lastSearch = []; + tsf.searching( c.table, filter, skipFirst ); + c.$table.triggerHandler( 'filterFomatterUpdate' ); + } + return valid.length !== 0; + }; + +})( jQuery ); + +/*! Widget: stickyHeaders - updated 9/27/2017 (v2.29.0) *//* + * Requires tablesorter v2.8+ and jQuery 1.4.3+ + * by Rob Garrison + */ +;(function ($, window) { + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + sticky : 'tablesorter-stickyHeader', // stickyHeader + stickyVis : 'tablesorter-sticky-visible', + stickyHide: 'tablesorter-sticky-hidden', + stickyWrap: 'tablesorter-sticky-wrapper' + }); + + // Add a resize event to table headers + ts.addHeaderResizeEvent = function(table, disable, settings) { + table = $(table)[0]; // make sure we're using a dom element + if ( !table.config ) { return; } + var defaults = { + timer : 250 + }, + options = $.extend({}, defaults, settings), + c = table.config, + wo = c.widgetOptions, + checkSizes = function( triggerEvent ) { + var index, headers, $header, sizes, width, height, + len = c.$headers.length; + wo.resize_flag = true; + headers = []; + for ( index = 0; index < len; index++ ) { + $header = c.$headers.eq( index ); + sizes = $header.data( 'savedSizes' ) || [ 0, 0 ]; // fixes #394 + width = $header[0].offsetWidth; + height = $header[0].offsetHeight; + if ( width !== sizes[0] || height !== sizes[1] ) { + $header.data( 'savedSizes', [ width, height ] ); + headers.push( $header[0] ); + } + } + if ( headers.length && triggerEvent !== false ) { + c.$table.triggerHandler( 'resize', [ headers ] ); + } + wo.resize_flag = false; + }; + clearInterval(wo.resize_timer); + if (disable) { + wo.resize_flag = false; + return false; + } + checkSizes( false ); + wo.resize_timer = setInterval(function() { + if (wo.resize_flag) { return; } + checkSizes(); + }, options.timer); + }; + + function getStickyOffset(c, wo) { + var $el = isNaN(wo.stickyHeaders_offset) ? $(wo.stickyHeaders_offset) : []; + return $el.length ? + $el.height() || 0 : + parseInt(wo.stickyHeaders_offset, 10) || 0; + } + + // Sticky headers based on this awesome article: + // http://css-tricks.com/13465-persistent-headers/ + // and https://github.com/jmosbech/StickyTableHeaders by Jonas Mosbech + // ************************** + ts.addWidget({ + id: 'stickyHeaders', + priority: 54, // sticky widget must be initialized after the filter & before pager widget! + options: { + stickyHeaders : '', // extra class name added to the sticky header row + stickyHeaders_appendTo : null, // jQuery selector or object to phycially attach the sticky headers + stickyHeaders_attachTo : null, // jQuery selector or object to attach scroll listener to (overridden by xScroll & yScroll settings) + stickyHeaders_xScroll : null, // jQuery selector or object to monitor horizontal scroll position (defaults: xScroll > attachTo > window) + stickyHeaders_yScroll : null, // jQuery selector or object to monitor vertical scroll position (defaults: yScroll > attachTo > window) + stickyHeaders_offset : 0, // number or jquery selector targeting the position:fixed element + stickyHeaders_filteredToTop: true, // scroll table top into view after filtering + stickyHeaders_cloneId : '-sticky', // added to table ID, if it exists + stickyHeaders_addResizeEvent : true, // trigger 'resize' event on headers + stickyHeaders_includeCaption : true, // if false and a caption exist, it won't be included in the sticky header + stickyHeaders_zIndex : 2 // The zIndex of the stickyHeaders, allows the user to adjust this to their needs + }, + format: function(table, c, wo) { + // filter widget doesn't initialize on an empty table. Fixes #449 + if ( c.$table.hasClass('hasStickyHeaders') || ($.inArray('filter', c.widgets) >= 0 && !c.$table.hasClass('hasFilters')) ) { + return; + } + var index, len, $t, + $table = c.$table, + // add position: relative to attach element, hopefully it won't cause trouble. + $attach = $(wo.stickyHeaders_attachTo || wo.stickyHeaders_appendTo), + namespace = c.namespace + 'stickyheaders ', + // element to watch for the scroll event + $yScroll = $(wo.stickyHeaders_yScroll || wo.stickyHeaders_attachTo || window), + $xScroll = $(wo.stickyHeaders_xScroll || wo.stickyHeaders_attachTo || window), + $thead = $table.children('thead:first'), + $header = $thead.children('tr').not('.sticky-false').children(), + $tfoot = $table.children('tfoot'), + stickyOffset = getStickyOffset(c, wo), + // is this table nested? If so, find parent sticky header wrapper (div, not table) + $nestedSticky = $table.parent().closest('.' + ts.css.table).hasClass('hasStickyHeaders') ? + $table.parent().closest('table.tablesorter')[0].config.widgetOptions.$sticky.parent() : [], + nestedStickyTop = $nestedSticky.length ? $nestedSticky.height() : 0, + // clone table, then wrap to make sticky header + $stickyTable = wo.$sticky = $table.clone() + .addClass('containsStickyHeaders ' + ts.css.sticky + ' ' + wo.stickyHeaders + ' ' + c.namespace.slice(1) + '_extra_table' ) + .wrap('<div class="' + ts.css.stickyWrap + '">'), + $stickyWrap = $stickyTable.parent() + .addClass(ts.css.stickyHide) + .css({ + position : $attach.length ? 'absolute' : 'fixed', + padding : parseInt( $stickyTable.parent().parent().css('padding-left'), 10 ), + top : stickyOffset + nestedStickyTop, + left : 0, + visibility : 'hidden', + zIndex : wo.stickyHeaders_zIndex || 2 + }), + $stickyThead = $stickyTable.children('thead:first'), + $stickyCells, + laststate = '', + setWidth = function($orig, $clone) { + var index, width, border, $cell, $this, + $cells = $orig.filter(':visible'), + len = $cells.length; + for ( index = 0; index < len; index++ ) { + $cell = $clone.filter(':visible').eq(index); + $this = $cells.eq(index); + // code from https://github.com/jmosbech/StickyTableHeaders + if ($this.css('box-sizing') === 'border-box') { + width = $this.outerWidth(); + } else { + if ($cell.css('border-collapse') === 'collapse') { + if (window.getComputedStyle) { + width = parseFloat( window.getComputedStyle($this[0], null).width ); + } else { + // ie8 only + border = parseFloat( $this.css('border-width') ); + width = $this.outerWidth() - parseFloat( $this.css('padding-left') ) - parseFloat( $this.css('padding-right') ) - border; + } + } else { + width = $this.width(); + } + } + $cell.css({ + 'width': width, + 'min-width': width, + 'max-width': width + }); + } + }, + getLeftPosition = function(yWindow) { + if (yWindow === false && $nestedSticky.length) { + return $table.position().left; + } + return $attach.length ? + parseInt($attach.css('padding-left'), 10) || 0 : + $table.offset().left - parseInt($table.css('margin-left'), 10) - $(window).scrollLeft(); + }, + resizeHeader = function() { + $stickyWrap.css({ + left : getLeftPosition(), + width: $table.outerWidth() + }); + setWidth( $table, $stickyTable ); + setWidth( $header, $stickyCells ); + }, + scrollSticky = function( resizing ) { + if (!$table.is(':visible')) { return; } // fixes #278 + // Detect nested tables - fixes #724 + nestedStickyTop = $nestedSticky.length ? $nestedSticky.offset().top - $yScroll.scrollTop() + $nestedSticky.height() : 0; + var tmp, + offset = $table.offset(), + stickyOffset = getStickyOffset(c, wo), + yWindow = $.isWindow( $yScroll[0] ), // $.isWindow needs jQuery 1.4.3 + yScroll = yWindow ? + $yScroll.scrollTop() : + // use parent sticky position if nested AND inside of a scrollable element - see #1512 + $nestedSticky.length ? parseInt($nestedSticky[0].style.top, 10) : $yScroll.offset().top, + attachTop = $attach.length ? yScroll : $yScroll.scrollTop(), + captionHeight = wo.stickyHeaders_includeCaption ? 0 : $table.children( 'caption' ).height() || 0, + scrollTop = attachTop + stickyOffset + nestedStickyTop - captionHeight, + tableHeight = $table.height() - ($stickyWrap.height() + ($tfoot.height() || 0)) - captionHeight, + isVisible = ( scrollTop > offset.top ) && ( scrollTop < offset.top + tableHeight ) ? 'visible' : 'hidden', + state = isVisible === 'visible' ? ts.css.stickyVis : ts.css.stickyHide, + needsUpdating = !$stickyWrap.hasClass( state ), + cssSettings = { visibility : isVisible }; + if ($attach.length) { + // attached sticky headers always need updating + needsUpdating = true; + cssSettings.top = yWindow ? scrollTop - $attach.offset().top : $attach.scrollTop(); + } + // adjust when scrolling horizontally - fixes issue #143 + tmp = getLeftPosition(yWindow); + if (tmp !== parseInt($stickyWrap.css('left'), 10)) { + needsUpdating = true; + cssSettings.left = tmp; + } + cssSettings.top = ( cssSettings.top || 0 ) + + // If nested AND inside of a scrollable element, only add parent sticky height + (!yWindow && $nestedSticky.length ? $nestedSticky.height() : stickyOffset + nestedStickyTop); + if (needsUpdating) { + $stickyWrap + .removeClass( ts.css.stickyVis + ' ' + ts.css.stickyHide ) + .addClass( state ) + .css(cssSettings); + } + if (isVisible !== laststate || resizing) { + // make sure the column widths match + resizeHeader(); + laststate = isVisible; + } + }; + // only add a position relative if a position isn't already defined + if ($attach.length && !$attach.css('position')) { + $attach.css('position', 'relative'); + } + // fix clone ID, if it exists - fixes #271 + if ($stickyTable.attr('id')) { $stickyTable[0].id += wo.stickyHeaders_cloneId; } + // clear out cloned table, except for sticky header + // include caption & filter row (fixes #126 & #249) - don't remove cells to get correct cell indexing + $stickyTable.find('> thead:gt(0), tr.sticky-false').hide(); + $stickyTable.find('> tbody, > tfoot').remove(); + $stickyTable.find('caption').toggle(wo.stickyHeaders_includeCaption); + // issue #172 - find td/th in sticky header + $stickyCells = $stickyThead.children().children(); + $stickyTable.css({ height:0, width:0, margin: 0 }); + // remove resizable block + $stickyCells.find('.' + ts.css.resizer).remove(); + // update sticky header class names to match real header after sorting + $table + .addClass('hasStickyHeaders') + .bind('pagerComplete' + namespace, function() { + resizeHeader(); + }); + + ts.bindEvents(table, $stickyThead.children().children('.' + ts.css.header)); + + if (wo.stickyHeaders_appendTo) { + $(wo.stickyHeaders_appendTo).append( $stickyWrap ); + } else { + // add stickyheaders AFTER the table. If the table is selected by ID, the original one (first) will be returned. + $table.after( $stickyWrap ); + } + + // onRenderHeader is defined, we need to do something about it (fixes #641) + if (c.onRenderHeader) { + $t = $stickyThead.children('tr').children(); + len = $t.length; + for ( index = 0; index < len; index++ ) { + // send second parameter + c.onRenderHeader.apply( $t.eq( index ), [ index, c, $stickyTable ] ); + } + } + // make it sticky! + $xScroll.add($yScroll) + .unbind( ('scroll resize '.split(' ').join( namespace )).replace(/\s+/g, ' ') ) + .bind('scroll resize '.split(' ').join( namespace ), function( event ) { + scrollSticky( event.type === 'resize' ); + }); + c.$table + .unbind('stickyHeadersUpdate' + namespace) + .bind('stickyHeadersUpdate' + namespace, function() { + scrollSticky( true ); + }); + + if (wo.stickyHeaders_addResizeEvent) { + ts.addHeaderResizeEvent(table); + } + + // look for filter widget + if ($table.hasClass('hasFilters') && wo.filter_columnFilters) { + // scroll table into view after filtering, if sticky header is active - #482 + $table.bind('filterEnd' + namespace, function() { + // $(':focus') needs jQuery 1.6+ + var $td = $(document.activeElement).closest('td'), + column = $td.parent().children().index($td); + // only scroll if sticky header is active + if ($stickyWrap.hasClass(ts.css.stickyVis) && wo.stickyHeaders_filteredToTop) { + // scroll to original table (not sticky clone) + window.scrollTo(0, $table.position().top); + // give same input/select focus; check if c.$filters exists; fixes #594 + if (column >= 0 && c.$filters) { + c.$filters.eq(column).find('a, select, input').filter(':visible').focus(); + } + } + }); + ts.filter.bindSearch( $table, $stickyCells.find('.' + ts.css.filter) ); + // support hideFilters + if (wo.filter_hideFilters) { + ts.filter.hideFilters(c, $stickyTable); + } + } + + // resize table (Firefox) + if (wo.stickyHeaders_addResizeEvent) { + $table.bind('resize' + c.namespace + 'stickyheaders', function() { + resizeHeader(); + }); + } + + // make sure sticky is visible if page is partially scrolled + scrollSticky( true ); + $table.triggerHandler('stickyHeadersInit'); + + }, + remove: function(table, c, wo) { + var namespace = c.namespace + 'stickyheaders '; + c.$table + .removeClass('hasStickyHeaders') + .unbind( ('pagerComplete resize filterEnd stickyHeadersUpdate '.split(' ').join(namespace)).replace(/\s+/g, ' ') ) + .next('.' + ts.css.stickyWrap).remove(); + if (wo.$sticky && wo.$sticky.length) { wo.$sticky.remove(); } // remove cloned table + $(window) + .add(wo.stickyHeaders_xScroll) + .add(wo.stickyHeaders_yScroll) + .add(wo.stickyHeaders_attachTo) + .unbind( ('scroll resize '.split(' ').join(namespace)).replace(/\s+/g, ' ') ); + ts.addHeaderResizeEvent(table, true); + } + }); + +})(jQuery, window); + +/*! Widget: resizable - updated 2018-03-26 (v2.30.2) */ +/*jshint browser:true, jquery:true, unused:false */ +;(function ($, window) { + 'use strict'; + var ts = $.tablesorter || {}; + + $.extend(ts.css, { + resizableContainer : 'tablesorter-resizable-container', + resizableHandle : 'tablesorter-resizable-handle', + resizableNoSelect : 'tablesorter-disableSelection', + resizableStorage : 'tablesorter-resizable' + }); + + // Add extra scroller css + $(function() { + var s = '<style>' + + 'body.' + ts.css.resizableNoSelect + ' { -ms-user-select: none; -moz-user-select: -moz-none;' + + '-khtml-user-select: none; -webkit-user-select: none; user-select: none; }' + + '.' + ts.css.resizableContainer + ' { position: relative; height: 1px; }' + + // make handle z-index > than stickyHeader z-index, so the handle stays above sticky header + '.' + ts.css.resizableHandle + ' { position: absolute; display: inline-block; width: 8px;' + + 'top: 1px; cursor: ew-resize; z-index: 3; user-select: none; -moz-user-select: none; }' + + '</style>'; + $('head').append(s); + }); + + ts.resizable = { + init : function( c, wo ) { + if ( c.$table.hasClass( 'hasResizable' ) ) { return; } + c.$table.addClass( 'hasResizable' ); + + var noResize, $header, column, storedSizes, tmp, + $table = c.$table, + $parent = $table.parent(), + marginTop = parseInt( $table.css( 'margin-top' ), 10 ), + + // internal variables + vars = wo.resizable_vars = { + useStorage : ts.storage && wo.resizable !== false, + $wrap : $parent, + mouseXPosition : 0, + $target : null, + $next : null, + overflow : $parent.css('overflow') === 'auto' || + $parent.css('overflow') === 'scroll' || + $parent.css('overflow-x') === 'auto' || + $parent.css('overflow-x') === 'scroll', + storedSizes : [] + }; + + // set default widths + ts.resizableReset( c.table, true ); + + // now get measurements! + vars.tableWidth = $table.width(); + // attempt to autodetect + vars.fullWidth = Math.abs( $parent.width() - vars.tableWidth ) < 20; + + /* + // Hacky method to determine if table width is set to 'auto' + // http://stackoverflow.com/a/20892048/145346 + if ( !vars.fullWidth ) { + tmp = $table.width(); + $header = $table.wrap('<span>').parent(); // temp variable + storedSizes = parseInt( $table.css( 'margin-left' ), 10 ) || 0; + $table.css( 'margin-left', storedSizes + 50 ); + vars.tableWidth = $header.width() > tmp ? 'auto' : tmp; + $table.css( 'margin-left', storedSizes ? storedSizes : '' ); + $header = null; + $table.unwrap('<span>'); + } + */ + + if ( vars.useStorage && vars.overflow ) { + // save table width + ts.storage( c.table, 'tablesorter-table-original-css-width', vars.tableWidth ); + tmp = ts.storage( c.table, 'tablesorter-table-resized-width' ) || 'auto'; + ts.resizable.setWidth( $table, tmp, true ); + } + wo.resizable_vars.storedSizes = storedSizes = ( vars.useStorage ? + ts.storage( c.table, ts.css.resizableStorage ) : + [] ) || []; + ts.resizable.setWidths( c, wo, storedSizes ); + ts.resizable.updateStoredSizes( c, wo ); + + wo.$resizable_container = $( '<div class="' + ts.css.resizableContainer + '">' ) + .css({ top : marginTop }) + .insertBefore( $table ); + // add container + for ( column = 0; column < c.columns; column++ ) { + $header = c.$headerIndexed[ column ]; + tmp = ts.getColumnData( c.table, c.headers, column ); + noResize = ts.getData( $header, tmp, 'resizable' ) === 'false'; + if ( !noResize ) { + $( '<div class="' + ts.css.resizableHandle + '">' ) + .appendTo( wo.$resizable_container ) + .attr({ + 'data-column' : column, + 'unselectable' : 'on' + }) + .data( 'header', $header ) + .bind( 'selectstart', false ); + } + } + ts.resizable.bindings( c, wo ); + }, + + updateStoredSizes : function( c, wo ) { + var column, $header, + len = c.columns, + vars = wo.resizable_vars; + vars.storedSizes = []; + for ( column = 0; column < len; column++ ) { + $header = c.$headerIndexed[ column ]; + vars.storedSizes[ column ] = $header.is(':visible') ? $header.width() : 0; + } + }, + + setWidth : function( $el, width, overflow ) { + // overflow tables need min & max width set as well + $el.css({ + 'width' : width, + 'min-width' : overflow ? width : '', + 'max-width' : overflow ? width : '' + }); + }, + + setWidths : function( c, wo, storedSizes ) { + var column, $temp, + vars = wo.resizable_vars, + $extra = $( c.namespace + '_extra_headers' ), + $col = c.$table.children( 'colgroup' ).children( 'col' ); + storedSizes = storedSizes || vars.storedSizes || []; + // process only if table ID or url match + if ( storedSizes.length ) { + for ( column = 0; column < c.columns; column++ ) { + // set saved resizable widths + ts.resizable.setWidth( c.$headerIndexed[ column ], storedSizes[ column ], vars.overflow ); + if ( $extra.length ) { + // stickyHeaders needs to modify min & max width as well + $temp = $extra.eq( column ).add( $col.eq( column ) ); + ts.resizable.setWidth( $temp, storedSizes[ column ], vars.overflow ); + } + } + $temp = $( c.namespace + '_extra_table' ); + if ( $temp.length && !ts.hasWidget( c.table, 'scroller' ) ) { + ts.resizable.setWidth( $temp, c.$table.outerWidth(), vars.overflow ); + } + } + }, + + setHandlePosition : function( c, wo ) { + var startPosition, + tableHeight = c.$table.height(), + $handles = wo.$resizable_container.children(), + handleCenter = Math.floor( $handles.width() / 2 ); + + if ( ts.hasWidget( c.table, 'scroller' ) ) { + tableHeight = 0; + c.$table.closest( '.' + ts.css.scrollerWrap ).children().each(function() { + var $this = $(this); + // center table has a max-height set + tableHeight += $this.filter('[style*="height"]').length ? $this.height() : $this.children('table').height(); + }); + } + + if ( !wo.resizable_includeFooter && c.$table.children('tfoot').length ) { + tableHeight -= c.$table.children('tfoot').height(); + } + // subtract out table left position from resizable handles. Fixes #864 + // jQuery v3.3.0+ appears to include the start position with the $header.position().left; see #1544 + startPosition = parseFloat($.fn.jquery) >= 3.3 ? 0 : c.$table.position().left; + $handles.each( function() { + var $this = $(this), + column = parseInt( $this.attr( 'data-column' ), 10 ), + columns = c.columns - 1, + $header = $this.data( 'header' ); + if ( !$header ) { return; } // see #859 + if ( + !$header.is(':visible') || + ( !wo.resizable_addLastColumn && ts.resizable.checkVisibleColumns(c, column) ) + ) { + $this.hide(); + } else if ( column < columns || column === columns && wo.resizable_addLastColumn ) { + $this.css({ + display: 'inline-block', + height : tableHeight, + left : $header.position().left - startPosition + $header.outerWidth() - handleCenter + }); + } + }); + }, + + // Fixes #1485 + checkVisibleColumns: function( c, column ) { + var i, + len = 0; + for ( i = column + 1; i < c.columns; i++ ) { + len += c.$headerIndexed[i].is( ':visible' ) ? 1 : 0; + } + return len === 0; + }, + + // prevent text selection while dragging resize bar + toggleTextSelection : function( c, wo, toggle ) { + var namespace = c.namespace + 'tsresize'; + wo.resizable_vars.disabled = toggle; + $( 'body' ).toggleClass( ts.css.resizableNoSelect, toggle ); + if ( toggle ) { + $( 'body' ) + .attr( 'unselectable', 'on' ) + .bind( 'selectstart' + namespace, false ); + } else { + $( 'body' ) + .removeAttr( 'unselectable' ) + .unbind( 'selectstart' + namespace ); + } + }, + + bindings : function( c, wo ) { + var namespace = c.namespace + 'tsresize'; + wo.$resizable_container.children().bind( 'mousedown', function( event ) { + // save header cell and mouse position + var column, + vars = wo.resizable_vars, + $extras = $( c.namespace + '_extra_headers' ), + $header = $( event.target ).data( 'header' ); + + column = parseInt( $header.attr( 'data-column' ), 10 ); + vars.$target = $header = $header.add( $extras.filter('[data-column="' + column + '"]') ); + vars.target = column; + + // if table is not as wide as it's parent, then resize the table + vars.$next = event.shiftKey || wo.resizable_targetLast ? + $header.parent().children().not( '.resizable-false' ).filter( ':last' ) : + $header.nextAll( ':not(.resizable-false)' ).eq( 0 ); + + column = parseInt( vars.$next.attr( 'data-column' ), 10 ); + vars.$next = vars.$next.add( $extras.filter('[data-column="' + column + '"]') ); + vars.next = column; + + vars.mouseXPosition = event.pageX; + ts.resizable.updateStoredSizes( c, wo ); + ts.resizable.toggleTextSelection(c, wo, true ); + }); + + $( document ) + .bind( 'mousemove' + namespace, function( event ) { + var vars = wo.resizable_vars; + // ignore mousemove if no mousedown + if ( !vars.disabled || vars.mouseXPosition === 0 || !vars.$target ) { return; } + if ( wo.resizable_throttle ) { + clearTimeout( vars.timer ); + vars.timer = setTimeout( function() { + ts.resizable.mouseMove( c, wo, event ); + }, isNaN( wo.resizable_throttle ) ? 5 : wo.resizable_throttle ); + } else { + ts.resizable.mouseMove( c, wo, event ); + } + }) + .bind( 'mouseup' + namespace, function() { + if (!wo.resizable_vars.disabled) { return; } + ts.resizable.toggleTextSelection( c, wo, false ); + ts.resizable.stopResize( c, wo ); + ts.resizable.setHandlePosition( c, wo ); + }); + + // resizeEnd event triggered by scroller widget + $( window ).bind( 'resize' + namespace + ' resizeEnd' + namespace, function() { + ts.resizable.setHandlePosition( c, wo ); + }); + + // right click to reset columns to default widths + c.$table + .bind( 'columnUpdate pagerComplete resizableUpdate '.split( ' ' ).join( namespace + ' ' ), function() { + ts.resizable.setHandlePosition( c, wo ); + }) + .bind( 'resizableReset' + namespace, function() { + ts.resizableReset( c.table ); + }) + .find( 'thead:first' ) + .add( $( c.namespace + '_extra_table' ).find( 'thead:first' ) ) + .bind( 'contextmenu' + namespace, function() { + // $.isEmptyObject() needs jQuery 1.4+; allow right click if already reset + var allowClick = wo.resizable_vars.storedSizes.length === 0; + ts.resizableReset( c.table ); + ts.resizable.setHandlePosition( c, wo ); + wo.resizable_vars.storedSizes = []; + return allowClick; + }); + + }, + + mouseMove : function( c, wo, event ) { + if ( wo.resizable_vars.mouseXPosition === 0 || !wo.resizable_vars.$target ) { return; } + // resize columns + var column, + total = 0, + vars = wo.resizable_vars, + $next = vars.$next, + tar = vars.storedSizes[ vars.target ], + leftEdge = event.pageX - vars.mouseXPosition; + if ( vars.overflow ) { + if ( tar + leftEdge > 0 ) { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidth( vars.$target, vars.storedSizes[ vars.target ], true ); + // update the entire table width + for ( column = 0; column < c.columns; column++ ) { + total += vars.storedSizes[ column ]; + } + ts.resizable.setWidth( c.$table.add( $( c.namespace + '_extra_table' ) ), total ); + } + if ( !$next.length ) { + // if expanding right-most column, scroll the wrapper + vars.$wrap[0].scrollLeft = c.$table.width(); + } + } else if ( vars.fullWidth ) { + vars.storedSizes[ vars.target ] += leftEdge; + vars.storedSizes[ vars.next ] -= leftEdge; + ts.resizable.setWidths( c, wo ); + } else { + vars.storedSizes[ vars.target ] += leftEdge; + ts.resizable.setWidths( c, wo ); + } + vars.mouseXPosition = event.pageX; + // dynamically update sticky header widths + c.$table.triggerHandler('stickyHeadersUpdate'); + }, + + stopResize : function( c, wo ) { + var vars = wo.resizable_vars; + ts.resizable.updateStoredSizes( c, wo ); + if ( vars.useStorage ) { + // save all column widths + ts.storage( c.table, ts.css.resizableStorage, vars.storedSizes ); + ts.storage( c.table, 'tablesorter-table-resized-width', c.$table.width() ); + } + vars.mouseXPosition = 0; + vars.$target = vars.$next = null; + // will update stickyHeaders, just in case, see #912 + c.$table.triggerHandler('stickyHeadersUpdate'); + c.$table.triggerHandler('resizableComplete'); + } + }; + + // this widget saves the column widths if + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'resizable', + priority: 40, + options: { + resizable : true, // save column widths to storage + resizable_addLastColumn : false, + resizable_includeFooter: true, + resizable_widths : [], + resizable_throttle : false, // set to true (5ms) or any number 0-10 range + resizable_targetLast : false + }, + init: function(table, thisWidget, c, wo) { + ts.resizable.init( c, wo ); + }, + format: function( table, c, wo ) { + ts.resizable.setHandlePosition( c, wo ); + }, + remove: function( table, c, wo, refreshing ) { + if (wo.$resizable_container) { + var namespace = c.namespace + 'tsresize'; + c.$table.add( $( c.namespace + '_extra_table' ) ) + .removeClass('hasResizable') + .children( 'thead' ) + .unbind( 'contextmenu' + namespace ); + + wo.$resizable_container.remove(); + ts.resizable.toggleTextSelection( c, wo, false ); + ts.resizableReset( table, refreshing ); + $( document ).unbind( 'mousemove' + namespace + ' mouseup' + namespace ); + } + } + }); + + ts.resizableReset = function( table, refreshing ) { + $( table ).each(function() { + var index, $t, + c = this.config, + wo = c && c.widgetOptions, + vars = wo.resizable_vars; + if ( table && c && c.$headerIndexed.length ) { + // restore the initial table width + if ( vars.overflow && vars.tableWidth ) { + ts.resizable.setWidth( c.$table, vars.tableWidth, true ); + if ( vars.useStorage ) { + ts.storage( table, 'tablesorter-table-resized-width', vars.tableWidth ); + } + } + for ( index = 0; index < c.columns; index++ ) { + $t = c.$headerIndexed[ index ]; + if ( wo.resizable_widths && wo.resizable_widths[ index ] ) { + ts.resizable.setWidth( $t, wo.resizable_widths[ index ], vars.overflow ); + } else if ( !$t.hasClass( 'resizable-false' ) ) { + // don't clear the width of any column that is not resizable + ts.resizable.setWidth( $t, '', vars.overflow ); + } + } + + // reset stickyHeader widths + c.$table.triggerHandler( 'stickyHeadersUpdate' ); + if ( ts.storage && !refreshing ) { + ts.storage( this, ts.css.resizableStorage, [] ); + } + } + }); + }; + +})( jQuery, window ); + +/*! Widget: saveSort - updated 2018-03-19 (v2.30.1) *//* +* Requires tablesorter v2.16+ +* by Rob Garrison +*/ +;(function ($) { + 'use strict'; + var ts = $.tablesorter || {}; + + function getStoredSortList(c) { + var stored = ts.storage( c.table, 'tablesorter-savesort' ); + return (stored && stored.hasOwnProperty('sortList') && $.isArray(stored.sortList)) ? stored.sortList : []; + } + + function sortListChanged(c, sortList) { + return (sortList || getStoredSortList(c)).join(',') !== c.sortList.join(','); + } + + // this widget saves the last sort only if the + // saveSort widget option is true AND the + // $.tablesorter.storage function is included + // ************************** + ts.addWidget({ + id: 'saveSort', + priority: 20, + options: { + saveSort : true + }, + init: function(table, thisWidget, c, wo) { + // run widget format before all other widgets are applied to the table + thisWidget.format(table, c, wo, true); + }, + format: function(table, c, wo, init) { + var time, + $table = c.$table, + saveSort = wo.saveSort !== false, // make saveSort active/inactive; default to true + sortList = { 'sortList' : c.sortList }, + debug = ts.debug(c, 'saveSort'); + if (debug) { + time = new Date(); + } + if ($table.hasClass('hasSaveSort')) { + if (saveSort && table.hasInitialized && ts.storage && sortListChanged(c)) { + ts.storage( table, 'tablesorter-savesort', sortList ); + if (debug) { + console.log('saveSort >> Saving last sort: ' + c.sortList + ts.benchmark(time)); + } + } + } else { + // set table sort on initial run of the widget + $table.addClass('hasSaveSort'); + sortList = ''; + // get data + if (ts.storage) { + sortList = getStoredSortList(c); + if (debug) { + console.log('saveSort >> Last sort loaded: "' + sortList + '"' + ts.benchmark(time)); + } + $table.bind('saveSortReset', function(event) { + event.stopPropagation(); + ts.storage( table, 'tablesorter-savesort', '' ); + }); + } + // 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 + if (sortListChanged(c, sortList)) { + ts.sortOn(c, sortList); + } + } + } + }, + remove: function(table, c) { + c.$table.removeClass('hasSaveSort'); + // clear storage + if (ts.storage) { ts.storage( table, 'tablesorter-savesort', '' ); } + } + }); + +})(jQuery); +return jQuery.tablesorter;})); |