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