/**
 * Copyright (c) 2006-2007, Bill W. Scott
 * All rights reserved.
 *
 * This work is licensed under the Creative Commons Attribution 2.5 License. To view a copy 
 * of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to 
 * Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
 *
 * This work was created by Bill Scott (billwscott.com, looksgoodworkswell.com).
 * 
 * The only attribution I require is to keep this notice of copyright & license 
 * in this original source file.
 *
 * Version 0.6.1 - 07.08.2007
 *
 */
SHEBANG.namespace("extension");
SHEBANG.extension.Carousel = function(carouselElementID, carouselCfg) {
 		this.init(carouselElementID, carouselCfg);
	};
SHEBANG.extension.Carousel.prototype = {
	UNBOUNDED_SIZE: 1000000,
	init: function(carouselElementID, carouselCfg) {
		var oThis = this;
		this.getCarouselItem = this.getItem;
		// CSS style classes
		var carouselListClass = "carousel-list";
		var carouselClipRegionClass = "carousel-clip-region";
		var carouselNextClass = "carousel-next";
		var carouselPrevClass = "carousel-prev";

 		this._carouselElemID = carouselElementID;
 		this.carouselElem = SHEBANG.util.Dom.get(carouselElementID);

 		this._prevEnabled = true;
 		this._nextEnabled = true;
 		
 		// Create the config object
 		this.cfg = new SHEBANG.util.Config(this);

		/**
		 * scrollBeforeAmount property. Normally, set to 0.
		 */
		this.cfg.addProperty("scrollBeforeAmount", { 
			value:0, 
			handler: function(type, args, carouselElem) {
			},
			validator: oThis.cfg.checkNumber
		} );		

		/**
		 * scrollAfterAmount property. Normally, set to 0.
		 */
		this.cfg.addProperty("scrollAfterAmount", { 
			value:0, 
			handler: function(type, args, carouselElem) {
			},
			validator: oThis.cfg.checkNumber
		} );		

		/**
		 * loadOnStart property. 
		 * If true, will call loadInitHandler on startup.
		 * If false, will not. Useful for delaying the initialization
		 * of the carousel for a later time after creation.
		 */
		this.cfg.addProperty("loadOnStart", { 
			value:true, 
			handler: function(type, args, carouselElem) {
				// no action, only affects startup
			},
			validator: oThis.cfg.checkBoolean
		} );		

		/**
		 * orientation property. 
		 * Either "horizontal" or "vertical". Changes carousel from a 
		 * left/right style carousel to a up/down style carousel.
		 */
		this.cfg.addProperty("orientation", { 
			value:"horizontal", 
			handler: function(type, args, carouselElem) {
				oThis.reload();
			},
			validator: function(orientation) {
			    if(typeof orientation == "string") {
			        return ("horizontal,vertical".indexOf(orientation.toLowerCase()) != -1);
			    } else {
					return false;
				}
			}
		} );		

		/**
		 * size property. 
		 * The upper bound for scrolling in the 'next' set of content. 
		 * Set to a large value by default.
		 */
		this.cfg.addProperty("size", { 
			value:this.UNBOUNDED_SIZE,
			handler: function(type, args, carouselElem) {
				oThis.reload();
			},
			validator: oThis.cfg.checkNumber
		} );

		/**
		 * numVisible property. 
		 * The number of items that will be visible.
		 */
		this.cfg.addProperty("numVisible", { 
			value:3,
			handler: function(type, args, carouselElem) {
				oThis.reload();
			},
			validator: oThis.cfg.checkNumber
		} );

		/**
		 * firstVisible property. 
		 * Sets which item should be the first visible item in the carousel. Use to set which item will
		 * display as the first element when the carousel is first displayed. After the carousel is created,
		 * you can manipulate which item is the first visible by using the moveTo() or scrollTo() convenience
		 * methods. Can be < 1 or greater than size if the scrollBeforeAmount or scrollAmountAfter has been set
		 * to non-zero values.
		 */
		this.cfg.addProperty("firstVisible", { 
			value:1,
			handler: function(type, args, carouselElem) {
				oThis.moveTo(args[0]);
			},
			validator: oThis.cfg.checkNumber
		} );

		/**
		 * scrollInc property. 
		 * The number of items to scroll by. Think of this as the page increment.
		 */
		this.cfg.addProperty("scrollInc", { 
			value:3,
			handler: function(type, args, carouselElem) {
			},
			validator: oThis.cfg.checkNumber
		} );
		
		/**
		 * animationSpeed property. 
		 * The time (in seconds) it takes to complete the scroll animation. 
		 * If set to 0, animated transitions are turned off and the new page of content is 
		 * moved immdediately into place.
		 */
		this.cfg.addProperty("animationSpeed", { 
			value:0.5,
			handler: function(type, args, carouselElem) {
				oThis.animationSpeed = args[0];
			},
			validator: oThis.cfg.checkNumber
		} );

		/**
		 * animationMethod property. 
		 */
		this.cfg.addProperty("animationMethod", { 
			value:  SHEBANG.util.Easing.elasticOut,
			handler: function(type, args, carouselElem) {
			}
		} );
		
		/**
		 * animationCompleteHandler property. 
		 * JavaScript function that is called when the Carousel finishes animation 
		 * after a next or previous nagivation. 
		 * Only invoked if animationSpeed > 0. 
		 * Two parameters are passed: type (set to 'onAnimationComplete') and 
		 * args array (args[0] = direction [either: 'next' or 'previous']).
		 */
		this.cfg.addProperty("animationCompleteHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._animationCompleteEvt) {
					oThis._animationCompleteEvt.unsubscribe(oThis._currAnimationCompleteHandler, oThis);
				}
				oThis._currAnimationCompleteHandler = args[0];
				if(oThis._currAnimationCompleteHandler) {
					if(!oThis._animationCompleteEvt) {
						oThis._animationCompleteEvt = new SHEBANG.util.CustomEvent("onAnimationComplete", oThis);
					}
					oThis._animationCompleteEvt.subscribe(oThis._currAnimationCompleteHandler, oThis);
				}
			}
		} );
		
		/**
		 * autoPlay property. 
		 * Specifies how many milliseconds to periodically auto scroll the content. 
		 * If set to 0 (default) then autoPlay is turned off. 
		 * If the user interacts by clicking left or right navigation, autoPlay is turned off. 
		 * You can restart autoPlay by calling the <em>startAutoPlay()</em>. 
		 * If you externally control navigation (with your own event handlers) 
		 * then you may want to turn off the autoPlay by calling<em>stopAutoPlay()</em>
		 */
		this.cfg.addProperty("autoPlay", { 
			value:0,
			handler: function(type, args, carouselElem) {
				var autoPlay = args[0];
				if(autoPlay > 0)
					oThis.startAutoPlay();
				else
					oThis.stopAutoPlay();
			}
		} );
		
		/**
		 * wrap property. 
		 * Specifies whether to wrap when at the end of scrolled content. When the end is reached,
		 * the carousel will scroll backwards to the item 1 (the animationSpeed parameter is used to 
		 * determine how quickly it should animate back to the start.)
		 * Ignored if the <em>size</em> attribute is not explicitly set 
		 * (i.e., value equals SHEBANG.extension.Carousel.UNBOUNDED_SIZE)
		 */
		this.cfg.addProperty("wrap", { 
			value:false,
			handler: function(type, args, carouselElem) {
			},
			validator: oThis.cfg.checkBoolean
		} );
		
		/**
		 * navMargin property. 
		 * The margin space for the navigation controls. This is only useful for horizontal carousels 
		 * in which you have embedded navigation controls. 
		 * The <em>navMargin</em> allocates space between the left and right margins 
		 * (each navMargin wide) giving space for the navigation controls.
		 */
		this.cfg.addProperty("navMargin", { 
			value:0.01,
			handler: function(type, args, carouselElem) {
				oThis.calculateSize();		
			},
			validator: oThis.cfg.checkNumber
		} );
		
		/**
		 * revealAmount property. 
		 * The amount to reveal of what comes before and what comes after the firstVisible and
		 * the lastVisible items. Setting this will provide a slight preview that something 
		 * exists before and after, providing an additional hint for the user.
		 * The <em>revealAmount</em> will reveal the specified number of pixels for any item
		 * before the firstVisible and an item after the lastVisible.
		 */
		this.cfg.addProperty("revealAmount", { 
			value:0.01,
			handler: function(type, args, carouselElem) {
				oThis.reload();
			},
			validator: oThis.cfg.checkNumber
		} );
		
		// For backward compatibility. Deprecated.
		this.cfg.addProperty("prevElementID", { 
			value: null,
			handler: function(type, args, carouselElem) {
				if(oThis._carouselPrev) {
					SHEBANG.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev);
				} 
				oThis._prevElementID = args[0];
				if(oThis._prevElementID == null) {
					oThis._carouselPrev = SHEBANG.util.Dom.getElementsByClassName(carouselPrevClass, 
														"div", oThis.carouselElem)[0];
				} else {
					oThis._carouselPrev = SHEBANG.util.Dom.get(oThis._prevElementID);
				}
				SHEBANG.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis);
			}
		});
		
		/**
		 * prevElement property. 
		 * An element or elements that will provide the previous navigation control.
		 * prevElement may be a single element or an array of elements. The values may be strings denoting
		 * the ID of the element or the object itself.
		 */
		this.cfg.addProperty("prevElement", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._carouselPrev) {
					SHEBANG.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev);
				} 
				oThis._prevElementID = args[0];
				if(oThis._prevElementID == null) {
					oThis._carouselPrev = SHEBANG.util.Dom.getElementsByClassName(carouselPrevClass, 
														"div", oThis.carouselElem)[0];
				} else {
					oThis._carouselPrev = SHEBANG.util.Dom.get(oThis._prevElementID);
				}
				SHEBANG.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis);
			}
		} );
		
		// For backward compatibility. Deprecated.
		this.cfg.addProperty("nextElementID", { 
			value: null,
			handler: function(type, args, carouselElem) {
				if(oThis._carouselNext) {
					SHEBANG.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext);
				} 
				oThis._nextElementID = args[0];
				if(oThis._nextElementID == null) {
					oThis._carouselNext = SHEBANG.util.Dom.getElementsByClassName(carouselNextClass, 
														"div", oThis.carouselElem);
				} else {
					oThis._carouselNext = SHEBANG.util.Dom.get(oThis._nextElementID);
				}
				if(oThis._carouselNext) {
					SHEBANG.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis);
				} 
			}
		});
		
		/**
		 * nextElement property. 
		 * An element or elements that will provide the next navigation control.
		 * nextElement may be a single element or an array of elements. The values may be strings denoting
		 * the ID of the element or the object itself.
		 */
		this.cfg.addProperty("nextElement", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._carouselNext) {
					SHEBANG.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext);
				} 
				oThis._nextElementID = args[0];
				if(oThis._nextElementID == null) {
					oThis._carouselNext = SHEBANG.util.Dom.getElementsByClassName(carouselNextClass, 
														"div", oThis.carouselElem);
				} else {
					oThis._carouselNext = SHEBANG.util.Dom.get(oThis._nextElementID);
				}
				if(oThis._carouselNext) {
					SHEBANG.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis);
				} 
			}
		} );
		
		/**
		 * loadInitHandler property. 
		 * JavaScript function that is called when the Carousel needs to load.
		 */
		this.cfg.addProperty("loadInitHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._loadInitHandlerEvt) {
					oThis._loadInitHandlerEvt.unsubscribe(oThis._currLoadInitHandler, oThis);
				}
				oThis._currLoadInitHandler = args[0];
				if(oThis._currLoadInitHandler) {
					if(!oThis._loadInitHandlerEvt) {
						oThis._loadInitHandlerEvt = new SHEBANG.util.CustomEvent("onLoadInit", oThis);
					}
					oThis._loadInitHandlerEvt.subscribe(oThis._currLoadInitHandler, oThis);
				}
			}
		} );
		
		/**
		 * loadNextHandler property. 
		 * JavaScript function that is called when the Carousel needs to load 
		 * the next set of items (in response to the user navigating to the next set.) 
		 * Two parameters are passed: type (set to 'onLoadNext') and 
		 * args array (args[0] = start index, args[1] = last index).
		 */
		this.cfg.addProperty("loadNextHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._loadNextHandlerEvt) {
					oThis._loadNextHandlerEvt.unsubscribe(oThis._currLoadNextHandler, oThis);
				}
				oThis._currLoadNextHandler = args[0];
				if(oThis._currLoadNextHandler) {
					if(!oThis._loadNextHandlerEvt) {
						oThis._loadNextHandlerEvt = new SHEBANG.util.CustomEvent("onLoadNext", oThis);
					}
					oThis._loadNextHandlerEvt.subscribe(oThis._currLoadNextHandler, oThis);
				}
			}
		} );
				
		/**
		 * loadPrevHandler property. 
		 * JavaScript function that is called when the Carousel needs to load 
		 * the previous set of items (in response to the user navigating to the previous set.) 
		 * Two parameters are passed: type (set to 'onLoadPrev') and args array 
		 * (args[0] = start index, args[1] = last index).
		 */
		this.cfg.addProperty("loadPrevHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._loadPrevHandlerEvt) {
					oThis._loadPrevHandlerEvt.unsubscribe(oThis._currLoadPrevHandler, oThis);
				}
				oThis._currLoadPrevHandler = args[0];
				if(oThis._currLoadPrevHandler) {
					if(!oThis._loadPrevHandlerEvt) {
						oThis._loadPrevHandlerEvt = new SHEBANG.util.CustomEvent("onLoadPrev", oThis);
					}
					oThis._loadPrevHandlerEvt.subscribe(oThis._currLoadPrevHandler, oThis);
				}
			}
		} );
		
		/**
		 * prevButtonStateHandler property. 
		 */
		this.cfg.addProperty("prevButtonStateHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._currPrevButtonStateHandler) {
					oThis._prevButtonStateHandlerEvt.unsubscribe(oThis._currPrevButtonStateHandler, oThis);
				}

				oThis._currPrevButtonStateHandler = args[0];
				
				if(oThis._currPrevButtonStateHandler) {
					if(!oThis._prevButtonStateHandlerEvt) {
						oThis._prevButtonStateHandlerEvt = new SHEBANG.util.CustomEvent("onPrevButtonStateChange", oThis);
					}
					oThis._prevButtonStateHandlerEvt.subscribe(oThis._currPrevButtonStateHandler, oThis);
				}
			}
		} );
		
		/**
		 * nextButtonStateHandler property. 
		 */
		this.cfg.addProperty("nextButtonStateHandler", { 
			value:null,
			handler: function(type, args, carouselElem) {
				if(oThis._currNextButtonStateHandler) {
					oThis._nextButtonStateHandlerEvt.unsubscribe(oThis._currNextButtonStateHandler, oThis);
				}
				oThis._currNextButtonStateHandler = args[0];
				
				if(oThis._currNextButtonStateHandler) {
					if(!oThis._nextButtonStateHandlerEvt) {
						oThis._nextButtonStateHandlerEvt = new SHEBANG.util.CustomEvent("onNextButtonStateChange", oThis);
					}
					oThis._nextButtonStateHandlerEvt.subscribe(oThis._currNextButtonStateHandler, oThis);
				}
			}
		} );
		
		
 		if(carouselCfg) {
 			this.cfg.applyConfig(carouselCfg);
 		}
 		
		this._origFirstVisible = this.cfg.getProperty("firstVisible");
		
		// keep a copy of curr handler so it can be removed when a new handler is set
		this._currLoadInitHandler = this.cfg.getProperty("loadInitHandler");
		this._currLoadNextHandler = this.cfg.getProperty("loadNextHandler");
		this._currLoadPrevHandler = this.cfg.getProperty("loadPrevHandler");
		this._currPrevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler");
		this._currNextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler");
		this._currAnimationCompleteHandler = this.cfg.getProperty("animationCompleteHandler");
		
		this._nextElementID = this.cfg.getProperty("nextElementID");
		if(!this._nextElementID) 
			this._nextElementID = this.cfg.getProperty("nextElement");
		
		this._prevElementID = this.cfg.getProperty("prevElementID");
		if(!this._prevElementID) 
			this._prevElementID = this.cfg.getProperty("prevElement");

		this._autoPlayTimer = null;
		this._priorLastVisible = this._priorFirstVisible = this.cfg.getProperty("firstVisible");
		this._lastPrebuiltIdx = 0;
		 		
 		// prefetch elements
 		this.carouselList = SHEBANG.util.Dom.getElementsByClassName(carouselListClass, 
												"ul", this.carouselElem)[0];
							
		if(this._nextElementID == null) {
			this._carouselNext = SHEBANG.util.Dom.getElementsByClassName(carouselNextClass, 
												"div", this.carouselElem)[0];
		} else {
			this._carouselNext = SHEBANG.util.Dom.get(this._nextElementID);
		}

		if(this._prevElementID == null) {
 			this._carouselPrev = SHEBANG.util.Dom.getElementsByClassName(carouselPrevClass, 
												"div", this.carouselElem)[0];
		} else {
			this._carouselPrev = SHEBANG.util.Dom.get(this._prevElementID);
		}
		
		this._clipReg = SHEBANG.util.Dom.getElementsByClassName(carouselClipRegionClass, 
												"div", this.carouselElem)[0];
														
		// initialize the animation objects for next/previous
 		this._scrollNextAnim = new SHEBANG.util.Motion(this.carouselList, this.scrollNextParams, 
   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
 		this._scrollPrevAnim = new SHEBANG.util.Motion(this.carouselList, this.scrollPrevParams, 
   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
		
		// If they supplied a nextElementID then wire an event listener for the click
		if(this._carouselNext) {
			SHEBANG.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this);
		} 
		
		// If they supplied a prevElementID then wire an event listener for the click
		if(this._carouselPrev) {
			SHEBANG.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this);
		}
				
		// Wire up the various event handlers that they might have supplied
		var loadInitHandler = this.cfg.getProperty("loadInitHandler");
		if(loadInitHandler) {
			this._loadInitHandlerEvt = new SHEBANG.util.CustomEvent("onLoadInit", this);
			this._loadInitHandlerEvt.subscribe(loadInitHandler, this);
		}
		var loadNextHandler = this.cfg.getProperty("loadNextHandler");
		if(loadNextHandler) {
			this._loadNextHandlerEvt = new SHEBANG.util.CustomEvent("onLoadNext", this);
			this._loadNextHandlerEvt.subscribe(loadNextHandler, this);
		}
		var loadPrevHandler = this.cfg.getProperty("loadPrevHandler");
		if(loadPrevHandler) {
			this._loadPrevHandlerEvt = new SHEBANG.util.CustomEvent("onLoadPrev", this);
			this._loadPrevHandlerEvt.subscribe(loadPrevHandler, this);
		}
		var animationCompleteHandler = this.cfg.getProperty("animationCompleteHandler");
		if(animationCompleteHandler) {
			this._animationCompleteEvt = new SHEBANG.util.CustomEvent("onAnimationComplete", this);
			this._animationCompleteEvt.subscribe(animationCompleteHandler, this);
		}
		var prevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler");
		if(prevButtonStateHandler) {
			this._prevButtonStateHandlerEvt = new SHEBANG.util.CustomEvent("onPrevButtonStateChange", 
							this);
			this._prevButtonStateHandlerEvt.subscribe(prevButtonStateHandler, this);
		}
		var nextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler");
		if(nextButtonStateHandler) {
			this._nextButtonStateHandlerEvt = new SHEBANG.util.CustomEvent("onNextButtonStateChange", this);
			this._nextButtonStateHandlerEvt.subscribe(nextButtonStateHandler, this);
		}
		
		// Since loading may take some time, wire up a listener to fire when at least the first
		// element actually gets loaded
		var visibleExtent = this._calculateVisibleExtent();
  		SHEBANG.util.Event.onAvailable(this._carouselElemID + "-item-"+
					visibleExtent.start,  this._calculateSize, this);
  		
  		// Call the initial loading sequence
		if(this.cfg.getProperty("loadOnStart"))
			this._loadInitial();	

	},
	
	// /////////////////// Public API //////////////////////////////////////////

	/**
	 * Clears all items from the list and resets to the carousel to its original initial state.
	 */
	clear: function() {
		// remove all items from the carousel for dynamic content
		var loadInitHandler = this.cfg.getProperty("loadInitHandler");
		if(loadInitHandler) {
			this._removeChildrenFromNode(this.carouselList);
			this._lastPrebuiltIdx = 0;
		}
		// turn off autoplay
		this.stopAutoPlay(); // should we only turn this off for dynamic during reload?
		
		this._priorLastVisible = this._priorFirstVisible = this._origFirstVisible;
		
		// is this redundant since moveTo will set this?	
		this.cfg.setProperty("firstVisible", this._origFirstVisible, true);		
		this.moveTo(this._origFirstVisible);
	},
	
	/**
	 * Clears all items from the list and calls the loadInitHandler to load new items into the list. 
	 * The carousel size is reset to the original size set during creation.
	 * @param {number}	numVisible	Optional parameter: numVisible. 
	 * If set, the carousel will resize on the reload to show numVisible items.
	 */
	reload: function(numVisible) {
		// this should be deprecated, not needed since can be set via property change
	    if(this._isValidObj(numVisible)) {
			this.cfg.setProperty("numVisible", numVisible);
	    }
		this.clear();
		
		// clear resets back to start
		var visibleExtent = this._calculateVisibleExtent();
		SHEBANG.util.Event.onAvailable(this._carouselElemID+"-item-"+visibleExtent.start,
		 								this._calculateSize, this);  		
		this._loadInitial();
		
	},

	load: function() {
		var visibleExtent = this._calculateVisibleExtent();
		
		SHEBANG.util.Event.onAvailable(this._carouselElemID + "-item-"+visibleExtent.start, 
						this._calculateSize, this);  		
		this._loadInitial();
	},
		
	/**
	 * With patch from Dan Hobbs for handling unordered loading.
	 * @param {number}	idx	which item in the list to potentially create. 
	 * If item already exists it will not create a new item.
	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
	 * @param {string}	itemClass	A class optionally supplied to add to the LI item created
	 */
	addItem: function(idx, innerHTMLOrElem, itemClass) {
		
		if(idx > this.cfg.getProperty("size")) {
			return null;
		}
		
        var liElem = this.getItem(idx);

		// Need to create the li
		if(!this._isValidObj(liElem)) {
			liElem = this._createItem(idx, innerHTMLOrElem);
			this.carouselList.appendChild(liElem);
			
		} else if(this._isValidObj(liElem.placeholder)) {		
	    	var newLiElem = this._createItem(idx, innerHTMLOrElem);
			this.carouselList.replaceChild(newLiElem, liElem);
			liElem = newLiElem;
		}
		
		// if they supplied an item class add it to the element
		if(this._isValidObj(itemClass)){
			SHEBANG.util.Dom.addClass(liElem, itemClass);
		}
		
		/**
		 * Not real comfortable with this line of code. It exists for vertical
		 * carousels for IE6. For some reason LI elements are not displaying
		 * unless you after the fact set the display to block. (Even though
	     * the CSS sets vertical LIs to display:block)
	     */
		if(this.isVertical())
			setTimeout( function() { liElem.style.display="block"; }, 1 );		
				
		return liElem;

	},

	/**
	 * Inserts a new LI item before the index specified. Uses the innerHTML to create the contents of the new LI item
	 * @param {number}	refIdx	which item in the list to insert this item before. 
	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
	 */
	insertBefore: function(refIdx, innerHTML) {
		// don't allow insertion beyond the size
		if(refIdx >= this.cfg.getProperty("size")) {
			return null;
		}
		
		if(refIdx < 1) {
			refIdx = 1;
		}
		
		var insertionIdx = refIdx - 1;
		
		if(insertionIdx > this._lastPrebuiltIdx) {
			this._prebuildItems(this._lastPrebuiltIdx, refIdx); // is this right?
		}
		
		var liElem = this._insertBeforeItem(refIdx, innerHTML);
		
		this._enableDisableControls();
		
		return liElem;
	},
	
	/**
	 * Inserts a new LI item after the index specified. Uses the innerHTML to create the contents of the new LI item
	 * @param {number}	refIdx	which item in the list to insert this item after. 
	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
	 */
	insertAfter: function(refIdx, innerHTML) {
	
		if(refIdx > this.cfg.getProperty("size")) {
			refIdx = this.cfg.getProperty("size");
		}
		
		var insertionIdx = refIdx + 1;			
		
		// if we are inserting this item past where we have prebuilt items, then
		// prebuild up to this point.
		if(insertionIdx > this._lastPrebuiltIdx) {
			this._prebuildItems(this._lastPrebuiltIdx, insertionIdx+1);
		}

		var liElem = this._insertAfterItem(refIdx, innerHTML);		

		if(insertionIdx > this.cfg.getProperty("size")) {
			this.cfg.setProperty("size", insertionIdx, true);
		}
		
		this._enableDisableControls();

		return liElem;
	},	

	/**
	 * Simulates a next button event. Causes the carousel to scroll the next set of content into view.
	 */
	scrollNext: function() {
		this._scrollNext(null, this);
		
		// we know the timer has expired.
		//if(this._autoPlayTimer) clearTimeout(this._autoPlayTimer);
		this._autoPlayTimer = null;
		if(this.cfg.getProperty("autoPlay") !== 0) {
			this._autoPlayTimer = this.startAutoPlay();
		}
	},
	
	/**
	 * Simulates a prev button event. Causes the carousel to scroll the previous set of content into view.
	 */
	scrollPrev: function() {
		this._scrollPrev(null, this);
	},
	
	/**
	 * Scrolls the content to place itemNum as the start item in the view 
	 * (if size is specified, the last element will not scroll past the end.). 
	 * Uses current animation speed & method.
	 * @param {number}	newStart	The item to scroll to. 
	 */
	scrollTo: function(newStart) {
		this._position(newStart, true);
	},

	/**
	 * Moves the content to place itemNum as the start item in the view 
	 * (if size is specified, the last element will not scroll past the end.) 
	 * Ignores animation speed & method; moves directly to the item. 
	 * Note that you can also set the <em>firstVisible</em> property upon initialization 
	 * to get the carousel to start at a position different than 1.	
	 * @param {number}	newStart	The item to move directly to. 
	 */
	moveTo: function(newStart) {
		this._position(newStart, false);
	},

	/**
	 * Starts up autoplay. If autoPlay has been stopped (by calling stopAutoPlay or by user interaction), 
	 * you can start it back up by using this method.
	 * @param {number}	interval	optional parameter that sets the interval 
	 * for auto play the next time that autoplay fires. 
	 */
	startAutoPlay: function(interval) {
		// if interval is passed as arg, then set autoPlay to this interval.
		if(this._isValidObj(interval)) {
			this.cfg.setProperty("autoPlay", interval, true);
		}
		
		// if we already are playing, then do nothing.
		if(this._autoPlayTimer !== null) {
			return this._autoPlayTimer;
		}
				
		var oThis = this;  
		var autoScroll = function() { oThis.scrollNext(); };
		this._autoPlayTimer = setTimeout( autoScroll, this.cfg.getProperty("autoPlay") );
		
		return this._autoPlayTimer;
	},

	/**
	 * Stops autoplay. Useful for when you want to control what events will stop the autoplay feature. 
	 * Call <em>startAutoPlay()</em> to restart autoplay.
	 */
	stopAutoPlay: function() {
		if (this._autoPlayTimer !== null) {
			clearTimeout(this._autoPlayTimer);
			this._autoPlayTimer = null;
		}
	},
	
	/**
	 * Returns whether the carousel's orientation is set to vertical.
	 */
	isVertical: function() {
		return (this.cfg.getProperty("orientation") != "horizontal");
	},
	
	
	/**
	 * Check to see if an element (by index) has been loaded or not. If the item is simply pre-built, but not
	 * loaded this will return false. If the item has not been pre-built it will also return false.
	 * @param {number}	idx	Index of the element to check load status for. 
	 */
	isItemLoaded: function(idx) {
		var liElem = this.getItem(idx);
		
		// if item exists and is not a placeholder, then it is already loaded.
		if(this._isValidObj(liElem) && !this._isValidObj(liElem.placeholder)) {
			return true;
		}
		
		return false;
	},
	
	/**
	 * Lookup the element object for a carousel list item by index.
	 * @param {number}	idx	Index of the element to lookup. 
	 */
	getItem: function(idx) {
		var elemName = this._carouselElemID + "-item-" + idx;
 		var liElem = SHEBANG.util.Dom.get(elemName);
		return liElem;	
	},
	
	show: function() {
		SHEBANG.util.Dom.setStyle(this.carouselElem, "display", "block");
		this.calculateSize();
	},
	
	hide: function() {
		SHEBANG.util.Dom.setStyle(this.carouselElem, "display", "none");
	},

	calculateSize: function() {
 		var ulKids = this.carouselList.childNodes;
 		var li = null;
		for(var i=0; i<ulKids.length; i++) {
		
			li = ulKids[i];
			if(li.tagName == "LI" || li.tagName == "li") {
				break;
			}
		}

		var navMargin = this.cfg.getProperty("navMargin");
		var numVisible = this.cfg.getProperty("numVisible");
		var firstVisible = this.cfg.getProperty("firstVisible");
		var pl = this._getStyleVal(li, "paddingLeft");
		var pr = this._getStyleVal(li, "paddingRight");
		var ml = this._getStyleVal(li, "marginLeft");
		var mr = this._getStyleVal(li, "marginRight");
		var pt = this._getStyleVal(li, "paddingTop");
		var pb = this._getStyleVal(li, "paddingBottom");
		var mt = this._getStyleVal(li, "marginTop");
		var mb = this._getStyleVal(li, "marginBottom");

		SHEBANG.util.Dom.removeClass(this.carouselList, "carousel-vertical");
		SHEBANG.util.Dom.removeClass(this.carouselList, "carousel-horizontal");
		if(this.isVertical()) {
			var liPaddingMarginWidth = pl + pr + ml + mr;
			SHEBANG.util.Dom.addClass(this.carouselList, "carousel-vertical");
			var liPaddingMarginHeight = pt + pb + mt + mb;
			
			var upt = this._getStyleVal(this.carouselList, "paddingTop");
			var upb = this._getStyleVal(this.carouselList, "paddingBottom");
			var umt = this._getStyleVal(this.carouselList, "marginTop")
			var umb = this._getStyleVal(this.carouselList, "marginBottom")
			var ulPaddingHeight = upt + upb + umt + umb;

			// try to reveal the amount taking into consideration the margin & padding.
			// This guarantees that this.revealAmount of pixels will be shown on both sides
			var revealAmt = (this._isExtraRevealed()) ?
			 			(this.cfg.getProperty("revealAmount")+(liPaddingMarginHeight)/2) : 0;

			// get the height from the height computed style not the offset height
			// The reason is that on IE the offsetHeight when some part of the margin is
			// explicitly set to 'auto' can cause accessing that value to crash AND
			// on FF, in certain cases the actual value used for the LI's height is fractional
			// For example, while li.offsetHeight might return 93, SHEBANG.util.Dom.getStyle(li, "height") 
			// would return "93.2px". This fractional value will affect the scrolling, so it must be
			// factored in for FF.
			// The caveat is that for IE, you will need to set the LI's height explicitly
			// REPLACED: this.scrollAmountPerInc = (li.offsetHeight + liPaddingMarginHeight);
			// WITH:
			var liHeight = this._getStyleVal(li, "height", true);
			this.scrollAmountPerInc = (liHeight + liPaddingMarginHeight);
			
			var liWidth = this._getStyleVal(li, "width");
			this.carouselElem.style.width = (liWidth + liPaddingMarginWidth) + "px";			
			this._clipReg.style.height = 
					(this.scrollAmountPerInc * numVisible + revealAmt*2 + 
					ulPaddingHeight) + "px";
//alert(this._clipReg.style.height);
			this.carouselElem.style.height = 
				(this.scrollAmountPerInc * numVisible + revealAmt*2 + navMargin*2 +
					ulPaddingHeight) + "px";

			// possible that the umt+upt is needed... need to test this.
			var revealTop = (this._isExtraRevealed()) ? 
					(revealAmt - (Math.abs(mt-mb)+Math.abs(pt-pb))/2
					) : 
					0;
			SHEBANG.util.Dom.setStyle(this.carouselList, "position", "relative");
			SHEBANG.util.Dom.setStyle(this.carouselList, "top", "" + revealTop + "px");

			// if we set the initial start > 1 then this will adjust the scrolled location
			var currY = SHEBANG.util.Dom.getY(this.carouselList);	
			SHEBANG.util.Dom.setY(this.carouselList, currY - this.scrollAmountPerInc*(firstVisible-1));

		// --- HORIZONTAL
		} else {
			SHEBANG.util.Dom.addClass(this.carouselList, "carousel-horizontal");

			var upl = this._getStyleVal(this.carouselList, "paddingLeft");
			var upr = this._getStyleVal(this.carouselList, "paddingRight");
			var uml = this._getStyleVal(this.carouselList, "marginLeft")
			var umr = this._getStyleVal(this.carouselList, "marginRight")
			var ulPaddingWidth = upl + upr + uml + umr;

			var liMarginWidth = ml + mr;
			var liPaddingMarginWidth = liMarginWidth + pr + pl;
			
			// try to reveal the amount taking into consideration the margin & padding.
			// This guarantees that this.revealAmount of pixels will be shown on both sides
			var revealAmt = (this._isExtraRevealed()) ?
			 					(this.cfg.getProperty("revealAmount")+(liPaddingMarginWidth)/2) : 0;
			
			var liWidth = li.offsetWidth; 
			this.scrollAmountPerInc = liWidth + liMarginWidth;
			
			this._clipReg.style.width = 
					(this.scrollAmountPerInc*numVisible + revealAmt*2) + "px";
			this.carouselElem.style.width =
			 		(this.scrollAmountPerInc*numVisible + navMargin*2 + revealAmt*2 + 
					ulPaddingWidth) + "px";
			
			var revealLeft = (this._isExtraRevealed()) ? 
					(revealAmt - (Math.abs(mr-ml)+Math.abs(pr-pl))/2 - (uml+upl)
					) : 
					0;
			SHEBANG.util.Dom.setStyle(this.carouselList, "position", "relative");
			SHEBANG.util.Dom.setStyle(this.carouselList, "left", "" + revealLeft + "px");

			// if we set the initial start > 1 then this will adjust the scrolled location
			var currX = SHEBANG.util.Dom.getX(this.carouselList);
			SHEBANG.util.Dom.setX(this.carouselList, currX - this.scrollAmountPerInc*(firstVisible-1));
		}
	},
	
	// Hides the cfg object
	setProperty: function(property, value, silent) {
		this.cfg.setProperty(property, value, silent);
	},
	
	getProperty: function(property) {
		return this.cfg.getProperty(property);
	},
	
	getFirstItemRevealed: function() {
		return this._firstItemRevealed;
	},
	getLastItemRevealed: function() {
		return this._lastItemRevealed;
	},
	
	// Just for convenience and to be symmetrical with getFirstVisible
	getFirstVisible: function() {
		return this.cfg.getProperty("firstVisible");
	},
	
	getLastVisible: function() {
		var firstVisible = this.cfg.getProperty("firstVisible");
		var numVisible = this.cfg.getProperty("numVisible");
		
		return firstVisible + numVisible - 1;
	},
	
	// /////////////////// PRIVATE API //////////////////////////////////////////
	_getStyleVal : function(li, style, returnFloat) {
		var styleValStr = SHEBANG.util.Dom.getStyle(li, style);
		
		var styleVal = returnFloat ? parseFloat(styleValStr) : parseInt(styleValStr, 10);
		if(style=="height" && isNaN(styleVal)) {
			styleVal = li.offsetHeight;
		} else if(isNaN(styleVal)) {
			styleVal = 0;
		}
		return styleVal;
	},
	
	_calculateSize: function(me) {
		me.calculateSize();
		me.show();
		//SHEBANG.util.Dom.setStyle(me.carouselElem, "visibility", "visible");
	},

	// From Mike Chambers: http://weblogs.macromedia.com/mesh/archives/2006/01/removing_html_e.html
	_removeChildrenFromNode: function(node)
	{
		if(!this._isValidObj(node))
		{
      		return;
		}
   
		var len = node.childNodes.length;
   
		while (node.hasChildNodes())
		{
			node.removeChild(node.firstChild);
		}
	},
	
	_prebuildLiElem: function(idx) {
		if(idx < 1) return;
		
		
		var liElem = document.createElement("li");
		liElem.id = this._carouselElemID + "-item-" + idx;
		// this is default flag to know that we're not really loaded yet.
		liElem.placeholder = true;   
		this.carouselList.appendChild(liElem);
		
		this._lastPrebuiltIdx = (idx > this._lastPrebuiltIdx) ? idx : this._lastPrebuiltIdx;
	},
	
	_createItem: function(idx, innerHTMLOrElem) {
		if(idx < 1) return;
		
		
		var liElem = document.createElement("li");
		liElem.id = this._carouselElemID + "-item-" + idx;

		// if String then assume innerHTML, else an elem object
		if(typeof(innerHTMLOrElem) === "string") {
			liElem.innerHTML = innerHTMLOrElem;
		} else {
			liElem.appendChild(innerHTMLOrElem);
		}
		
		return liElem;
	},
	
	// idx is the location to insert after
	_insertAfterItem: function(refIdx, innerHTMLOrElem) {
		return this._insertBeforeItem(refIdx+1, innerHTMLOrElem);
	},
	
	
	_insertBeforeItem: function(refIdx, innerHTMLOrElem) {

		var refItem = this.getItem(refIdx);
		var size = this.cfg.getProperty("size");
		if(size != this.UNBOUNDED_SIZE) {
			this.cfg.setProperty("size", size + 1, true);
		}
				
		for(var i=this._lastPrebuiltIdx; i>=refIdx; i--) {
			var anItem = this.getItem(i);
			if(this._isValidObj(anItem)) {
				anItem.id = this._carouselElemID + "-item-" + (i+1);
			}
		}

		var liElem = this._createItem(refIdx, innerHTMLOrElem);
		
		var insertedItem = this.carouselList.insertBefore(liElem, refItem);
		this._lastPrebuiltIdx += 1;
		
		return liElem;
	},
	
	// TEST THIS... think it has to do with prebuild
	insertAfterEnd: function(innerHTMLOrElem) {
		return this.insertAfter(this.cfg.getProperty("size"), innerHTMLOrElem);
	},
		
	_position: function(newStart, showAnimation) {
		// do we bypass the isAnimated check?
		var currStart = this._priorFirstVisible;
		if(newStart > currStart) {
			var inc = newStart - currStart;
			this._scrollNextInc(inc, showAnimation);
		} else {
			var dec = currStart - newStart;
			this._scrollPrevInc(dec, showAnimation);
		}
	},

	_scrollPrev: function(e, carousel) {
		if(e !== null) { // event fired this so disable autoplay
			carousel.stopAutoPlay();
		}
		if(carousel._scrollPrevAnim.isAnimated()) {
			return false;
		}
		carousel._scrollPrevInc(carousel.cfg.getProperty("scrollInc"), 
							(carousel.cfg.getProperty("animationSpeed") !== 0));
	},
	
	// event handler
	_scrollNext: function(e, carousel) {		
		if(e !== null) { // event fired this so disable autoplay
			carousel.stopAutoPlay();
		}
		if(carousel._scrollNextAnim.isAnimated()) {
			return false; // might be better to set ourself waiting for animation completion and
			// then just do this function. that will allow faster scroll responses.
		}

		carousel._scrollNextInc(carousel.cfg.getProperty("scrollInc"), 
								(carousel.cfg.getProperty("animationSpeed") !== 0));
	},
	
	
	_handleAnimationComplete: function(type, args, argList) {
		var carousel = argList[0];
		var direction = argList[1];
		
		carousel._animationCompleteEvt.fire(direction);

		
	},
	
	// If EVERY item is already loaded in the range then return true
	// Also prebuild whatever is not already created.
	_areAllItemsLoaded: function(first, last) {
		var itemsLoaded = true;
		for(var i=first; i<=last; i++) {
			var liElem = this.getItem(i);
			
			// If the li elem does not exist, then prebuild it in the correct order
			// but still flag as not loaded (just prebuilt the li item.
			if(!this._isValidObj(liElem)) {
				this._prebuildLiElem(i);
				itemsLoaded = false;
			// but if the item exists and is a placeholder, then
			// note that this item is not loaded (only a placeholder)
			} else if(this._isValidObj(liElem.placeholder)) {
				itemsLoaded = false;
			}
		}
		return itemsLoaded;
	}, 
	
	_prebuildItems: function(first, last) {
		for(var i=first; i<=last; i++) {
			var liElem = this.getItem(i);
			
			// If the li elem does not exist, then prebuild it in the correct order
			// but still flag as not loaded (just prebuilt the li item.
			if(!this._isValidObj(liElem)) {
				this._prebuildLiElem(i);
			}
		}
	}, 
	
	_isExtraRevealed: function() {
		return (this.cfg.getProperty("revealAmount") > 0);
	},

	// probably no longer need carousel passed in, this should be correct now.
	_scrollNextInc: function(inc, showAnimation) {
		var numVisible = this.cfg.getProperty("numVisible");
		var currStart = this._priorFirstVisible;
		var currEnd = this._priorLastVisible;
		var size = this.cfg.getProperty("size");

		var scrollExtent = this._calculateAllowableScrollExtent();
		
		if(this.cfg.getProperty("wrap") && currEnd == scrollExtent.end) {
			this.scrollTo(scrollExtent.start); // might need to check animation is on or not
			return;
		}

		// increment start by inc
		var newStart = currStart + inc;		
		var newEnd = newStart + numVisible - 1;

		// If we are past the end, adjust or wrap
		if(newEnd > scrollExtent.end) {
			newEnd = scrollExtent.end;
			newStart = newEnd - numVisible + 1;
		}

		inc = newStart - currStart;

		// at this point the following variables are set
		// inc... amount to increment by
		// newStart... the firstVisible item after the scroll
		// newEnd... the last item visible after the scroll

		this.cfg.setProperty("firstVisible", newStart, true);


		if(inc > 0) {
			if(this._isValidObj(this.cfg.getProperty("loadNextHandler"))) {
				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
				var cacheStart = (currEnd+1) < visibleExtent.start ? (currEnd+1) : visibleExtent.start;						
				var alreadyCached = this._areAllItemsLoaded(cacheStart, visibleExtent.end);
				this._loadNextHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
			}

			if(showAnimation) {
	 			var nextParams = { points: { by: [-this.scrollAmountPerInc*inc, 0] } };
	 			if(this.isVertical()) {
	 				nextParams = { points: { by: [0, -this.scrollAmountPerInc*inc] } };
	 			}

	 			this._scrollNextAnim = new SHEBANG.util.Motion(this.carouselList, 
	 							nextParams, 
   								this.cfg.getProperty("animationSpeed"), 
								this.cfg.getProperty("animationMethod"));

// is this getting added multiple times?
				if(this.cfg.getProperty("animationCompleteHandler")) {
					this._scrollNextAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "next"]);
				}
				this._scrollNextAnim.animate();
			} else {
				if(this.isVertical()) {
					var currY = SHEBANG.util.Dom.getY(this.carouselList);

					SHEBANG.util.Dom.setY(this.carouselList, 
								currY - this.scrollAmountPerInc*inc);
				} else {
					var currX = SHEBANG.util.Dom.getX(this.carouselList);
					SHEBANG.util.Dom.setX(this.carouselList, 
								currX - this.scrollAmountPerInc*inc);
				}
			}

		}
		this._priorFirstVisible = newStart;
		this._priorLastVisible = newEnd;	

		this._enableDisableControls();
		return false;
	},

	// firstVisible is already set
	_scrollPrevInc: function(dec, showAnimation) {
		var numVisible = this.cfg.getProperty("numVisible");
		var currStart = this._priorFirstVisible;
		var currEnd = this._priorLastVisible;
		var size = this.cfg.getProperty("size");

		// decrement start by dec
		var newStart = currStart - dec;	

		var scrollExtent = this._calculateAllowableScrollExtent();
	
		// How to decide whether to stop at 1 or not
		newStart = (newStart < scrollExtent.start) ? scrollExtent.start : newStart;
		
		// if we are going to extend past the end, then we need to correct the start
		var newEnd = newStart + numVisible - 1;
		if(newEnd > scrollExtent.end) {
			newEnd = scrollExtent.end;
			newStart = newEnd - numVisible + 1;
		}
				
		dec = currStart - newStart;

		// at this point the following variables are set
		// dec... amount to decrement by
		// newStart... the firstVisible item after the scroll
		// newEnd... the last item visible after the scroll
		this.cfg.setProperty("firstVisible", newStart, true);
				
		// if we are decrementing
		if(dec > 0) {			
			if(this._isValidObj(this.cfg.getProperty("loadPrevHandler"))) {	
				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
				var cacheEnd = (currStart-1) > visibleExtent.end ? (currStart-1) : visibleExtent.end;						
				var alreadyCached = this._areAllItemsLoaded(visibleExtent.start, cacheEnd);
				
				this._loadPrevHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
			}

			if(showAnimation) {
	 			var prevParams = { points: { by: [this.scrollAmountPerInc*dec, 0] } };
	 			if(this.isVertical()) {
	 				prevParams = { points: { by: [0, this.scrollAmountPerInc*dec] } };
	 			}
 		
	 			this._scrollPrevAnim = new SHEBANG.util.Motion(this.carouselList,
	 							prevParams, 
   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
				if(this.cfg.getProperty("animationCompleteHandler")) {
					this._scrollPrevAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "prev"]);
				}
				this._scrollPrevAnim.animate();
			} else {
				if(this.isVertical()) {
					var currY = SHEBANG.util.Dom.getY(this.carouselList);
					SHEBANG.util.Dom.setY(this.carouselList, currY + 
							this.scrollAmountPerInc*dec);				
				} else {
					var currX = SHEBANG.util.Dom.getX(this.carouselList);
					SHEBANG.util.Dom.setX(this.carouselList, currX + 
							this.scrollAmountPerInc*dec);
				}
			}
		}
		this._priorFirstVisible = newStart;
		this._priorLastVisible = newEnd;	
		
		this._enableDisableControls();

		return false;
	},
	
	// Check for all cases and enable/disable controls as needed by current state
	_enableDisableControls: function() {
	
		var firstVisible = this.cfg.getProperty("firstVisible");
		var lastVisible = this.getLastVisible();
		var scrollExtent = this._calculateAllowableScrollExtent();
				
		// previous arrow is turned on. Check to see if we need to turn it off
		if(this._prevEnabled) {
			if(firstVisible === scrollExtent.start) {
				this._disablePrev();
			}
		}

		// previous arrow is turned off. Check to see if we need to turn it on
		if(this._prevEnabled === false) {
			if(firstVisible > scrollExtent.start) {
				this._enablePrev();
			}
		}
	
		// next arrow is turned on. Check to see if we need to turn it off
		if(this._nextEnabled) {
			if(lastVisible === scrollExtent.end) {
				this._disableNext();
			}
		}

		// next arrow is turned off. Check to see if we need to turn it on
		if(this._nextEnabled === false) {
			if(lastVisible < scrollExtent.end) {
				this._enableNext();
			}
		}	
	},
	
	/**
	 * _loadInitial looks at firstItemVisible for the start (not necessarily 1)
	 */
	_loadInitial: function() {
		var firstVisible = this.cfg.getProperty("firstVisible");
		this._priorLastVisible = this.getLastVisible();
		// Load from 1 to the last visible
		// The _calculateSize method will adjust the scroll position
		// for starts > 1
		if(this._loadInitHandlerEvt) {
			var visibleExtent = this._calculateVisibleExtent(firstVisible, this._priorLastVisible);
			// still treat the first real item as starting at 1 
			var alreadyCached = this._areAllItemsLoaded(1, visibleExtent.end);
			
			this._loadInitHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached); 
		}
		
		if(this.cfg.getProperty("autoPlay") !== 0) {
			this._autoPlayTimer = this.startAutoPlay();
		}	
		
		this._enableDisableControls();	
    },
	
	_calculateAllowableScrollExtent: function() {
		var scrollBeforeAmount = this.cfg.getProperty("scrollBeforeAmount");
		var scrollAfterAmount = this.cfg.getProperty("scrollAfterAmount");
		var size = this.cfg.getProperty("size");
		
		var extent = {start: 1-scrollBeforeAmount, end: size+scrollAfterAmount};
		return extent;
		
	},
	
	_calculateVisibleExtent: function(start, end) {
		if(!start) {
			start = this.cfg.getProperty("firstVisible");
			end = this.getLastVisible();
		}
		
		var size = this.cfg.getProperty("size");
		
		// we ignore the firstItem property... this method is used
		// for prebuilding the cache and signaling the developer
		// what to render on a given scroll.
		start = start<1?1:start;
		end = end>size?size:end;
		
		var extent = {start: start, end: end};
		
		// set up the indices for revealed items. If there is no item revealed, then set
		// the index to -1
		this._firstItemRevealed = -1;
		this._lastItemRevealed = -1;
		if(this._isExtraRevealed()) {
			if(start > 1) {
				this._firstItemRevealed = start - 1;
				extent.start = this._firstItemRevealed;
			}
			if(end < size) {
				this._lastItemRevealed = end + 1;
				extent.end = this._lastItemRevealed;
			}
		}

		return extent;
	},
	
	_disablePrev: function() {
		this._prevEnabled = false;
		if(this._prevButtonStateHandlerEvt) {
			this._prevButtonStateHandlerEvt.fire(false, this._carouselPrev);
		}
		if(this._isValidObj(this._carouselPrev)) {
			SHEBANG.util.Event.removeListener(this._carouselPrev, "click", this._scrollPrev);
		}
	},
	
	_enablePrev: function() {
		this._prevEnabled = true;
		if(this._prevButtonStateHandlerEvt) {
			this._prevButtonStateHandlerEvt.fire(true, this._carouselPrev);
		}
		if(this._isValidObj(this._carouselPrev)) {
			SHEBANG.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this);
		}
	},
		
	_disableNext: function() {
		if(this.cfg.getProperty("wrap")) {
			return;
		}
		this._nextEnabled = false;
		if(this._isValidObj(this._nextButtonStateHandlerEvt)) {
			this._nextButtonStateHandlerEvt.fire(false, this._carouselNext);
		}
		if(this._isValidObj(this._carouselNext)) {
			SHEBANG.util.Event.removeListener(this._carouselNext, "click", this._scrollNext);
		}
	},
	
	_enableNext: function() {
		this._nextEnabled = true;
		if(this._isValidObj(this._nextButtonStateHandlerEvt)) {
			this._nextButtonStateHandlerEvt.fire(true, this._carouselNext);
		}
		if(this._isValidObj(this._carouselNext)) {
			SHEBANG.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this);
		}
	},
		
	_isValidObj: function(obj) {

		if (null == obj) {
			return false;
		}
		if ("undefined" == typeof(obj) ) {
			return false;
		}
		return true;
	}
};

SHEBANG.util.Config=function(owner){
	if(owner){
		this.init(owner);
	}
};

SHEBANG.util.Config.CONFIG_CHANGED_EVENT="configChanged";SHEBANG.util.Config.BOOLEAN_TYPE="boolean";SHEBANG.util.Config.prototype={
	owner:null,queueInProgress:false,config:null,initialConfig:null,eventQueue:null,configChangedEvent:null,checkBoolean:function(val){return(typeof val==SHEBANG.util.Config.BOOLEAN_TYPE);
	},checkNumber:function(val){return(!isNaN(val));},fireEvent:function(key,value){var property=this.config[key];if(property&&property.event){property.event.fire(value);}},addProperty:function(key,propertyObject){key=key.toLowerCase();this.config[key]=propertyObject;propertyObject.event=new SHEBANG.util.CustomEvent(key,this.owner);propertyObject.key=key;if(propertyObject.handler){propertyObject.event.subscribe(propertyObject.handler,this.owner);}
this.setProperty(key,propertyObject.value,true);if(!propertyObject.suppressEvent){this.queueProperty(key,propertyObject.value);}},getConfig:function(){var cfg={};for(var prop in this.config){var property=this.config[prop];if(property&&property.event){cfg[prop]=property.value;}}
return cfg;},getProperty:function(key){var property=this.config[key.toLowerCase()];if(property&&property.event){return property.value;}else{return undefined;}},resetProperty:function(key){key=key.toLowerCase();var property=this.config[key];if(property&&property.event){if(this.initialConfig[key]&&!SHEBANG.lang.isUndefined(this.initialConfig[key])){this.setProperty(key,this.initialConfig[key]);}
return true;}else{return false;}},setProperty:function(key,value,silent){key=key.toLowerCase();if(this.queueInProgress&&!silent){this.queueProperty(key,value);return true;}else{var property=this.config[key];if(property&&property.event){if(property.validator&&!property.validator(value)){return false;}else{property.value=value;if(!silent){this.fireEvent(key,value);this.configChangedEvent.fire([key,value]);}
return true;}}else{return false;}}},queueProperty:function(key,value){key=key.toLowerCase();var property=this.config[key];if(property&&property.event){if(!SHEBANG.lang.isUndefined(value)&&property.validator&&!property.validator(value)){return false;}else{if(!SHEBANG.lang.isUndefined(value)){property.value=value;}else{value=property.value;}
var foundDuplicate=false;var iLen=this.eventQueue.length;for(var i=0;i<iLen;i++){var queueItem=this.eventQueue[i];if(queueItem){var queueItemKey=queueItem[0];var queueItemValue=queueItem[1];if(queueItemKey==key){this.eventQueue[i]=null;this.eventQueue.push([key,(!SHEBANG.lang.isUndefined(value)?value:queueItemValue)]);foundDuplicate=true;break;}}}
if(!foundDuplicate&&!SHEBANG.lang.isUndefined(value)){this.eventQueue.push([key,value]);}}
if(property.supercedes){var sLen=property.supercedes.length;for(var s=0;s<sLen;s++){var supercedesCheck=property.supercedes[s];var qLen=this.eventQueue.length;for(var q=0;q<qLen;q++){var queueItemCheck=this.eventQueue[q];if(queueItemCheck){var queueItemCheckKey=queueItemCheck[0];var queueItemCheckValue=queueItemCheck[1];if(queueItemCheckKey==supercedesCheck.toLowerCase()){this.eventQueue.push([queueItemCheckKey,queueItemCheckValue]);this.eventQueue[q]=null;break;}}}}}
return true;}else{return false;}},refireEvent:function(key){key=key.toLowerCase();var property=this.config[key];if(property&&property.event&&!SHEBANG.lang.isUndefined(property.value)){if(this.queueInProgress){this.queueProperty(key);}else{this.fireEvent(key,property.value);}}},applyConfig:function(userConfig,init){if(init){this.initialConfig=userConfig;}
for(var prop in userConfig){this.queueProperty(prop,userConfig[prop]);}},refresh:function(){for(var prop in this.config){this.refireEvent(prop);}},fireQueue:function(){this.queueInProgress=true;for(var i=0;i<this.eventQueue.length;i++){var queueItem=this.eventQueue[i];if(queueItem){var key=queueItem[0];var value=queueItem[1];var property=this.config[key];property.value=value;this.fireEvent(key,value);}}
this.queueInProgress=false;this.eventQueue=[];},subscribeToConfigEvent:function(key,handler,obj,override){var property=this.config[key.toLowerCase()];if(property&&property.event){if(!SHEBANG.util.Config.alreadySubscribed(property.event,handler,obj)){property.event.subscribe(handler,obj,override);}
return true;}else{return false;}},unsubscribeFromConfigEvent:function(key,handler,obj){var property=this.config[key.toLowerCase()];if(property&&property.event){return property.event.unsubscribe(handler,obj);}else{return false;}},toString:function(){var output="Config";if(this.owner){output+=" ["+this.owner.toString()+"]";}
return output;},outputEventQueue:function(){var output="";for(var q=0;q<this.eventQueue.length;q++){var queueItem=this.eventQueue[q];if(queueItem){output+=queueItem[0]+"="+queueItem[1]+", ";}}
return output;}};SHEBANG.util.Config.prototype.init=function(owner){this.owner=owner;this.configChangedEvent=new SHEBANG.util.CustomEvent(SHEBANG.util.CONFIG_CHANGED_EVENT,this);this.queueInProgress=false;this.config={};this.initialConfig={};this.eventQueue=[];};SHEBANG.util.Config.alreadySubscribed=function(evt,fn,obj){for(var e=0;e<evt.subscribers.length;e++){var subsc=evt.subscribers[e];if(subsc&&subsc.obj==obj&&subsc.fn==fn){return true;}}
return false;};SHEBANG.widget.Module=function(el,userConfig){if(el){this.init(el,userConfig);}else{}};SHEBANG.widget.Module.IMG_ROOT=null;SHEBANG.widget.Module.IMG_ROOT_SSL=null;SHEBANG.widget.Module.CSS_MODULE="yui-module";SHEBANG.widget.Module.CSS_HEADER="hd";SHEBANG.widget.Module.CSS_BODY="bd";SHEBANG.widget.Module.CSS_FOOTER="ft";SHEBANG.widget.Module.RESIZE_MONITOR_SECURE_URL="javascript:false;";SHEBANG.widget.Module.textResizeEvent=new SHEBANG.util.CustomEvent("textResize");SHEBANG.widget.Module._EVENT_TYPES={"BEFORE_INIT":"beforeInit","INIT":"init","APPEND":"append","BEFORE_RENDER":"beforeRender","RENDER":"render","CHANGE_HEADER":"changeHeader","CHANGE_BODY":"changeBody","CHANGE_FOOTER":"changeFooter","CHANGE_CONTENT":"changeContent","DESTORY":"destroy","BEFORE_SHOW":"beforeShow","SHOW":"show","BEFORE_HIDE":"beforeHide","HIDE":"hide"};SHEBANG.widget.Module._DEFAULT_CONFIG={"VISIBLE":{key:"visible",value:true,validator:SHEBANG.lang.isBoolean},"EFFECT":{key:"effect",suppressEvent:true,supercedes:["visible"]},"MONITOR_RESIZE":{key:"monitorresize",value:true}};SHEBANG.widget.Module.prototype={constructor:SHEBANG.widget.Module,element:null,header:null,body:null,footer:null,id:null,imageRoot:SHEBANG.widget.Module.IMG_ROOT,initEvents:function(){var EVENT_TYPES=SHEBANG.widget.Module._EVENT_TYPES;this.beforeInitEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.BEFORE_INIT,this);this.initEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.INIT,this);this.appendEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.APPEND,this);this.beforeRenderEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.BEFORE_RENDER,this);this.renderEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.RENDER,this);this.changeHeaderEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.CHANGE_HEADER,this);this.changeBodyEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.CHANGE_BODY,this);this.changeFooterEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.CHANGE_FOOTER,this);this.changeContentEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.CHANGE_CONTENT,this);this.destroyEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.DESTORY,this);this.beforeShowEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.BEFORE_SHOW,this);this.showEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.SHOW,this);this.beforeHideEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.BEFORE_HIDE,this);this.hideEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.HIDE,this);},platform:function(){var ua=navigator.userAgent.toLowerCase();if(ua.indexOf("windows")!=-1||ua.indexOf("win32")!=-1){return"windows";}else if(ua.indexOf("macintosh")!=-1){return"mac";}else{return false;}}(),browser:function(){var ua=navigator.userAgent.toLowerCase();if(ua.indexOf('opera')!=-1){return'opera';}else if(ua.indexOf('msie 7')!=-1){return'ie7';}else if(ua.indexOf('msie')!=-1){return'ie';}else if(ua.indexOf('safari')!=-1){return'safari';}else if(ua.indexOf('gecko')!=-1){return'gecko';}else{return false;}}(),isSecure:function(){if(window.location.href.toLowerCase().indexOf("https")===0){return true;}else{return false;}}(),initDefaultConfig:function(){var DEFAULT_CONFIG=SHEBANG.widget.Module._DEFAULT_CONFIG;this.cfg.addProperty(DEFAULT_CONFIG.VISIBLE.key,{handler:this.configVisible,value:DEFAULT_CONFIG.VISIBLE.value,validator:DEFAULT_CONFIG.VISIBLE.validator});this.cfg.addProperty(DEFAULT_CONFIG.EFFECT.key,{suppressEvent:DEFAULT_CONFIG.EFFECT.suppressEvent,supercedes:DEFAULT_CONFIG.EFFECT.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.MONITOR_RESIZE.key,{handler:this.configMonitorResize,value:DEFAULT_CONFIG.MONITOR_RESIZE.value});},init:function(el,userConfig){this.initEvents();this.beforeInitEvent.fire(SHEBANG.widget.Module);this.cfg=new SHEBANG.util.Config(this);if(this.isSecure){this.imageRoot=SHEBANG.widget.Module.IMG_ROOT_SSL;}
if(typeof el=="string"){var elId=el;el=document.getElementById(el);if(!el){el=document.createElement("div");el.id=elId;}}
this.element=el;if(el.id){this.id=el.id;}
var childNodes=this.element.childNodes;if(childNodes){for(var i=0;i<childNodes.length;i++){var child=childNodes[i];switch(child.className){case SHEBANG.widget.Module.CSS_HEADER:this.header=child;break;case SHEBANG.widget.Module.CSS_BODY:this.body=child;break;case SHEBANG.widget.Module.CSS_FOOTER:this.footer=child;break;}}}
this.initDefaultConfig();SHEBANG.util.Dom.addClass(this.element,SHEBANG.widget.Module.CSS_MODULE);if(userConfig){this.cfg.applyConfig(userConfig,true);}
if(!SHEBANG.util.Config.alreadySubscribed(this.renderEvent,this.cfg.fireQueue,this.cfg)){this.renderEvent.subscribe(this.cfg.fireQueue,this.cfg,true);}
this.initEvent.fire(SHEBANG.widget.Module);},initResizeMonitor:function(){if(this.browser!="opera"){var resizeMonitor=document.getElementById("_yuiResizeMonitor");if(!resizeMonitor){resizeMonitor=document.createElement("iframe");var bIE=(this.browser.indexOf("ie")===0);if(this.isSecure&&SHEBANG.widget.Module.RESIZE_MONITOR_SECURE_URL&&bIE){resizeMonitor.src=SHEBANG.widget.Module.RESIZE_MONITOR_SECURE_URL;}
resizeMonitor.id="_yuiResizeMonitor";resizeMonitor.style.visibility="hidden";document.body.appendChild(resizeMonitor);resizeMonitor.style.width="10em";resizeMonitor.style.height="10em";resizeMonitor.style.position="absolute";var nLeft=-1*resizeMonitor.offsetWidth;var nTop=-1*resizeMonitor.offsetHeight;resizeMonitor.style.top=nTop+"px";resizeMonitor.style.left=nLeft+"px";resizeMonitor.style.borderStyle="none";resizeMonitor.style.borderWidth="0";SHEBANG.util.Dom.setStyle(resizeMonitor,"opacity","0");resizeMonitor.style.visibility="visible";if(!bIE){var doc=resizeMonitor.contentWindow.document;doc.open();doc.close();}}
var fireTextResize=function(){SHEBANG.widget.Module.textResizeEvent.fire();};if(resizeMonitor&&resizeMonitor.contentWindow){this.resizeMonitor=resizeMonitor;SHEBANG.widget.Module.textResizeEvent.subscribe(this.onDomResize,this,true);if(!SHEBANG.widget.Module.textResizeInitialized){if(!SHEBANG.util.Event.addListener(this.resizeMonitor.contentWindow,"resize",fireTextResize)){SHEBANG.util.Event.addListener(this.resizeMonitor,"resize",fireTextResize);}
SHEBANG.widget.Module.textResizeInitialized=true;}}}},onDomResize:function(e,obj){var nLeft=-1*this.resizeMonitor.offsetWidth,nTop=-1*this.resizeMonitor.offsetHeight;this.resizeMonitor.style.top=nTop+"px";this.resizeMonitor.style.left=nLeft+"px";},setHeader:function(headerContent){if(!this.header){this.header=document.createElement("div");this.header.className=SHEBANG.widget.Module.CSS_HEADER;}
if(typeof headerContent=="string"){this.header.innerHTML=headerContent;}else{this.header.innerHTML="";this.header.appendChild(headerContent);}
this.changeHeaderEvent.fire(headerContent);this.changeContentEvent.fire();},appendToHeader:function(element){if(!this.header){this.header=document.createElement("div");this.header.className=SHEBANG.widget.Module.CSS_HEADER;}
this.header.appendChild(element);this.changeHeaderEvent.fire(element);this.changeContentEvent.fire();},setBody:function(bodyContent){if(!this.body){this.body=document.createElement("div");this.body.className=SHEBANG.widget.Module.CSS_BODY;}
if(typeof bodyContent=="string")
{this.body.innerHTML=bodyContent;}else{this.body.innerHTML="";this.body.appendChild(bodyContent);}
this.changeBodyEvent.fire(bodyContent);this.changeContentEvent.fire();},appendToBody:function(element){if(!this.body){this.body=document.createElement("div");this.body.className=SHEBANG.widget.Module.CSS_BODY;}
this.body.appendChild(element);this.changeBodyEvent.fire(element);this.changeContentEvent.fire();},setFooter:function(footerContent){if(!this.footer){this.footer=document.createElement("div");this.footer.className=SHEBANG.widget.Module.CSS_FOOTER;}
if(typeof footerContent=="string"){this.footer.innerHTML=footerContent;}else{this.footer.innerHTML="";this.footer.appendChild(footerContent);}
this.changeFooterEvent.fire(footerContent);this.changeContentEvent.fire();},appendToFooter:function(element){if(!this.footer){this.footer=document.createElement("div");this.footer.className=SHEBANG.widget.Module.CSS_FOOTER;}
this.footer.appendChild(element);this.changeFooterEvent.fire(element);this.changeContentEvent.fire();},render:function(appendToNode,moduleElement){this.beforeRenderEvent.fire();if(!moduleElement){moduleElement=this.element;}
var me=this;var appendTo=function(element){if(typeof element=="string"){element=document.getElementById(element);}
if(element){element.appendChild(me.element);me.appendEvent.fire();}};if(appendToNode){appendTo(appendToNode);}else{if(!SHEBANG.util.Dom.inDocument(this.element)){return false;}}
if(this.header&&!SHEBANG.util.Dom.inDocument(this.header)){var firstChild=moduleElement.firstChild;if(firstChild){moduleElement.insertBefore(this.header,firstChild);}else{moduleElement.appendChild(this.header);}}
if(this.body&&!SHEBANG.util.Dom.inDocument(this.body)){if(this.footer&&SHEBANG.util.Dom.isAncestor(this.moduleElement,this.footer)){moduleElement.insertBefore(this.body,this.footer);}else{moduleElement.appendChild(this.body);}}
if(this.footer&&!SHEBANG.util.Dom.inDocument(this.footer)){moduleElement.appendChild(this.footer);}
this.renderEvent.fire();return true;},destroy:function(){var parent;if(this.element){SHEBANG.util.Event.purgeElement(this.element,true);parent=this.element.parentNode;}
if(parent){parent.removeChild(this.element);}
this.element=null;this.header=null;this.body=null;this.footer=null;for(var e in this){if(e instanceof SHEBANG.util.CustomEvent){e.unsubscribeAll();}}
SHEBANG.widget.Module.textResizeEvent.unsubscribe(this.onDomResize,this);this.destroyEvent.fire();},show:function(){this.cfg.setProperty("visible",true);},hide:function(){this.cfg.setProperty("visible",false);},configVisible:function(type,args,obj){var visible=args[0];if(visible){this.beforeShowEvent.fire();SHEBANG.util.Dom.setStyle(this.element,"display","block");this.showEvent.fire();}else{this.beforeHideEvent.fire();SHEBANG.util.Dom.setStyle(this.element,"display","none");this.hideEvent.fire();}},configMonitorResize:function(type,args,obj){var monitor=args[0];if(monitor){this.initResizeMonitor();}else{SHEBANG.widget.Module.textResizeEvent.unsubscribe(this.onDomResize,this,true);this.resizeMonitor=null;}}};SHEBANG.widget.Module.prototype.toString=function(){return"Module "+this.id;};SHEBANG.widget.Overlay=function(el,userConfig){SHEBANG.widget.Overlay.superclass.constructor.call(this,el,userConfig);};SHEBANG.extend(SHEBANG.widget.Overlay,SHEBANG.widget.Module);SHEBANG.widget.Overlay._EVENT_TYPES={"BEFORE_MOVE":"beforeMove","MOVE":"move"};SHEBANG.widget.Overlay._DEFAULT_CONFIG={"X":{key:"x",validator:SHEBANG.lang.isNumber,suppressEvent:true,supercedes:["iframe"]},"Y":{key:"y",validator:SHEBANG.lang.isNumber,suppressEvent:true,supercedes:["iframe"]},"XY":{key:"xy",suppressEvent:true,supercedes:["iframe"]},"CONTEXT":{key:"context",suppressEvent:true,supercedes:["iframe"]},"FIXED_CENTER":{key:"fixedcenter",value:false,validator:SHEBANG.lang.isBoolean,supercedes:["iframe","visible"]},"WIDTH":{key:"width",suppressEvent:true,supercedes:["iframe"]},"HEIGHT":{key:"height",suppressEvent:true,supercedes:["iframe"]},"ZINDEX":{key:"zindex",value:null},"CONSTRAIN_TO_VIEWPORT":{key:"constraintoviewport",value:false,validator:SHEBANG.lang.isBoolean,supercedes:["iframe","x","y","xy"]},"IFRAME":{key:"iframe",value:(SHEBANG.widget.Module.prototype.browser=="ie"?true:false),validator:SHEBANG.lang.isBoolean,supercedes:["zIndex"]}};SHEBANG.widget.Overlay.IFRAME_SRC="javascript:false;";SHEBANG.widget.Overlay.TOP_LEFT="tl";SHEBANG.widget.Overlay.TOP_RIGHT="tr";SHEBANG.widget.Overlay.BOTTOM_LEFT="bl";SHEBANG.widget.Overlay.BOTTOM_RIGHT="br";SHEBANG.widget.Overlay.CSS_OVERLAY="yui-overlay";SHEBANG.widget.Overlay.prototype.init=function(el,userConfig){SHEBANG.widget.Overlay.superclass.init.call(this,el);this.beforeInitEvent.fire(SHEBANG.widget.Overlay);SHEBANG.util.Dom.addClass(this.element,SHEBANG.widget.Overlay.CSS_OVERLAY);if(userConfig){this.cfg.applyConfig(userConfig,true);}
if(this.platform=="mac"&&this.browser=="gecko"){if(!SHEBANG.util.Config.alreadySubscribed(this.showEvent,this.showMacGeckoScrollbars,this)){this.showEvent.subscribe(this.showMacGeckoScrollbars,this,true);}
if(!SHEBANG.util.Config.alreadySubscribed(this.hideEvent,this.hideMacGeckoScrollbars,this)){this.hideEvent.subscribe(this.hideMacGeckoScrollbars,this,true);}}
this.initEvent.fire(SHEBANG.widget.Overlay);};SHEBANG.widget.Overlay.prototype.initEvents=function(){SHEBANG.widget.Overlay.superclass.initEvents.call(this);var EVENT_TYPES=SHEBANG.widget.Overlay._EVENT_TYPES;this.beforeMoveEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.BEFORE_MOVE,this);this.moveEvent=new SHEBANG.util.CustomEvent(EVENT_TYPES.MOVE,this);};SHEBANG.widget.Overlay.prototype.initDefaultConfig=function(){SHEBANG.widget.Overlay.superclass.initDefaultConfig.call(this);var DEFAULT_CONFIG=SHEBANG.widget.Overlay._DEFAULT_CONFIG;this.cfg.addProperty(DEFAULT_CONFIG.X.key,{handler:this.configX,validator:DEFAULT_CONFIG.X.validator,suppressEvent:DEFAULT_CONFIG.X.suppressEvent,supercedes:DEFAULT_CONFIG.X.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.Y.key,{handler:this.configY,validator:DEFAULT_CONFIG.Y.validator,suppressEvent:DEFAULT_CONFIG.Y.suppressEvent,supercedes:DEFAULT_CONFIG.Y.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.XY.key,{handler:this.configXY,suppressEvent:DEFAULT_CONFIG.XY.suppressEvent,supercedes:DEFAULT_CONFIG.XY.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.CONTEXT.key,{handler:this.configContext,suppressEvent:DEFAULT_CONFIG.CONTEXT.suppressEvent,supercedes:DEFAULT_CONFIG.CONTEXT.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.FIXED_CENTER.key,{handler:this.configFixedCenter,value:DEFAULT_CONFIG.FIXED_CENTER.value,validator:DEFAULT_CONFIG.FIXED_CENTER.validator,supercedes:DEFAULT_CONFIG.FIXED_CENTER.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.WIDTH.key,{handler:this.configWidth,suppressEvent:DEFAULT_CONFIG.WIDTH.suppressEvent,supercedes:DEFAULT_CONFIG.WIDTH.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.HEIGHT.key,{handler:this.configHeight,suppressEvent:DEFAULT_CONFIG.HEIGHT.suppressEvent,supercedes:DEFAULT_CONFIG.HEIGHT.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.ZINDEX.key,{handler:this.configzIndex,value:DEFAULT_CONFIG.ZINDEX.value});this.cfg.addProperty(DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.key,{handler:this.configConstrainToViewport,value:DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.value,validator:DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.validator,supercedes:DEFAULT_CONFIG.CONSTRAIN_TO_VIEWPORT.supercedes});this.cfg.addProperty(DEFAULT_CONFIG.IFRAME.key,{handler:this.configIframe,value:DEFAULT_CONFIG.IFRAME.value,validator:DEFAULT_CONFIG.IFRAME.validator,supercedes:DEFAULT_CONFIG.IFRAME.supercedes});};SHEBANG.widget.Overlay.prototype.moveTo=function(x,y){this.cfg.setProperty("xy",[x,y]);};SHEBANG.widget.Overlay.prototype.hideMacGeckoScrollbars=function(){SHEBANG.util.Dom.removeClass(this.element,"show-scrollbars");SHEBANG.util.Dom.addClass(this.element,"hide-scrollbars");};SHEBANG.widget.Overlay.prototype.showMacGeckoScrollbars=function(){SHEBANG.util.Dom.removeClass(this.element,"hide-scrollbars");SHEBANG.util.Dom.addClass(this.element,"show-scrollbars");};SHEBANG.widget.Overlay.prototype.configVisible=function(type,args,obj){var visible=args[0];var currentVis=SHEBANG.util.Dom.getStyle(this.element,"visibility");if(currentVis=="inherit"){var e=this.element.parentNode;while(e.nodeType!=9&&e.nodeType!=11){currentVis=SHEBANG.util.Dom.getStyle(e,"visibility");if(currentVis!="inherit"){break;}
e=e.parentNode;}
if(currentVis=="inherit"){currentVis="visible";}}
var effect=this.cfg.getProperty("effect");var effectInstances=[];if(effect){if(effect instanceof Array){for(var i=0;i<effect.length;i++){var eff=effect[i];effectInstances[effectInstances.length]=eff.effect(this,eff.duration);}}else{effectInstances[effectInstances.length]=effect.effect(this,effect.duration);}}
var isMacGecko=(this.platform=="mac"&&this.browser=="gecko");if(visible){if(isMacGecko){this.showMacGeckoScrollbars();}
if(effect){if(visible){if(currentVis!="visible"||currentVis===""){this.beforeShowEvent.fire();for(var j=0;j<effectInstances.length;j++){var ei=effectInstances[j];if(j===0&&!SHEBANG.util.Config.alreadySubscribed(ei.animateInCompleteEvent,this.showEvent.fire,this.showEvent)){ei.animateInCompleteEvent.subscribe(this.showEvent.fire,this.showEvent,true);}
ei.animateIn();}}}}else{if(currentVis!="visible"||currentVis===""){this.beforeShowEvent.fire();SHEBANG.util.Dom.setStyle(this.element,"visibility","visible");this.cfg.refireEvent("iframe");this.showEvent.fire();}}}else{if(isMacGecko){this.hideMacGeckoScrollbars();}
if(effect){if(currentVis=="visible"){this.beforeHideEvent.fire();for(var k=0;k<effectInstances.length;k++){var h=effectInstances[k];if(k===0&&!SHEBANG.util.Config.alreadySubscribed(h.animateOutCompleteEvent,this.hideEvent.fire,this.hideEvent)){h.animateOutCompleteEvent.subscribe(this.hideEvent.fire,this.hideEvent,true);}
h.animateOut();}}else if(currentVis===""){SHEBANG.util.Dom.setStyle(this.element,"visibility","hidden");}}else{if(currentVis=="visible"||currentVis===""){this.beforeHideEvent.fire();SHEBANG.util.Dom.setStyle(this.element,"visibility","hidden");this.cfg.refireEvent("iframe");this.hideEvent.fire();}}}};SHEBANG.widget.Overlay.prototype.doCenterOnDOMEvent=function(){if(this.cfg.getProperty("visible")){this.center();}};SHEBANG.widget.Overlay.prototype.configFixedCenter=function(type,args,obj){var val=args[0];if(val){this.center();if(!SHEBANG.util.Config.alreadySubscribed(this.beforeShowEvent,this.center,this)){this.beforeShowEvent.subscribe(this.center,this,true);}
if(!SHEBANG.util.Config.alreadySubscribed(SHEBANG.widget.Overlay.windowResizeEvent,this.doCenterOnDOMEvent,this)){SHEBANG.widget.Overlay.windowResizeEvent.subscribe(this.doCenterOnDOMEvent,this,true);}
if(!SHEBANG.util.Config.alreadySubscribed(SHEBANG.widget.Overlay.windowScrollEvent,this.doCenterOnDOMEvent,this)){SHEBANG.widget.Overlay.windowScrollEvent.subscribe(this.doCenterOnDOMEvent,this,true);}}else{SHEBANG.widget.Overlay.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent,this);SHEBANG.widget.Overlay.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent,this);}};SHEBANG.widget.Overlay.prototype.configHeight=function(type,args,obj){var height=args[0];var el=this.element;SHEBANG.util.Dom.setStyle(el,"height",height);this.cfg.refireEvent("iframe");};SHEBANG.widget.Overlay.prototype.configWidth=function(type,args,obj){var width=args[0];var el=this.element;SHEBANG.util.Dom.setStyle(el,"width",width);this.cfg.refireEvent("iframe");};SHEBANG.widget.Overlay.prototype.configzIndex=function(type,args,obj){var zIndex=args[0];var el=this.element;if(!zIndex){zIndex=SHEBANG.util.Dom.getStyle(el,"zIndex");if(!zIndex||isNaN(zIndex)){zIndex=0;}}
if(this.iframe){if(zIndex<=0){zIndex=1;}
SHEBANG.util.Dom.setStyle(this.iframe,"zIndex",(zIndex-1));}
SHEBANG.util.Dom.setStyle(el,"zIndex",zIndex);this.cfg.setProperty("zIndex",zIndex,true);};SHEBANG.widget.Overlay.prototype.configXY=function(type,args,obj){var pos=args[0];var x=pos[0];var y=pos[1];this.cfg.setProperty("x",x);this.cfg.setProperty("y",y);this.beforeMoveEvent.fire([x,y]);x=this.cfg.getProperty("x");y=this.cfg.getProperty("y");this.cfg.refireEvent("iframe");this.moveEvent.fire([x,y]);};SHEBANG.widget.Overlay.prototype.configX=function(type,args,obj){var x=args[0];var y=this.cfg.getProperty("y");this.cfg.setProperty("x",x,true);this.cfg.setProperty("y",y,true);this.beforeMoveEvent.fire([x,y]);x=this.cfg.getProperty("x");y=this.cfg.getProperty("y");SHEBANG.util.Dom.setX(this.element,x,true);this.cfg.setProperty("xy",[x,y],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([x,y]);};SHEBANG.widget.Overlay.prototype.configY=function(type,args,obj){var x=this.cfg.getProperty("x");var y=args[0];this.cfg.setProperty("x",x,true);this.cfg.setProperty("y",y,true);this.beforeMoveEvent.fire([x,y]);x=this.cfg.getProperty("x");y=this.cfg.getProperty("y");SHEBANG.util.Dom.setY(this.element,y,true);this.cfg.setProperty("xy",[x,y],true);this.cfg.refireEvent("iframe");this.moveEvent.fire([x,y]);};SHEBANG.widget.Overlay.prototype.showIframe=function(){if(this.iframe){this.iframe.style.display="block";}};SHEBANG.widget.Overlay.prototype.hideIframe=function(){if(this.iframe){this.iframe.style.display="none";}};SHEBANG.widget.Overlay.prototype.configIframe=function(type,args,obj){var val=args[0];if(val){if(!SHEBANG.util.Config.alreadySubscribed(this.showEvent,this.showIframe,this)){this.showEvent.subscribe(this.showIframe,this,true);}
if(!SHEBANG.util.Config.alreadySubscribed(this.hideEvent,this.hideIframe,this)){this.hideEvent.subscribe(this.hideIframe,this,true);}
var x=this.cfg.getProperty("x");var y=this.cfg.getProperty("y");if(!x||!y){this.syncPosition();x=this.cfg.getProperty("x");y=this.cfg.getProperty("y");}
if(!isNaN(x)&&!isNaN(y)){if(!this.iframe){this.iframe=document.createElement("iframe");if(this.isSecure){this.iframe.src=SHEBANG.widget.Overlay.IFRAME_SRC;}
var parent=this.element.parentNode;if(parent){parent.appendChild(this.iframe);}else{document.body.appendChild(this.iframe);}
SHEBANG.util.Dom.setStyle(this.iframe,"position","absolute");SHEBANG.util.Dom.setStyle(this.iframe,"border","none");SHEBANG.util.Dom.setStyle(this.iframe,"margin","0");SHEBANG.util.Dom.setStyle(this.iframe,"padding","0");SHEBANG.util.Dom.setStyle(this.iframe,"opacity","0");if(this.cfg.getProperty("visible")){this.showIframe();}else{this.hideIframe();}}
var iframeDisplay=SHEBANG.util.Dom.getStyle(this.iframe,"display");if(iframeDisplay=="none"){this.iframe.style.display="block";}
SHEBANG.util.Dom.setXY(this.iframe,[x,y]);var width=this.element.clientWidth;var height=this.element.clientHeight;SHEBANG.util.Dom.setStyle(this.iframe,"width",(width+2)+"px");SHEBANG.util.Dom.setStyle(this.iframe,"height",(height+2)+"px");if(iframeDisplay=="none"){this.iframe.style.display="none";}}}else{if(this.iframe){this.iframe.style.display="none";}
this.showEvent.unsubscribe(this.showIframe,this);this.hideEvent.unsubscribe(this.hideIframe,this);}};SHEBANG.widget.Overlay.prototype.configConstrainToViewport=function(type,args,obj){var val=args[0];if(val){if(!SHEBANG.util.Config.alreadySubscribed(this.beforeMoveEvent,this.enforceConstraints,this)){this.beforeMoveEvent.subscribe(this.enforceConstraints,this,true);}}else{this.beforeMoveEvent.unsubscribe(this.enforceConstraints,this);}};SHEBANG.widget.Overlay.prototype.configContext=function(type,args,obj){var contextArgs=args[0];if(contextArgs){var contextEl=contextArgs[0];var elementMagnetCorner=contextArgs[1];var contextMagnetCorner=contextArgs[2];if(contextEl){if(typeof contextEl=="string"){this.cfg.setProperty("context",[document.getElementById(contextEl),elementMagnetCorner,contextMagnetCorner],true);}
if(elementMagnetCorner&&contextMagnetCorner){this.align(elementMagnetCorner,contextMagnetCorner);}}}};SHEBANG.widget.Overlay.prototype.align=function(elementAlign,contextAlign){var contextArgs=this.cfg.getProperty("context");if(contextArgs){var context=contextArgs[0];var element=this.element;var me=this;if(!elementAlign){elementAlign=contextArgs[1];}
if(!contextAlign){contextAlign=contextArgs[2];}
if(element&&context){var contextRegion=SHEBANG.util.Dom.getRegion(context);var doAlign=function(v,h){switch(elementAlign){case SHEBANG.widget.Overlay.TOP_LEFT:me.moveTo(h,v);break;case SHEBANG.widget.Overlay.TOP_RIGHT:me.moveTo(h-element.offsetWidth,v);break;case SHEBANG.widget.Overlay.BOTTOM_LEFT:me.moveTo(h,v-element.offsetHeight);break;case SHEBANG.widget.Overlay.BOTTOM_RIGHT:me.moveTo(h-element.offsetWidth,v-element.offsetHeight);break;}};switch(contextAlign){case SHEBANG.widget.Overlay.TOP_LEFT:doAlign(contextRegion.top,contextRegion.left);break;case SHEBANG.widget.Overlay.TOP_RIGHT:doAlign(contextRegion.top,contextRegion.right);break;case SHEBANG.widget.Overlay.BOTTOM_LEFT:doAlign(contextRegion.bottom,contextRegion.left);break;case SHEBANG.widget.Overlay.BOTTOM_RIGHT:doAlign(contextRegion.bottom,contextRegion.right);break;}}}};SHEBANG.widget.Overlay.prototype.enforceConstraints=function(type,args,obj){var pos=args[0];var x=pos[0];var y=pos[1];var offsetHeight=this.element.offsetHeight;var offsetWidth=this.element.offsetWidth;var viewPortWidth=SHEBANG.util.Dom.getViewportWidth();var viewPortHeight=SHEBANG.util.Dom.getViewportHeight();var scrollX=document.documentElement.scrollLeft||document.body.scrollLeft;var scrollY=document.documentElement.scrollTop||document.body.scrollTop;var topConstraint=scrollY+10;var leftConstraint=scrollX+10;var bottomConstraint=scrollY+viewPortHeight-offsetHeight-10;var rightConstraint=scrollX+viewPortWidth-offsetWidth-10;if(x<leftConstraint){x=leftConstraint;}else if(x>rightConstraint){x=rightConstraint;}
if(y<topConstraint){y=topConstraint;}else if(y>bottomConstraint){y=bottomConstraint;}
this.cfg.setProperty("x",x,true);this.cfg.setProperty("y",y,true);this.cfg.setProperty("xy",[x,y],true);};SHEBANG.widget.Overlay.prototype.center=function(){var scrollX=document.documentElement.scrollLeft||document.body.scrollLeft;var scrollY=document.documentElement.scrollTop||document.body.scrollTop;var viewPortWidth=SHEBANG.util.Dom.getClientWidth();var viewPortHeight=SHEBANG.util.Dom.getClientHeight();var elementWidth=this.element.offsetWidth;var elementHeight=this.element.offsetHeight;var x=(viewPortWidth/2)-(elementWidth/2)+scrollX;var y=(viewPortHeight/2)-(elementHeight/2)+scrollY;this.cfg.setProperty("xy",[parseInt(x,10),parseInt(y,10)]);this.cfg.refireEvent("iframe");};SHEBANG.widget.Overlay.prototype.syncPosition=function(){var pos=SHEBANG.util.Dom.getXY(this.element);this.cfg.setProperty("x",pos[0],true);this.cfg.setProperty("y",pos[1],true);this.cfg.setProperty("xy",pos,true);};SHEBANG.widget.Overlay.prototype.onDomResize=function(e,obj){SHEBANG.widget.Overlay.superclass.onDomResize.call(this,e,obj);var me=this;setTimeout(function(){me.syncPosition();me.cfg.refireEvent("iframe");me.cfg.refireEvent("context");},0);};SHEBANG.widget.Overlay.prototype.destroy=function(){if(this.iframe){this.iframe.parentNode.removeChild(this.iframe);}
this.iframe=null;SHEBANG.widget.Overlay.windowResizeEvent.unsubscribe(this.doCenterOnDOMEvent,this);SHEBANG.widget.Overlay.windowScrollEvent.unsubscribe(this.doCenterOnDOMEvent,this);SHEBANG.widget.Overlay.superclass.destroy.call(this);};SHEBANG.widget.Overlay.prototype.toString=function(){return"Overlay "+this.id;};SHEBANG.widget.Overlay.windowScrollEvent=new SHEBANG.util.CustomEvent("windowScroll");SHEBANG.widget.Overlay.windowResizeEvent=new SHEBANG.util.CustomEvent("windowResize");SHEBANG.widget.Overlay.windowScrollHandler=function(e){if(SHEBANG.widget.Module.prototype.browser=="ie"||SHEBANG.widget.Module.prototype.browser=="ie7"){if(!window.scrollEnd){window.scrollEnd=-1;}
clearTimeout(window.scrollEnd);window.scrollEnd=setTimeout(function(){SHEBANG.widget.Overlay.windowScrollEvent.fire();},1);}else{SHEBANG.widget.Overlay.windowScrollEvent.fire();}};SHEBANG.widget.Overlay.windowResizeHandler=function(e){if(SHEBANG.widget.Module.prototype.browser=="ie"||SHEBANG.widget.Module.prototype.browser=="ie7"){if(!window.resizeEnd){window.resizeEnd=-1;}
clearTimeout(window.resizeEnd);window.resizeEnd=setTimeout(function(){SHEBANG.widget.Overlay.windowResizeEvent.fire();},100);}else{SHEBANG.widget.Overlay.windowResizeEvent.fire();}};SHEBANG.widget.Overlay._initialized=null;if(SHEBANG.widget.Overlay._initialized===null){SHEBANG.util.Event.addListener(window,"scroll",SHEBANG.widget.Overlay.windowScrollHandler);SHEBANG.util.Event.addListener(window,"resize",SHEBANG.widget.Overlay.windowResizeHandler);SHEBANG.widget.Overlay._initialized=true;}
SHEBANG.widget.OverlayManager=function(userConfig){this.init(userConfig);};SHEBANG.widget.OverlayManager.CSS_FOCUSED="focused";SHEBANG.widget.OverlayManager.prototype={constructor:SHEBANG.widget.OverlayManager,overlays:null,initDefaultConfig:function(){this.cfg.addProperty("overlays",{suppressEvent:true});this.cfg.addProperty("focusevent",{value:"mousedown"});},init:function(userConfig){this.cfg=new SHEBANG.util.Config(this);this.initDefaultConfig();if(userConfig){this.cfg.applyConfig(userConfig,true);}
this.cfg.fireQueue();var activeOverlay=null;this.getActive=function(){return activeOverlay;};this.focus=function(overlay){var o=this.find(overlay);if(o){if(activeOverlay!=o){if(activeOverlay){activeOverlay.blur();}
activeOverlay=o;SHEBANG.util.Dom.addClass(activeOverlay.element,SHEBANG.widget.OverlayManager.CSS_FOCUSED);this.overlays.sort(this.compareZIndexDesc);var topZIndex=SHEBANG.util.Dom.getStyle(this.overlays[0].element,"zIndex");if(!isNaN(topZIndex)&&this.overlays[0]!=overlay){activeOverlay.cfg.setProperty("zIndex",(parseInt(topZIndex,10)+2));}
this.overlays.sort(this.compareZIndexDesc);o.focusEvent.fire();}}};this.remove=function(overlay){var o=this.find(overlay);if(o){var originalZ=SHEBANG.util.Dom.getStyle(o.element,"zIndex");o.cfg.setProperty("zIndex",-1000,true);this.overlays.sort(this.compareZIndexDesc);this.overlays=this.overlays.slice(0,this.overlays.length-1);o.hideEvent.unsubscribe(o.blur);o.destroyEvent.unsubscribe(this._onOverlayDestroy,o);if(o.element){SHEBANG.util.Event.removeListener(o.element,this.cfg.getProperty("focusevent"),this._onOverlayElementFocus);}
o.cfg.setProperty("zIndex",originalZ,true);o.cfg.setProperty("manager",null);o.focusEvent.unsubscribeAll();o.blurEvent.unsubscribeAll();o.focusEvent=null;o.blurEvent=null;o.focus=null;o.blur=null;}};this.blurAll=function(){for(var o=0;o<this.overlays.length;o++){this.overlays[o].blur();}};this._onOverlayBlur=function(p_sType,p_aArgs){activeOverlay=null;};var overlays=this.cfg.getProperty("overlays");if(!this.overlays){this.overlays=[];}
if(overlays){this.register(overlays);this.overlays.sort(this.compareZIndexDesc);}},_onOverlayElementFocus:function(p_oEvent){var oTarget=SHEBANG.util.Event.getTarget(p_oEvent),oClose=this.close;if(oClose&&(oTarget==oClose||SHEBANG.util.Dom.isAncestor(oClose,oTarget))){this.blur();}
else{this.focus();}},_onOverlayDestroy:function(p_sType,p_aArgs,p_oOverlay){this.remove(p_oOverlay);},register:function(overlay){if(overlay instanceof SHEBANG.widget.Overlay){overlay.cfg.addProperty("manager",{value:this});overlay.focusEvent=new SHEBANG.util.CustomEvent("focus",overlay);overlay.blurEvent=new SHEBANG.util.CustomEvent("blur",overlay);var mgr=this;overlay.focus=function(){mgr.focus(this);};overlay.blur=function(){if(mgr.getActive()==this){SHEBANG.util.Dom.removeClass(this.element,SHEBANG.widget.OverlayManager.CSS_FOCUSED);this.blurEvent.fire();}};overlay.blurEvent.subscribe(mgr._onOverlayBlur);overlay.hideEvent.subscribe(overlay.blur);overlay.destroyEvent.subscribe(this._onOverlayDestroy,overlay,this);SHEBANG.util.Event.addListener(overlay.element,this.cfg.getProperty("focusevent"),this._onOverlayElementFocus,null,overlay);var zIndex=SHEBANG.util.Dom.getStyle(overlay.element,"zIndex");if(!isNaN(zIndex)){overlay.cfg.setProperty("zIndex",parseInt(zIndex,10));}else{overlay.cfg.setProperty("zIndex",0);}
this.overlays.push(overlay);return true;}else if(overlay instanceof Array){var regcount=0;for(var i=0;i<overlay.length;i++){if(this.register(overlay[i])){regcount++;}}
if(regcount>0){return true;}}else{return false;}},find:function(overlay){if(overlay instanceof SHEBANG.widget.Overlay){for(var o=0;o<this.overlays.length;o++){if(this.overlays[o]==overlay){return this.overlays[o];}}}else if(typeof overlay=="string"){for(var p=0;p<this.overlays.length;p++){if(this.overlays[p].id==overlay){return this.overlays[p];}}}
return null;},compareZIndexDesc:function(o1,o2){var zIndex1=o1.cfg.getProperty("zIndex");var zIndex2=o2.cfg.getProperty("zIndex");if(zIndex1>zIndex2){return-1;}else if(zIndex1<zIndex2){return 1;}else{return 0;}},showAll:function(){for(var o=0;o<this.overlays.length;o++){this.overlays[o].show();}},hideAll:function(){for(var o=0;o<this.overlays.length;o++){this.overlays[o].hide();}},toString:function(){return"OverlayManager";}};SHEBANG.widget.ContainerEffect=function(overlay,attrIn,attrOut,targetElement,animClass){if(!animClass){animClass=SHEBANG.util.Anim;}
this.overlay=overlay;this.attrIn=attrIn;this.attrOut=attrOut;this.targetElement=targetElement||overlay.element;this.animClass=animClass;};SHEBANG.widget.ContainerEffect.prototype.init=function(){this.beforeAnimateInEvent=new SHEBANG.util.CustomEvent("beforeAnimateIn",this);this.beforeAnimateOutEvent=new SHEBANG.util.CustomEvent("beforeAnimateOut",this);this.animateInCompleteEvent=new SHEBANG.util.CustomEvent("animateInComplete",this);this.animateOutCompleteEvent=new SHEBANG.util.CustomEvent("animateOutComplete",this);this.animIn=new this.animClass(this.targetElement,this.attrIn.attributes,this.attrIn.duration,this.attrIn.method);this.animIn.onStart.subscribe(this.handleStartAnimateIn,this);this.animIn.onTween.subscribe(this.handleTweenAnimateIn,this);this.animIn.onComplete.subscribe(this.handleCompleteAnimateIn,this);this.animOut=new this.animClass(this.targetElement,this.attrOut.attributes,this.attrOut.duration,this.attrOut.method);this.animOut.onStart.subscribe(this.handleStartAnimateOut,this);this.animOut.onTween.subscribe(this.handleTweenAnimateOut,this);this.animOut.onComplete.subscribe(this.handleCompleteAnimateOut,this);};SHEBANG.widget.ContainerEffect.prototype.animateIn=function(){this.beforeAnimateInEvent.fire();this.animIn.animate();};SHEBANG.widget.ContainerEffect.prototype.animateOut=function(){this.beforeAnimateOutEvent.fire();this.animOut.animate();};SHEBANG.widget.ContainerEffect.prototype.handleStartAnimateIn=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.handleTweenAnimateIn=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.handleCompleteAnimateIn=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.handleStartAnimateOut=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.handleTweenAnimateOut=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.handleCompleteAnimateOut=function(type,args,obj){};SHEBANG.widget.ContainerEffect.prototype.toString=function(){var output="ContainerEffect";if(this.overlay){output+=" ["+this.overlay.toString()+"]";}
return output;};SHEBANG.widget.ContainerEffect.FADE=function(overlay,dur){var fade=new SHEBANG.widget.ContainerEffect(overlay,{attributes:{opacity:{from:0,to:1}},duration:dur,method:SHEBANG.util.Easing.easeIn},{attributes:{opacity:{to:0}},duration:dur,method:SHEBANG.util.Easing.easeOut},overlay.element);fade.handleStartAnimateIn=function(type,args,obj){SHEBANG.util.Dom.addClass(obj.overlay.element,"hide-select");if(!obj.overlay.underlay){obj.overlay.cfg.refireEvent("underlay");}
if(obj.overlay.underlay){obj.initialUnderlayOpacity=SHEBANG.util.Dom.getStyle(obj.overlay.underlay,"opacity");obj.overlay.underlay.style.filter=null;}
SHEBANG.util.Dom.setStyle(obj.overlay.element,"visibility","visible");SHEBANG.util.Dom.setStyle(obj.overlay.element,"opacity",0);};fade.handleCompleteAnimateIn=function(type,args,obj){SHEBANG.util.Dom.removeClass(obj.overlay.element,"hide-select");if(obj.overlay.element.style.filter){obj.overlay.element.style.filter=null;}
if(obj.overlay.underlay){SHEBANG.util.Dom.setStyle(obj.overlay.underlay,"opacity",obj.initialUnderlayOpacity);}
obj.overlay.cfg.refireEvent("iframe");obj.animateInCompleteEvent.fire();};fade.handleStartAnimateOut=function(type,args,obj){SHEBANG.util.Dom.addClass(obj.overlay.element,"hide-select");if(obj.overlay.underlay){obj.overlay.underlay.style.filter=null;}};fade.handleCompleteAnimateOut=function(type,args,obj){SHEBANG.util.Dom.removeClass(obj.overlay.element,"hide-select");if(obj.overlay.element.style.filter){obj.overlay.element.style.filter=null;}
SHEBANG.util.Dom.setStyle(obj.overlay.element,"visibility","hidden");SHEBANG.util.Dom.setStyle(obj.overlay.element,"opacity",1);obj.overlay.cfg.refireEvent("iframe");obj.animateOutCompleteEvent.fire();};fade.init();return fade;};SHEBANG.widget.ContainerEffect.SLIDE=function(overlay,dur){var x=overlay.cfg.getProperty("x")||SHEBANG.util.Dom.getX(overlay.element);var y=overlay.cfg.getProperty("y")||SHEBANG.util.Dom.getY(overlay.element);var clientWidth=SHEBANG.util.Dom.getClientWidth();var offsetWidth=overlay.element.offsetWidth;var slide=new SHEBANG.widget.ContainerEffect(overlay,{attributes:{points:{to:[x,y]}},duration:dur,method:SHEBANG.util.Easing.easeIn},{attributes:{points:{to:[(clientWidth+25),y]}},duration:dur,method:SHEBANG.util.Easing.easeOut},overlay.element,SHEBANG.util.Motion);slide.handleStartAnimateIn=function(type,args,obj){obj.overlay.element.style.left=(-25-offsetWidth)+"px";obj.overlay.element.style.top=y+"px";};slide.handleTweenAnimateIn=function(type,args,obj){var pos=SHEBANG.util.Dom.getXY(obj.overlay.element);var currentX=pos[0];var currentY=pos[1];if(SHEBANG.util.Dom.getStyle(obj.overlay.element,"visibility")=="hidden"&&currentX<x){SHEBANG.util.Dom.setStyle(obj.overlay.element,"visibility","visible");}
obj.overlay.cfg.setProperty("xy",[currentX,currentY],true);obj.overlay.cfg.refireEvent("iframe");};slide.handleCompleteAnimateIn=function(type,args,obj){obj.overlay.cfg.setProperty("xy",[x,y],true);obj.startX=x;obj.startY=y;obj.overlay.cfg.refireEvent("iframe");obj.animateInCompleteEvent.fire();};slide.handleStartAnimateOut=function(type,args,obj){var vw=SHEBANG.util.Dom.getViewportWidth();var pos=SHEBANG.util.Dom.getXY(obj.overlay.element);var yso=pos[1];var currentTo=obj.animOut.attributes.points.to;obj.animOut.attributes.points.to=[(vw+25),yso];};slide.handleTweenAnimateOut=function(type,args,obj){var pos=SHEBANG.util.Dom.getXY(obj.overlay.element);var xto=pos[0];var yto=pos[1];obj.overlay.cfg.setProperty("xy",[xto,yto],true);obj.overlay.cfg.refireEvent("iframe");};slide.handleCompleteAnimateOut=function(type,args,obj){SHEBANG.util.Dom.setStyle(obj.overlay.element,"visibility","hidden");obj.overlay.cfg.setProperty("xy",[x,y]);obj.animateOutCompleteEvent.fire();};slide.init();return slide;};SHEBANG.register("container_core",SHEBANG.widget.Module,{version:"2.2.2",build:"204"});