1 /** 2 * jQuery jPages v0.7 3 * Client side pagination with jQuery 4 * http://luis-almeida.github.com/jPages 5 * 6 * Licensed under the MIT license. 7 * Copyright 2012 LuÃs Almeida 8 * https://github.com/luis-almeida 9 */ 10;(function($, window, document, undefined) { 11 12 var name = "jPages", 13 instance = null, 14 defaults = { 15 containerID: "", 16 first: false, 17 previous: "Zp\u011Bt", 18 next: "Dal\u0161\u00ED", 19 last: false, 20 links: "numeric", // blank || title 21 startPage: 1, 22 perPage: 10, 23 midRange: 5, 24 startRange: 1, 25 endRange: 1, 26 keyBrowse: true, 27 scrollBrowse: true, 28 pause: 0, 29 clickStop: false, 30 delay: 50, 31 direction: "forward", // backwards || auto || random || 32 animation: "", // http://daneden.me/animate/ - any entrance animations 33 fallback: 400, 34 minHeight: true, 35 callback: undefined // function( pages, items ) { } 36 }; 37 38 39 function Plugin(element, options) { 40 this.options = $.extend({}, defaults, options); 41 42 this._container = $("#" + this.options.containerID); 43 if (!this._container.length) return; 44 45 this.jQwindow = $(window); 46 this.jQdocument = $(document); 47 48 this._holder = $(element); 49 this._nav = {}; 50 51 this._first = $(this.options.first); 52 this._previous = $(this.options.previous); 53 this._next = $(this.options.next); 54 this._last = $(this.options.last); 55 56 /* only visible items! */ 57 this._items = this._container.children(":visible"); 58 this._itemsShowing = $([]); 59 this._itemsHiding = $([]); 60 61 this._numPages = Math.ceil(this._items.length / this.options.perPage); 62 this._currentPageNum = this.options.startPage; 63 64 this._clicked = false; 65 this._cssAnimSupport = this.getCSSAnimationSupport(); 66 67 this.init(); 68 } 69 70 Plugin.prototype = { 71 72 constructor : Plugin, 73 74 getCSSAnimationSupport : function() { 75 var animation = false, 76 animationstring = 'animation', 77 keyframeprefix = '', 78 domPrefixes = 'Webkit Moz O ms Khtml'.split(' '), 79 pfx = '', 80 elm = this._container.get(0); 81 82 if (elm.style.animationName) animation = true; 83 84 if (animation === false) { 85 for (var i = 0; i < domPrefixes.length; i++) { 86 if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) { 87 pfx = domPrefixes[i]; 88 animationstring = pfx + 'Animation'; 89 keyframeprefix = '-' + pfx.toLowerCase() + '-'; 90 animation = true; 91 break; 92 } 93 } 94 } 95 96 return animation; 97 }, 98 99 init : function() { 100 this.setStyles(); 101 this.setNav(); 102 this.paginate(this._currentPageNum); 103 this.setMinHeight(); 104 }, 105 106 setStyles : function() { 107 var requiredStyles = "<style>" + 108 ".jp-invisible { visibility: hidden !important; } " + 109 ".jp-hidden { display: none !important; }" + 110 "</style>"; 111 112 $(requiredStyles).appendTo("head"); 113 114 if (this._cssAnimSupport && this.options.animation.length) 115 this._items.addClass("animated jp-hidden"); 116 else this._items.hide(); 117 118 }, 119 120 setNav : function() { 121 var navhtml = this.writeNav(); 122 123 this._holder.each(this.bind(function(index, element) { 124 var holder = $(element); 125 holder.html(navhtml); 126 this.cacheNavElements(holder, index); 127 this.bindNavHandlers(index); 128 this.disableNavSelection(element); 129 }, this)); 130 131 if (this.options.keyBrowse) this.bindNavKeyBrowse(); 132 if (this.options.scrollBrowse) this.bindNavScrollBrowse(); 133 }, 134 135 writeNav : function() { 136 var i = 1, navhtml; 137 navhtml = this.writeBtn("first") + this.writeBtn("previous"); 138 139 for (; i <= this._numPages; i++) { 140 if (i === 1 && this.options.startRange === 0) navhtml += "<span>...</span>"; 141 if (i > this.options.startRange && i <= this._numPages - this.options.endRange) 142 navhtml += "<a href='#' class='jp-hidden'>"; 143 else 144 navhtml += "<a>"; 145 146 switch (this.options.links) { 147 case "numeric": 148 navhtml += i; 149 break; 150 case "blank": 151 break; 152 case "title": 153 var title = this._items.eq(i - 1).attr("data-title"); 154 navhtml += title !== undefined ? title : ""; 155 break; 156 } 157 158 navhtml += "</a>"; 159 if (i === this.options.startRange || i === this._numPages - this.options.endRange) 160 navhtml += "<span>...</span>"; 161 } 162 navhtml += this.writeBtn("next") + this.writeBtn("last") + "</div>"; 163 return navhtml; 164 }, 165 166 writeBtn : function(which) { 167 168 return this.options[which] !== false && !$(this["_" + which]).length ? 169 "<a class='jp-" + which + "'>" + this.options[which] + "</a>" : ""; 170 171 }, 172 173 cacheNavElements : function(holder, index) { 174 this._nav[index] = {}; 175 this._nav[index].holder = holder; 176 this._nav[index].first = this._first.length ? this._first : this._nav[index].holder.find("a.jp-first"); 177 this._nav[index].previous = this._previous.length ? this._previous : this._nav[index].holder.find("a.jp-previous"); 178 this._nav[index].next = this._next.length ? this._next : this._nav[index].holder.find("a.jp-next"); 179 this._nav[index].last = this._last.length ? this._last : this._nav[index].holder.find("a.jp-last"); 180 this._nav[index].fstBreak = this._nav[index].holder.find("span:first"); 181 this._nav[index].lstBreak = this._nav[index].holder.find("span:last"); 182 this._nav[index].pages = this._nav[index].holder.find("a").not(".jp-first, .jp-previous, .jp-next, .jp-last"); 183 this._nav[index].permPages = 184 this._nav[index].pages.slice(0, this.options.startRange) 185 .add(this._nav[index].pages.slice(this._numPages - this.options.endRange, this._numPages)); 186 this._nav[index].pagesShowing = $([]); 187 this._nav[index].currentPage = $([]); 188 }, 189 190 bindNavHandlers : function(index) { 191 var nav = this._nav[index]; 192 193 // default nav 194 nav.holder.bind("click.jPages", this.bind(function(evt) { 195 var newPage = this.getNewPage(nav, $(evt.target)); 196 if (this.validNewPage(newPage)) { 197 this._clicked = true; 198 this.paginate(newPage); 199 } 200 evt.preventDefault(); 201 }, this)); 202 203 // custom first 204 if (this._first.length) { 205 this._first.bind("click.jPages", this.bind(function() { 206 if (this.validNewPage(1)) { 207 this._clicked = true; 208 this.paginate(1); 209 } 210 }, this)); 211 } 212 213 // custom previous 214 if (this._previous.length) { 215 this._previous.bind("click.jPages", this.bind(function() { 216 var newPage = this._currentPageNum - 1; 217 if (this.validNewPage(newPage)) { 218 this._clicked = true; 219 this.paginate(newPage); 220 } 221 }, this)); 222 } 223 224 // custom next 225 if (this._next.length) { 226 this._next.bind("click.jPages", this.bind(function() { 227 var newPage = this._currentPageNum + 1; 228 if (this.validNewPage(newPage)) { 229 this._clicked = true; 230 this.paginate(newPage); 231 } 232 }, this)); 233 } 234 235 // custom last 236 if (this._last.length) { 237 this._last.bind("click.jPages", this.bind(function() { 238 if (this.validNewPage(this._numPages)) { 239 this._clicked = true; 240 this.paginate(this._numPages); 241 } 242 }, this)); 243 } 244 245 }, 246 247 disableNavSelection : function(element) { 248 if (typeof element.onselectstart != "undefined") 249 element.onselectstart = function() { 250 return false;
251 }; 252 else if (typeof element.style.MozUserSelect != "undefined") 253 element.style.MozUserSelect = "none"; 254 else 255 element.onmousedown = function() { 256 return false; 257 }; 258 }, 259 260 bindNavKeyBrowse : function() { 261 this.jQdocument.bind("keydown.jPages", this.bind(function(evt) { 262 var target = evt.target.nodeName.toLowerCase(); 263 if (this.elemScrolledIntoView() && target !== "input" && target != "textarea") { 264 var newPage = this._currentPageNum; 265 266 if (evt.which == 37) newPage = this._currentPageNum - 1; 267 if (evt.which == 39) newPage = this._currentPageNum + 1; 268 269 if (this.validNewPage(newPage)) { 270 this._clicked = true; 271 this.paginate(newPage); 272 } 273 } 274 }, this)); 275 }, 276 277 elemScrolledIntoView : function() { 278 var docViewTop, docViewBottom, elemTop, elemBottom; 279 docViewTop = this.jQwindow.scrollTop(); 280 docViewBottom = docViewTop + this.jQwindow.height(); 281 elemTop = this._container.offset().top; 282 elemBottom = elemTop + this._container.height(); 283 return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom)); 284 285 // comment above and uncomment below if you want keyBrowse to happen 286 // only when container is completely visible in the page 287 /*return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) && 288 (elemBottom <= docViewBottom) && (elemTop >= docViewTop) );*/ 289 }, 290 291 bindNavScrollBrowse : function() { 292 this._container.bind("mousewheel.jPages DOMMouseScroll.jPages", this.bind(function(evt) { 293 var newPage = (evt.originalEvent.wheelDelta || -evt.originalEvent.detail) > 0 ? 294 (this._currentPageNum - 1) : (this._currentPageNum + 1); 295 if (this.validNewPage(newPage)) { 296 this._clicked = true; 297 this.paginate(newPage); 298 } 299 evt.preventDefault(); 300 return false; 301 }, this)); 302 }, 303 304 getNewPage : function(nav, target) { 305 if (target.is(nav.currentPage)) return this._currentPageNum; 306 if (target.is(nav.pages)) return nav.pages.index(target) + 1; 307 if (target.is(nav.first)) return 1; 308 if (target.is(nav.last)) return this._numPages; 309 if (target.is(nav.previous)) return nav.pages.index(nav.currentPage); 310 if (target.is(nav.next)) return nav.pages.index(nav.currentPage) + 2; 311 }, 312 313 validNewPage : function(newPage) { 314 return newPage !== this._currentPageNum && newPage > 0 && newPage <= this._numPages; 315 }, 316 317 paginate : function(page) { 318 var itemRange, pageInterval; 319 itemRange = this.updateItems(page); 320 pageInterval = this.updatePages(page); 321 this._currentPageNum = page; 322 if ($.isFunction(this.options.callback)) 323 this.callback(page, itemRange, pageInterval); 324 325 this.updatePause(); 326 }, 327 328 updateItems : function(page) { 329 var range = this.getItemRange(page); 330 this._itemsHiding = this._itemsShowing; 331 this._itemsShowing = this._items.slice(range.start, range.end+1); 332 if (this._cssAnimSupport && this.options.animation.length) this.cssAnimations(page); 333 else this.jQAnimations(page); 334 return range; 335 }, 336 337 getItemRange : function(page) { 338 var range = {}; 339 range.start = (page - 1) * this.options.perPage; 340 range.end = range.start + this.options.perPage; 341 if (range.end > this._items.length) range.end = this._items.length; 342 return range; 343 }, 344 345 cssAnimations : function(page) { 346 clearInterval(this._delay); 347 348 this._itemsHiding 349 .removeClass(this.options.animation + " jp-invisible") 350 .addClass("jp-hidden"); 351 352 this._itemsShowing 353 .removeClass("jp-hidden") 354 .addClass("jp-invisible"); 355 356 this._itemsOriented = this.getDirectedItems(page); 357 this._index = 0; 358 359 this._delay = setInterval(this.bind(function() { 360 if (this._index === this._itemsOriented.length) clearInterval(this._delay); 361 else { 362 this._itemsOriented 363 .eq(this._index) 364 .removeClass("jp-invisible") 365 .addClass(this.options.animation); 366 } 367 this._index = this._index + 1; 368 }, this), this.options.delay); 369 }, 370 371 jQAnimations : function(page) { 372 clearInterval(this._delay); 373 this._itemsHiding.addClass("jp-hidden"); 374 this._itemsShowing.fadeTo(0, 0).removeClass("jp-hidden"); 375 this._itemsOriented = this.getDirectedItems(page); 376 this._index = 0;
377 this._delay = setInterval(this.bind(function() { 378 if (this._index === this._itemsOriented.length) clearInterval(this._delay); 379 else { 380 this._itemsOriented 381 .eq(this._index) 382 .fadeTo(this.options.fallback, 1); 383 } 384 this._index = this._index + 1; 385 }, this), this.options.delay); 386 }, 387 388 getDirectedItems : function(page) { 389 var itemsToShow; 390 391 switch (this.options.direction) { 392 case "backwards": 393 itemsToShow = $(this._itemsShowing.get().reverse()); 394 break; 395 case "random": 396 itemsToShow = $(this._itemsShowing.get().sort(function() { 397 return (Math.round(Math.random()) - 0.5); 398 })); 399 break; 400 case "auto": 401 itemsToShow = page >= this._currentPageNum ? 402 this._itemsShowing : $(this._itemsShowing.get().reverse()); 403 break; 404 default: 405 itemsToShow = this._itemsShowing; 406 } 407 408 return itemsToShow; 409 }, 410 411 updatePages : function(page) { 412 var interval, index, nav; 413 interval = this.getInterval(page); 414 for (index in this._nav) { 415 if (this._nav.hasOwnProperty(index)) { 416 nav = this._nav[index]; 417 this.updateBtns(nav, page); 418 this.updateCurrentPage(nav, page); 419 this.updatePagesShowing(nav, interval); 420 this.updateBreaks(nav, interval); 421 } 422 } 423 return interval; 424 }, 425 426 getInterval : function(page) { 427 var neHalf, upperLimit, start, end; 428 neHalf = Math.ceil(this.options.midRange / 2); 429 upperLimit = this._numPages - this.options.midRange; 430 start = page > neHalf ? Math.max(Math.min(page - neHalf, upperLimit), 0) : 0; 431 end = page > neHalf ? 432 Math.min(page + neHalf - (this.options.midRange % 2 > 0 ? 1 : 0), this._numPages) : 433 Math.min(this.options.midRange, this._numPages); 434 return {start: start,end: end}; 435 }, 436 437 updateBtns : function(nav, page) { 438 if (page === 1) { 439 nav.first.addClass("jp-disabled"); 440 nav.previous.addClass("jp-disabled"); 441 } 442 if (page === this._numPages) { 443 nav.next.addClass("jp-disabled"); 444 nav.last.addClass("jp-disabled"); 445 } 446 if (this._currentPageNum === 1 && page > 1) { 447 nav.first.removeClass("jp-disabled"); 448 nav.previous.removeClass("jp-disabled"); 449 } 450 if (this._currentPageNum === this._numPages && page < this._numPages) { 451 nav.next.removeClass("jp-disabled"); 452 nav.last.removeClass("jp-disabled"); 453 } 454 }, 455 456 updateCurrentPage : function(nav, page) { 457 nav.currentPage.removeClass("jp-current"); 458 nav.currentPage = nav.pages.eq(page - 1).addClass("jp-current"); 459 }, 460 461 updatePagesShowing : function(nav, interval) { 462 var newRange = nav.pages.slice(interval.start, interval.end).not(nav.permPages); 463 nav.pagesShowing.not(newRange).addClass("jp-hidden"); 464 newRange.not(nav.pagesShowing).removeClass("jp-hidden"); 465 nav.pagesShowing = newRange; 466 }, 467 468 updateBreaks : function(nav, interval) { 469 if ( 470 interval.start > this.options.startRange || 471 (this.options.startRange === 0 && interval.start > 0) 472 ) nav.fstBreak.removeClass("jp-hidden"); 473 else nav.fstBreak.addClass("jp-hidden"); 474 475 if (interval.end < this._numPages - this.options.endRange) nav.lstBreak.removeClass("jp-hidden"); 476 else nav.lstBreak.addClass("jp-hidden"); 477 }, 478 479 callback : function(page, itemRange, pageInterval) { 480 var pages = { 481 current: page, 482 interval: pageInterval, 483 count: this._numPages 484 }, 485 items = { 486 showing: this._itemsShowing, 487 oncoming: this._items.slice(itemRange.start + this.options.perPage, itemRange.end + this.options.perPage), 488 range: itemRange, 489 count: this._items.length 490 }; 491 492 pages.interval.start = pages.interval.start + 1; 493 items.range.start = items.range.start + 1; 494 this.options.callback(pages, items); 495 }, 496 497 updatePause : function() { 498 if (this.options.pause && this._numPages > 1) { 499 clearTimeout(this._pause); 500 if (this.options.clickStop && this._clicked) return; 501 else { 502 this._pause = setTimeout(this.bind(function() { 503 this.paginate(this._currentPageNum !== this._numPages ? this._currentPageNum + 1 : 1); 504 }, this), this.options.pause); 505 } 506 } 507 }, 508 509 setMinHeight : function() { 510 if (this.options.minHeight && !this._container.is("table, tbody")) { 511 setTimeout(this.bind(function() { 512 this._container.css({ "min-height": this._container.css("height") }); 513 }, this), 1000); 514 } 515 }, 516 517 bind : function(fn, me) { 518 return function() { 519 return fn.apply(me, arguments); 520 }; 521 }, 522 523 destroy : function() {
524 this.jQdocument.unbind("keydown.jPages"); 525 this._container.unbind("mousewheel.jPages DOMMouseScroll.jPages"); 526 527 if (this.options.minHeight) this._container.css("min-height", ""); 528 if (this._cssAnimSupport && this.options.animation.length) 529 this._items.removeClass("animated jp-hidden jp-invisible " + this.options.animation); 530 else this._items.removeClass("jp-hidden").fadeTo(0, 1); 531 this._holder.unbind("click.jPages").empty(); 532 } 533 534 }; 535 536 $.fn[name] = function(arg) { 537 var type = $.type(arg); 538 539 if (type === "object") { 540 if (this.length && !$.data(this, name)) { 541 instance = new Plugin(this, arg); 542 this.each(function() { 543 $.data(this, name, instance); 544 }); 545 } 546 return this; 547 } 548 549 if (type === "string" && arg === "destroy") { 550 instance.destroy(); 551 this.each(function() { 552 $.removeData(this, name); 553 }); 554 return this; 555 } 556 557 if (type === 'number' && arg % 1 === 0) { 558 if (instance.validNewPage(arg)) instance.paginate(arg); 559 return this; 560 } 561 562 return this; 563 }; 564 565})(jQuery, window, document);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.