You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
11 KiB
288 lines
11 KiB
/*! |
|
* jQuery fancyTable plugin |
|
* https://github.com/myspace-nu |
|
* |
|
* Copyright 2018 Johan Johansson |
|
* Released under the MIT license |
|
*/ |
|
(function($) { |
|
$.fn.fancyTable = function(options) { |
|
var settings = $.extend({ |
|
inputStyle: "", |
|
inputPlaceholder: "Search...", |
|
pagination: false, |
|
paginationClass: "btn btn-light", |
|
paginationClassActive: "active", |
|
pagClosest: 3, |
|
perPage: 10, |
|
sortable: true, |
|
searchable: true, |
|
matchCase: false, |
|
exactMatch: false, |
|
localeCompare: false, |
|
onInit: function(){ }, |
|
onUpdate: function(){ }, |
|
sortFunction: function(a, b, fancyTableObject, rowA, rowB){ |
|
if(a==b && rowA && rowB){ |
|
// If sort values are of equal priority, sort by last order |
|
return(fancyTableObject.rowSortOrder[$(rowA).data("rowid")] > fancyTableObject.rowSortOrder[$(rowB).data("rowid")]); |
|
} |
|
if(fancyTableObject.sortAs[fancyTableObject.sortColumn] == 'numeric'){ |
|
return( |
|
(fancyTableObject.sortOrder>0) ? parseFloat(a)-parseFloat(b) : parseFloat(b)-parseFloat(a) |
|
); |
|
} else { |
|
if(settings.localeCompare){ |
|
return((a.localeCompare(b)<0)?-fancyTableObject.sortOrder:(a.localeCompare(b)>0)?fancyTableObject.sortOrder:0); |
|
} else { |
|
return((a<b)?-fancyTableObject.sortOrder:(a>b)?fancyTableObject.sortOrder:0); |
|
} |
|
} |
|
}, |
|
testing: false |
|
}, options); |
|
var instance = this; |
|
this.settings = settings; |
|
this.tableUpdate = function (elm) { |
|
elm.fancyTable.matches = 0; |
|
$(elm).find("tbody tr").each(function() { |
|
var n=0; |
|
var match = true; |
|
var globalMatch = false; |
|
$(this).find("td").each(function() { |
|
if(!settings.globalSearch && elm.fancyTable.searchArr[n] && !(instance.isSearchMatch($(this).html(),elm.fancyTable.searchArr[n]) )){ |
|
match = false; |
|
} else if(settings.globalSearch && (!elm.fancyTable.search || (instance.isSearchMatch($(this).html(),elm.fancyTable.search) ))){ |
|
if(!Array.isArray(settings.globalSearchExcludeColumns) || !settings.globalSearchExcludeColumns.includes(n+1)){ |
|
globalMatch = true; |
|
} |
|
} |
|
n++; |
|
}); |
|
if((settings.globalSearch && globalMatch) || (!settings.globalSearch && match)){ |
|
elm.fancyTable.matches++ |
|
if(!settings.pagination || (elm.fancyTable.matches>(elm.fancyTable.perPage*(elm.fancyTable.page-1)) && elm.fancyTable.matches<=(elm.fancyTable.perPage*elm.fancyTable.page))){ |
|
$(this).show(); |
|
} else { |
|
$(this).hide(); |
|
} |
|
} else { |
|
$(this).hide(); |
|
} |
|
}); |
|
elm.fancyTable.pages = Math.ceil(elm.fancyTable.matches/elm.fancyTable.perPage); |
|
if(settings.pagination){ |
|
var paginationElement = (elm.fancyTable.paginationElement) ? $(elm.fancyTable.paginationElement) : $(elm).find(".pag"); |
|
paginationElement.empty(); |
|
for(var n=1; n<=elm.fancyTable.pages; n++){ |
|
if(n==1 || (n>(elm.fancyTable.page-(settings.pagClosest+1)) && n<(elm.fancyTable.page+(settings.pagClosest+1))) || n==elm.fancyTable.pages){ |
|
var a = $("<a>",{ |
|
html:n, |
|
"data-n": n, |
|
style:"margin:0 px;padding:0 px;font-size: 10px;", |
|
class:settings.paginationClass+" "+((n==elm.fancyTable.page)?settings.paginationClassActive:"") |
|
}).css("cursor","pointer").bind("click",function(){ |
|
elm.fancyTable.page = $(this).data("n"); |
|
instance.tableUpdate(elm); |
|
}); |
|
if(n==elm.fancyTable.pages && elm.fancyTable.page<(elm.fancyTable.pages-settings.pagClosest-1)){ |
|
paginationElement.append($("<span>...</span>")); |
|
} |
|
paginationElement.append(a); |
|
if(n==1 && elm.fancyTable.page>settings.pagClosest+2){ |
|
paginationElement.append($("<span>...</span>")); |
|
} |
|
} |
|
} |
|
} |
|
settings.onUpdate.call(this,elm); |
|
}; |
|
this.isSearchMatch = function(data, search){ |
|
if(!settings.matchCase){ data=data.toUpperCase(); search = search.toUpperCase(); } |
|
if(settings.exactMatch == "auto" && search.match(/^".*?"$/)){ |
|
// Exact match due to "quoted" value |
|
search = search.substring(1,search.length-1); |
|
return (data==search); |
|
} else if(settings.exactMatch == "auto" && search.replace(/\s+/g,"").match(/^[<>]=?/)){ |
|
// Less < or greater > than |
|
var comp = search.replace(/\s+/g,"").match(/^[<>]=?/)[0]; |
|
var val = search.replace(/\s+/g,"").substring(comp.length); |
|
return ((comp == '>' && data*1 > val*1) || (comp == '<' && data*1 < val*1) || (comp == '>=' && data*1 >= val*1) || (comp == '<=' && data*1 <= val*1)) |
|
} else if(settings.exactMatch == "auto" && search.replace(/\s+/g,"").match(/^.+(\.\.|-).+$/)){ |
|
// Intervall 10..20 or 10-20 |
|
var arr = search.replace(/\s+/g,"").split(/\.\.|-/); |
|
return (data*1 >= arr[0]*1 && data*1 <= arr[1]*1); |
|
} |
|
try { |
|
return (settings.exactMatch === true) ? (data==search) : (new RegExp(search).test(data)); |
|
} |
|
catch { |
|
return false; |
|
} |
|
}; |
|
this.reinit = function(){ |
|
$(this).each(function(){ |
|
$(this).find("th a").contents().unwrap(); |
|
$(this).find("tr.fancySearchRow").remove(); |
|
}); |
|
$(this).fancyTable(this.settings); |
|
}; |
|
this.tableSort = function (elm) { |
|
if(typeof elm.fancyTable.sortColumn !== "undefined" && elm.fancyTable.sortColumn < elm.fancyTable.nColumns){ |
|
var iElm = 0; |
|
$(elm).find("thead th").each(function(){ |
|
$(this).attr("aria-sort", |
|
(iElm == elm.fancyTable.sortColumn) ? |
|
( (elm.fancyTable.sortOrder == 1) ? "ascending" : (elm.fancyTable.sortOrder == -1) ? "descending" : "other" ) |
|
: null // "none" // Remove the attribute instead of setting to "none" to avoid spamming screen readers. |
|
); |
|
iElm++; |
|
}); |
|
$(elm).find("thead th div.sortArrow").each(function(){ |
|
$(this).remove(); |
|
}); |
|
var sortArrow = $("<div>",{"class":"sortArrow"}).css({"margin":"0.1em","display":"inline-block","width":0,"height":0,"border-left":"0.4em solid transparent","border-right":"0.4em solid transparent"}); |
|
sortArrow.css( |
|
(elm.fancyTable.sortOrder>0) ? |
|
{"border-top":"0.4em solid #000"} : |
|
{"border-bottom":"0.4em solid #000"} |
|
); |
|
$(elm).find("thead th a").eq(elm.fancyTable.sortColumn).append(sortArrow); |
|
var rows = $(elm).find("tbody tr").toArray().sort( |
|
function(a, b) { |
|
var elma = $(a).find("td").eq(elm.fancyTable.sortColumn); |
|
var elmb = $(b).find("td").eq(elm.fancyTable.sortColumn); |
|
var cmpa = $(elma).attr("data-sortvalue") ? $(elma).data("sortvalue") : elma.html(); |
|
var cmpb = $(elmb).attr("data-sortvalue") ? $(elmb).data("sortvalue") : elmb.html(); |
|
if(elm.fancyTable.sortAs[elm.fancyTable.sortColumn] == 'case-insensitive') { |
|
cmpa = cmpa.toLowerCase(); |
|
cmpb = cmpb.toLowerCase(); |
|
} |
|
return settings.sortFunction.call(this,cmpa,cmpb,elm.fancyTable,a,b); |
|
} |
|
); |
|
$(rows).each(function(index) { |
|
elm.fancyTable.rowSortOrder[$(this).data("rowid")] = index; |
|
}); |
|
$(elm).find("tbody").empty().append(rows); |
|
} |
|
}; |
|
this.each(function() { |
|
if($(this).prop("tagName")!=="TABLE"){ |
|
console.warn("fancyTable: Element is not a table."); |
|
return true; |
|
} |
|
var elm = this; |
|
elm.fancyTable = { |
|
nColumns: $(elm).find("td").first().parent().find("td").length, |
|
nRows : $(this).find("tbody tr").length, |
|
perPage : settings.perPage, |
|
page : 1, |
|
pages : 0, |
|
matches : 0, |
|
searchArr : [], |
|
search : "", |
|
sortColumn : settings.sortColumn, |
|
sortOrder : (typeof settings.sortOrder === "undefined") ? 1 : (new RegExp("desc","i").test(settings.sortOrder) || settings.sortOrder == -1) ? -1 : 1, |
|
sortAs:[], // null, numeric or case-insensitive |
|
paginationElement : settings.paginationElement |
|
}; |
|
elm.fancyTable.rowSortOrder = new Array(elm.fancyTable.nRows); |
|
if($(elm).find("tbody").length==0){ |
|
var content = $(elm).html(); |
|
$(elm).empty(); |
|
$(elm).append("<tbody>").append($(content)); |
|
} |
|
if($(elm).find("thead").length==0){ |
|
$(elm).prepend($("<thead>")); |
|
// Maybe add generated headers at some point |
|
//var c=$(elm).find("tr").first().find("td").length; |
|
//for(var n=0; n<c; n++){ |
|
// $(elm).find("thead").append($("<th></th>")); |
|
//} |
|
} |
|
$(elm).find("tbody tr").each(function(index) { |
|
// $(this).attr("data-rowid", index); |
|
$(this).data("rowid", index); |
|
}); |
|
if(settings.sortable){ |
|
var nAElm=0; |
|
$(elm).find("thead th").each(function() { |
|
elm.fancyTable.sortAs.push( |
|
($(this).data('sortas')=='numeric') ? 'numeric' : |
|
($(this).data('sortas')=='case-insensitive') ? 'case-insensitive' : |
|
null |
|
); |
|
var content = $(this).html(); |
|
var a = $("<a>",{ |
|
href: "#", |
|
"aria-label": "Sort by " + $(this).text(), |
|
html:content, |
|
"data-n": nAElm, |
|
class:"" |
|
}).css({"cursor":"pointer","color":"inherit","text-decoration":"none","white-space":"nowrap"}).bind("click",function(){ |
|
if(elm.fancyTable.sortColumn == $(this).data("n")){ |
|
elm.fancyTable.sortOrder=-elm.fancyTable.sortOrder; |
|
} else { |
|
elm.fancyTable.sortOrder=1; |
|
} |
|
elm.fancyTable.sortColumn = $(this).data("n"); |
|
instance.tableSort(elm); |
|
instance.tableUpdate(elm); |
|
return false; |
|
}); |
|
$(this).empty(); |
|
$(this).append(a); |
|
nAElm++; |
|
}); |
|
} |
|
if(settings.searchable){ |
|
var searchHeader = $("<tr>").addClass("fancySearchRow"); |
|
if(settings.globalSearch){ |
|
var searchField = $("<input>",{ |
|
"aria-label": "Search table", |
|
"placeholder": settings.inputPlaceholder, |
|
style:"width:100%;box-sizing:border-box;"+settings.inputStyle |
|
}).bind("change paste keyup",function(){ |
|
elm.fancyTable.search = $(this).val(); |
|
elm.fancyTable.page = 1; |
|
instance.tableUpdate(elm); |
|
}); |
|
var th = $("<th>",{ style:"padding:2px;" }).attr("colspan",elm.fancyTable.nColumns); |
|
$(searchField).appendTo($(th)); |
|
$(th).appendTo($(searchHeader)); |
|
} else { |
|
var nInputElm=0; |
|
$(elm).find("td").first().parent().find("td").each(function() { |
|
elm.fancyTable.searchArr.push(""); |
|
var searchField = $("<input>",{ |
|
"aria-label": "Search column", |
|
"data-n": nInputElm, |
|
"placeholder": settings.inputPlaceholder, |
|
style:"width:100%;box-sizing:border-box;"+settings.inputStyle |
|
}).bind("change paste keyup",function(){ |
|
elm.fancyTable.searchArr[$(this).data("n")] = $(this).val(); |
|
elm.fancyTable.page = 1; |
|
instance.tableUpdate(elm); |
|
}); |
|
var th = $("<th>",{ style:"padding:2px;" }); |
|
$(searchField).appendTo($(th)); |
|
$(th).appendTo($(searchHeader)); |
|
nInputElm++; |
|
}); |
|
} |
|
searchHeader.appendTo($(elm).find("thead")); |
|
} |
|
// Sort |
|
instance.tableSort(elm); |
|
if(settings.pagination && !settings.paginationElement){ |
|
$(elm).find("tfoot").remove(); |
|
$(elm).append($("<tfoot><tr></tr></tfoot>")); |
|
$(elm).find("tfoot tr").append($("<td class='pag' style='padding: 0px;font-size: 10px;margin:0px;'></td>",{ }).attr("colspan",elm.fancyTable.nColumns)); |
|
} |
|
instance.tableUpdate(elm); |
|
settings.onInit.call(this,elm); |
|
}); |
|
return this; |
|
}; |
|
}(jQuery));
|
|
|