/**
* jquery.scrollable 0.13. Put your HTML scroll.
*
* http://flowplayer.org/tools/scrollable.html
*
* Copyright (c) 2008 Tero Piirainen (support@flowplayer.org)
*
* Released under the MIT License:
* http://www.opensource.org/licenses/mit-license.php
*
* >> Basically you can do anything you want but leave this header as is <<
*
* Since : 0.10 - 03/01/2008
* Version: 0.13 - Wed Nov 05 2008 12:04:04 GMT-0000 (GMT+00:00)
*/
(function($) {
// constructor
function Scrollable(el, config) {
// current instance
var self = this;
if (!Scrollable.current) {
Scrollable.current = this;
}
var opts = {
size: 5,
vertical:false,
activeClass:'active',
speed: 300,
onSeek: null,
clickable: true,
// jquery selectors
items: '.items',
prev:'.prev',
next:'.next',
navi:'.navi',
naviItem:'span',
loop: false
};
this.opts = $.extend(opts, config);
this.opts.horizontal = !opts.vertical;
// root / itemRoot
this.root = $(el);
var root = this.root;
var itemRoot = $(opts.items, root);
if (!itemRoot.length) { itemRoot = root; }
// wrap itemRoot.children() inside container
itemRoot.css({position:'relative', overflow:'hidden', visibility:'visible'});
itemRoot.children().wrapAll('
');
this.wrap = itemRoot.find(":first");
this.wrap.css(opts.horizontal ? "width" : "height", "200000em").after('
');
this.items = this.wrap.children();
this.index = 0;
// set dimensions based on offsets of the two first elements
if (opts.horizontal) {
itemRoot.width(opts.size * (this.items.eq(1).offset().left - this.items.eq(0).offset().left) -2);
} else {
itemRoot.height(opts.size * (this.items.eq(1).offset().top - this.items.eq(0).offset().top) -2);
}
// mousewheel
if ($.isFunction($.fn.mousewheel)) {
root.bind("mousewheel.scrollable", function(e, delta) {
self.move(-delta, 50);
return false;
});
}
// item.click()
if (opts.clickable) {
this.items.each(function(index, arg) {
$(this).bind("click.scrollable", function() {
self.click(index);
});
});
}
this.activeIndex = 0;
// prev
$(opts.prev, root).click(function() {
self.prev();
});
// next
$(opts.next, root).click(function() {
self.next();
});
// navi
$(opts.navi, root).each(function() {
var navi = $(this);
var status = self.getStatus();
// generate new entries
if (navi.is(":empty")) {
for (var i = 0; i < status.pages; i++) {
var item = $("<" + opts.naviItem + "/>").attr("page", i).click(function(e) {
var el = $(this);
el.parent().children().removeClass(opts.activeClass);
el.addClass(opts.activeClass);
self.setPage(el.attr("page"));
e.preventDefault();
});
if (i === 0) { item.addClass(opts.activeClass); }
navi.append(item);
}
// assign onClick events to existing entries
} else {
// find a entries first -> syntaxically correct
var els = navi.find("a");
if (!els.length) {
els = navi.children();
}
els.each(function(i) {
var item = $(this);
item.attr("page", i);
if (i === 0) { item.addClass(opts.activeClass); }
item.click(function() {
navi.find("." + opts.activeClass).removeClass(opts.activeClass);
item.addClass(opts.activeClass);
self.setPage(item.attr("page"));
});
});
}
});
}
// methods
$.extend(Scrollable.prototype, {
getVersion: function() {
return '@VERSION';
},
click: function(index) {
var item = this.items.eq(index);
var klass = this.opts.activeClass;
if (!item.hasClass(klass) && (index >= 0 || index < this.items.size())) {
this.items.removeClass(klass);
item.addClass(klass);
var delta = Math.floor(this.opts.size / 2);
var to = index - delta;
if (to !== this.activeIndex) {
this.seekTo(to);
}
}
},
getStatus: function() {
var len = this.items.size();
return {
size: this.opts.size,
total: len,
index: this.index,
pages: Math.ceil(len / this.opts.size),
page: Math.ceil(this.index / this.opts.size)
};
},
// all other seeking functions depend on this generic seeking function
seekTo: function(index, time) {
if (index < 0) { index = 0; }
var max = Math.min(index, this.items.length - this.opts.size);
if (index <= max) {
var item = this.items.eq(index);
this.index = index;
if (this.opts.horizontal) {
var left = this.wrap.offset().left - item.offset().left;
this.wrap.animate({left: left}, time || this.opts.speed);
} else {
var top = this.wrap.offset().top - item.offset().top;
this.wrap.animate({top: top}, time || this.opts.speed);
}
Scrollable.current = this;
}
// custom onSeek callback
if ($.isFunction(this.opts.onSeek)) {
this.opts.onSeek.call(this);
}
// navi status update
var navi = $(this.opts.navi, this.root);
if (navi.length) {
var klass = this.opts.activeClass;
var page = Math.ceil(index / this.opts.size);
page = Math.min(page, navi.children().length - 1);
navi.children().removeClass(klass).eq(page).addClass(klass);
}
this.activeIndex = index;
return true;
},
move: function(offset, time) {
var to = this.index + offset;
if (this.opts.loop && to > (this.items.length - this.opts.size)) {
to = 0;
}
this.seekTo(to, time);
},
next: function(time) {
this.move(1, time);
},
prev: function(time) {
this.move(-1, time);
},
movePage: function(offset, time) {
this.move(this.opts.size * offset, time);
},
setPage: function(page, time) {
var size = this.opts.size;
var index = size * page;
var lastPage = index + size >= this.items.size();
if (lastPage) {
index = this.items.size() - this.opts.size;
}
this.seekTo(index, time);
},
prevPage: function(time) {
this.setPage(this.getStatus().page - 1, time);
},
nextPage: function(time) {
this.setPage(this.getStatus().page + 1, time);
},
begin: function(time) {
this.seekTo(0, time);
},
end: function(time) {
this.seekTo(this.items.size() - this.opts.size, time);
}
});
// keyboard
$(window).bind("keypress.scrollable", function(evt) {
var el = Scrollable.current;
if (!el) { return; }
if (el.opts.horizontal && (evt.keyCode == 37 || evt.keyCode == 39)) {
el.move(evt.keyCode == 37 ? -1 : 1);
return evt.preventDefault();
}
if (!el.opts.horizontal && (evt.keyCode == 38 || evt.keyCode == 40)) {
el.move(evt.keyCode == 38 ? -1 : 1);
return evt.preventDefault();
}
return true;
});
// jQuery plugin implementation
jQuery.prototype.scrollable = function(opts, arg0, arg1) {
// return API associated with this instance
if (!opts || typeof opts == 'number') {
var index = opts || 0;
var el = $.data(this.get()[index], "scrollable");
if (el) { return el; }
}
this.each(function() {
// @deprecated way of accessing API
if (typeof opts == "string") {
var el = $.data(this, "scrollable");
el[opts].apply(el, [arg0, arg1]);
// create new Scrollable instance
} else {
var instance = new Scrollable(this, opts);
$.data(this, "scrollable", instance);
}
});
return this;
};
})(jQuery);