├── .eslintrc.js ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── dist └── fancyTable.min.js ├── example └── index.html ├── package.json └── src └── fancyTable.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true, 5 | "jquery": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 13 10 | } 11 | }; -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 99 8 | 9 | - package-ecosystem: npm 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | open-pull-requests-limit: 99 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches-ignore: 6 | - "dependabot/**" 7 | pull_request: 8 | 9 | jobs: 10 | run: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Clone repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Set up Node.js 18 | uses: actions/setup-node@v4 19 | with: 20 | node-version: lts/* 21 | 22 | - name: Install npm dependencies 23 | run: npm install 24 | 25 | - name: Run tests 26 | run: npm test 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('package.json'), 4 | uglify: { 5 | options: { 6 | banner: '/*!\n'+ 7 | ' * jQuery fancyTable plugin v<%= pkg.version %>\n'+ 8 | ' * https://github.com/myspace-nu\n'+ 9 | ' *\n'+ 10 | ' * Copyright 2018 Johan Johansson\n'+ 11 | ' * Released under the MIT license\n'+ 12 | ' */\n' 13 | }, 14 | build: { 15 | src: 'src/fancyTable.js', 16 | dest: 'dist/fancyTable.min.js' 17 | } 18 | }, 19 | eslint: { 20 | target: ['src/fancyTable.js'] 21 | } 22 | }); 23 | grunt.loadNpmTasks('grunt-contrib-uglify'); 24 | grunt.loadNpmTasks('grunt-eslint'); 25 | grunt.registerTask('default', ['eslint', 'uglify']); 26 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Johan Johansson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery.fancyTable 2 | 3 | A jQuery plugin for making html tables searchable and sortable with pagination. 4 | 5 | [![GitHub](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/myspace-nu/jquery.fancyTable/blob/master/LICENSE) 6 | [![Code size](https://img.shields.io/github/languages/code-size/myspace-nu/jquery.fancyTable)](https://github.com/myspace-nu/jquery.fancyTable) 7 | [![Issues](https://img.shields.io/github/issues-raw/myspace-nu/jquery.fancyTable)](https://github.com/myspace-nu/jquery.fancyTable/issues) 8 | 9 | ## Live demo 10 | 11 | See a live demo on [CodePen](https://codepen.io/myspace-nu/full/ZVEKyR) 12 | 13 | ## Installation 14 | 15 | Using npm 16 | 17 | npm install jquery.fancytable --save 18 | 19 | Using CDN 20 | 21 | 22 | 23 | Or manually by including the script *after* the jQuery library 24 | 25 | 26 | 27 | ## Usage 28 | 29 | 39 | 40 | ## Options 41 | 42 | **exactMatch** - Use exact match. If set to true, search will not match substrings such as "cat" in "catastrophic". If set to "auto", search will be exact if the search term is enclosed in quotation marks. 43 | 44 | exactMatch: true 45 | 46 | *Default: false* 47 | 48 | **globalSearch** - Use global search for all columns 49 | 50 | globalSearch: false 51 | 52 | *Default: false* 53 | 54 | **globalSearchExcludeColumns** - Defines a number of columns to exclude from the global search. 55 | 56 | globalSearchExcludeColumns: [2,5] // Exclude 2nd and 5th column. 57 | 58 | *Default: undefined* 59 | 60 | **inputPlaceholder** - Placeholder to use for <input> 61 | 62 | inputPlaceholder: 'Sök...' 63 | 64 | *Default: 'Search...'* 65 | 66 | **inputStyle** - Style attributes to use for <input> 67 | 68 | inputStyle: 'color:black;' 69 | 70 | *Default: ''* 71 | 72 | **localeCompare** - Use [localeCompare](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare) when sorting 73 | 74 | localeCompare: true 75 | 76 | *Default: false* 77 | 78 | **matchCase** - Use case sensitive search 79 | 80 | matchCase: true 81 | 82 | *Default: false* 83 | 84 | **onInit** - Function called after initialization 85 | 86 | onInit:function(){ 87 | console.log({ element:this }); 88 | } 89 | 90 | **onUpdate** - Function called after each update (sort and search) 91 | 92 | onUpdate:function(){ 93 | console.log({ element:this }); 94 | } 95 | 96 | **beforeUpdate** - Function called before each update (sort and search) 97 | 98 | beforeUpdate:function(){ 99 | console.log({ element:this }); 100 | } 101 | 102 | **pagination** - Use pagination or not 103 | 104 | pagination: true 105 | 106 | *Default: false* 107 | 108 | **paginationClass** - CSS class to use for pagination buttons 109 | 110 | paginationClass: 'btn btn-primary' 111 | 112 | *Default: 'btn btn-light'* 113 | 114 | **paginationClassActive** - CSS class to use for active pagination buttons 115 | 116 | paginationClassActive: 'someClass' 117 | 118 | *Default: 'active'* 119 | 120 | **paginationElement** - Selector for element to place pagination controls in. 121 | 122 | paginationElement: '#someElement' 123 | 124 | *Default: undefined* - Undefined will create a (remove any existing) table footer to place controls in. 125 | 126 | **pagClosest** - Create pagination buttons for tbe n closest pages 127 | 128 | pagClosest: 5 129 | 130 | *Default: 3* 131 | 132 | **perPage** - Rows per page when using pagination 133 | 134 | perPage: 5 135 | 136 | *Default: 10* 137 | 138 | **searchable** - Should the table be searchable or not 139 | 140 | searchable: false 141 | 142 | *Default: true* 143 | 144 | **sortable** - Should the table be sortable or not 145 | 146 | sortable: false 147 | 148 | *Default: true* 149 | 150 | **sortColumn** - Column number for initial sorting 151 | 152 | sortColumn: 5 153 | 154 | *Default: undefined* 155 | 156 | **sortFunction** - Function for custom sorting 157 | 158 | sortFunction: function(a, b, fancyTableObject, rowA, rowB){ 159 | if(a==b && rowA && rowB){ 160 | return(fancyTableObject.rowSortOrder[$(rowA).data("rowid")] > fancyTableObject.rowSortOrder[$(rowB).data("rowid")]); 161 | } 162 | if(fancyTableObject.sortAs[fancyTableObject.sortColumn] == 'numeric'){ 163 | return( 164 | (fancyTableObject.sortOrder>0) ? (parseFloat(a)||0)-(parseFloat(b)||0) : (parseFloat(b)||0)-(parseFloat(a)||0) 165 | ); 166 | } 167 | if (fancyTableObject.sortAs[fancyTableObject.sortColumn] == 'datetime') { 168 | return (fancyTableObject.sortOrder > 0) ? (Date.parse(a) - Date.parse(b)) : (Date.parse(b) - Date.parse(a)); 169 | } else { 170 | return((ab)?fancyTableObject.sortOrder:0); 171 | } 172 | } 173 | 174 | **sortOrder** - Initial sort order 175 | 176 | sortOrder: 'descending' // Valid values are 'desc', 'descending', 'asc', 'ascending', -1 (descending) and 1 (ascending) 177 | 178 | *Default: 'ascending'* 179 | 180 | ## Data attributes 181 | 182 | **data-sortas="numeric"** - Used in the table header element to define that values in the column should be sorted in numerical order (..., 8, 9, 10, 10.1, 12, ...) 183 | 184 | 185 | 186 | **data-sortas="datetime"** - Used in the table header element to define that values in the column should be sorted in chronological order (..., Jan 1 2023, 2/1/2023, "March 19, 2023 4:15 PM", , 5/5/23 10:00, 5/5/23 18:03:21 -0600 (Mountain Daylight Time), ...) 187 | 188 | 189 | 190 | **data-sortas="case-insensitive"** - Used in the table header element to define that values in the column should be sorted case insensitive (a, B, c, D, ...) 191 | 192 | 193 | 194 | **data-sortas="none"** - This column should not be sortable 195 | 196 | 197 | 198 | **data-sortvalue="``"** - Used in the table data element to define an alternate value to be used when sorting 199 | 200 | 1 201 | Two 202 | 3 203 | 204 | Ghost 205 | The Fox and the Hound 206 | I Know What You Did Last Summer 207 | 208 | 209 | ### Author: [Johan Johansson](https://github.com/myspace-nu) 210 | -------------------------------------------------------------------------------- /dist/fancyTable.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery fancyTable plugin v1.0.36 3 | * https://github.com/myspace-nu 4 | * 5 | * Copyright 2018 Johan Johansson 6 | * Released under the MIT license 7 | */ 8 | 9 | !function(i){i.fn.fancyTable=function(a){var s=i.extend({inputStyle:"",inputPlaceholder:"Search...",pagination:!1,paginationClass:"btn btn-light",paginationClassActive:"active",pagClosest:3,perPage:10,sortable:!0,searchable:!0,matchCase:!1,exactMatch:!1,localeCompare:!1,onInit:function(){},beforeUpdate:function(){},onUpdate:function(){},sortFunction:function(a,e,t,n,r){return a==e&&n&&r?t.rowSortOrder[i(n).data("rowid")]>t.rowSortOrder[i(r).data("rowid")]:"numeric"==t.sortAs[t.sortColumn]?0Date.parse(a)||0),0n.fancyTable.perPage*(n.fancyTable.page-1)&&n.fancyTable.matches<=n.fancyTable.perPage*n.fancyTable.page?i(this).show():i(this).hide()):i(this).hide()}),n.fancyTable.pages=Math.ceil(n.fancyTable.matches/n.fancyTable.perPage),s.pagination){var a=n.fancyTable.paginationElement?i(n.fancyTable.paginationElement):i(n).find(".pag");a.empty();for(var e,t=1;t<=n.fancyTable.pages;t++)(1==t||t>n.fancyTable.page-(s.pagClosest+1)&&t",{html:t,"data-n":t,style:"margin:0.2em",class:s.paginationClass+" "+(t==n.fancyTable.page?s.paginationClassActive:"")}).css("cursor","pointer").bind("click",function(){n.fancyTable.page=i(this).data("n"),l.tableUpdate(n)}),t==n.fancyTable.pages&&n.fancyTable.page...")),a.append(e),1==t&&n.fancyTable.page>s.pagClosest+2&&a.append(i("...")))}s.onUpdate.call(this,n)},this.isSearchMatch=function(a,e){if(s.matchCase||(a=a.toUpperCase(),e=e.toUpperCase()),"auto"==s.exactMatch&&e.match(/^".*?"$/))return a==(e=e.substring(1,e.length-1));if("auto"==s.exactMatch&&e.replace(/\s+/g,"").match(/^[<>]=?/)){var t=e.replace(/\s+/g,"").match(/^[<>]=?/)[0],n=e.replace(/\s+/g,"").substring(t.length);return">"==t&&+n<+a||"<"==t&&+a<+n||">="==t&&+n<=+a||"<="==t&&+a<=+n}if("auto"==s.exactMatch&&e.replace(/\s+/g,"").match(/^.+(\.\.|-).+$/)){n=e.replace(/\s+/g,"").split(/\.\.|-/);return+a>=+n[0]&&+a<=+n[1]}try{return!0===s.exactMatch?a==e:new RegExp(e).test(a)}catch{return!1}},this.reinit=function(){i(this).each(function(){i(this).find("th a").contents().unwrap(),i(this).find("tr.fancySearchRow").remove()}),i(this).fancyTable(this.settings)},this.tableSort=function(r){var a,e;void 0!==r.fancyTable.sortColumn&&r.fancyTable.sortColumn",{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"})).css(0").append(i(n))),0==i(o).find("thead").length&&i(o).prepend(i("")),i(o).find("tbody tr").each(function(a){i(this).data("rowid",a)}),s.sortable&&(e=0,i(o).find("thead th").each(function(){var a;o.fancyTable.sortAs.push(i(this).data("sortas")),"none"!=i(this).data("sortas")&&(a=i(this).html(),a=i("",{href:"#","aria-label":"Sort by "+i(this).text(),html:a,"data-n":e,class:""}).css({cursor:"pointer",color:"inherit","text-decoration":"none","white-space":"nowrap"}).bind("click",function(){return o.fancyTable.sortColumn==i(this).data("n")?o.fancyTable.sortOrder=-o.fancyTable.sortOrder:o.fancyTable.sortOrder=1,o.fancyTable.sortColumn=i(this).data("n"),l.tableSort(o),l.tableUpdate(o),!1}),i(this).empty(),i(this).append(a)),e++})),s.searchable&&(t=i("").addClass("fancySearchRow"),s.globalSearch?(a=i("",{"aria-label":"Search table",placeholder:s.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+s.inputStyle}).bind("change paste keyup",function(){o.fancyTable.search=i(this).val(),o.fancyTable.page=1,l.tableUpdate(o)}),n=i("",{style:"padding:2px;"}).attr("colspan",o.fancyTable.nColumns),i(a).appendTo(i(n)),i(n).appendTo(i(t))):(r=0,i(o).find("td").first().parent().find("td").each(function(){o.fancyTable.searchArr.push("");var a=i("",{"aria-label":"Search column","data-n":r,placeholder:s.inputPlaceholder,style:"width:100%;box-sizing:border-box;"+s.inputStyle}).bind("change paste keyup",function(){o.fancyTable.searchArr[i(this).data("n")]=i(this).val(),o.fancyTable.page=1,l.tableUpdate(o)}),e=i("",{style:"padding:2px;"});i(a).appendTo(i(e)),i(e).appendTo(i(t)),r++})),t.appendTo(i(o).find("thead"))),l.tableSort(o),s.pagination&&!s.paginationElement&&(i(o).find("tfoot").remove(),i(o).append(i("")),i(o).find("tfoot tr").append(i("",{}).attr("colspan",o.fancyTable.nColumns))),l.tableUpdate(o),s.onInit.call(this,o)}),this}}(jQuery); -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Sample 7 | 8 | 9 | 10 |
11 |

Table with sortable headings and global search

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 |
Col ACol BCol C
23 |

24 | 25 |

26 | 27 |

Simple table with column search

28 | 29 |
30 | 31 |

Sort by nearest city

32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
CityCoordinatesDistance
Lindesberg@59.5994689,15.2132325
Tokyo@35.6708364,139.7532926
Sydney@-33.8981029,151.1612396
Stockholm@59.3275993,18.0524296
New York@40.7026091,-74.1197629
Rio de Janeiro@-22.9097071,-43.1925625
49 |
50 | 51 | or, find by Swedish zip code 52 |
53 | 58 | 59 |

Case sensitivity, numeric values and dates

60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
Case sensitive text valuesCase insensitive text valuesNumeric valuesDates
bbbbbbTwoJan 1 2023
AAAAAA35/5/23 10:00
DDDDDD-12/1/2023
gggGGGXVI2023-04-05 12:00:00+0100
eeeEEEZero2023-03-12T18:30:00Z
cccccc104 Dec 2023 00:12:00 GMT
fFfFfFTenFeb 12 2023 11:00
114 |
115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery.fancytable", 3 | "version": "1.0.36", 4 | "description": "A jQuery plugin for making html tables searchable and sortable with pagination.", 5 | "main": "dist/fancyTable.min.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "test": "grunt" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/myspace-nu/jquery.fancyTable.git" 15 | }, 16 | "keywords": [ 17 | "jquery", 18 | "table", 19 | "searchable", 20 | "sortable", 21 | "pagination" 22 | ], 23 | "author": "Johan Johansson", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/myspace-nu/jquery.fancyTable/issues" 27 | }, 28 | "homepage": "https://github.com/myspace-nu/jquery.fancyTable#readme", 29 | "devDependencies": { 30 | "eslint": "^9.0.0", 31 | "grunt": "^1.3.0", 32 | "grunt-contrib-uglify": "^5.0.0", 33 | "grunt-eslint": "^24.0.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/fancyTable.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery fancyTable plugin 3 | * https://github.com/myspace-nu 4 | * 5 | * Copyright 2018 Johan Johansson 6 | * Released under the MIT license 7 | */ 8 | (function($) { 9 | $.fn.fancyTable = function(options) { 10 | var settings = $.extend({ 11 | inputStyle: "", 12 | inputPlaceholder: "Search...", 13 | pagination: false, 14 | paginationClass: "btn btn-light", 15 | paginationClassActive: "active", 16 | pagClosest: 3, 17 | perPage: 10, 18 | sortable: true, 19 | searchable: true, 20 | matchCase: false, 21 | exactMatch: false, 22 | localeCompare: false, 23 | onInit: function(){ }, 24 | beforeUpdate: function(){ }, 25 | onUpdate: function(){ }, 26 | sortFunction: function(a, b, fancyTableObject, rowA, rowB){ 27 | if(a==b && rowA && rowB){ 28 | // If sort values are of equal priority, sort by last order 29 | return(fancyTableObject.rowSortOrder[$(rowA).data("rowid")] > fancyTableObject.rowSortOrder[$(rowB).data("rowid")]); 30 | } 31 | if(fancyTableObject.sortAs[fancyTableObject.sortColumn] == 'numeric'){ 32 | return( 33 | (fancyTableObject.sortOrder>0) ? (parseFloat(a)||0)-(parseFloat(b)||0) : (parseFloat(b)||0)-(parseFloat(a)||0) // NaN values will be sorted as 0 34 | ); 35 | } 36 | if (fancyTableObject.sortAs[fancyTableObject.sortColumn] == 'datetime') { 37 | [a, b] = [a, b].map(x => { 38 | return Date.parse(x) || 0; // NaN values will be sorted as epoch (1/1/1970 00:00:00 UTC) 39 | }); 40 | return (fancyTableObject.sortOrder > 0) ? (a - b) : (b - a); 41 | } 42 | else { 43 | if(settings.localeCompare){ 44 | return((a.localeCompare(b)<0)?-fancyTableObject.sortOrder:(a.localeCompare(b)>0)?fancyTableObject.sortOrder:0); 45 | } else { 46 | return((ab)?fancyTableObject.sortOrder:0); 47 | } 48 | } 49 | }, 50 | testing: false 51 | }, options); 52 | var instance = this; 53 | this.settings = settings; 54 | this.tableUpdate = function (elm) { 55 | settings.beforeUpdate.call(this,elm); 56 | elm.fancyTable.matches = 0; 57 | $(elm).find("tbody tr").each(function() { 58 | var n=0; 59 | var match = true; 60 | var globalMatch = false; 61 | $(this).find("td").each(function() { 62 | if(!settings.globalSearch && elm.fancyTable.searchArr[n] && !(instance.isSearchMatch($(this).html(),elm.fancyTable.searchArr[n]) )){ 63 | match = false; 64 | } else if(settings.globalSearch && (!elm.fancyTable.search || (instance.isSearchMatch($(this).html(),elm.fancyTable.search) ))){ 65 | if(!Array.isArray(settings.globalSearchExcludeColumns) || !settings.globalSearchExcludeColumns.includes(n+1)){ 66 | globalMatch = true; 67 | } 68 | } 69 | n++; 70 | }); 71 | if((settings.globalSearch && globalMatch) || (!settings.globalSearch && match)){ 72 | elm.fancyTable.matches++ 73 | if(!settings.pagination || (elm.fancyTable.matches>(elm.fancyTable.perPage*(elm.fancyTable.page-1)) && elm.fancyTable.matches<=(elm.fancyTable.perPage*elm.fancyTable.page))){ 74 | $(this).show(); 75 | } else { 76 | $(this).hide(); 77 | } 78 | } else { 79 | $(this).hide(); 80 | } 81 | }); 82 | elm.fancyTable.pages = Math.ceil(elm.fancyTable.matches/elm.fancyTable.perPage); 83 | if(settings.pagination){ 84 | var paginationElement = (elm.fancyTable.paginationElement) ? $(elm.fancyTable.paginationElement) : $(elm).find(".pag"); 85 | paginationElement.empty(); 86 | for(var n=1; n<=elm.fancyTable.pages; n++){ 87 | if(n==1 || (n>(elm.fancyTable.page-(settings.pagClosest+1)) && n<(elm.fancyTable.page+(settings.pagClosest+1))) || n==elm.fancyTable.pages){ 88 | var a = $("
",{ 89 | html:n, 90 | "data-n": n, 91 | style:"margin:0.2em", 92 | class:settings.paginationClass+" "+((n==elm.fancyTable.page)?settings.paginationClassActive:"") 93 | }).css("cursor","pointer").bind("click",function(){ 94 | elm.fancyTable.page = $(this).data("n"); 95 | instance.tableUpdate(elm); 96 | }); 97 | if(n==elm.fancyTable.pages && elm.fancyTable.page<(elm.fancyTable.pages-settings.pagClosest-1)){ 98 | paginationElement.append($("...")); 99 | } 100 | paginationElement.append(a); 101 | if(n==1 && elm.fancyTable.page>settings.pagClosest+2){ 102 | paginationElement.append($("...")); 103 | } 104 | } 105 | } 106 | } 107 | settings.onUpdate.call(this,elm); 108 | }; 109 | this.isSearchMatch = function(data, search){ 110 | if(!settings.matchCase){ data=data.toUpperCase(); search = search.toUpperCase(); } 111 | if(settings.exactMatch == "auto" && search.match(/^".*?"$/)){ 112 | // Exact match due to "quoted" value 113 | search = search.substring(1,search.length-1); 114 | return (data==search); 115 | } else if(settings.exactMatch == "auto" && search.replace(/\s+/g,"").match(/^[<>]=?/)){ 116 | // Less < or greater > than 117 | var comp = search.replace(/\s+/g,"").match(/^[<>]=?/)[0]; 118 | var val = search.replace(/\s+/g,"").substring(comp.length); 119 | return ((comp == '>' && data*1 > val*1) || (comp == '<' && data*1 < val*1) || (comp == '>=' && data*1 >= val*1) || (comp == '<=' && data*1 <= val*1)) 120 | } else if(settings.exactMatch == "auto" && search.replace(/\s+/g,"").match(/^.+(\.\.|-).+$/)){ 121 | // Intervall 10..20 or 10-20 122 | var arr = search.replace(/\s+/g,"").split(/\.\.|-/); 123 | return (data*1 >= arr[0]*1 && data*1 <= arr[1]*1); 124 | } 125 | try { 126 | return (settings.exactMatch === true) ? (data==search) : (new RegExp(search).test(data)); 127 | } 128 | catch { 129 | return false; 130 | } 131 | }; 132 | this.reinit = function(){ 133 | $(this).each(function(){ 134 | $(this).find("th a").contents().unwrap(); 135 | $(this).find("tr.fancySearchRow").remove(); 136 | }); 137 | $(this).fancyTable(this.settings); 138 | }; 139 | this.tableSort = function (elm) { 140 | if(typeof elm.fancyTable.sortColumn !== "undefined" && elm.fancyTable.sortColumn < elm.fancyTable.nColumns){ 141 | var iElm = 0; 142 | $(elm).find("thead th").each(function(){ 143 | $(this).attr("aria-sort", 144 | (iElm == elm.fancyTable.sortColumn) ? 145 | ( (elm.fancyTable.sortOrder == 1) ? "ascending" : (elm.fancyTable.sortOrder == -1) ? "descending" : "other" ) 146 | : null // "none" // Remove the attribute instead of setting to "none" to avoid spamming screen readers. 147 | ); 148 | iElm++; 149 | }); 150 | $(elm).find("thead th div.sortArrow").each(function(){ 151 | $(this).remove(); 152 | }); 153 | var sortArrow = $("
",{"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"}); 154 | sortArrow.css( 155 | (elm.fancyTable.sortOrder>0) ? 156 | {"border-top":"0.4em solid #000"} : 157 | {"border-bottom":"0.4em solid #000"} 158 | ); 159 | $(elm).find("thead th a").eq(elm.fancyTable.sortColumn).append(sortArrow); 160 | var rows = $(elm).find("tbody tr").toArray().sort( 161 | function(a, b) { 162 | var elma = $(a).find("td").eq(elm.fancyTable.sortColumn); 163 | var elmb = $(b).find("td").eq(elm.fancyTable.sortColumn); 164 | var cmpa = typeof $(elma).data('sortvalue') !== 'undefined' ? $(elma).data('sortvalue') : elma.html(); 165 | var cmpb = typeof $(elmb).data('sortvalue') !== 'undefined' ? $(elmb).data('sortvalue') : elmb.html(); 166 | if(elm.fancyTable.sortAs[elm.fancyTable.sortColumn] == 'case-insensitive') { 167 | cmpa = cmpa.toLowerCase(); 168 | cmpb = cmpb.toLowerCase(); 169 | } 170 | return settings.sortFunction.call(this,cmpa,cmpb,elm.fancyTable,a,b); 171 | } 172 | ); 173 | $(rows).each(function(index) { 174 | elm.fancyTable.rowSortOrder[$(this).data("rowid")] = index; 175 | }); 176 | $(elm).find("tbody").empty().append(rows); 177 | } 178 | }; 179 | this.each(function() { 180 | if($(this).prop("tagName")!=="TABLE"){ 181 | console.warn("fancyTable: Element is not a table."); 182 | return true; 183 | } 184 | var elm = this; 185 | elm.fancyTable = { 186 | nColumns: $(elm).find("td").first().parent().find("td").length, 187 | nRows : $(this).find("tbody tr").length, 188 | perPage : settings.perPage, 189 | page : 1, 190 | pages : 0, 191 | matches : 0, 192 | searchArr : [], 193 | search : "", 194 | sortColumn : settings.sortColumn, 195 | sortOrder : (typeof settings.sortOrder === "undefined") ? 1 : (new RegExp("desc","i").test(settings.sortOrder) || settings.sortOrder == -1) ? -1 : 1, 196 | sortAs:[], // undefined, numeric, datetime, case-insensitive, none, or custom 197 | paginationElement : settings.paginationElement 198 | }; 199 | elm.fancyTable.rowSortOrder = new Array(elm.fancyTable.nRows); 200 | if($(elm).find("tbody").length==0){ 201 | var content = $(elm).html(); 202 | $(elm).empty(); 203 | $(elm).append("").append($(content)); 204 | } 205 | if($(elm).find("thead").length==0){ 206 | $(elm).prepend($("")); 207 | // Maybe add generated headers at some point 208 | //var c=$(elm).find("tr").first().find("td").length; 209 | //for(var n=0; n")); 211 | //} 212 | } 213 | $(elm).find("tbody tr").each(function(index) { 214 | // $(this).attr("data-rowid", index); 215 | $(this).data("rowid", index); 216 | }); 217 | if(settings.sortable){ 218 | var nAElm=0; 219 | $(elm).find("thead th").each(function() { 220 | elm.fancyTable.sortAs.push($(this).data('sortas')); 221 | if($(this).data('sortas') == 'none'){ nAElm++; return } 222 | var content = $(this).html(); 223 | var a = $("",{ 224 | href: "#", 225 | "aria-label": "Sort by " + $(this).text(), 226 | html:content, 227 | "data-n": nAElm, 228 | class:"" 229 | }).css({"cursor":"pointer","color":"inherit","text-decoration":"none","white-space":"nowrap"}).bind("click",function(){ 230 | if(elm.fancyTable.sortColumn == $(this).data("n")){ 231 | elm.fancyTable.sortOrder=-elm.fancyTable.sortOrder; 232 | } else { 233 | elm.fancyTable.sortOrder=1; 234 | } 235 | elm.fancyTable.sortColumn = $(this).data("n"); 236 | instance.tableSort(elm); 237 | instance.tableUpdate(elm); 238 | return false; 239 | }); 240 | $(this).empty(); 241 | $(this).append(a); 242 | nAElm++; 243 | }); 244 | } 245 | if(settings.searchable){ 246 | var searchHeader = $("").addClass("fancySearchRow"); 247 | if(settings.globalSearch){ 248 | var searchField = $("",{ 249 | "aria-label": "Search table", 250 | "placeholder": settings.inputPlaceholder, 251 | style:"width:100%;box-sizing:border-box;"+settings.inputStyle 252 | }).bind("change paste keyup",function(){ 253 | elm.fancyTable.search = $(this).val(); 254 | elm.fancyTable.page = 1; 255 | instance.tableUpdate(elm); 256 | }); 257 | var th = $("",{ style:"padding:2px;" }).attr("colspan",elm.fancyTable.nColumns); 258 | $(searchField).appendTo($(th)); 259 | $(th).appendTo($(searchHeader)); 260 | } else { 261 | var nInputElm=0; 262 | $(elm).find("td").first().parent().find("td").each(function() { 263 | elm.fancyTable.searchArr.push(""); 264 | var searchField = $("",{ 265 | "aria-label": "Search column", 266 | "data-n": nInputElm, 267 | "placeholder": settings.inputPlaceholder, 268 | style:"width:100%;box-sizing:border-box;"+settings.inputStyle 269 | }).bind("change paste keyup",function(){ 270 | elm.fancyTable.searchArr[$(this).data("n")] = $(this).val(); 271 | elm.fancyTable.page = 1; 272 | instance.tableUpdate(elm); 273 | }); 274 | var th = $("",{ style:"padding:2px;" }); 275 | $(searchField).appendTo($(th)); 276 | $(th).appendTo($(searchHeader)); 277 | nInputElm++; 278 | }); 279 | } 280 | searchHeader.appendTo($(elm).find("thead")); 281 | } 282 | // Sort 283 | instance.tableSort(elm); 284 | if(settings.pagination && !settings.paginationElement){ 285 | $(elm).find("tfoot").remove(); 286 | $(elm).append($("")); 287 | $(elm).find("tfoot tr").append($("",{ }).attr("colspan",elm.fancyTable.nColumns)); 288 | } 289 | instance.tableUpdate(elm); 290 | settings.onInit.call(this,elm); 291 | }); 292 | return this; 293 | }; 294 | }(jQuery)); 295 | --------------------------------------------------------------------------------