/***************************************************
** sliderule.js
**
** relies on prototype.js and scriptaculous effects.js
** Design Kitchen
** @author: Matthew Story
****************************************************/
var SlideRule = Class.create();
SlideRule.prototype = {
	showBox: false,
	slider: false,
	listeners: [],
	transition: false,
	onPageChange: false,
	
	//this is a scriptaculous transition effect . . . the real effect not the name:
	//e.g. Effect.Transitions.slowstop
	transitionEffect: false,
	
	/*the constructor takes 2 arguments and an optional 3rd
	 * 1. the viewer/showBox element, this is the outer element
	 * 2. the element that will be sliding to and fro inside the viewer/showBox
	 * 3. (optional) options object with the following options:
	 * 	- pageForeward: element/element id or array of elements/element ids that on click go foreward a page
	 * 	- pageBackward: element/element id or array of elements/element ids that on click go backward a page
	 * 	- transition: boolean, weather or not to use a scriptaculous effect to slide
	 * 	- transitionEffect: class constructor of a scriptaculous transition effect, to define a custom transition
	 * 	- onPageChange: function to be called after a page change (usefull for enabling/disabling nav
	*/
	initialize: function(showBox, slider) {
		var defaults = {
			pageForeward: false,
			pageBackward: false,
			transition: false,
			transitionEffect: false,
			onPageChange: function() {}
		};
		var options = (arguments[2]) ? arguments[2]:{};
		Object.extend(defaults, options);
		defaults.pageForeward = this.toArray(defaults.pageForeward);
		defaults.pageBackward = this.toArray(defaults.pageBackward);
		defaults.transition = (defaults.transitionEffect) ? true:defaults.transition;

		//set up the sliderule
		this.showBox = $(showBox);
		this.slider = $(slider);

		//lets hard set the slider width if it's not
		if (!this.slider.style.width) {
			this.hardWidthifySlider();
		}

		this.clip(this.showBox);
		this.relativize(this.slider);
		this.position(this.slider);
		
		//set up effect
		this.transition = defaults.transition;
		this.transitionEffect = defaults.transitionEffect;
		this.onPageChange = defaults.onPageChange;

		//set up listeners
		for (var i=0;i<defaults.pageForeward.length;i++) {
			this.registerPageForewardListener(defaults.pageForeward[i]);
		}
		for (var i=0;i<defaults.pageBackward.length;i++) {
			this.registerPageBackwardListener(defaults.pageBackward[i]);
		}
		
		//turn to first page
		this.goToPage(1);
	},

	destroy: function() {
		this.showBox = false;
		this.slider = false;
		this.destroyListeners();
		this.transition = false;
		this.transitionEffect = false;

		return true;
	},
	
	getNumPages: function() {
		return Math.floor(this.slider.offsetWidth/this.showBox.offsetWidth)+1;
	},

	getCurrentPage: function() {
		return Math.floor(Math.abs(parseInt(this.slider.style.left)/this.showBox.offsetWidth) + 1);
	},
	
	turnPage: function() {
		this.goToPage(this.getCurrentPage() + 1);
		return true;
	},

	turnBackPage: function() {
		this.goToPage(Math.max((this.getCurrentPage() - 1), 1));
		return true;
	},

	goToPage: function(pageNum) {
		pageNum = (pageNum > this.getNumPages()) ? this.getNumPages()-1:pageNum-1;
		if (!this.transition) {
			this.position(this.slider, this.showBox.offsetWidth*pageNum*-1);
		} else if (this.pageNum != this.getCurrentPage()-1) {
			var options = (this.transitionEffect) ? {transition: this.transitionEffect}:{};
			new Effect.MoveBy(this.slider, 0, (this.getCurrentPage()-pageNum-1)*this.showBox.offsetWidth, options);
		}
		
		this.onPageChange(pageNum+1, this);
		return true;
	},
	
	bindEventToPage: function(page, element) {
		var event = (arguments[2]) ? arguments[2]:'click';
		var listener = this.goToPage.bindAsEventListener(this);
		this.registerListener(element, event, function() {listener(page);});

		return true;
	},
	
	registerPageForewardListener: function(element) {
		var event = (arguments[1]) ? arguments[1]:'click';
		this.registerListener(element, event, this.turnPage.bindAsEventListener(this));

		return true;
	},

	registerPageBackwardListener: function(element) {
		var event = (arguments[1]) ? arguments[1]:'click';
		this.registerListener(element, event, this.turnBackPage.bindAsEventListener(this));

		return true;
	},

	registerListener: function(element, event, handler) {
		this.listeners.push({
			element: element,
			event: event,
			handler: handler
		});
		Event.observe(element, event, handler);
	},
	
	destroyListener: function(element, event, handler) {
		var newListeners = [];
		for (var i=0;i<this.listeners.length;i++) {
			if (this.listeners[i] == {element: element, event: event, handler: handler}) {
				Event.stopObserving(this.listeners[i].element, this.listeners[i].event, this.listeners[i].handler);
			} else {
				newListeners.push(this.listeners[i]);
			}
		}
		this.listeners = newListeners;

		return true;
	},
	
	destroyListeners: function() {
		for (var i=0;i<this.listeners.length;i++) {
			Event.stopObserving(this.listeners[i].element, this.listeners[i].event, this.listeners[i].handler);
		}
		this.listeners = [];
	},

	clip: function(element) {
		element.style.overflow = 'hidden';
		return true;
	},

	unclip: function(element) {
		element.style.overflow = '';
		return true;
	},

	relativize: function(element) {
		element.style.position = 'relative';
		return true;
	},

	unrelativize: function(element) {
		element.style.position = '';
		return true;
	},

	position: function(element) {
		var offsetRight = (arguments[1]) ? arguments[1]:0;
		element.style.left = offsetRight + 'px';

		return true;
	},

	unposition: function(element) {
		element.style.right = '';
		return true;
	},
	
	hardWidthifySlider: function() {
		this.slider.style.position = 'absolute';
		this.slider.style.width = this.slider.offsetWidth + 'px';
		this.relativize(this.slider);
		this.showBox.appendChild(this.slider);
	},

	toArray: function(value) {
		value = (value && value.constructor != Array) ? [value]:value;
		return value;
	}
};
