├── CHANGELOG ├── README.md ├── bower.json ├── example ├── basic.html ├── circular.html ├── easy-paging.html ├── endless.html ├── jquery.easy-paging.js ├── lock.html ├── onclick.html ├── slice.html ├── stepwidth.html └── table.html ├── jquery.paging.js ├── jquery.paging.min.js └── paging.jquery.json /CHANGELOG: -------------------------------------------------------------------------------- 1 | jQuery Paging Changelog 2 | 3 | 4 | 1.2.0 5 | * Improved locking mechanism (dropped onLock callback and added "lock" option) 6 | 7 | 1.1.1 8 | * Added equation derivation 9 | * Simplified code 10 | * Added easyPaging example plugin 11 | 12 | 1.1.0 13 | * onSelect shouldn't be called when $().length == 0 14 | * setOptions({perpage: }) should work as well 15 | * li/options should work as well -> Collect as string, not as DOM element 16 | * Simplified active state of last/first (Show only if the first page can not be accessed via the block) buttons 17 | 18 | 1.0.1 19 | * Fixed clicks on nested images in navigation 20 | 21 | 1.0.0 22 | * First stable release 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Pagination Plugin 2 | ==================== 3 | 4 | Description 5 | ----------- 6 | The jQuery Paging plugin aims to be as simple as possible by a native callback design, which in turn allows to design a pagination navigation in almost every feasible variation. 7 | 8 | Usage 9 | ----- 10 | Include the jquery.paging.min.js in your website and start working. There are no styles shipped with the library, it all depends on your needs and the library only does the link calculations and event management for you, plus a few extra things. 11 | 12 | In order to use the Paging plugin, you're done by defining the following simple snippet: 13 | 14 | ```javascript 15 | $(".pagination").paging(1337, { // make 1337 elements navigatable 16 | format: '[< ncnnn! >]', // define how the navigation should look like and in which order onFormat() get's called 17 | perpage: 10, // show 10 elements per page 18 | lapping: 0, // don't overlap pages for the moment 19 | page: 1, // start at page, can also be "null" or negative 20 | onSelect: function (page) { 21 | // add code which gets executed when user selects a page, how about $.ajax() or $(...).slice()? 22 | console.log(this); 23 | }, 24 | onFormat: function (type) { // Gets called for each character of "format" and returns a HTML representation 25 | switch (type) { 26 | case 'block': // n and c 27 | return '' + this.value + ''; 28 | case 'next': // > 29 | return '>'; 30 | case 'prev': // < 31 | return '<'; 32 | case 'first': // [ 33 | return 'first'; 34 | case 'last': // ] 35 | return 'last'; 36 | } 37 | } 38 | }); 39 | ``` 40 | 41 | The strength of the library is that every parameter you would need is pre calculated and accessable via the `this`-object inside the callbacks. The most important part is the `nncnn`-block. 42 | 43 | Options 44 | ===== 45 | 46 | - perpage: The number of elements per page 47 | - page: The current page to start on 48 | - format: A format string, look at Format 49 | - lock: Boolean to lock/disable the pager for a while (see examples/lock.html) 50 | - lapping: The number of elements to overlap over the coming pages, see the [mathematical derivation](https://raw.org/article/derivation-of-pagination-calculation/). 51 | - circular: Boolean if next/prev buttons are allowed to go circular 52 | - stepwidth: Number of steps prev/next has to go. Default=1. =0 Gives blockwise steps 53 | - onClick: Raw callback to be called instead of the `onSelect` precedure (see examples/onclick.html) 54 | - onFormat: Called for every `format` directive. See Format 55 | - refresh: `timeout` and `url` to be called periodically for updates. 56 | - onRefresh: Callback to be called for every refresh interval. (see jquery.paging.js) 57 | 58 | 59 | onSelect Callback 60 | ===== 61 | 62 | Every onSelect callback gets a lot of pre-calculated information on the `this` object: 63 | 64 | - number: The number of elements, configured 65 | - lapping: The number of elements overlapping, configured 66 | - pages: Number of pages 67 | - perpage: Number of elements per page 68 | - page: Current page on 69 | - slice: Two element array with bounds to slice elements on the current page (see examples/slice.html) 70 | 71 | The return code of `onSelect` indicates if the link of the clicked format element should be followed (otherwise only the click-event is used) 72 | 73 | Format 74 | ====== 75 | The "format"-parameter defines how the layout should look like. The string is processed character by character from left to right. For each character, the onFormat-callback is called and a final output string is generated and applied to the selected container. 76 | 77 | n = number 78 | c = current 79 | 80 | A string "nncnn!" handles several definitions at once: How many digits to show? 5 by the length of the block. Where to show the currrent page in the set? Always at the beginning? "cnnnn". Always at the end? "nnc". Always in the middle? "nncnn". The exclamation mark in the initial example means that always 5 digits will be printed. All inactive elements have a "this.active" set to false. 81 | 82 | Additionally, there are other format-tokens like "<" and ">" for prev and next, "[" and "]" for first and last, "." and "-" for simple text replacements and "q" and "p" in order to build previous and next-blocks. A more of examples can be found on my blog (link below). 83 | 84 | 85 | Build your own paginator! 86 | ================= 87 | jQuery paging is just a small framework. You can use it as the calculation base of your own paginator. In the file *easy-paging.html* you'll find a small example plugin, which uses jQuery Paging to make a typical HTML based paginator work: 88 | ``` 89 |
    90 |
  1. Prev
  2. 91 |
  3. Page #n
  4. 92 |
  5. Page #n
  6. 93 |
  7. Page #c
  8. 94 |
  9. Page #n
  10. 95 |
  11. Page #n
  12. 96 |
  13. Page #n
  14. 97 |
  15. Page #n
  16. 98 |
  17. Next
  18. 99 |
100 | ``` 101 | 102 | ```javascript 103 | $("#paging").easyPaging(1000, { 104 | onSelect: function(page) { 105 | console.log("You are on page " + page + " and you will select elements "+(this.slice[0]+1) + "-" + this.slice[1]+"!!!"); 106 | } 107 | }); 108 | ``` 109 | 110 | 111 | Ajax Select Callback 112 | ==================== 113 | ```javascript 114 | function onSelectCB(page) { 115 | 116 | $.ajax({ 117 | "url": '/data.php?start=' + this.slice[0] + '&end=' + this.slice[1] + '&page=' + page, 118 | "success": function(data) { 119 | // content replace 120 | } 121 | }); 122 | } 123 | ``` 124 | 125 | Slice Select Callback 126 | ====================== 127 | ```javascript 128 | function onSelectCB(page) { 129 | 130 | var data = this.slice; 131 | 132 | content.slice(prev[0], prev[1]).css('display', 'none'); 133 | content.slice(data[0], data[1]).css('display', 'block'); 134 | 135 | prev = data; 136 | } 137 | ``` 138 | 139 | Using cookies 140 | ============= 141 | ```javascript 142 | $(".pagination").paging(1337, { 143 | format: '[< ncnnn! >]', 144 | perpage: 10, 145 | page: getCookie("current") || 1, 146 | onSelect: function (page) { 147 | setCookie("current", page) 148 | console.log(this); 149 | }, 150 | onFormat: onFormatCB 151 | }); 152 | ``` 153 | 154 | Lock the pager 155 | ============ 156 | It's sometimes necessary to lock the pagination, because you want to disable the navigation. You can use setOptions to disable the navigation like this: 157 | ```javascript 158 | var paging = $(".pagination").paging(1337, { 159 | format: '[< ncnnn! >]', 160 | onSelect: function (page) { 161 | 162 | // onSelect is called for locked pagers as well, but nothing happens, except this: 163 | if (page === 0) { 164 | console.log("Pager was clicked, while it is disabled!"); 165 | return; 166 | } 167 | // ... rest 168 | }, 169 | onFormat: onFormatCB 170 | }); 171 | 172 | // Lock the pager 173 | paging.setOptions({lock: true}); 174 | 175 | // Unlock the pager 176 | paging.setOptions({lock: false}); 177 | 178 | ``` 179 | 180 | 181 | Further examples and documentation 182 | ========================== 183 | For further details and code examples take a look at the demonstration and documentation page on: 184 | 185 | [jQuery Pagination](https://raw.org/article/jquery-pagination-revised/) 186 | 187 | Build 188 | ===== 189 | The library is aggressively size optimized and works best with Closure-Compiler Advanced mode. 190 | 191 | 192 | License 193 | ====== 194 | Copyright (c) 2013, [Robert Eisele](https://raw.org/) 195 | Dual licensed under the MIT or GPL Version 2 licenses. 196 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "paging", 3 | "main": "jquery.paging.js", 4 | "version": "1.3.1", 5 | "homepage": "http://www.xarg.org/2011/09/jquery-pagination-revised/", 6 | "description": "Probably the most advanced jQuery pagination plugin.", 7 | "keywords": [ 8 | "paging", 9 | "pagination", 10 | "pager", 11 | "navigation" 12 | ], 13 | "authors": [ 14 | "Robert Eisele (http://www.xarg.org/)" 15 | ], 16 | "dependencies": { 17 | "jquery": ">=1.5" 18 | }, 19 | "license": [ 20 | "MIT", 21 | "GPL" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/infusion/jQuery-Paging.git" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /example/basic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 49 | 50 | -------------------------------------------------------------------------------- /example/circular.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/easy-paging.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Easy-Paging Test 5 | 6 | 7 | 8 | 9 |
    10 |
  1. Prev
  2. 11 |
  3. Page #n
  4. 12 |
  5. Page #n
  6. 13 |
  7. Page #c
  8. 14 |
  9. Page #n
  10. 15 |
  11. Page #n
  12. 16 |
  13. Page #n
  14. 17 |
  15. Page #n
  16. 18 |
  17. Next
  18. 19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /example/endless.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /example/jquery.easy-paging.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license jQuery paging plugin v1.1.1 21/06/2014 3 | * http://www.xarg.org/2011/09/jquery-pagination-revised/ 4 | * 5 | * Copyright (c) 2011, Robert Eisele (robert@xarg.org) 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | **/ 8 | 9 | (function($) { 10 | 11 | $["fn"]["easyPaging"] = function(num, o) { 12 | 13 | if (!$["fn"]["paging"]) { 14 | return this; 15 | } 16 | 17 | // Normal Paging config 18 | var opts = { 19 | "perpage": 10, 20 | "elements": 0, 21 | "page": 1, 22 | "format": "", 23 | "lapping": 0, 24 | "onSelect": function() { 25 | } 26 | }; 27 | 28 | $["extend"](opts, o || {}); 29 | 30 | var $li = $("li", this); 31 | 32 | var masks = {}; 33 | 34 | $li.each(function(i) { 35 | 36 | if (0 === i) { 37 | masks.prev = this.innerHTML; 38 | opts.format += "<"; 39 | } else if (i + 1 === $li.length) { 40 | masks.next = this.innerHTML; 41 | opts.format += ">"; 42 | } else { 43 | masks[i] = this.innerHTML.replace(/#[nc]/, function(str) { 44 | opts["format"] += str.replace("#", ""); 45 | return "([...])"; 46 | }); 47 | } 48 | }); 49 | 50 | opts["onFormat"] = function(type) { 51 | 52 | var value = ""; 53 | 54 | switch (type) { 55 | case 'block': 56 | 57 | value = masks[this["pos"]].replace("([...])", this["value"]); 58 | 59 | if (!this['active']) 60 | return '
  • ' + value + '
  • '; 61 | if (this["page"] !== this["value"]) 62 | return '
  • ' + value + '
  • '; 63 | return '
  • ' + value + '
  • '; 64 | 65 | case 'next': 66 | case 'prev': 67 | if (!this['active']) 68 | return '
  • ' + masks[type] + '
  • '; 69 | return '
  • ' + masks[type] + '
  • '; 70 | } 71 | }; 72 | 73 | $(this)["paging"](num, opts); 74 | 75 | return this; 76 | }; 77 | 78 | }(jQuery)); 79 | -------------------------------------------------------------------------------- /example/lock.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /example/onclick.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 6 | 7 | 8 | 9 |
    10 | 11 | 12 | 13 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /example/slice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | jQuery Paging Test 6 | 7 | 8 | 9 | 10 |
    11 |
    Las Vegas

    37°C / 98.6°F

    Cairo

    35°C / 95°F

    Houston

    33°C / 91.4°F

    Madrid

    31°C / 87.8°F

    Athens

    30°C / 86°F

    Lisbon

    27°C / 80.6°F

    Rome

    27°C / 80.6°F

    Moscow

    23°C / 73.4°F

    Los Angeles

    22°C / 71.6°F

    San Francisco

    22°C / 71.6°F

    Lima

    19°C / 66.2°F

    Sydney

    18°C / 64.4°F

    Johannesburg

    17°C / 62.6°F

    Buenos Aires

    16° / 60.8°F

    12 |
    13 | 14 | 15 | 16 | 17 | 117 | 118 | -------------------------------------------------------------------------------- /example/stepwidth.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 45 | 46 | -------------------------------------------------------------------------------- /example/table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Paging Test 5 | 6 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 |
    38 | 39 | 42 |
    43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
    #abcd
    122 |

    P: Prime number, F: Fibonacci number, E: Perfect number, B: Power of two, T: Power of ten

    123 | 124 | 288 | 289 | -------------------------------------------------------------------------------- /jquery.paging.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license jQuery paging plugin v1.3.1 23/06/2014 3 | * http://www.xarg.org/2011/09/jquery-pagination-revised/ 4 | * 5 | * Copyright (c) 2011, Robert Eisele (robert@xarg.org) 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | **/ 8 | 9 | (function($, window, undefined) { 10 | 11 | 12 | $["fn"]["paging"] = function(number, opts) { 13 | 14 | var self = this, 15 | Paging = { 16 | 17 | "setOptions": function(opts) { 18 | 19 | var parseFormat = function(format) { 20 | 21 | var gndx = 0, group = 0, num = 1, res = { 22 | fstack: [], // format stack 23 | asterisk: 0, // asterisk? 24 | inactive: 0, // fill empty pages with inactives up to w? 25 | blockwide: 5, // width of number block 26 | current: 3, // position of current element in number block 27 | rights: 0, // num of rights 28 | lefts: 0 // num of lefts 29 | }, tok, pattern = /[*<>pq\[\]().-]|[nc]+!?/g; 30 | 31 | var known = { 32 | "[": "first", 33 | "]": "last", 34 | "<": "prev", 35 | ">": "next", 36 | "q": "left", 37 | "p": "right", 38 | "-": "fill", 39 | ".": "leap" 40 | }, count = {}; 41 | 42 | while ((tok = pattern["exec"](format))) { 43 | 44 | tok = "" + (tok); 45 | 46 | if (undefined === known[tok]) { 47 | 48 | if ("(" === tok) { 49 | group = ++gndx; 50 | } else if (")" === tok) { 51 | group = 0; 52 | } else if (num) { 53 | 54 | if ("*" === tok) { 55 | res.asterisk = 1; 56 | res.inactive = 0; 57 | } else { 58 | // number block is the only thing left here 59 | res.asterisk = 0; 60 | res.inactive = "!" === tok.charAt(tok.length - 1); 61 | res.blockwide = tok["length"] - res.inactive; 62 | if (!(res.current = 1 + tok.indexOf("c"))) { 63 | res.current = (1 + res.blockwide) >> 1; 64 | } 65 | } 66 | 67 | res.fstack.push({ 68 | ftype: "block", // type 69 | fgroup: 0, // group 70 | fpos: 0 // pos 71 | }); 72 | num = 0; 73 | } 74 | 75 | } else { 76 | 77 | res.fstack.push({ 78 | ftype: known[tok], // type 79 | fgroup: group, // group 80 | fpos: undefined === count[tok] ? count[tok] = 1 : ++count[tok] // pos 81 | }); 82 | 83 | if ("q" === tok) 84 | ++res.lefts; 85 | else if ("p" === tok) 86 | ++res.rights; 87 | } 88 | } 89 | return res; 90 | }; 91 | 92 | Paging.opts = $.extend(Paging.opts || { 93 | 94 | "lapping": 0, // number of elements overlap 95 | 96 | "perpage": 10, // number of elements per page 97 | 98 | "page": 1, // current page 99 | 100 | "stepwidth": 1, // stepwidth for prev/next -> if = 0 then blockwise steps 101 | 102 | "refresh": { 103 | "interval": 10, 104 | "url": null 105 | }, // refresh callback information 106 | 107 | "format": "", // visual format string 108 | 109 | "lock": false, // set to true, if you want to disable the pagination for a while. 110 | 111 | "circular": false, // set to true if you want the next/prev buttons go circular 112 | 113 | "onClick": null, // Alternative click handler to bypass onSelect mechanism 114 | 115 | "onFormat": function(type) { // callback for every format element 116 | 117 | /** EXAMPLE ** 118 | 119 | switch (type) { 120 | 121 | case 'block': 122 | 123 | if (!this.active) 124 | return '' + this.value + ''; 125 | else if (this.value != this.page) 126 | return '' + this.value + ''; 127 | return '' + this.value + ''; 128 | 129 | case 'right': 130 | case 'left': 131 | 132 | if (!this.active) { 133 | return ""; 134 | } 135 | return '' + this.value + ''; 136 | 137 | case 'next': 138 | 139 | if (this.active) { 140 | return ''; 141 | } 142 | return 'Next »'; 143 | 144 | case 'prev': 145 | 146 | if (this.active) { 147 | return ''; 148 | } 149 | return '« Previous'; 150 | 151 | case 'first': 152 | 153 | if (this.active) { 154 | return '|<'; 155 | } 156 | return '|<'; 157 | 158 | case 'last': 159 | 160 | if (this.active) { 161 | return ''; 162 | } 163 | return '>|'; 164 | 165 | case 'fill': 166 | if (this.active) { 167 | return "..."; 168 | } 169 | } 170 | return ""; // return nothing for missing branches 171 | 172 | **/ 173 | }, 174 | "onSelect": function(page) { // callback for page selection 175 | 176 | /** EXAMPLE SLICE ** 177 | 178 | var data = this.slice; 179 | 180 | content.slice(prev[0], prev[1]).css('display', 'none'); 181 | content.slice(data[0], data[1]).css('display', 'block'); 182 | 183 | prev = data; 184 | 185 | **/ 186 | 187 | 188 | /** EXAMPLE AJAX ** 189 | 190 | $.ajax({ 191 | "url": '/data.php?start=' + this.slice[0] + '&end=' + this.slice[1] + '&page=' + page, 192 | "success": function(data) { 193 | // content replace 194 | } 195 | }); 196 | 197 | **/ 198 | 199 | // Return code indicates if the link of the clicked format element should be followed (otherwise only the click-event is used) 200 | return true; 201 | }, 202 | "onRefresh": function(json) { // callback for new data of refresh api 203 | 204 | /** EXAMPLE ** 205 | if (json.number) { 206 | Paging.setNumber(json.number); 207 | } 208 | 209 | if (json.options) { 210 | Paging.setOptions(json.options); 211 | } 212 | 213 | Paging.setPage(); // Call with empty params to reload the paginator 214 | **/ 215 | } 216 | }, opts || {}); 217 | 218 | Paging.opts["lapping"]|= 0; 219 | Paging.opts["perpage"]|= 0; 220 | if (Paging.opts["page"] !== null) 221 | Paging.opts["page"]|= 0; 222 | 223 | // If the number of elements per page is less then 1, set it to default 224 | if (Paging.opts["perpage"] < 1) { 225 | Paging.opts["perpage"] = 10; 226 | } 227 | 228 | if (Paging.interval) 229 | window.clearInterval(Paging.interval); 230 | 231 | if (Paging.opts["refresh"]["url"]) { 232 | 233 | Paging.interval = window.setInterval(function() { 234 | 235 | $["ajax"]({ 236 | "url": Paging.opts["refresh"]["url"], 237 | "success": function(data) { 238 | 239 | if (typeof(data) === "string") { 240 | 241 | try { 242 | data = $["parseJSON"](data); 243 | } catch (o) { 244 | return; 245 | } 246 | } 247 | Paging.opts["onRefresh"](data); 248 | } 249 | }); 250 | 251 | }, 1000 * Paging.opts["refresh"]["interval"]); 252 | } 253 | 254 | Paging.format = parseFormat(Paging.opts["format"]); 255 | return Paging; 256 | }, 257 | 258 | "setNumber": function(number) { 259 | Paging.number = (undefined === number || number < 0) ? -1 : number; 260 | return Paging; 261 | }, 262 | 263 | "setPage": function(page) { 264 | 265 | if (Paging.opts["lock"]) { 266 | Paging.opts["onSelect"](0, self); 267 | return Paging; 268 | } 269 | 270 | if (undefined === page) { 271 | 272 | page = Paging.opts["page"]; 273 | 274 | if (null === page) { 275 | return Paging; 276 | } 277 | 278 | } else if (Paging.opts["page"] == page) { // Necessary to be ==, not === 279 | return Paging; 280 | } 281 | 282 | Paging.opts["page"] = (page|= 0); 283 | 284 | var number = Paging.number; 285 | var opts = Paging.opts; 286 | 287 | var rStart, rStop; 288 | 289 | var pages, buffer; 290 | 291 | var groups = 1, format = Paging.format; 292 | 293 | var data, tmp, node, lapping; 294 | 295 | var count = format.fstack["length"], i = count; 296 | 297 | 298 | // If the lapping is greater than perpage, reduce it to perpage - 1 to avoid endless loops 299 | if (opts["perpage"] <= opts["lapping"]) { 300 | opts["lapping"] = opts["perpage"] - 1; 301 | } 302 | 303 | lapping = number <= opts["lapping"] ? 0 : opts["lapping"] | 0; 304 | 305 | 306 | // If the number is negative, the value doesn"t matter, we loop endlessly with a constant width 307 | if (number < 0) { 308 | 309 | number = -1; 310 | pages = -1; 311 | 312 | rStart = Math.max(1, page - format.current + 1 - lapping); 313 | rStop = rStart + format.blockwide; 314 | 315 | } else { 316 | 317 | /* Calculate the number of pages 318 | * http://www.xarg.org/2016/10/derivation-of-pagination-calculation/ 319 | * 320 | */ 321 | pages = 1 + Math.ceil((number - opts["perpage"]) / (opts["perpage"] - lapping)); 322 | 323 | // If current page is negative, start at the end and 324 | // Set the current page into a valid range, includes 0, which is set to 1 325 | page = Math.max(1, Math.min(page < 0 ? 1 + pages + page : page, pages)); 326 | 327 | // Do we need to print all numbers? 328 | if (format.asterisk) { 329 | rStart = 1; 330 | rStop = 1 + pages; 331 | 332 | // Disable :first and :last for asterisk mode as we see all buttons 333 | format.current = page; 334 | format.blockwide = pages; 335 | 336 | } else { 337 | 338 | // If no, start at the best position and stop at max width or at num of pages 339 | rStart = Math.max(1, Math.min(page - format.current, pages - format.blockwide) + 1); 340 | rStop = format.inactive ? rStart + format.blockwide : Math.min(rStart + format.blockwide, 1 + pages); 341 | } 342 | } 343 | 344 | while (i--) { 345 | 346 | tmp = 0; // default everything is visible 347 | node = format.fstack[i]; 348 | 349 | switch (node.ftype) { 350 | 351 | case "left": 352 | tmp = (node.fpos < rStart); 353 | break; 354 | case "right": 355 | tmp = (rStop <= pages - format.rights + node.fpos); 356 | break; 357 | 358 | case "first": 359 | tmp = (format.current < page); 360 | break; 361 | case "last": 362 | tmp = (format.blockwide < format.current + pages - page); 363 | break; 364 | 365 | case "prev": 366 | tmp = (1 < page); 367 | break; 368 | case "next": 369 | tmp = (page < pages); 370 | break; 371 | } 372 | groups|= tmp << node.fgroup; // group visible? 373 | } 374 | 375 | data = { 376 | "number": number, // number of elements 377 | "lapping": lapping, // overlapping 378 | "pages": pages, // number of pages 379 | "perpage": opts["perpage"], // number of elements per page 380 | "page": page, // current page 381 | "slice": [ // two element array with bounds of the current page selection 382 | (tmp = page * (opts["perpage"] - lapping) + lapping) - opts["perpage"], // Lower bound 383 | Math.min(tmp, number) // Upper bound 384 | ] 385 | }; 386 | 387 | buffer = ""; 388 | 389 | function buffer_append(opts, data, type) { 390 | 391 | type = "" + (opts["onFormat"].call(data, type)); 392 | 393 | if (data["value"]) { 394 | buffer += type.replace(/> node.fgroup & 1); 405 | 406 | switch (node.ftype) { 407 | case "block": 408 | for (; rStart < rStop; ++rStart) { 409 | 410 | data["value"] = rStart; 411 | data["pos"] = 1 + format.blockwide - rStop + rStart; 412 | 413 | data["active"] = rStart <= pages || number < 0; // true if infinity series and rStart <= pages 414 | data["first"] = 1 === rStart; // check if it is the first page 415 | data["last"] = rStart === pages && 0 < number; // false if infinity series or rStart != pages 416 | 417 | buffer_append(opts, data, node.ftype); 418 | } 419 | continue; 420 | 421 | case "left": 422 | data["value"] = node.fpos; 423 | data["active"] = node.fpos < rStart; // Don't take group-visibility into account! 424 | break; 425 | 426 | case "right": 427 | data["value"] = pages - format.rights + node.fpos; 428 | data["active"] = rStop <= data["value"]; // Don't take group-visibility into account! 429 | break; 430 | 431 | case "prev": 432 | case "next": 433 | 434 | var p_ = 0; 435 | 436 | if (opts["stepwidth"] === 0) { 437 | 438 | if (node.ftype === "next") { 439 | 440 | if (page <= format.current) { 441 | p_ = format.current + format.blockwide; 442 | } else { 443 | p_ = page + format.blockwide; 444 | } 445 | 446 | } else { 447 | p_ = page - format.blockwide; 448 | } 449 | 450 | } else { 451 | p_ = node.ftype ==="next" ? page + opts["stepwidth"] : page - opts["stepwidth"]; 452 | } 453 | 454 | if (opts["circular"]) { 455 | data["active"] = 1; 456 | data["value"] = 1 + (pages + p_ - 1) % pages; 457 | } else if (node.ftype === "next" && number < 0) { // if type=next and infinity navigation 458 | data["active"] = 1; 459 | data["value"] = p_; 460 | } else { 461 | data["value"] = Math.max(1, Math.min(p_, pages)); 462 | data["active"] = tmp && 1 < page && page < pages; 463 | } 464 | break; 465 | 466 | case "first": 467 | data["value"] = 1; 468 | data["active"] = tmp && 1 < page; 469 | break; 470 | 471 | case "last": 472 | if (number < 0) { 473 | data["active"] = 1; 474 | data["value"] = 1 + page; 475 | } else { 476 | data["value"] = pages; 477 | data["active"] = tmp && page < pages; 478 | } 479 | break; 480 | 481 | case "leap": 482 | case "fill": 483 | data["pos"] = node.fpos; 484 | data["active"] = tmp; // tmp is true by default and changes only for group behaviour 485 | buffer_append(opts, data, node.ftype); 486 | continue; 487 | } 488 | 489 | data["pos"] = node.fpos; 490 | data["last"] = /* void */ 491 | data["first"] = undefined; 492 | 493 | buffer_append(opts, data, node.ftype); 494 | } 495 | 496 | if (self.length) { 497 | 498 | $("a", self["html"](buffer)).click(opts['onClick'] || function(ev) { 499 | ev["preventDefault"](); 500 | 501 | var obj = this; 502 | 503 | do { 504 | 505 | if ('a' === obj["nodeName"].toLowerCase()) { 506 | break; 507 | } 508 | 509 | } while ((obj = obj["parentNode"])); 510 | 511 | Paging["setPage"]($(obj).data("page")); 512 | 513 | if (Paging.locate) { 514 | window.location = obj["href"]; 515 | } 516 | }); 517 | 518 | Paging.locate = opts["onSelect"].call({ 519 | "number": number, 520 | "lapping": lapping, 521 | "pages": pages, 522 | "slice": data["slice"] 523 | }, page, self); 524 | } 525 | return Paging; 526 | } 527 | }; 528 | 529 | return Paging 530 | ["setNumber"](number) 531 | ["setOptions"](opts) 532 | ["setPage"](); 533 | }; 534 | 535 | }(jQuery, this)); 536 | -------------------------------------------------------------------------------- /jquery.paging.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery paging plugin v1.3.1 23/06/2014 3 | http://www.xarg.org/2011/09/jquery-pagination-revised/ 4 | 5 | Copyright (c) 2011, Robert Eisele (robert@xarg.org) 6 | Dual licensed under the MIT or GPL Version 2 licenses. 7 | */ 8 | (function(p,x,u){p.fn.paging=function(A,B){var v=this,b={setOptions:function(c){b.a=p.extend(b.a||{lapping:0,perpage:10,page:1,stepwidth:1,refresh:{interval:10,url:null},format:"",lock:!1,circular:!1,onClick:null,onFormat:function(){},onSelect:function(){return!0},onRefresh:function(){}},c||{});b.a.lapping|=0;b.a.perpage|=0;null!==b.a.page&&(b.a.page|=0);1>b.a.perpage&&(b.a.perpage=10);b.interval&&x.clearInterval(b.interval);b.a.refresh.url&&(b.interval=x.setInterval(function(){p.ajax({url:b.a.refresh.url, 9 | success:function(c){if("string"===typeof c)try{c=p.parseJSON(c)}catch(m){return}b.a.onRefresh(c)}})},1E3*b.a.refresh.interval));b.format=function(c){for(var b=0,e=0,q=1,h={g:[],i:0,h:0,b:5,current:3,l:0,m:0},a,p=/[*<>pq\[\]().-]|[nc]+!?/g,r={"[":"first","]":"last","<":"prev",">":"next",q:"left",p:"right","-":"fill",".":"leap"},n={};a=p.exec(c);)a=""+a,u===r[a]?"("===a?e=++b:")"===a?e=0:q&&("*"===a?(h.i=1,h.h=0):(h.i=0,h.h="!"===a.charAt(a.length-1),h.b=a.length-h.h,(h.current=1+a.indexOf("c"))||(h.current= 10 | 1+h.b>>1)),h.g.push({c:"block",j:0,f:0}),q=0):(h.g.push({c:r[a],j:e,f:u===n[a]?n[a]=1:++n[a]}),"q"===a?++h.m:"p"===a&&++h.l);return h}(b.a.format);return b},setNumber:function(c){b.s=u===c||0>c?-1:c;return b},setPage:function(c){function y(c,a,b){b=""+c.onFormat.call(a,b);q=a.value?q+b.replace(/m){var f=m=-1;var k=Math.max(1,c-a.current+1-n);var t=k+a.b}else f=1+Math.ceil((m-e.perpage)/(e.perpage-n)),c=Math.max(1,Math.min(0>c?1+f+c:c,f)),a.i?(k=1,t=1+f,a.current=c,a.b=f):(k=Math.max(1,Math.min(c-a.current,f-a.b)+1),t=a.h?k+a.b:Math.min(k+a.b,1+f));for(;r--;){var l=0;var g=a.g[r];switch(g.c){case "left":l=g.f>g.j&1;switch(g.c){case "block":for(;km,d.first=1===k,d.last=k===f&&0m?(d.active=1,d.value=w):(d.value=Math.max(1,Math.min(w,f)),d.active=l&&1m?(d.active=1,d.value=1+c):(d.value=f,d.active=l&&c=1.5" 25 | }, 26 | "homepage": "https://github.com/infusion/jQuery-Paging", 27 | "demo": "http://www.xarg.org/2011/09/jquery-pagination-revised/" 28 | } 29 | --------------------------------------------------------------------------------