/*
It's now cycle-ps.
Version 2.2011.09.09.05

NOTICE OF LICENSE

This source file is subject to the Academic Free License (AFL 3.0)
that is bundled with this package in the file LICENSE_AFL.txt.
It is also available through the world-wide-web at this URL:
http://opensource.org/licenses/afl-3.0.php
If you did not receive a copy of the license and are unable to
obtain it through the world-wide-web, please send an email
to license@sitesquad.net so we can send you a copy immediately.

@category		Sitesquad_Cycle
@package		cycle-ps
@author			Sitesquad Engineering Team
@author			Dante Piombino
@copyright	Copyright (c) 2011 Sitesquad LLC (http://www.sitesquad.net)
@license		http://opensource.org/licenses/afl-3.0.php  Academic Free License (AFL 3.0)

Easing Equations:
Based on Easing Equations v2.0
(c) 2003 Robert Penner, all rights reserved. 
OPEN-SOURCE TERMS OF USE: http://www.robertpenner.com/easing_terms_of_use.html
Adapted for Scriptaculous by Ken Snyder (kendsnyder.com) June 2006
Adapted for Sitesquad Cycle-ps.js by Dante Piombino
*/

Cycle = Class.create(Abstract, {
	initialize: function (scroller, options) {
		this.scrolling	= false;
		this.scroller	= $(scroller);
		if(!this.scroller) { return false; }
		
		this.scope = this.scroller.identify();
		
		this.options = Object.extend({
			speed: 1.0,
			speedin: null,
			speedout: null,
			auto: true,
			timeout: 3.0, //0 is the same as auto:false
			visibleSlides: 1,
			wrap: true,
			fx: 'scrollLeft',
			easing: 'linear',
			easein: null,
			easeout: null,
			next: 'cycle-next',
			prev: 'cycle-prev',
			pager: 'cycle-pager', //id for pager block
			onPagerEvent: null, //callback fn for pager events: function(slide index 0+, slide element)
			onPrevNextEvent: null, //callback fn for prev/next events: function(isNext, slide index 0+, slide element)
			pause: false, // true to enable "pause on hover" on the slide
			pauseOnPagerHover: false // true to pause when hovering over pager links
		}, options || {});
		
		if(this.options.effect) { this.options.fx = this.options.effect; }
		this.paused = false;
		
		this.slides		= this.scroller.firstDescendant().childElements();
		this.slides.each(function(slide, index) {
			slide._index = index;
		});

		//special setups
		switch (this.options.fx) {
			case 'fade':
				this.options.wrap = true;
				this.scroller.style.overflow = 'hidden';
				break;
			case 'scrollLeft':
			case 'scrollRight':
			case 'scrollHorz':
			case 'scroll':
				this.scroller.firstDescendant().setStyle({'width':'1000000px'});
				this.scroller.style.overflow = 'hidden';
				this.scroller.firstDescendant().childElements().invoke('setStyle', {'float':'left', 'position':'relative'});
				this.scroller.style.position = this.scroller.style.position ? this.scroller.style.position : 'relative'; //IE7 overflow fix
				break;
			case 'scrollUp':
			case 'scrollDown':
			case 'scrollVert':
				this.scroller.firstDescendant().setStyle({'height':'1000000px'});
				this.scroller.style.overflow = 'hidden';
				this.scroller.firstDescendant().childElements().invoke('setStyle', {'float':'none', 'position':'relative'});
				this.scroller.style.position = this.scroller.style.position ? this.scroller.style.position : 'relative'; //IE7 overflow fix
				break;
		}
		
    //check the timeout duration to stay on slide
    if(!Object.isNumber(this.options.timeout) || this.options.timeout < 0) { 
    	this.options.timeout = 0;
    	this.options.auto = false;
    }
		
		//check speeds
		if(!Object.isNumber(this.options.speed)) { this.options.speedin = 1; }
		if(!Object.isNumber(this.options.speedin)) { this.options.speedin = this.options.speed; }
		if(!Object.isNumber(this.options.speedout)) { this.options.speedout = this.options.speed; }
		
		//check easings
		if(!this.options.easing) { this.options.easing = 'linear';}
		if(!this.options.easein) { this.options.easein = this.options.easing; }
		if(!this.options.easeout) { this.options.easeout = this.options.easing; }
		this.easing = Effect.Easing.use(this.options.easing);
		this.easein = Effect.Easing.use(this.options.easein);
		this.easeout = Effect.Easing.use(this.options.easeout);

		if(this.options.wrap) {
			var temp = this.slides.first().cloneNode(true);
			this.slides.push(temp);
			this.scroller.firstDescendant().insert(temp);
		}

		if(this.options.pause || this.options.pauseOnPagerHover) {
			this.scroller.observe('cycle:pause', this.pause.bind(this));
			this.scroller.observe('cycle:resume', this.resume.bind(this));
		}			

		//pause on hover
		if(this.options.pause) {
			this.scroller.observe('mouseover', this.pause.bind(this));
		}

		if($(this.options.pager)) {
			this.options.pageritemclass = this.options.pager+'item';
			this.options.pageractiveclass = this.options.pager+'item-active';
			this.pager = new Cycle.Pager(this.options.pager, this);
		}

		if($(this.options.next)) {
			$(this.options.next).observe('click', this.nextclick.bind(this));
		}
		if($(this.options.prev)) {
			$(this.options.prev).observe('click', this.prevclick.bind(this));
		}
		
		//autostart
		if (this.options.auto) {
			this.start();
		}

	},

	jumpTo: function (element) {
		this.stop();
		this.timer = null;
		this.moveTo(element);
		this.start();
	},

	moveTo: function (element) {
		
		this.previous = this.current ? this.current : this.slides[0];
		
		if(this.slides.indexOf(this.current) == this.slides.size()-1) {
			this.current = this.slides[0];
			this.scroller.firstDescendant().setStyle({'left':'0px'});
		} else {
			this.current = $(element);
		}
		
		if(!this.current) { return false; }
		
		if (this.options.beforeMove && (typeof this.options.beforeMove == 'function')) {
			this.options.beforeMove();
		}

		var scrollerOffset = this.scroller.cumulativeOffset();
		var elementOffset  = $(this.current).cumulativeOffset();

		if (this.scrolling) {
			this.scrolling.cancel();
		}
		this.scroller.fire("cycle:moved", {to:this.current});
		
		switch (this.options.fx) {
			case 'fade':
				this.scrolling = new Effect.Opacity(this.scroller, {
					from:   1.0,
					to:     0,
					transition: this.easeout,
					queue: { position: 'end', scope: this.scope },
					duration: this.options.speedout,
					afterFinish: (function () {
						this.scroller.scrollLeft = elementOffset[0] - scrollerOffset[0];
						this.scroller.scrollTop  = elementOffset[1] - scrollerOffset[1];
						new Effect.Opacity(this.scroller, {
							from: 0,
							to: 1.0,
							duration: this.options.speedin,
							transition: this.easein,
							queue: { position: 'end', scope: this.scope },
							afterFinish: (function () {
								if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
									this.options.afterMove();
								}
							}).bind(this)
						});
					}).bind(this)
				});
				break;
			case 'scroll':
			default:	
				this.scrolling = new Effect.Move(this.scroller.firstDescendant(), {
					duration: this.options.speed,
					queue: { position: 'end', scope: this.scope },
					mode: 'absolute',
					x: -1*($(this.scroller).getWidth()*this.slides.indexOf(this.current)),
					y: (elementOffset[1] - scrollerOffset[1]),
					transition: this.easing,
					afterFinish: (function () {
						if(this.slides.indexOf(this.current) == this.slides.size()-1) {
							this.current = this.slides[0];
							this.scroller.firstDescendant().setStyle({'left':'0px'});
						}
						if (this.options.afterMove && (typeof this.options.afterMove == 'function')) {
							this.options.afterMove();
						}
						this.scrolling = false;
					}).bind(this)
				});
				break;
			}

		return false;
	},

	prevclick: function(ev) {
		if(this.options.auto) {
			this.stop();
			this.timer = null;
		}
		this.prev();
		if(this.options.auto) {
			this.start();
		}
	},
	prev: function () {
		var prevs = 1; //(this.options.wrap ? this.slides.size() - 1 : 0);
		
		if (this.current) {
			if(this.options.wrap && this.slides.indexOf(this.current) == 0) {
				prevs = this.slides.size() -1;
				this.scroller.firstDescendant().setStyle({'left':(-1*($(this.scroller).getWidth()*(this.slides.size()-1)))+'px'});
			} else {
				prevs = this.slides.indexOf(this.current);
			}
		}
		prevs = prevs - 1 < 0 ? 0 : prevs - 1;

		//next/prev event
		if(Object.isFunction(this.options.onPrevNextEvent)) {
			this.options.onPrevNextEvent(false, prevs, this.slides[prevs]);
		}
		this.moveTo(this.slides[prevs]);
	},
	
	nextclick: function(ev) {
		if(this.options.auto) {
			this.stop();
			this.start();
		} else {
			this.next();
		}
	},
	next: function () {
		var nxt = 1;

		if (this.current) {
			nxt = (this.slides.length - 1 == this.current._index) ? (
			this.options.wrap ? 0 : this.current._index) 
			: this.current._index + 1;
		} 
		
		if (nxt == 0 && this.options.wrap && this.options.fx != 'fade') {
			this.scroller.scrollLeft = 0;
//			this.scroller.scrollTop  = 0;
			nxt = 1;
		}

		if (nxt > this.slides.length - (this.options.visibleSlides + 1)) {
			nxt = this.slides.length - this.options.visibleSlides;
		}		

		if(Object.isFunction(this.options.onPrevNextEvent)) {
			this.options.onPrevNextEvent(true, nxt, this.slides[nxt]);
		}
		this.moveTo(this.slides[nxt]);
	},

	first: function () {
		this.moveTo(this.slides[0]);
	},

	last: function () {
		this.moveTo(this.slides[this.slides.length - 1]);
	},

	toggle: function () {
		if (this.previous) {
			this.moveTo(this.slides[this.previous._index]);
		} else {
			return false;
		}
	},

	stop: function () {
		if (this.timer) {
			clearInterval(this.timer);
		}
	},

	start: function () {
			this.periodicallyUpdate();
	},

	pause: function () {
		this.paused = new Date();
		this.scroller.observe('mouseout', this.resume.bind(this));
		this.stop();
	},

	resume: function (event) {
		this.scroller.stopObserving('mouseout');
		if (event || this.paused != 0) {
			this.paused = new Date() - this.paused;
			this.paused = this.paused > this.options.timeout * 1000 ? 0 : (this.options.timeout * 1000) - this.paused;
			var related = event.relatedTarget || event.toElement;
			if (!related || (!this.slides.include(related) && !this.slides.any(function (slide) { return related.descendantOf(slide); }))) {
				this.timer = setInterval(this.periodicallyUpdate.bind(this), this.paused);
				this.paused = 0;
			}
		} else {
			this.paused = false;
			this.start();
		}
	},

	periodicallyUpdate: function () {
		if (this.timer != null) {
			clearInterval(this.timer);
			this.next();
		}
		this.timer = setInterval(this.periodicallyUpdate.bind(this), this.options.timeout * 1000);
	},

	currentitem: function() {
		var at = 0;
		if(this.current) {
			at = this.slides.indexOf(this.current);
		}
		if(this.options.wrap && at + 1 >= this.slides.length) {
			at = 0;
		}
		return at;
	}

});

// add extra transitions and make the access a little easier
Effect.Easing = Object.extend(Effect.Transitions, {
	use: function(trans) {
		switch (trans.toLowerCase()) {
			case 'spring':
				transition = this.spring;
				break;
			case 'sinoidal':
				transition = this.sinoidal;
				break;
			case 'pulse':
				transition = this.pulse;
				break;
			case 'flicker':
				transition = this.flicker;
				break;
			case 'wobble':
				transition = this.wobble;
				break;
			case 'elastic':
				transition = this.elastic;
				break;
			case 'easeto':
				transition = this.easeTo;
				break;
			case 'strongeaseto':
				transition = this.strongEaseTo;
				break;
			case 'easefrom':
				transition = this.easeFrom;
				break;
			case 'strongeasefrom':
				transition = this.strongEaseFrom;
				break;
			case 'easefromto':
				transition = this.easeFromTo;
				break;
			case 'swingfrom':
				transition = this.swingFrom;
				break;
			case 'swingto':
				transition = this.swingTo;
				break;
			case 'swingfromto':
				transition = this.swingFromTo;
				break;
			case 'bounce':
				transition = this.bounce;
				break;
			case 'bouncepast':
				transtion = this.bouncePast;
				break;
			case 'instant':
			case 'full':
				transition = this.full;
				break;
			case 'none':
			case '':
				transition = this.none;
				break;
			case 'linear':
			default:
				transition = this.linear;
				break;
		}
		return transition;
	},
  elastic: function(pos) {
    return -1*Math.pow(4,-8*pos)*Math.sin((pos*6-1)*(2*Math.PI)/2)+1;
  },
  easeFromTo: function(pos) {
    return -0.5*((pos-=2)*pos*pos*pos-2);
  },
  easeFrom: function(pos) {
    return Math.pow(pos,2);
  },
	strongEaseFrom: function(pos) {
		return Math.pow(pos, 4)*2;
	},
  easeTo: function(pos) {
    return Math.pow(-1*pos,4);
  },
  strongEaseTo: function(pos) {
    return -1*Math.pow(2,-10*pos)+1;
  },
  swingFromTo: function(pos) {
    var s=1.70158;
    if((pos/=0.5)<1) return 0.5*(pos*pos*(((s*=(1.525))+1)*pos-s));
    return 0.5*((pos-=2)*pos*(((s*=(1.525))+1)*pos+s)+2);
  },
  swingFrom: function(pos) {
    var s=1.70158;
    return pos*pos*((s+1)*pos-s);
  },
  swingTo: function(pos) {
    var s=1.70158;
    return(pos-=1)*pos*((s+1)*pos+s)+1;
  },
  bounce: function(pos) { 
    if(pos<(1/2.75)) {
      return (7.5625*pos*pos);
    } else if(pos<(2/2.75)) {
      return (7.5625*(pos-=(1.5/2.75))*pos+.75);
    } else if(pos<(2.5/2.75)) {
      return (7.5625*(pos-=(2.25/2.75))*pos+.9375);
    } else {
      return (7.5625*(pos-=(2.625/2.75))*pos+.984375);
    }
  },
  bouncePast: function(pos) {
    if(pos<(1/2.75)) { 
      return (7.5625*pos*pos);
    } else if(pos<(2/2.75)) {
      return 2-(7.5625*(pos-=(1.5/2.75))*pos+.75);
    } else if(pos<(2.5/2.75)) {
      return 2-(7.5625*(pos-=(2.25/2.75))*pos+.9375);
    } else {
      return 2-(7.5625*(pos-=(2.625/2.75))*pos+.984375);
    }
  }	
});


Cycle.Pager = Class.create(Abstract, {
    // Constructor
    initialize : function(element, scroller) {
      this.pager = $(element);
      this.scope = this.pager.identify();
      this.options = scroller.options;
      this.itemclass = this.options.pageritemclass || 'cycle-pageritem';
      this.activeclass = this.options.pageractiveclass || 'cycle-pageritem-active';

      this.cycle = scroller;
      this.createpager();
			
			if(this.options.pauseOnPagerHover) {
				this.pager.observe('mouseover', this.hover.bind(this.cycle.scroller));
				this.pager.observe('mouseout', this.nohover.bind(this.cycle.scroller));
			}

      this.cycle.scroller.observe("cycle:moved", this.updatepager.bind(this));
    },

    goToPage: function(page) {
      this.cycle.jumpTo(this.cycle.slides[page]);
      this.updatepager();
			//pager event
			if(Object.isFunction(this.cycle.options.onPagerEvent)) {
				//function(zeroBasedSlideIndex, slideElement)
				this.options.onPagerEvent(page, this.cycle.slides[page]);
			}      
    },

    createpager: function() {
      var pages = Math.ceil(this.cycle.slides.length / this.options.visibleSlides);
      pages -= this.options.wrap ? 1 : 0;
      this.ul = this.pager.down('ul') || new Element('ul');

      var fragment = document.createDocumentFragment();
      for (var i=0; i < pages; i++) {
        fragment.appendChild(new Element('li', {'id':this.itemclass+'-'+(i+1)}).addClassName(this.itemclass).update(i+1));
      }
      
      this.ul.appendChild(fragment);
      
			//if the UL didn't already exist
      if (!this.ul.parentNode) {
        this.pager.insert(this.ul);
      }
      this.ul.observe('click', this.scrollpager.bind(this));

      this.list = this.ul.select('li');
      this.updatepager();
    },

    updatepager: function (event) {
      this.list.invoke('removeClassName', this.activeclass);
      this.list[this.currentpage()].addClassName(this.activeclass);
    },

    currentpage: function() {
      return Math.round(this.cycle.currentitem() / this.options.visibleSlides);
    },

    scrollpager: function (event) {
    	if(event) {
    		var el = Event.findElement(event, 'li.'+this.itemclass);
    		if(el) {
		      this.goToPage(this.list.indexOf(el));
	      //this.updatepager();
	    	}
	    Event.stop(event);
	    }
    },
    
    hover: function(event) {
    	if(event) {
				this.fire("cycle:pause");
				Event.stop(event);
			}
    },
    
    nohover: function(event) {
    	if(event) {
				this.fire("cycle:resume");
				Event.stop(event);
			}
    }
});

