/**
 * DTK Carousel Component.
 * @description http://twiki.corp.yahoo.com/view/Media/DTKDaemonManager
 **/

(function(){

var $U = YAHOO.util;
var $D = $U.Dom;
var $C = $U.CustomEvent;
var DTK = YAHOO.namespace('Media.Dtk');
YAHOO.namespace('Media.Dtk.util');

/**
 * spawn {function}
 * This function creates an object of type "cls", constructed with arguments in the "args" array.
 * spawn : new :: apply : call
 * Note: even though this function is private, you can still arbitrarily spawn unmanaged daemons using the public spawnDaemon method.
 * @private
 * @param cls {Class} The function constructor to use.
 * @param args {Array} The arguments to pass to the constructor.
 **/
var spawn = function(cls,args) {
	if(!args) args = [];
	else if(!args instanceof Array) args = [args];
	var f=function(){};
	f.prototype=cls.prototype;
	f.prototype.constructor = cls;
	var obj = new f();
	cls.apply(obj,args);
	return obj;
};

/**
 * YAHOO.Media.Dtk.util.Manager
 * Daemon Manager Constructor
 * @public
 * @param daemonClass {Class} The constructor that will be used to create daemons.
 * If this daemonClass does not implement the getId or toString functions, or an "id" property, then generic versions will be added.
 **/
DTK.util.Manager = function(daemonClass) {

	/**
	 * daemons {object}
	 * A collection of all the managed daemons.  manager.daemons[daemon.getId()] == daemon
	 * @public
	 **/
	this.daemons={};
	
	/**
	 * stack {Array}
	 * An array of all the managed daemons.  (Refers to the same objects as the daemons collection.)
	 * @public
	 **/
	this.stack=[];
	
	
	var idCounter = [0];
	var p=daemonClass.prototype;
	
	if(typeof p.getId !== 'function') {
		p.getId = function(){
			if(this.id) {
				return this.id;
			}
			return (this.id = 'daemon_'+(idCounter[0]++));
		};
	}
	if(typeof p.toString !== 'function') {
		p.toString = function() {
			return 'Daemon ' + this.getId();
		};
	}
	/**
	 * onCreate {CustomEvent}
	 * Event that fires whenever a daemon is spawned.
	 * @privileged
	 **/
	this.onCreate = new $C('create',this);
	/**
	 * onDaemonEvent {CustomEvent}
	 * Event that fires whenever any daemon's custom event of any sort fires.
	 * As far as the listener is concerned, it will be just as if it was listening to the actual event that fired.
	 * @privileged
	 **/
	this.onDaemonEvent = new $C('daemonEvent'); // actual event time at firing time will likely be different.
	/**
	 * daemonClass
	 * The class that's used for daemons.
	 * @public
	 **/
	this.daemonClass = daemonClass;
	
	/**
	 * unload {function}
	 * A function to help with memory management in IE; called on window.unload.
	 * If daemonClass implements an unload method, then all managed daemons will be unloaded.
	 * Then the references to the daemons are nulled.
	 * @private
	 **/
	var unload = function(e){
		var s=this.stack;
		for(var i=s.length-1; i>-1; i--) {
			if(s[i]) {
				s[i].manager = null;
				if(typeof(s[i].unload) == 'function') {
					s[i].unload();
				}
				this.daemons[s[i].getId()] = null;
				s[i] = null;
			}
		}
	};
	$U.Event.addListener(window,'unload',unload,this,true);
};
DTK.util.Manager.prototype={
	/**
	 * daemonEvents {Object}
	 * If any daemon implements any CustomEvents, then a correllary CustomEvent is created as a pass-through.
	 * As a result, assigning a listener to manager.daemonEvents.onFoo is the same as assigning a listener to all of the managed daemons' onFoo event.
	 * @public
	 **/
	daemonEvents:{},
	
	/**
	 * spawnDaemon {Function}
	 * method to create a daemon object.  Fires the onCreate event.
	 * manager.spawnDaemon('a','b') is equivalent to new manager.daemonClass('a','b'), except that the onCreate event will be fired.
	 * @params {Optional} Any parameters passed to this function will be sent to the daemon constructor.
	 * @public
	 **/
	spawnDaemon:function() {
		var obj = spawn(this.daemonClass,arguments)
		this.onCreate.fire(obj);
		return obj;
	},
	/**
	 * idString {String}
	 * String that identifies what kind of manager this object is.  Designed to be overwritten by the classes that extend the Manager class.
	 * @public
	 **/
	idString:'Generic Daemon Manager', // designed to be overwritten in classes that extend the Manager class.
	/**
	 * toString {function}
	 * Method to identify this object.  Uses the idString property.
	 * @public
	 **/
	toString:function(){
		var s=[this.idString,'{\n'];
		for(var c in this.daemons) {
			if(this.daemons[c] instanceof this.daemonClass) {
				s.push('\t',c,' : (',this.daemons[c].toString(),')\n');
			}
		}
		s.push('}');
		return s.join('');
	},
	/**
	 * init {Function}
	 * Create a single daemon and manage it.
	 * @params {Optional} Any parameters sent to this function will be passed to the daemon constructor.
	 * @return A reference to the created daemon object.
	 **/
	init:function() {
		var d=this.spawnDaemon.apply(this,arguments);
		
		if(d && d.manager != this) {
			var onDaemonEvent = this.onDaemonEvent;
			d.manager = this;
			this.daemons[d.getId()] = d;
			this.stack.push(d);
			for(var e in d){
				// walk through all the daemon's properties looking for customevents to watch for.
				// this implements a "bubbling" sort of functionality.
				if(d[e] instanceof $C) {
					if(!this.daemonEvents[e]) {
						this.daemonEvents[e]=new $C(d[e].type);
					}
					var evMgr = this.daemonEvents[e];
					var fn=function(type,data){
						// fire the manager's version of the child event, and the onDaemonEvent, as if they were the ones that happened in the first place -- same scope, same type, same everything.
						evMgr.scope=this;
						evMgr.fire.apply(evMgr,data);
						onDaemonEvent.scope=this;
						onDaemonEvent.type=type;
						onDaemonEvent.fire.apply(onDaemonEvent,data);
					};
					d[e].subscribe(fn);
				}
			}
		}
		return d;
	},
	/**
	 * initAll {Function}
	 * Run init() a bunch of times.
	 * Note: often overridden or extended in classes that extend the Manager class.
	 * @param finder {Function} Function that returns an array of objects that can be sent as the first argument to the daemon constructor.  For example, it could be a function that returns an array of DOM nodes.
	 * @params {Optional} Additional parameters are passes as additional arguments to the daemon constructor function.
	 * @return An array of references to the created daemon objects.
	 **/
	initAll:function(finder) {
		if(typeof(finder) != 'function') return [];
		var things = finder();
		var ret = [];
		var len = things.length;
		var args = [null];
		var arglen=arguments.length;
		for(var i=1;i<arglen;i++) {
			args.push(arguments[i]);
		}
		for(var i = 0; i < len; i++) {
			args[0] = things[i];
			var d=this.init.apply(this, args);
			if(d) {
				ret.push(d);
			}
		}
		return ret;
	},
	/**
	 * getDaemonById {Function}
	 * Get a reference to a certain daemon by its ID.
	 * @param id {string || HTMLElement} The ID of the daemon, or an HTML element with an id that is the id of the daemon.
	 * @return A reference to the daemon, or null if not found.
	 **/
	getDaemonById:function(id) {
		if(id.id) return this.getDaemonById(id.id);
		return this.daemons[id] || null;
	}
};

})();



/**
 * DTK Carousel Component.
 * @description http://twiki.corp.yahoo.com/view/Media/DTKCarousel
 * @requires DTK Manager Utility
 **/

// keep out of global scope.
(function() {

// shorthand
var $U=YAHOO.util;
var $D=$U.Dom;
var $E=$U.Event;
var $S=$U.Scroll;
var DTK=YAHOO.namespace('Media.Dtk');

var Carousel; // varred here, but defined inside its own scope.  This is the line in.
(function(){

// private static methods used by DTK.Carousel
/**
 * addPageIndicators Method
 * @description Adds the links to individual pages
 * @private
 * @param C {Carousel Object} Reference to the carousel that is getting set up.
 **/
var addPageIndicators=function() {
	
	// check to see if there's already one there.  If so, we're going to enslave its babies.
	var n=$D.getElementsByClassName('scrollnav','div',this.getElement());
	n=n[0] || document.createElement('div');
	n.className='scrollnav';
	
	removeNavLinks.call(this);
	
	var p=this.pages.length;
	for(var x=0;x<p;x++){
		var a=this.navLinks[x] || document.createElement('a');
		a.href='#pg:'+(x+1);
		a.index=x;
		a.carousel=this;
		a.onmousedown=a.onclick=this.scrollTo_click;
		if(x==this.current) {
			a.className='current';
		}
		n.appendChild(a);
		this.navLinks[x]=a;
	}
	
	var s=this.scrollBody;
	s.parentNode.insertBefore(n,s);
	return;
};

/**
 * removeNavLinks Method
 * @description Clears out the navLinks array.  Called by unload, and also by addPageIndicators if pages were removed by an ajax call.
 * @private
 * @param C {Carousel Object} Reference to the carousel whose navLinks are getting yanked out.
 **/
var removeNavLinks=function() {
	if(this.navLinks) {
		for(var j=this.navLinks.length-1;j>-1;j--) {
			var a=this.navLinks[j];
			if(a) {
				a.onclick=null;
				a.onmousedown=null;
				a.onmouseup=null;
				a.carousel=null;
			}
			a=null;
			this.navLinks[j]=null;
	//		delete this.navLinks[j];
		}
	}
	this.navLinks=[];
};
	
/**
 * addNavButtons Method
 * @description Adds the next/prev page indicators.
 * @private
 * @param C {Carousel Object} Reference to the carousel that is getting set up.
 **/
var addNavButtons=function() {
	// create prev/next links, if they haven't already been done.
	if(this.prev || this.next) return;
	var p=document.createElement('a'), n=p.cloneNode(true);
	
	var i=this.getElement().id;
	
	n.href=p.href="#"+i;
	
	// add appropriate classes to each
	p.className="prev";
	n.className="next";
	
	// insert nodes into dom before <div class="scrollbody">
	var s=this.scrollBody;
	s.parentNode.insertBefore(p, s);
	s.parentNode.insertBefore(n, s);
	
	// add handlers
	p.onmousedown=p.onclick=this.scrollPrev_click;
	n.onmousedown=n.onclick=this.scrollNext_click;
	
	this.prev=p;
	this.next=n;
	n.carousel=p.carousel=this;
	s=null;
};
/**
 * updateNavState Method
 * @description Update the state of the page/prev/next links.
 * @private
 * @param C {Carousel Object} Reference to the carousel that is getting updated.
 **/
var updateNavState=function() {
	// get # of pages
	var l = this.navLinks.length;
	
	// update page indicator styles
	for (var x=0; x<l; x++) {
		if(x == this.current) {
			this.navLinks[x].className='current';
		} else {
			this.navLinks[x].className='';
		}
	}
	// update button styles
	if(this.current == 0 && !this.roundRobin){
		// first page (left inactive)
		$D.addClass(this.prev,'off');
		//$D.setStyle(this.prev,'opacity',0.6);
		$D.removeClass(this.next,'off');
		//$D.setStyle(this.next,'opacity',1);
		$D.setStyle(this.next,'cursor','')
		$D.setStyle(this.prev,'cursor','default')
	}else if(this.current == (l-1) && !this.roundRobin){
		// last page (right inactive)
		$D.removeClass(this.prev,'off');
		//$D.setStyle(this.prev,'opacity',1);
		$D.addClass(this.next,'off');
		//$D.setStyle(this.next,'opacity',0.6);
		$D.setStyle(this.prev,'cursor','')
		$D.setStyle(this.next,'cursor','default')
	} else {
		// all other pages (all active)
		$D.removeClass(this.prev,'off');
		//$D.setStyle(this.prev,'opacity',1);
		$D.removeClass(this.next,'off');
		//$D.setStyle(this.next,'opacity',1);
		$D.setStyle(this.next,'cursor','')
		$D.setStyle(this.prev,'cursor','')
	}
};
/**
 * clickHandler method
 * @description Creates an event handler which can be assigned to a carousel A tag.  Used to create the scrollNext_click, scrollPrev_click, and scrollTo_click methods.
 * @private
 * @param whichFn {String} The name of the member function of the Carousel object to call, if appropriate.  Note: Not a function--send in the NAME of a function as a string.
 * @param fnFailure {Function} In addition to !a and !a.carousel, additional failure cases can be specified.  This function is called with the A tag passed as an arg.  If it returns True, then the carousel operation is aborted and normal behavior is used.
 * @param fnArgs {Function} This function can be specified to send additional arguments to the carousel function.  The A tag is passed as an argument, and the return value is sent to fnWhich.
 **/
var clickHandler=function(whichFn,fnFailure,fnArgs) {
	return function(e) {
		e=e||window.event;
		fnFailure=fnFailure || function(){return false;};
		fnArgs=fnArgs || function(){};
		var a=$E.getTarget(e);
		if(!a || !a.carousel || fnFailure(a)) {
			return true;
		}
		if(!a.didMouseDown) {
			// do the stuff, only the first time.
			a.carousel.autoPlay=false;
			a.carousel[whichFn](fnArgs(a));
		} else {
			// if we got a mousedown, then this is click, and it's time to blur.
			// flag will be reset on the next line.
			a.blur();
		}
		// record or reset.
		a.didMouseDown=(e.type=='mousedown');
		// break reference to prevent the leak.
		a=null;
		$E.stopEvent(e);
		return false;
	};
};

Carousel = function(el,args) {
	
	/**
	 * self-reference.
	 * @private
	 **/
	var me=this;
	/**
	 * currentScroll property
	 * Stores the current position of the scroller.
	 * @private
	 **/
	var currentScroll=0;
	/**
	 * getCurrentScroll method
	 * @return {Number} the current position of the scroller.
	 * @privileged
	 **/
	this.getCurrentScroll=function() {
		return currentScroll;
	};
	/**
	 * ontween Animation onTween handler.
	 * Used to keep track of the scrollLeft property of the scrollBody.
	 * @private
	 **/
	var ontween=function(e,data){
		// this is faster than just looking it up from the DOM.
		currentScroll=this.anim.doMethod('scroll', this.animAttrs.scroll.from, this.animAttrs.scroll.to)[0];
	};
	
	/**
	 * oncomplete Animation onComplete handler.
	 * Fires the onPageChange event.
	 * @private
	 **/
	var oncomplete=function(e,data){
		// only fire if it actually finished.
		if(data[0].duration >= this.animDur) {
			data=data[0];
			data.carousel=this;
			var _toString=data.toString;
			data.toString = function(){ return _toString() + ', current page:' + this.carousel.current; };
			//data.toString=function(){return this.time.getTime()+' duration:'+this.duration+', frames:'+this.frames+', fps:'+this.fps+', carousel:'+this.carousel.toString();};
			this.onPageChange.fire(data);
		}
	};
	/**
	 * onclick Method
	 * Attached to click event of scrollBody
	 * Through event bubbling, fires whenever an element in the scrolling body is clicked, unless event is caught and killed before bubbling up.
	 * fires the onClick event.
	 * @private
	 **/
	var onclick=function(e) {
		this.onClick.fire(e);
	};
	/**
	 * element Object
	 * Reference to the root of the Carousel element.
	 * Accessible via the privileged getElement() method.
	 * @private
	 **/
	var _element=null;
	
	/**
	 * getElement prototype method
	 * @privileged
	 * @return {HTMLElement | null} The root element of the Carousel.
	 * @description Note: The element is not set until init is called.
	 **/
	this.getElement=function() {
		return _element;
	};
	/**
	 * init Method
	 * @param el { String | HTMLElement } ID of or reference to the root element of the carousel
	 * @param args { Object } Name-value pair of any public member items that should be replaced.  For example, you send it {easeMethod:YAHOO.util.easeNone,animDur:2} to overwrite the default easing method and animation duration.
	 *  With great power comes great responsibility!
	 * @description Typically called by CarouselMgr.init or CarouselMgr.initAll.
	 * @privileged
	 **/
	this.init=function(el,args) {
		el=$D.get(el);
		if(el) {
			_element=el;
		}
		$D.generateId(el,'carousel_');
		s = $D.getElementsByClassName('scrollbody','div',el)[0];
		
		if(typeof(args) == 'object') {
			for(var i in args) {
				this[i]=args[i];
			}
		}
		
		
		var me=this;
		var list = this.pages = $D.getElementsBy(function(el) { return me.pageFinder(el); },this.pageTagName,el);
		var len = list.length;
		
		if(!el || !s || !len) {
			return false;
		}
		
		// set up the exposed variables that don't exist pre-init.
		this.onScrollStart=new $U.CustomEvent('scrollstart',this);
		this.onPageChange=new $U.CustomEvent('scrollcomplete',this);
		this.onClick=new $U.CustomEvent('click',this);
		this.onAutoPlayStart=new $U.CustomEvent('autoplaystart',this);
		this.onAutoPlayStop=new $U.CustomEvent('autoplaystop',this);
		
		var r = $D.getRegion(list[0]);		// get region of first item, as all "page" items should be same width
		this.scrollDistance = r.right - r.left;			// width of first "page" item
		
		this.scrollBody = s;
		$E.addListener(s,'click',onclick,this,true);
		
		// set some styles here to make carousels less rude to the myBar
		var h=$D.getRegion(s.parentNode);
		h=(h.bottom-h.top)+'px';
		s.parentNode.style.height=h;
		s.style.height=h;
		s.style.position='absolute';
		//s.style.overflow='auto';
		var p=$D.getElementsByClassName('scrollpages','div',s)[0];
		p.style.width=(len * this.scrollDistance * 1.0)+'px';
		p.style.position='absolute';
		
		// http://bug.corp.yahoo.com/show_bug.cgi?id=832779
		// figure out what page we're REALLY on (which will usually be 0), and then go there.
		// this needs to be down here, because the browser won't get the right values unless the styles and heights above are set.
		// This is the ONLY time that we manually read the scrollLeft property, since this tends to be very sluggish to read in Mozilla.
		var sl = Math.round(s.scrollLeft / this.scrollDistance);
		if(sl < 0) sl = 0;
		else if(sl >= this.pages.length) sl = this.pages.length-1;
		this.current = sl;
		
		
		this.scrollBody.scrollLeft = currentScroll = sl * this.scrollDistance;
		
		this.anim=new $S(this.scrollBody , this.animAttrs, this.animDur, this.easeMethod);
		this.anim.onTween.subscribe(ontween,this,true);
		this.anim.onComplete.subscribe(oncomplete,this,true);
		
		if( len > 1 ){
			// only add nav buttons and page indicators if more than one page
			addNavButtons.call(this);
			addPageIndicators.call(this);
			updateNavState.call(this);
		} else this.navLinks=[];
		list=s=null;
		return true;
	};
	
	
	/**
	 * autoPlayTimeOut
	 * @private
	 * Recording the timeout ID so that it can be cleared when autoplay is stopped.
	 **/
	var autoPlayTimeOut=0;
	/**
	 * autoPlayFn {null | Function}
	 * @private
	 * @description Function that actually switches the card.  Set as a timeout by autoPlayer.
	 **/
	var autoPlayFn=function(){
		me.autoPlay= (me.autoPlayDirection>0)?me.scrollNext():me.scrollPrev();
	};
	/**
	 * autoPlaySubscribed {Boolean}
	 * True if autoPlayer has subscribed to the onPageChange event.
	 * @private
	 **/
	var autoPlaySubscribed=false;
	/**
	 * autoPlayer {Function}
	 * @private
	 * @description The autoPlay workhorse.  Sets up the proper things based on the value of this.autoPlay.
	 **/
	var autoPlayer=function() {
		clearTimeout(autoPlayTimeOut);
		
		// check to see if it's going to fail before it does.
		// this ends autoplay as soon as it's known that it will stop.
		if(me.autoPlay && !me.roundRobin && (me.current == me.pages.length-1 && me.autoPlayDirection > 0 || me.autoPlayDirection <= 0 && me.current == 0) ) {
			me.autoPlay = false;
		}
		
		if(!me.autoPlay) {
			// stop if running.  Unsubscribe.
			me.onPageChange.unsubscribe(autoPlayer);
			autoPlaySubscribed=false;
			me.onAutoPlayStop.fire(me.current);
		} else {
			/*
			1. In me.autoPlayDur seconds, call autoPlayFn
			2. This scrolls next or prev.
			3. When the scrolling is done, it triggers the onPageChange event.
			4. This calls autoPlayer.  (Goto step 1.)
			*/
			if(!autoPlaySubscribed) {
				autoPlaySubscribed=true;
				me.onPageChange.subscribe(autoPlayer);
				autoPlayFn();
			} else {
				autoPlayTimeOut=window.setTimeout(autoPlayFn,me.autoPlayDur*1000);
			}
		}
	};
	/**
	 * autoPlayStart method
	 * @description Starts the autoPlay
	 * @privileged
	 **/
	this.autoPlayStart=function() {
		if(!this.autoPlay) {
			this.onAutoPlayStart.fire(this.current);
		}
		this.autoPlay=true;
		autoPlayer();
	};
	/**
	 * autoPlayStop method
	 * @description Stops the autoPlay
	 * @privileged
	 **/
	this.autoPlayStop=function() {
		this.autoPlay=false;
		autoPlayer();
	};
	
	/**
	 * getData method
	 * @description Called to make an ajax call for the rest of a collection.  It expects that the data to be found at url will be a JSON string of the form [{pgIdx:<page number>, pgHtml:<html of the page>},{...},...]
	 * @param url {String} url to request
	 * @param postExecute {Function} function to call after elements have been loaded 
	 * @privileged
	 **/
	this.getData = function(url,postExecute) {
		var id=this.getId();
		var me=this;
		var s = function() { me.getDataSuccess.apply(me,arguments); };
		var f = function() { me.getDataFailure.apply(me,arguments); };
		var callback = {
			success: s,
			failure: f,
			argument: {
				postExecute:postExecute
			}
		};
		var oConObj = $U.Connect.asyncRequest('GET',url,callback,null);
	};
	
    /**
	 * getDataSuccess method
	 * @description Called upon successful execution of ajax call
	 * @privileged
	 **/
	this.getDataSuccess = function(o) {
		if(o.responseText){
			var sText = o.responseText;
			/*
			* remove any comments that might have been added, 
			* yapache shouldn't send them if the header is application/x-json,
			* but you never know 
			*/
			sText = sText.replace(/<\!--.+-->/gim,'');
			// instantiate json
			var oArr = eval('(' + sText + ')');
			// loop through end of array
			var createdPages = false, removedPages=false;
			if( oArr ){
				var numPages = this.pages.length;
				var len=oArr.length;
				for(var i=0;i<len;i++){
					var pg = oArr[i].pgIdx;
					var html = oArr[i].pgHtml;
					var page;
					if(pg < numPages && pg >= 0){
						page=this.pages[pg];
					} else {
						// page is out of bounds.
						// make a new one.
						var p=this.pages[numPages-1];
						page = p.cloneNode(false);
						p.parentNode.appendChild(page);
						this.pages[numPages++]=page;
						createdPages=true;
					}
					page.innerHTML=html;
					if(oArr[i].attributes) {
						for(var a in oArr[i].attributes) {
							page.setAttribute(a,oArr[i].attributes[a]);
						}
					}
				}
				// trim the pages that didn't get data, if there are any.
				while(i < numPages) {
					this.pages[i].parentNode.removeChild(this.pages[i]);
					delete this.pages[i];
					removedPages = true;
					i++;
				}
				
				// now we might need to re-do the navLinks.
				if(createdPages || removedPages) {
					addPageIndicators.call(this);
				}
			}
			
			//if a "postExecute" function is passed in, execute it.
			if (o.argument.postExecute) {
				o.argument.postExecute();
			}
		}
	};
	
	/**
	 * getDataFailure method
	 * @description Called when getData call returns an error
	 * @privileged
	 **/
	this.getDataFailure = function(o) {
		// stub func
	};
	
	/**
	 * An unload handler that releases all elements and breaks any circular links caused by this object.
	 * Called by the CarouselMgr on page unload.
	 * @privileged
	 **/
	this.unload = function() {
		if(!this.navLinks){this.navLinks = [];}
		this.navLinks.push(_element, this.prev, this.next, this.scrollBody, this.anim);
		removeNavLinks.call(this);
		for(var j=this.pages.length-1;j>-1;j--) {
			this.pages[j]=null;
		}
	};
	
	/**
	 * animAttrs
	 * @public
	 * Attributes passed to the animation object.
	 **/
	this.animAttrs={
		scroll:{
			from:[0,0],
			to:[0,0]
		}
	};
	
	// initialize this carousel.
	if(el)this.init(el,args);
};

Carousel.prototype = {
	/**
	 * pageFinder { Function }
	 * Passed to Dom.getElementsBy to define what a "page" is.
	 * Defaults to getting by className of this.pageClassName
	 * @public
	 **/
	pageFinder:function(el) {
		return $D.hasClass(el,this.pageClassName);
	},
	/**
	 * pageTagName { String }
	 * Passed to Dom.getElementsBy to define what a "page" is.  This speeds up the grabbing of pages.
	 * @default 'div'
	 * @public
	 **/
	pageTagName:'div',
	/**
	 * pageClassName {String}
	 * Passed to dom.getElementsBy to define what a "page" is.  This is the className that is searched for by default.
	 * Note that if pageFinder is overwritten, then this might not have any effect.
	 * @public
	 * @default scrollpage
	 **/
	pageClassName:'scrollpage',
	
	/**
	 * easeMethod
	 * Easing method to be used by animation.
	 * @default YAHOO.util.Easing.easeOut
	 * @public
	 **/
	easeMethod:$U.Easing.easeOut,
	/**
	 * roundRobin
	 * Whether or not this carousel can go around in circles.
	 * @public
	 **/
	roundRobin:false,
	/**
	 * animDur
	 * duration of the animation, in seconds.
	 * @public
	 * @default 1.5 seconds
	 **/
	animDur:1.5,
	/**
	 * autoPlayDur
	 * @public
	 * The duration in seconds that autoplay should switch the card.
	 **/
	autoPlayDur:5,
	/**
	 * onScrollStart
	 * {Object CustomEvent} Event that occurs when the scrolling starts.
	 * @public
	 **/
	 onScrollStart:null,
	 /**
	  * onPageChange
	 * {Object CustomEvent} Event that occurs when the scrolling completes.
	 * @public
	 **/
	onPageChange:null,
	 /**
	  * onClick
	 * {Object CustomEvent} Event that occurs when the contents of the carousel are clicked.
	 * @public
	 **/
	onClick:null,
	
	/**
	 * autoPlayDirection
	 * {Number} The direction that autoPlay should cycle.  Positive for "next," negative for "prev"
	 * @public
	 * @default 1
	 **/
	autoPlayDirection:1, // set to negative for previous scrolling.
	/**
	 * onAutoPlayStart
	 * {CustomEvent} Event that fires when the autoplay starts.
	 * @public
	 **/
	onAutoPlayStart:null,
	/**
	 * onAutoPlayStop
	 * {CustomEvent} Event that fires when autoplay stops.
	 * @public
	 **/
	onAutoPlayStop:null,
	
	/**
	 * scrollTo Method
	 * @param index {Integer} The index of the page to scroll to.
	 * @public
	 * @description Scrolls to a given "index" (page numbers, starting with 0)  Called by scrollNext and scrollPrev.
	 **/
	scrollTo:function(index) {
//		console.log('scrollto(' +index+') in ',this);
//		this.anim.fps=10;
		//console.log(this.anim);
		// this is an exposed function, so deal with bad arg.
		// do nothing if:
			// index is null or undefined
			// index is too big or too small (and not roundRobin)
			// index refers to the current page
		if(this.roundRobin) {
			if(index < 0) {
				index=this.pages.length-1;
			} else if(index >= this.pages.length) {
				index=0;
			}
		}
		if((!index && index !== 0) || index >= this.pages.length || index < 0 || index == this.current) {
			return false;
		}
		
		if(this.anim.isAnimated()) {
			this.anim.stop();
		}
		
		this.onScrollStart.fire({to:index,from:this.current,toString:function(){return 'from:'+this.from+', to:'+this.to;}});
		
		// set the destination.
		var end=index * this.scrollDistance;
		this.animAttrs.scroll.from=[this.getCurrentScroll(),0];
		this.animAttrs.scroll.to=[end,0];
		this.current=index;
		updateNavState.call(this);
		
		// set the attributes each time so that we can take into consideration changes that may occur to the exposed object.
		this.anim.attributes=this.animAttrs;
		this.anim.duration = this.animDur;
		
		// this setTimeout makes it work properly in Firefox, by making it asynchronous.
		var a=this.anim;
		window.setTimeout(function(){a.animate();},0);
		//a.animate();
		return true;
	},
	/**
	 * scrollNext Method
	 * @public
	 * @description Scrolls to the next page.
	 **/
	scrollNext : function() {
		var ret=this.scrollTo(this.current+1);
		return ret;
	},
	/**
	 * scrollNext_click Method
	 * @public
	 * @description Scrolls to the next page.  If attached to the click and mousedown events, it will handle keyboard and mouse events properly and accessibly.
	 * <p>Usage: var a=YAHOO.util.Dom.get('nextpagelink'); a.carousel=YAHOO.Media.Dtk.CarouselMgr.init('dtk-car-0'); a.onclick=a.carousel.scrollNext_click; </p>
	 **/
	scrollNext_click : clickHandler('scrollNext'),
	/**
	 * scrollNext Method
	 * @public
	 * @description Scrolls to the previous page.
	 **/
	scrollPrev : function() {
		var ret= this.scrollTo(this.current-1);
		return ret;
	},
	/**
	 * scrollPrev_click Method
	 * @public
	 * @description Scrolls to the prev page.  If attached to the click and mousedown events, it will handle keyboard and mouse events properly and accessibly.
	 * <p>Usage: var a=YAHOO.util.Dom.get('prevpagelink'); a.carousel=YAHOO.Media.Dtk.CarouselMgr.init('dtk-car-0'); a.onclick=a.carousel.scrollPrev_click; </p>
	 **/
	scrollPrev_click:clickHandler('scrollPrev'),
	/**
	 * scrollTo_click Method
	 * @public
	 * @description Scrolls to a given next page.  If attached to the click and mousedown events, it will handle keyboard and mouse events properly and accessibly.
	 * <p>Usage: var a=YAHOO.util.Dom.get('page2link'); a.index=2; a.carousel=YAHOO.Media.Dtk.CarouselMgr.init('dtk-car-0'); a.mousedown=a.onclick=a.carousel.scrollTo_click; </p>
	 **/
	scrollTo_click:clickHandler('scrollTo',function(el){return (!el.index && el.index!==0);},function(el){return el.index;}),
	/**
	* toString method
	* @return {String} string represenation of carousel obj
	*/
	toString:function(){
		var el=this.getElement();
		if(el) {
			return 'Carousel #'+el.id+' .'+el.className;
		} else {
			return 'Carousel [Not Initiated]';
		}
		el=null;
	},
	/**
	 * getId methods
	 * @public
	 * @description Returns the ID of the carousel's root element.
	 * Used by the CarouselMgr to identify individual Carousel daemons.
	 **/
	getId:function(){
		return this.getElement().id;
	}
}

})(); // end of the Carousel scope.


(function(){ // begin scope for CarouselMgr.
/**
 * CarouselMgr
 * @class Singleton for managing Carousel Widgets
 * <p>Usage: YAHOO.Media.Dtk.CarouselMgr.init("myDtkElement");</p>
 * @requires YAHOO.util.Scroll
 * @requires YAHOO.util.Dom
 * @requires YAHOO.util.Event
 * @requires Carousel
 **/
var CarouselMgr = function(){
	CarouselMgr.superclass.constructor.call(this,Carousel);
};
YAHOO.extend(CarouselMgr,DTK.util.Manager);
/**
 * Method to init all carousels that have a certain className
 * @privileged
 * @param cls { String } The className that designates a carousel element. Defaults to "dtk-carousel"
 * @param args { Object } Optional.  Arguments passed to Carousel.init()
 * @return Collection of all carousels created by this method.
 **/
CarouselMgr.prototype.initAll = function(cls,args){
	return CarouselMgr.superclass.initAll.call(this, function(){ return $D.getElementsByClassName(cls||'dtk-carousel','div',document);}, args);
};
CarouselMgr.prototype.getCarousel = CarouselMgr.prototype.getDaemonById;
CarouselMgr.prototype.idString='Carousel Manager';

DTK.CarouselMgr = new CarouselMgr();

})(); // end of the CarouselMgr scope



})();
