/**
* jquery jpages v0.7
* client side pagination with jquery
* http://luis-almeida.github.com/jpages
*
* licensed under the mit license.
* copyright 2012 luís almeida
* https://github.com/luis-almeida
*/
;(function($, window, document, undefined) {
var name = "jpages",
instance = null,
defaults = {
containerid: "",
first: false,
previous: "上一页",
next: "下一页",
last: false,
links: "numeric", // blank || title
startpage: 1,
perpage: 6,
midrange: 5,
startrange: 1,
endrange: 1,
keybrowse: false,
scrollbrowse: false,
pause: 0,
clickstop: false,
delay: 50,
direction: "forward", // backwards || auto || random ||
animation: "", // http://daneden.me/animate/ - any entrance animations
fallback: 400,
minheight: false,
callback: undefined // function( pages, items ) { }
};
function plugin(element, options) {
this.options = $.extend({}, defaults, options);
this._container = $("#" + this.options.containerid);
if (!this._container.length) return;
this.jqwindow = $(window);
this.jqdocument = $(document);
this._holder = $(element);
this._nav = {};
this._first = $(this.options.first);
this._previous = $(this.options.previous);
this._next = $(this.options.next);
this._last = $(this.options.last);
/* only visible items! */
this._items = this._container.children(":visible");
this._itemsshowing = $([]);
this._itemshiding = $([]);
this._numpages = math.ceil(this._items.length / this.options.perpage);
this._currentpagenum = this.options.startpage;
this._clicked = false;
this._cssanimsupport = this.getcssanimationsupport();
this.init();
}
plugin.prototype = {
constructor : plugin,
getcssanimationsupport : function() {
var animation = false,
animationstring = 'animation',
keyframeprefix = '',
domprefixes = 'webkit moz o ms khtml'.split(' '),
pfx = '',
elm = this._container.get(0);
if (elm.style.animationname) animation = true;
if (animation === false) {
for (var i = 0; i < domprefixes.length; i++) {
if (elm.style[domprefixes[i] + 'animationname'] !== undefined) {
pfx = domprefixes[i];
animationstring = pfx + 'animation';
keyframeprefix = '-' + pfx.tolowercase() + '-';
animation = true;
break;
}
}
}
return animation;
},
init : function() {
this.setstyles();
this.setnav();
this.paginate(this._currentpagenum);
this.setminheight();
},
setstyles : function() {
var requiredstyles = "";
$(requiredstyles).appendto("head");
if (this._cssanimsupport && this.options.animation.length)
this._items.addclass("animated jp-hidden");
else this._items.hide();
},
setnav : function() {
var navhtml = this.writenav();
this._holder.each(this.bind(function(index, element) {
var holder = $(element);
holder.html(navhtml);
this.cachenavelements(holder, index);
this.bindnavhandlers(index);
this.disablenavselection(element);
}, this));
if (this.options.keybrowse) this.bindnavkeybrowse();
if (this.options.scrollbrowse) this.bindnavscrollbrowse();
},
writenav : function() {
var i = 1, navhtml;
navhtml = this.writebtn("first") + this.writebtn("previous");
for (; i <= this._numpages; i++) {
if (i === 1 && this.options.startrange === 0) navhtml += "...";
if (i > this.options.startrange && i <= this._numpages - this.options.endrange)
navhtml += "";
else
navhtml += "";
switch (this.options.links) {
case "numeric":
navhtml += i;
break;
case "blank":
break;
case "title":
var title = this._items.eq(i - 1).attr("data-title");
navhtml += title !== undefined ? title : "";
break;
}
navhtml += "";
if (i === this.options.startrange || i === this._numpages - this.options.endrange)
navhtml += "...";
}
navhtml += this.writebtn("next") + this.writebtn("last") + "";
return navhtml;
},
writebtn : function(which) {
return this.options[which] !== false && !$(this["_" + which]).length ?
"" + this.options[which] + "" : "";
},
cachenavelements : function(holder, index) {
this._nav[index] = {};
this._nav[index].holder = holder;
this._nav[index].first = this._first.length ? this._first : this._nav[index].holder.find("a.jp-first");
this._nav[index].previous = this._previous.length ? this._previous : this._nav[index].holder.find("a.jp-previous");
this._nav[index].next = this._next.length ? this._next : this._nav[index].holder.find("a.jp-next");
this._nav[index].last = this._last.length ? this._last : this._nav[index].holder.find("a.jp-last");
this._nav[index].fstbreak = this._nav[index].holder.find("span:first");
this._nav[index].lstbreak = this._nav[index].holder.find("span:last");
this._nav[index].pages = this._nav[index].holder.find("a").not(".jp-first, .jp-previous, .jp-next, .jp-last");
this._nav[index].permpages =
this._nav[index].pages.slice(0, this.options.startrange)
.add(this._nav[index].pages.slice(this._numpages - this.options.endrange, this._numpages));
this._nav[index].pagesshowing = $([]);
this._nav[index].currentpage = $([]);
},
bindnavhandlers : function(index) {
var nav = this._nav[index];
// default nav
nav.holder.bind("click.jpages", this.bind(function(evt) {
var newpage = this.getnewpage(nav, $(evt.target));
if (this.validnewpage(newpage)) {
this._clicked = true;
this.paginate(newpage);
}
evt.preventdefault();
}, this));
// custom first
if (this._first.length) {
this._first.bind("click.jpages", this.bind(function() {
if (this.validnewpage(1)) {
this._clicked = true;
this.paginate(1);
}
}, this));
}
// custom previous
if (this._previous.length) {
this._previous.bind("click.jpages", this.bind(function() {
var newpage = this._currentpagenum - 1;
if (this.validnewpage(newpage)) {
this._clicked = true;
this.paginate(newpage);
}
}, this));
}
// custom next
if (this._next.length) {
this._next.bind("click.jpages", this.bind(function() {
var newpage = this._currentpagenum + 1;
if (this.validnewpage(newpage)) {
this._clicked = true;
this.paginate(newpage);
}
}, this));
}
// custom last
if (this._last.length) {
this._last.bind("click.jpages", this.bind(function() {
if (this.validnewpage(this._numpages)) {
this._clicked = true;
this.paginate(this._numpages);
}
}, this));
}
},
disablenavselection : function(element) {
if (typeof element.onselectstart != "undefined")
element.onselectstart = function() {
return false;
};
else if (typeof element.style.mozuserselect != "undefined")
element.style.mozuserselect = "none";
else
element.onmousedown = function() {
return false;
};
},
bindnavkeybrowse : function() {
this.jqdocument.bind("keydown.jpages", this.bind(function(evt) {
var target = evt.target.nodename.tolowercase();
if (this.elemscrolledintoview() && target !== "input" && target != "textarea") {
var newpage = this._currentpagenum;
if (evt.which == 37) newpage = this._currentpagenum - 1;
if (evt.which == 39) newpage = this._currentpagenum + 1;
if (this.validnewpage(newpage)) {
this._clicked = true;
this.paginate(newpage);
}
}
}, this));
},
elemscrolledintoview : function() {
var docviewtop, docviewbottom, elemtop, elembottom;
docviewtop = this.jqwindow.scrolltop();
docviewbottom = docviewtop + this.jqwindow.height();
elemtop = this._container.offset().top;
elembottom = elemtop + this._container.height();
return ((elembottom >= docviewtop) && (elemtop <= docviewbottom));
// comment above and uncomment below if you want keybrowse to happen
// only when container is completely visible in the page
/*return ((elembottom >= docviewtop) && (elemtop <= docviewbottom) &&
(elembottom <= docviewbottom) && (elemtop >= docviewtop) );*/
},
bindnavscrollbrowse : function() {
this._container.bind("mousewheel.jpages dommousescroll.jpages", this.bind(function(evt) {
var newpage = (evt.originalevent.wheeldelta || -evt.originalevent.detail) > 0 ?
(this._currentpagenum - 1) : (this._currentpagenum + 1);
if (this.validnewpage(newpage)) {
this._clicked = true;
this.paginate(newpage);
}
evt.preventdefault();
return false;
}, this));
},
getnewpage : function(nav, target) {
if (target.is(nav.currentpage)) return this._currentpagenum;
if (target.is(nav.pages)) return nav.pages.index(target) + 1;
if (target.is(nav.first)) return 1;
if (target.is(nav.last)) return this._numpages;
if (target.is(nav.previous)) return nav.pages.index(nav.currentpage);
if (target.is(nav.next)) return nav.pages.index(nav.currentpage) + 2;
},
validnewpage : function(newpage) {
return newpage !== this._currentpagenum && newpage > 0 && newpage <= this._numpages;
},
paginate : function(page) {
var itemrange, pageinterval;
itemrange = this.updateitems(page);
pageinterval = this.updatepages(page);
this._currentpagenum = page;
if ($.isfunction(this.options.callback))
this.callback(page, itemrange, pageinterval);
this.updatepause();
},
updateitems : function(page) {
var range = this.getitemrange(page);
this._itemshiding = this._itemsshowing;
this._itemsshowing = this._items.slice(range.start, range.end);
if (this._cssanimsupport && this.options.animation.length) this.cssanimations(page);
else this.jqanimations(page);
return range;
},
getitemrange : function(page) {
var range = {};
range.start = (page - 1) * this.options.perpage;
range.end = range.start + this.options.perpage;
if (range.end > this._items.length) range.end = this._items.length;
return range;
},
cssanimations : function(page) {
clearinterval(this._delay);
this._itemshiding
.removeclass(this.options.animation + " jp-invisible")
.addclass("jp-hidden");
this._itemsshowing
.removeclass("jp-hidden")
.addclass("jp-invisible");
this._itemsoriented = this.getdirecteditems(page);
this._index = 0;
this._delay = setinterval(this.bind(function() {
if (this._index === this._itemsoriented.length) clearinterval(this._delay);
else {
this._itemsoriented
.eq(this._index)
.removeclass("jp-invisible")
.addclass(this.options.animation);
}
this._index = this._index + 1;
}, this), this.options.delay);
},
jqanimations : function(page) {
clearinterval(this._delay);
this._itemshiding.addclass("jp-hidden");
this._itemsshowing.fadeto(0, 0).removeclass("jp-hidden");
this._itemsoriented = this.getdirecteditems(page);
this._index = 0;
this._delay = setinterval(this.bind(function() {
if (this._index === this._itemsoriented.length) clearinterval(this._delay);
else {
this._itemsoriented
.eq(this._index)
.fadeto(this.options.fallback, 1);
}
this._index = this._index + 1;
}, this), this.options.delay);
},
getdirecteditems : function(page) {
var itemstoshow;
switch (this.options.direction) {
case "backwards":
itemstoshow = $(this._itemsshowing.get().reverse());
break;
case "random":
itemstoshow = $(this._itemsshowing.get().sort(function() {
return (math.round(math.random()) - 0.5);
}));
break;
case "auto":
itemstoshow = page >= this._currentpagenum ?
this._itemsshowing : $(this._itemsshowing.get().reverse());
break;
default:
itemstoshow = this._itemsshowing;
}
return itemstoshow;
},
updatepages : function(page) {
var interval, index, nav;
interval = this.getinterval(page);
for (index in this._nav) {
if (this._nav.hasownproperty(index)) {
nav = this._nav[index];
this.updatebtns(nav, page);
this.updatecurrentpage(nav, page);
this.updatepagesshowing(nav, interval);
this.updatebreaks(nav, interval);
}
}
return interval;
},
getinterval : function(page) {
var nehalf, upperlimit, start, end;
nehalf = math.ceil(this.options.midrange / 2);
upperlimit = this._numpages - this.options.midrange;
start = page > nehalf ? math.max(math.min(page - nehalf, upperlimit), 0) : 0;
end = page > nehalf ?
math.min(page + nehalf - (this.options.midrange % 2 > 0 ? 1 : 0), this._numpages) :
math.min(this.options.midrange, this._numpages);
return {start: start,end: end};
},
updatebtns : function(nav, page) {
if (page === 1) {
nav.first.addclass("jp-disabled");
nav.previous.addclass("jp-disabled");
}
if (page === this._numpages) {
nav.next.addclass("jp-disabled");
nav.last.addclass("jp-disabled");
}
if (this._currentpagenum === 1 && page > 1) {
nav.first.removeclass("jp-disabled");
nav.previous.removeclass("jp-disabled");
}
if (this._currentpagenum === this._numpages && page < this._numpages) {
nav.next.removeclass("jp-disabled");
nav.last.removeclass("jp-disabled");
}
},
updatecurrentpage : function(nav, page) {
nav.currentpage.removeclass("jp-current current");
nav.currentpage = nav.pages.eq(page - 1).addclass("jp-current current");
},
updatepagesshowing : function(nav, interval) {
var newrange = nav.pages.slice(interval.start, interval.end).not(nav.permpages);
nav.pagesshowing.not(newrange).addclass("jp-hidden");
newrange.not(nav.pagesshowing).removeclass("jp-hidden");
nav.pagesshowing = newrange;
},
updatebreaks : function(nav, interval) {
if (
interval.start > this.options.startrange ||
(this.options.startrange === 0 && interval.start > 0)
) nav.fstbreak.removeclass("jp-hidden");
else nav.fstbreak.addclass("jp-hidden");
if (interval.end < this._numpages - this.options.endrange) nav.lstbreak.removeclass("jp-hidden");
else nav.lstbreak.addclass("jp-hidden");
},
callback : function(page, itemrange, pageinterval) {
var pages = {
current: page,
interval: pageinterval,
count: this._numpages
},
items = {
showing: this._itemsshowing,
oncoming: this._items.slice(itemrange.start + this.options.perpage, itemrange.end + this.options.perpage),
range: itemrange,
count: this._items.length
};
pages.interval.start = pages.interval.start + 1;
items.range.start = items.range.start + 1;
this.options.callback(pages, items);
},
updatepause : function() {
if (this.options.pause && this._numpages > 1) {
cleartimeout(this._pause);
if (this.options.clickstop && this._clicked) return;
else {
this._pause = settimeout(this.bind(function() {
this.paginate(this._currentpagenum !== this._numpages ? this._currentpagenum + 1 : 1);
}, this), this.options.pause);
}
}
},
setminheight : function() {
if (this.options.minheight && !this._container.is("table, tbody")) {
settimeout(this.bind(function() {
this._container.css({ "min-height": this._container.css("height") });
}, this), 1000);
}
},
bind : function(fn, me) {
return function() {
return fn.apply(me, arguments);
};
},
destroy : function() {
this.jqdocument.unbind("keydown.jpages");
this._container.unbind("mousewheel.jpages dommousescroll.jpages");
if (this.options.minheight) this._container.css("min-height", "");
if (this._cssanimsupport && this.options.animation.length)
this._items.removeclass("animated jp-hidden jp-invisible " + this.options.animation);
else this._items.removeclass("jp-hidden").fadeto(0, 1);
this._holder.unbind("click.jpages").empty();
}
};
$.fn[name] = function(arg) {
var type = $.type(arg);
if (type === "object") {
if (this.length && !$.data(this, name)) {
instance = new plugin(this, arg);
this.each(function() {
$.data(this, name, instance);
});
}
return this;
}
if (type === "string" && arg === "destroy") {
instance.destroy();
this.each(function() {
$.removedata(this, name);
});
return this;
}
if (type === 'number' && arg % 1 === 0) {
if (instance.validnewpage(arg)) instance.paginate(arg);
return this;
}
return this;
};
})(jquery, window, document);