var slider, make_slider;

(function($) {
     var FLOAT_BUFFER = 10, SLIDE_DURATION = 150, SLIDE_EASING = 'linear',
	 NUDGE_OFFSET = 30, NUDGE_DURATION = 150;

     make_slider = function(item_list) {
	 var wrap = item_list.wrap('<div class="slider-wrap"/>').parent(),
	     slider = wrap.wrap('<div class="slider"/>').parent(),
	     prev_btn = $('<a href="#prev" class="slide-prev"><span>&laquo;</span></a>').prependTo(slider),
	     next_btn = $('<a href="#next" class="slide-next"><span>&raquo;</span></a>').appendTo(slider);
	 return slider;
     };

     slider = function($elem, per_page, item_width, focused_index, adjust_rule, width_rule) {
	 var item_list = $elem.find('div.item-list,div.items'),
	     items = $elem.find('div.item'),
	     total_width = items.length * item_width,
	     visible_width = per_page * item_width,
	     pages = Math.ceil(items.length / per_page),
	     page = 1;

	 focused_index = focused_index || 1;
	 adjust_rule = adjust_rule || 'marginLeft';
	 width_rule = width_rule || 'width';

	 var prev_btn = $elem.find('a.slide-prev').click(prev),
	     next_btn = $elem.find('a.slide-next').click(next);

	 function prev(e) {
	     e.preventDefault(); $(this).blur();
	     (page > 1) ? slide(page - 1) : nudge(NUDGE_OFFSET);
	 }

	 function next(e) {
	     e.preventDefault(); $(this).blur();
	     (page < pages) ? slide(page + 1) : nudge(-1 * NUDGE_OFFSET);
	 }

	 function adjust(value) {
	     var params = {};
	     params[adjust_rule] = value;
	     return params;
	 }

	 function slide(new_page, no_animation) {
	     if (page != new_page) {
		 page = new_page;
		 if (no_animation) {
		     item_list.css(adjust(margin()));
		 }
		 else {
		     item_list.animate(adjust(margin()), SLIDE_DURATION, SLIDE_EASING);
		 }
		 disable();
	     }
	 }

	 function margin() {
	     return -1 * (page - 1) * visible_width;
	 }

	 function nudge(offset) {
	     var orig = margin();
	     item_list
		 .animate(adjust(orig + offset), { duration: NUDGE_DURATION })
		 .animate(adjust(orig), { duration: NUDGE_DURATION });
	 }

	 function page_of(index) {
	     return Math.ceil(index / per_page);
	 }

	 function disabled(button, is_disabled) {
	     button[is_disabled ? 'addClass' : 'removeClass']('disabled');
	 }

	 function disable() {
	     disabled(prev_btn, page == 1);
	     disabled(next_btn, page == pages);
	 }

	 //
	 // setup
	 //

	 item_list.css(width_rule, total_width + FLOAT_BUFFER);

	 disable();
	 slide(page_of(focused_index), true);

	 return slider;
     };
 })(jQuery);
