/**
* @license
* jQuery Tools 1.2.2 Scrollable - New wave UI design
*
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
*
* http://flowplayer.org/tools/scrollable.html
*
* Since: March 2008
* Date: Wed May 19 06:53:17 2010 +0000
*/
(function ($) {
// static constructs
$.tools = $.tools || {version: '1.2.2'};
$.tools.scrollable = {
conf: {
activeClass: 'active',
circular: false,
clonedClass: 'cloned',
disabledClass: 'disabled',
easing: 'swing',
initialIndex: 0,
item: null,
items: '.items',
keyboard: true,
mousewheel: false,
next: '.next',
prev: '.prev',
speed: 400,
vertical: false,
wheelSpeed: 0
}
};
// get hidden element's width or height even though it's hidden
function dim(el, key) {
var v = parseInt(el.css(key), 10);
if (v) {
return v;
}
var s = el[0].currentStyle;
return s && s.width && parseInt(s.width, 10);
}
function find(root, query) {
var el = $(query);
return el.length < 2 ? el : root.parent().find(query);
}
var current;
// constructor
function Scrollable(root, conf) {
// current instance
var self = this,
fire = root.add(self),
itemWrap = root.children(),
index = 0,
vertical = conf.vertical;
if (!current) {
current = self;
}
if (itemWrap.length > 1) {
itemWrap = $(conf.items, root);
}
// methods
$.extend(self, {
getConf: function () {
return conf;
},
getIndex: function () {
return index;
},
getSize: function () {
return self.getItems().size();
},
getNaviButtons: function () {
return prev.add(next);
},
getRoot: function () {
return root;
},
getItemWrap: function () {
return itemWrap;
},
getItems: function () {
return itemWrap.children(conf.item).not("." + conf.clonedClass);
},
move: function (offset, time) {
return self.seekTo(index + offset, time);
},
next: function (time) {
return self.move(1, time);
},
prev: function (time) {
return self.move(-1, time);
},
begin: function (time) {
return self.seekTo(0, time);
},
end: function (time) {
return self.seekTo(self.getSize() - 1, time);
},
focus: function () {
current = self;
return self;
},
addItem: function (item) {
item = $(item);
if (!conf.circular) {
itemWrap.append(item);
} else {
$(".cloned:last").before(item);
$(".cloned:first").replaceWith(item.clone().addClass(conf.clonedClass));
}
fire.trigger("onAddItem", [item]);
return self;
},
/* all seeking functions depend on this */
seekTo: function (i, time, fn) {
// avoid seeking from end clone to the beginning
if (conf.circular && i === 0 && index == -1 && time !== 0) {
return self;
}
// check that index is sane
if (!conf.circular && i < 0 || i > self.getSize() || i < -1) {
return self;
}
var item = i;
if (i.jquery) {
i = self.getItems().index(i);
} else {
item = self.getItems().eq(i);
}
// onBeforeSeek
var e = $.Event("onBeforeSeek");
if (!fn) {
fire.trigger(e, [i, time]);
if (e.isDefaultPrevented() || !item.length) {
return self;
}
}
var props = vertical ? {top: -item.position().top} : {left: -item.position().left};
index = i;
current = self;
if (time === undefined) {
time = conf.speed;
}
itemWrap.animate(props, time, conf.easing, fn || function () {
fire.trigger("onSeek", [i]);
});
return self;
}
});
// callbacks
$.each(['onBeforeSeek', 'onSeek', 'onAddItem'], function (i, name) {
// configuration
if ($.isFunction(conf[name])) {
$(self).bind(name, conf[name]);
}
self[name] = function (fn) {
$(self).bind(name, fn);
return self;
};
});
// circular loop
if (conf.circular) {
var cloned1 = self.getItems().slice(-1).clone().prependTo(itemWrap),
cloned2 = self.getItems().eq(1).clone().appendTo(itemWrap);
cloned1.add(cloned2).addClass(conf.clonedClass);
self.onBeforeSeek(function (e, i, time) {
if (e.isDefaultPrevented()) {
return;
}
/*
1. animate to the clone without event triggering
2. seek to correct position with 0 speed
*/
if (i == -1) {
self.seekTo(cloned1, time, function () {
self.end(0);
});
return e.preventDefault();
} else if (i == self.getSize()) {
self.seekTo(cloned2, time, function () {
self.begin(0);
});
}
});
// seek over the cloned item
self.seekTo(0, 0);
}
// next/prev buttons
var prev = find(root, conf.prev).click(function () {
self.prev();
}),
next = find(root, conf.next).click(function () {
self.next();
});
if (!conf.circular && self.getSize() > 1) {
self.onBeforeSeek(function (e, i) {
prev.toggleClass(conf.disabledClass, i <= 0);
next.toggleClass(conf.disabledClass, i >= self.getSize() - 1);
});
}
// mousewheel support
if (conf.mousewheel && $.fn.mousewheel) {
root.mousewheel(function (e, delta) {
if (conf.mousewheel) {
self.move(delta < 0 ? 1 : -1, conf.wheelSpeed || 50);
return false;
}
});
}
if (conf.keyboard) {
$(document).bind("keydown.scrollable", function (evt) {
// skip certain conditions
if (!conf.keyboard || evt.altKey || evt.ctrlKey || $(evt.target).is(":input")) {
return;
}
// does this instance have focus?
if (conf.keyboard != 'static' && current != self) {
return;
}
var key = evt.keyCode;
if (vertical && (key == 38 || key == 40)) {
self.move(key == 38 ? -1 : 1);
return evt.preventDefault();
}
if (!vertical && (key == 37 || key == 39)) {
self.move(key == 37 ? -1 : 1);
return evt.preventDefault();
}
});
}
// initial index
$(self).trigger("onBeforeSeek", [conf.initialIndex]);
}
// jQuery plugin implementation
$.fn.scrollable = function (conf) {
// already constructed --> return API
var el = this.data("scrollable");
if (el) {
return el;
}
conf = $.extend({}, $.tools.scrollable.conf, conf);
this.each(function () {
el = new Scrollable($(this), conf);
$(this).data("scrollable", el);
});
return conf.api ? el : this;
};
})(jQuery);