PageSourceSearch

https://kava.fr/js/carousel.js

js kava.fr collected 2026-10-04 00:57:35 UTC 54,706 bytes, 1,567 lines download raw bytes

1/**
2 * Copyright (c) 2006-2007, Bill W. Scott
3 * All rights reserved.
4 *
5 * This work is licensed under the Creative Commons Attribution 2.5 License. To view a copy 
6 * of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to 
7 * Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
8 *
9 * This work was created by Bill Scott (billwscott.com, looksgoodworkswell.com).
10 * 
11 * The only attribution I require is to keep this notice of copyright & license 
12 * in this original source file.
13 *
14 * Version 0.6.1 - 07.08.2007
15 *
16 */
17YAHOO.namespace("extension");
18
19/**
20* @class 
21* The carousel class manages a content list (a set of LI elements within an UL list)  that can be displayed horizontally or vertically. The content can be scrolled back and forth  with or without animation. The content can reference static HTML content or the list items can  be created dynamically on-the-fly (with or without Ajax). The navigation and event handling  can be externalized from the class.
22* @param {object|string} carouselElementID The element ID (id name or id object) of the DIV that will become a carousel
23* @param {object} carouselCfg The configuration object literal containing the configuration that should be set for this module. See configuration documentation for more details.
24* @constructor
25*/
26YAHOO.extension.Carousel = function(carouselElementID, carouselCfg) {
27 		this.init(carouselElementID, carouselCfg);
28	};
29
30YAHOO.extension.Carousel.prototype = {
31
32
33	/**
34	 * Constant denoting that the carousel size is unbounded (no limits set on scrolling)
35	 * @type number
36	 */
37	UNBOUNDED_SIZE: 1000000,
38	
39	/**
40	 * Initializes the carousel object and all of its local members.
41     * @param {object|string} carouselElementID The element ID (id name or id object) 
42     * of the DIV that will become a carousel
43     * @param {object} carouselCfg The configuration object literal containing the 
44     * configuration that should be set for this module. See configuration documentation for more details.
45	 */
46	init: function(carouselElementID, carouselCfg) {
47
48		var oThis = this;
49		
50		/**
51		 * For deprecation.
52		 * getItem is the replacement for getCarouselItem
53		 */
54		this.getCarouselItem = this.getItem;
55		
56		// CSS style classes
57		var carouselListClass = "carousel-list";
58		var carouselClipRegionClass = "carousel-clip-region";
59		var carouselNextClass = "carousel-next";
60		var carouselPrevClass = "carousel-prev";
61
62 		this._carouselElemID = carouselElementID;
63 		this.carouselElem = YAHOO.util.Dom.get(carouselElementID);
64
65 		this._prevEnabled = true;
66 		this._nextEnabled = true;
67 		
68 		// Create the config object
69 		this.cfg = new YAHOO.util.Config(this);
70
71		/**
72		 * scrollBeforeAmount property. 
73		 * Normally, set to 0, this is how much you are allowed to
74		 * scroll below the first item. Setting it to 2 allows you
75		 * to scroll to the -1 position. 
76		 * However, the load handlers will not be asked to load anything
77		 * below 1.
78		 *
79		 * A good example is the spotlight example which treats the middle item
80		 * as the "selected" item. It sets scrollBeforeAmount to 2 and 
81		 * scrollAfterAmount to 2.
82		 *
83		 * The actual items loaded would be from 1 to 15 (size=15),
84		 * but scrolling range would be -1 to 17.
85		 */
86		this.cfg.addProperty("scrollBeforeAmount", { 
87			value:0, 
88			handler: function(type, args, carouselElem) {
89			},
90			validator: oThis.cfg.checkNumber
91		} );		
92
93		/**
94		 * scrollAfterAmount property. 
95		 * Normally, set to 0, this is how much you are allowed to
96		 * scroll past the size. Setting it to 2 allows you
97		 * to scroll to the size+scrollAfterAmount position. 
98		 * However, the load handlers will not be asked to load anything
99		 * beyond size.
100		 *
101		 * A good example is the spotlight example which treats the middle item
102		 * as the "selected" item. It sets scrollBeforeAmount to 2 and 
103		 * scrollAfterAmount to 2.
104		 *
105		 * The actual items loaded would be from 1 to 15 (size=15),
106		 * but scrolling range would be -1 to 17.
107		 */
108		this.cfg.addProperty("scrollAfterAmount", { 
109			value:0, 
110			handler: function(type, args, carouselElem) {
111			},
112			validator: oThis.cfg.checkNumber
113		} );		
114
115		/**
116		 * loadOnStart property. 
117		 * If true, will call loadInitHandler on startup.
118		 * If false, will not. Useful for delaying the initialization
119		 * of the carousel for a later time after creation.
120		 */
121		this.cfg.addProperty("loadOnStart", { 
122			value:true, 
123			handler: function(type, args, carouselElem) {
124				// no action, only affects startup
125			},
126			validator: oThis.cfg.checkBoolean
127		} );		
128
129		/**
130		 * orientation property. 
131		 * Either "horizontal" or "vertical". Changes carousel from a 
132		 * left/right style carousel to a up/down style carousel.
133		 */
134		this.cfg.addProperty("orientation", { 
135			value:"horizontal", 
136			handler: function(type, args, carouselElem) {
137				oThis.reload();
138			},
139			validator: function(orientation) {
140			    if(typeof orientation == "string") {
141			        return ("horizontal,vertical".indexOf(orientation.toLowerCase()) != -1);
142			    } else {
143					return false;
144				}
145			}
146		} );		
147
148		/**
149		 * size property. 
150		 * The upper bound for scrolling in the 'next' set of content. 
151		 * Set to a large value by default (this means unlimited scrolling.) 
152		 */
153		this.cfg.addProperty("size", { 
154			value:this.UNBOUNDED_SIZE,
155			handler: function(type, args, carouselElem) {
156				oThis.reload();
157			},
158			validator: oThis.cfg.checkNumber
159		} );
160
161		/**
162		 * numVisible property. 
163		 * The number of items that will be visible.
164		 */
165		this.cfg.addProperty("numVisible", { 
166			value:3,
167			handler: function(type, args, carouselElem) {
168				oThis.reload();
169			},
170			validator: oThis.cfg.checkNumber
171		} );
172
173		/**
174		 * firstVisible property. 
175		 * Sets which item should be the first visible item in the carousel. Use to set which item will
176		 * display as the first element when the carousel is first displayed. After the carousel is created,
177		 * you can manipulate which item is the first visible by using the moveTo() or scrollTo() convenience
178		 * methods. Can be < 1 or greater than size if the scrollBeforeAmount or scrollAmountAfter has been set
179		 * to non-zero values.
180		 */
181		this.cfg.addProperty("firstVisible", { 
182			value:1,
183			handler: function(type, args, carouselElem) {
184				oThis.moveTo(args[0]);
185			},
186			validator: oThis.cfg.checkNumber
187		} );
188
189		/**
190		 * scrollInc property. 
191		 * The number of items to scroll by. Think of this as the page increment.
192		 */
193		this.cfg.addProperty("scrollInc", { 
194			value:3,
195			handler: function(type, args, carouselElem) {
196			},
197			validator: oThis.cfg.checkNumber
198		} );
199		
200		/**
201		 * animationSpeed property. 
202		 * The time (in seconds) it takes to complete the scroll animation. 
203		 * If set to 0, animated transitions are turned off and the new page of content is 
204		 * moved immdediately into place.
205		 */
206		this.cfg.addProperty("animationSpeed", { 
207			value:0.25,
208			handler: function(type, args, carouselElem) {
209				oThis.animationSpeed = args[0];
210			},
211			validator: oThis.cfg.checkNumber
212		} );
213
214		/**
215		 * animationMethod property. 
216		 * The <a href="http://developer.yahoo.com/yui/docs/animation/YAHOO.util.Easing.html">YAHOO.util.Easing</a> 
217		 * method.
218		 */
219		this.cfg.addProperty("animationMethod", { 
220			value:  YAHOO.util.Easing.easeOut,
221			handler: function(type, args, carouselElem) {
222			}
223		} );
224		
225		/**
226		 * animationCompleteHandler property. 
227		 * JavaScript function that is called when the Carousel finishes animation 
228		 * after a next or previous nagivation. 
229		 * Only invoked if animationSpeed > 0. 
230		 * Two parameters are passed: type (set to 'onAnimationComplete') and 
231		 * args array (args[0] = direction [either: 'next' or 'previous']).
232		 */
233		this.cfg.addProperty("animationCompleteHandler", { 
234			value:null,
235			handler: function(type, args, carouselElem) {
236				if(oThis._animationCompleteEvt) {
237					oThis._animationCompleteEvt.unsubscribe(oThis._currAnimationCompleteHandler, oThis);
238				}
239				oThis._currAnimationCompleteHandler = args[0];
240				if(oThis._currAnimationCompleteHandler) {
241					if(!oThis._animationCompleteEvt) {
242						oThis._animationCompleteEvt = new YAHOO.util.CustomEvent("onAnimationComplete", oThis);
243					}
244					oThis._animationCompleteEvt.subscribe(oThis._currAnimationCompleteHandler, oThis);
245				}
246			}
247		} );
248		
249		/**
250		 * autoPlay property. 
251		 * Specifies how many milliseconds to periodically auto scroll the content. 
252		 * If set to 0 (default) then autoPlay is turned off. 
253		 * If the user interacts by clicking left or right navigation, autoPlay is turned off. 
254		 * You can restart autoPlay by calling the <em>startAutoPlay()</em>. 
255		 * If you externally control navigation (with your own event handlers) 
256		 * then you may want to turn off the autoPlay by calling<em>stopAutoPlay()</em>
257		 */
258		this.cfg.addProperty("autoPlay", { 
259			value:0,
260			handler: function(type, args, carouselElem) {
261				var autoPlay = args[0];
262				if(autoPlay > 0)
263					oThis.startAutoPlay();
264				else
265					oThis.stopAutoPlay();
266			}
267		} );
268		
269		/**
270		 * wrap property. 
271		 * Specifies whether to wrap when at the end of scrolled content. When the end is reached,
272		 * the carousel will scroll backwards to the item 1 (the animationSpeed parameter is used to 
273		 * determine how quickly it should animate back to the start.)
274		 * Ignored if the <em>size</em> attribute is not explicitly set 
275		 * (i.e., value equals YAHOO.extension.Carousel.UNBOUNDED_SIZE)
276		 */
277		this.cfg.addProperty("wrap", { 
278			value:false,
279			handler: function(type, args, carouselElem) {
280			},
281			validator: oThis.cfg.checkBoolean
282		} );
283		
284		/**
285		 * navMargin property. 
286		 * The margin space for the navigation controls. This is only useful for horizontal carousels 
287		 * in which you have embedded navigation controls. 
288		 * The <em>navMargin</em> allocates space between the left and right margins 
289		 * (each navMargin wide) giving space for the navigation controls.
290		 */
291		this.cfg.addProperty("navMargin", { 
292			value:0,
293			handler: function(type, args, carouselElem) {
294				oThis.calculateSize();		
295			},
296			validator: oThis.cfg.checkNumber
297		} );
298		
299		/**
300		 * revealAmount property. 
301		 * The amount to reveal of what comes before and what comes after the firstVisible and
302		 * the lastVisible items. Setting this will provide a slight preview that something 
303		 * exists before and after, providing an additional hint for the user.
304		 * The <em>revealAmount</em> will reveal the specified number of pixels for any item
305		 * before the firstVisible and an item after the lastVisible. Additionall, the
306		 * loadNextHandler and loadPrevHandler methods will be passed a start or end that guarantees
307		 * the revealed item will be loaded (if set to non-zero).
308		 */
309		this.cfg.addProperty("revealAmount", { 
310			value:0,
311			handler: function(type, args, carouselElem) {
312				oThis.reload();
313			},
314			validator: oThis.cfg.checkNumber
315		} );
316		
317		// For backward compatibility. Deprecated.
318		this.cfg.addProperty("prevElementID", { 
319			value: null,
320			handler: function(type, args, carouselElem) {
321				if(oThis._carouselPrev) {
322					YAHOO.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev);
323				} 
324				oThis._prevElementID = args[0];
325				if(oThis._prevElementID == null) {
326					oThis._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, 
327														"div", oThis.carouselElem)[0];
328				} else {
329					oThis._carouselPrev = YAHOO.util.Dom.get(oThis._prevElementID);
330				}
331				YAHOO.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis);
332			}
333		});
334		
335		/**
336		 * prevElement property. 
337		 * An element or elements that will provide the previous navigation control.
338		 * prevElement may be a single element or an array of elements. The values may be strings denoting
339		 * the ID of the element or the object itself.
340		 * If supplied, then events are wired to this control to fire scroll events to move the carousel to
341		 * the previous content. 
342		 * You may want to provide your own interaction for controlling the carousel. If
343		 * so leave this unset and provide your own event handling mechanism.
344		 */
345		this.cfg.addProperty("prevElement", { 
346			value:null,
347			handler: function(type, args, carouselElem) {
348				if(oThis._carouselPrev) {
349					YAHOO.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev);
350				} 
351				oThis._prevElementID = args[0];
352				if(oThis._prevElementID == null) {
353					oThis._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, 
354														"div", oThis.carouselElem)[0];
355				} else {
356					oThis._carouselPrev = YAHOO.util.Dom.get(oThis._prevElementID);
357				}
358				YAHOO.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis);
359			}
360		} );
361		
362		// For backward compatibility. Deprecated.
363		this.cfg.addProperty("nextElementID", { 
364			value: null,
365			handler: function(type, args, carouselElem) {
366				if(oThis._carouselNext) {
367					YAHOO.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext);
368				} 
369				oThis._nextElementID = args[0];
370				if(oThis._nextElementID == null) {
371					oThis._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, 
372														"div", oThis.carouselElem);
373				} else {
374					oThis._carouselNext = YAHOO.util.Dom.get(oThis._nextElementID);
375				}
376				if(oThis._carouselNext) {
377					YAHOO.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis);
378				} 
379			}
380		});
381		
382		/**
383		 * nextElement property. 
384		 * An element or elements that will provide the next navigation control.
385		 * nextElement may be a single element or an array of elements. The values may be strings denoting
386		 * the ID of the element or the object itself.
387		 * If supplied, then events are wired to this control to fire scroll events to move the carousel to
388		 * the next content. 
389		 * You may want to provide your own interaction for controlling the carousel. If
390		 * so leave this unset and provide your own event handling mechanism.
391		 */
392		this.cfg.addProperty("nextElement", { 
393			value:null,
394			handler: function(type, args, carouselElem) {
395				if(oThis._carouselNext) {
396					YAHOO.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext);
397				} 
398				oThis._nextElementID = args[0];
399				if(oThis._nextElementID == null) {
400					oThis._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, 
401														"div", oThis.carouselElem);
402				} else {
403					oThis._carouselNext = YAHOO.util.Dom.get(oThis._nextElementID);
404				}
405				if(oThis._carouselNext) {
406					YAHOO.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis);
407				} 
408			}
409		} );
410		
411		/**
412		 * loadInitHandler property. 
413		 * JavaScript function that is called when the Carousel needs to load 
414		 * the initial set of visible items. Two parameters are passed: 
415		 * type (set to 'onLoadInit') and an argument array (args[0] = start index, args[1] = last index).
416		 */
417		this.cfg.addProperty("loadInitHandler", { 
418			value:null,
419			handler: function(type, args, carouselElem) {
420				if(oThis._loadInitHandlerEvt) {
421					oThis._loadInitHandlerEvt.unsubscribe(oThis._currLoadInitHandler, oThis);
422				}
423				oThis._currLoadInitHandler = args[0];
424				if(oThis._currLoadInitHandler) {
425					if(!oThis._loadInitHandlerEvt) {
426						oThis._loadInitHandlerEvt = new YAHOO.util.CustomEvent("onLoadInit", oThis);
427					}
428					oThis._loadInitHandlerEvt.subscribe(oThis._currLoadInitHandler, oThis);
429				}
430			}
431		} );
432		
433		/**
434		 * loadNextHandler property. 
435		 * JavaScript function that is called when the Carousel needs to load 
436		 * the next set of items (in response to the user navigating to the next set.) 
437		 * Two parameters are passed: type (set to 'onLoadNext') and 
438		 * args array (args[0] = start index, args[1] = last index).
439		 */
440		this.cfg.addProperty("loadNextHandler", { 
441			value:null,
442			handler: function(type, args, carouselElem) {
443				if(oThis._loadNextHandlerEvt) {
444					oThis._loadNextHandlerEvt.unsubscribe(oThis._currLoadNextHandler, oThis);
445				}
446				oThis._currLoadNextHandler = args[0];
447				if(oThis._currLoadNextHandler) {
448					if(!oThis._loadNextHandlerEvt) {
449						oThis._loadNextHandlerEvt = new YAHOO.util.CustomEvent("onLoadNext", oThis);
450					}
451					oThis._loadNextHandlerEvt.subscribe(oThis._currLoadNextHandler, oThis);
452				}
453			}
454		} );
455				
456		/**
457		 * loadPrevHandler property. 
458		 * JavaScript function that is called when the Carousel needs to load 
459		 * the previous set of items (in response to the user navigating to the previous set.) 
460		 * Two parameters are passed: type (set to 'onLoadPrev') and args array 
461		 * (args[0] = start index, args[1] = last index).
462		 */
463		this.cfg.addProperty("loadPrevHandler", { 
464			value:null,
465			handler: function(type, args, carouselElem) {
466				if(oThis._loadPrevHandlerEvt) {
467					oThis._loadPrevHandlerEvt.unsubscribe(oThis._currLoadPrevHandler, oThis);
468				}
469				oThis._currLoadPrevHandler = args[0];
470				if(oThis._currLoadPrevHandler) {
471					if(!oThis._loadPrevHandlerEvt) {
472						oThis._loadPrevHandlerEvt = new YAHOO.util.CustomEvent("onLoadPrev", oThis);
473					}
474					oThis._loadPrevHandlerEvt.subscribe(oThis._currLoadPrevHandler, oThis);
475				}
476			}
477		} );
478		
479		/**
480		 * prevButtonStateHandler property. 
481		 * JavaScript function that is called when the enabled state of the 
482		 * 'previous' control is changing. The responsibility of 
483		 * this method is to enable or disable the 'previous' control. 
484		 * Two parameters are passed to this method: <em>type</em> 
485		 * (which is set to "onPrevButtonStateChange") and <em>args</em>, 
486		 * an array that contains two values. 
487		 * The parameter args[0] is a flag denoting whether the 'previous' control 
488		 * is being enabled or disabled. The parameter args[1] is the element object 
489		 * derived from the <em>prevElement</em> parameter.
490		 * If you do not supply a prevElement then you will need to track
491		 * the elements that you would want to enable/disable while handling the state change.
492		 */
493		this.cfg.addProperty("prevButtonStateHandler", { 
494			value:null,
495			handler: function(type, args, carouselElem) {
496				if(oThis._currPrevButtonStateHandler) {
497					oThis._prevButtonStateHandlerEvt.unsubscribe(oThis._currPrevButtonStateHandler, oThis);
498				}
499
500				oThis._currPrevButtonStateHandler = args[0];
501				
502				if(oThis._currPrevButtonStateHandler) {
503					if(!oThis._prevButtonStateHandlerEvt) {
504						oThis._prevButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onPrevButtonStateChange", oThis);
505					}
506					oThis._prevButtonStateHandlerEvt.subscribe(oThis._currPrevButtonStateHandler, oThis);
507				}
508			}
509		} );
510		
511		/**
512		 * nextButtonStateHandler property. 
513		 * JavaScript function that is called when the enabled state of the 
514		 * 'next' control is changing. The responsibility of 
515		 * this method is to enable or disable the 'next' control. 
516		 * Two parameters are passed to this method: <em>type</em> 
517		 * (which is set to "onNextButtonStateChange") and <em>args</em>, 
518		 * an array that contains two values. 
519		 * The parameter args[0] is a flag denoting whether the 'next' control 
520		 * is being enabled or disabled. The parameter args[1] is the element object 
521		 * derived from the <em>nextElement</em> parameter.
522		 * If you do not supply a nextElement then you will need to track
523		 * the elements that you would want to enable/disable while handling the state change.
524		 */
525		this.cfg.addProperty("nextButtonStateHandler", { 
526			value:null,
527			handler: function(type, args, carouselElem) {
528				if(oThis._currNextButtonStateHandler) {
529					oThis._nextButtonStateHandlerEvt.unsubscribe(oThis._currNextButtonStateHandler, oThis);
530				}
531				oThis._currNextButtonStateHandler = args[0];
532				
533				if(oThis._currNextButtonStateHandler) {
534					if(!oThis._nextButtonStateHandlerEvt) {
535						oThis._nextButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onNextButtonStateChange", oThis);
536					}
537					oThis._nextButtonStateHandlerEvt.subscribe(oThis._currNextButtonStateHandler, oThis);
538				}
539			}
540		} );
541		
542		
543 		if(carouselCfg) {
544 			this.cfg.applyConfig(carouselCfg);
545 		}
546 		
547		this._origFirstVisible = this.cfg.getProperty("firstVisible");
548		
549		// keep a copy of curr handler so it can be removed when a new handler is set
550		this._currLoadInitHandler = this.cfg.getProperty("loadInitHandler");
551		this._currLoadNextHandler = this.cfg.getProperty("loadNextHandler");
552		this._currLoadPrevHandler = this.cfg.getProperty("loadPrevHandler");
553		this._currPrevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler");
554		this._currNextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler");
555		this._currAnimationCompleteHandler = this.cfg.getProperty("animationCompleteHandler");
556		
557		this._nextElementID = this.cfg.getProperty("nextElementID");
558		if(!this._nextElementID) 
559			this._nextElementID = this.cfg.getProperty("nextElement");
560		
561		this._prevElementID = this.cfg.getProperty("prevElementID");
562		if(!this._prevElementID) 
563			this._prevElementID = this.cfg.getProperty("prevElement");
564
565		this._autoPlayTimer = null;
566		this._priorLastVisible = this._priorFirstVisible = this.cfg.getProperty("firstVisible");
567		this._lastPrebuiltIdx = 0;
568// this._currSize = 0;
569		 		
570 		// prefetch elements
571 		this.carouselList = YAHOO.util.Dom.getElementsByClassName(carouselListClass, 
572												"ul", this.carouselElem)[0];
573							
574		if(this._nextElementID == null) {
575			this._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, 
576												"div", this.carouselElem)[0];
577		} else {
578			this._carouselNext = YAHOO.util.Dom.get(this._nextElementID);
579		}
580
581		if(this._prevElementID == null) {
582 			this._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, 
583												"div", this.carouselElem)[0];
584		} else {
585			this._carouselPrev = YAHOO.util.Dom.get(this._prevElementID);
586		}
587		
588		this._clipReg = YAHOO.util.Dom.getElementsByClassName(carouselClipRegionClass, 
589												"div", this.carouselElem)[0];
590												
591		// add a style class dynamically so that the correct styles get applied for a vertical carousel
592		if(this.isVertical()) {
593			YAHOO.util.Dom.addClass(this.carouselList, "carousel-vertical");
594		}
595		
596		// initialize the animation objects for next/previous
597 		this._scrollNextAnim = new YAHOO.util.Motion(this.carouselList, this.scrollNextParams, 
598   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
599 		this._scrollPrevAnim = new YAHOO.util.Motion(this.carouselList, this.scrollPrevParams, 
600   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
601		
602		// If they supplied a nextElementID then wire an event listener for the click
603		if(this._carouselNext) {
604			YAHOO.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this);
605		} 
606		
607		// If they supplied a prevElementID then wire an event listener for the click
608		if(this._carouselPrev) {
609			YAHOO.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this);
610		}
611				
612		// Wire up the various event handlers that they might have supplied
613		var loadInitHandler = this.cfg.getProperty("loadInitHandler");
614		if(loadInitHandler) {
615			this._loadInitHandlerEvt = new YAHOO.util.CustomEvent("onLoadInit", this);
616			this._loadInitHandlerEvt.subscribe(loadInitHandler, this);
617		}
618		var loadNextHandler = this.cfg.getProperty("loadNextHandler");
619		if(loadNextHandler) {
620			this._loadNextHandlerEvt = new YAHOO.util.CustomEvent("onLoadNext", this);
621			this._loadNextHandlerEvt.subscribe(loadNextHandler, this);
622		}
623		var loadPrevHandler = this.cfg.getProperty("loadPrevHandler");
624		if(loadPrevHandler) {
625			this._loadPrevHandlerEvt = new YAHOO.util.CustomEvent("onLoadPrev", this);
626			this._loadPrevHandlerEvt.subscribe(loadPrevHandler, this);
627		}
628		var animationCompleteHandler = this.cfg.getProperty("animationCompleteHandler");
629		if(animationCompleteHandler) {
630			this._animationCompleteEvt = new YAHOO.util.CustomEvent("onAnimationComplete", this);
631			this._animationCompleteEvt.subscribe(animationCompleteHandler, this);
632		}
633		var prevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler");
634		if(prevButtonStateHandler) {
635			this._prevButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onPrevButtonStateChange", 
636							this);
637			this._prevButtonStateHandlerEvt.subscribe(prevButtonStateHandler, this);
638		}
639		var nextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler");
640		if(nextButtonStateHandler) {
641			this._nextButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onNextButtonStateChange", this);
642			this._nextButtonStateHandlerEvt.subscribe(nextButtonStateHandler, this);
643		}
644		
645		// Since loading may take some time, wire up a listener to fire when at least the first
646		// element actually gets loaded
647		var visibleExtent = this._calculateVisibleExtent();
648  		YAHOO.util.Event.onAvailable(this._carouselElemID + "-item-"+
649					visibleExtent.start,  this._calculateSize, this);
650  		
651  		// Call the initial loading sequence
652		if(this.cfg.getProperty("loadOnStart"))
653			this._loadInitial();	
654
655	},
656	
657	// /////////////////// Public API //////////////////////////////////////////
658
659	/**
660	 * Clears all items from the list and resets to the carousel to its original initial state.
661	 */
662	clear: function() {
663		// remove all items from the carousel for dynamic content
664		var loadInitHandler = this.cfg.getProperty("loadInitHandler");
665		if(loadInitHandler) {
666			this._removeChildrenFromNode(this.carouselList);
667			this._lastPrebuiltIdx = 0;
668		}
669		// turn off autoplay
670		this.stopAutoPlay(); // should we only turn this off for dynamic during reload?
671		
672		this._priorLastVisible = this._priorFirstVisible = this._origFirstVisible;
673		
674		// is this redundant since moveTo will set this?	
675		this.cfg.setProperty("firstVisible", this._origFirstVisible, true);		
676		this.moveTo(this._origFirstVisible);
677	},
678	
679	/**
680	 * Clears all items from the list and calls the loadInitHandler to load new items into the list. 
681	 * The carousel size is reset to the original size set during creation.
682	 * @param {number}	numVisible	Optional parameter: numVisible. 
683	 * If set, the carousel will resize on the reload to show numVisible items.
684	 */
685	reload: function(numVisible) {
686		// this should be deprecated, not needed since can be set via property change
687	    if(this._isValidObj(numVisible)) {
688			this.cfg.setProperty("numVisible", numVisible);
689	    }
690		this.clear();
691		
692		// clear resets back to start
693		var visibleExtent = this._calculateVisibleExtent();
694		YAHOO.util.Event.onAvailable(this._carouselElemID+"-item-"+visibleExtent.start,
695		 								this._calculateSize, this);  		
696		this._loadInitial();
697		
698	},
699
700	load: function() {
701		var visibleExtent = this._calculateVisibleExtent();
702		
703		YAHOO.util.Event.onAvailable(this._carouselElemID + "-item-"+visibleExtent.start, 
704						this._calculateSize, this);  		
705		this._loadInitial();
706	},
707		
708	/**
709	 * With patch from Dan Hobbs for handling unordered loading.
710	 * @param {number}	idx	which item in the list to potentially create. 
711	 * If item already exists it will not create a new item.
712	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
713	 * @param {string}	itemClass	A class optionally supplied to add to the LI item created
714	 */
715	addItem: function(idx, innerHTMLOrElem, itemClass) {
716		
717		if(idx > this.cfg.getProperty("size")) {
718			return null;
719		}
720		
721        var liElem = this.getItem(idx);
722
723		// Need to create the li
724		if(!this._isValidObj(liElem)) {
725			liElem = this._createItem(idx, innerHTMLOrElem);
726			this.carouselList.appendChild(liElem);
727			
728		} else if(this._isValidObj(liElem.placeholder)) {		
729	    	var newLiElem = this._createItem(idx, innerHTMLOrElem);
730			this.carouselList.replaceChild(newLiElem, liElem);
731			liElem = newLiElem;
732		}
733		
734		// if they supplied an item class add it to the element
735		if(this._isValidObj(itemClass)){
736			YAHOO.util.Dom.addClass(liElem, itemClass);
737		}
738		
739		/**
740		 * Not real comfortable with this line of code. It exists for vertical
741		 * carousels for IE6. For some reason LI elements are not displaying
742		 * unless you after the fact set the display to block. (Even though
743	     * the CSS sets vertical LIs to display:block)
744	     */
745		if(this.isVertical())
746			setTimeout( function() { liElem.style.display="block"; }, 1 );		
747				
748		return liElem;
749
750	},
751
752	/**
753	 * Inserts a new LI item before the index specified. Uses the innerHTML to create the contents of the new LI item
754	 * @param {number}	refIdx	which item in the list to insert this item before. 
755	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
756	 */
757	insertBefore: function(refIdx, innerHTML) {
758		// don't allow insertion beyond the size
759		if(refIdx >= this.cfg.getProperty("size")) {
760			return null;
761		}
762		
763		if(refIdx < 1) {
764			refIdx = 1;
765		}
766		
767		var insertionIdx = refIdx - 1;
768		
769		if(insertionIdx > this._lastPrebuiltIdx) {
770			this._prebuildItems(this._lastPrebuiltIdx, refIdx); // is this right?
771		}
772		
773		var liElem = this._insertBeforeItem(refIdx, innerHTML);
774		
775		this._enableDisableControls();
776		
777		return liElem;
778	},
779	
780	/**
781	 * Inserts a new LI item after the index specified. Uses the innerHTML to create the contents of the new LI item
782	 * @param {number}	refIdx	which item in the list to insert this item after. 
783	 * @param {string}	innerHTML	The innerHTML string to use to create the contents of an LI element.
784	 */
785	insertAfter: function(refIdx, innerHTML) {
786	
787		if(refIdx > this.cfg.getProperty("size")) {
788			refIdx = this.cfg.getProperty("size");
789		}
790		
791		var insertionIdx = refIdx + 1;			
792		
793		// if we are inserting this item past where we have prebuilt items, then
794		// prebuild up to this point.
795		if(insertionIdx > this._lastPrebuiltIdx) {
796			this._prebuildItems(this._lastPrebuiltIdx, insertionIdx+1);
797		}
798
799		var liElem = this._insertAfterItem(refIdx, innerHTML);		
800
801		if(insertionIdx > this.cfg.getProperty("size")) {
802			this.cfg.setProperty("size", insertionIdx, true);
803		}
804		
805		this._enableDisableControls();
806
807		return liElem;
808	},	
809
810	/**
811	 * Simulates a next button event. Causes the carousel to scroll the next set of content into view.
812	 */
813	scrollNext: function() {
814		this._scrollNext(null, this);
815		
816		// we know the timer has expired.
817		//if(this._autoPlayTimer) clearTimeout(this._autoPlayTimer);
818		this._autoPlayTimer = null;
819		if(this.cfg.getProperty("autoPlay") !== 0) {
820			this._autoPlayTimer = this.startAutoPlay();
821		}
822	},
823	
824	/**
825	 * Simulates a prev button event. Causes the carousel to scroll the previous set of content into view.
826	 */
827	scrollPrev: function() {
828		this._scrollPrev(null, this);
829	},
830	
831	/**
832	 * Scrolls the content to place itemNum as the start item in the view 
833	 * (if size is specified, the last element will not scroll past the end.). 
834	 * Uses current animation speed & method.
835	 * @param {number}	newStart	The item to scroll to. 
836	 */
837	scrollTo: function(newStart) {
838		this._position(newStart, true);
839	},
840
841	/**
842	 * Moves the content to place itemNum as the start item in the view 
843	 * (if size is specified, the last element will not scroll past the end.) 
844	 * Ignores animation speed & method; moves directly to the item. 
845	 * Note that you can also set the <em>firstVisible</em> property upon initialization 
846	 * to get the carousel to start at a position different than 1.	
847	 * @param {number}	newStart	The item to move directly to. 
848	 */
849	moveTo: function(newStart) {
850		this._position(newStart, false);
851	},
852
853	/**
854	 * Starts up autoplay. If autoPlay has been stopped (by calling stopAutoPlay or by user interaction), 
855	 * you can start it back up by using this method.
856	 * @param {number}	interval	optional parameter that sets the interval 
857	 * for auto play the next time that autoplay fires. 
858	 */
859	startAutoPlay: function(interval) {
860		// if interval is passed as arg, then set autoPlay to this interval.
861		if(this._isValidObj(interval)) {
862			this.cfg.setProperty("autoPlay", interval, true);
863		}
864		
865		// if we already are playing, then do nothing.
866		if(this._autoPlayTimer !== null) {
867			return this._autoPlayTimer;
868		}
869				
870		var oThis = this;  
871		var autoScroll = function() { oThis.scrollNext(); };
872		this._autoPlayTimer = setTimeout( autoScroll, this.cfg.getProperty("autoPlay") );
873		
874		return this._autoPlayTimer;
875	},
876
877	/**
878	 * Stops autoplay. Useful for when you want to control what events will stop the autoplay feature. 
879	 * Call <em>startAutoPlay()</em> to restart autoplay.
880	 */
881	stopAutoPlay: function() {
882		if (this._autoPlayTimer !== null) {
883			clearTimeout(this._autoPlayTimer);
884			this._autoPlayTimer = null;
885		}
886	},
887	
888	/**
889	 * Returns whether the carousel's orientation is set to vertical.
890	 */
891	isVertical: function() {
892		return (this.cfg.getProperty("orientation") != "horizontal");
893	},
894	
895	
896	/**
897	 * Check to see if an element (by index) has been loaded or not. If the item is simply pre-built, but not
898	 * loaded this will return false. If the item has not been pre-built it will also return false.
899	 * @param {number}	idx	Index of the element to check load status for. 
900	 */
901	isItemLoaded: function(idx) {
902		var liElem = this.getItem(idx);
903		
904		// if item exists and is not a placeholder, then it is already loaded.
905		if(this._isValidObj(liElem) && !this._isValidObj(liElem.placeholder)) {
906			return true;
907		}
908		
909		return false;
910	},
911	
912	/**
913	 * Lookup the element object for a carousel list item by index.
914	 * @param {number}	idx	Index of the element to lookup. 
915	 */
916	getItem: function(idx) {
917		var elemName = this._carouselElemID + "-item-" + idx;
918 		var liElem = YAHOO.util.Dom.get(elemName);
919		return liElem;	
920	},
921	
922	show: function() {
923		YAHOO.util.Dom.setStyle(this.carouselElem, "display", "block");
924		this.calculateSize();
925	},
926	
927	hide: function() {
928		YAHOO.util.Dom.setStyle(this.carouselElem, "display", "none");
929	},
930
931	calculateSize: function() {
932 		var ulKids = this.carouselList.childNodes;
933 		var li = null;
934		for(var i=0; i<ulKids.length; i++) {
935		
936			li = ulKids[i];
937			if(li.tagName == "LI" || li.tagName == "li") {
938				break;
939			}
940		}
941
942		var navMargin = this.cfg.getProperty("navMargin");
943		var numVisible = this.cfg.getProperty("numVisible");
944		var firstVisible = this.cfg.getProperty("firstVisible");
945		var pl = this._getStyleVal(li, "paddingLeft");
946		var pr = this._getStyleVal(li, "paddingRight");
947		var ml = this._getStyleVal(li, "marginLeft");
948		var mr = this._getStyleVal(li, "marginRight");
949		var pt = this._getStyleVal(li, "paddingTop");
950		var pb = this._getStyleVal(li, "paddingBottom");
951		var mt = this._getStyleVal(li, "marginTop");
952		var mb = this._getStyleVal(li, "marginBottom");
953
954		YAHOO.util.Dom.removeClass(this.carouselList, "carousel-vertical");
955		YAHOO.util.Dom.removeClass(this.carouselList, "carousel-horizontal");
956		if(this.isVertical()) {
957			var liPaddingMarginWidth = pl + pr + ml + mr;
958			YAHOO.util.Dom.addClass(this.carouselList, "carousel-vertical");
959			var liPaddingMarginHeight = pt + pb + mt + mb;
960			
961			var upt = this._getStyleVal(this.carouselList, "paddingTop");
962			var upb = this._getStyleVal(this.carouselList, "paddingBottom");
963			var umt = this._getStyleVal(this.carouselList, "marginTop")
964			var umb = this._getStyleVal(this.carouselList, "marginBottom")
965			var ulPaddingHeight = upt + upb + umt + umb;
966
967			// try to reveal the amount taking into consideration the margin & padding.
968			// This guarantees that this.revealAmount of pixels will be shown on both sides
969			var revealAmt = (this._isExtraRevealed()) ?
970			 			(this.cfg.getProperty("revealAmount")+(liPaddingMarginHeight)/2) : 0;
971
972			// get the height from the height computed style not the offset height
973			// The reason is that on IE the offsetHeight when some part of the margin is
974			// explicitly set to 'auto' can cause accessing that value to crash AND
975			// on FF, in certain cases the actual value used for the LI's height is fractional
976			// For example, while li.offsetHeight might return 93, YAHOO.util.Dom.getStyle(li, "height") 
977			// would return "93.2px". This fractional value will affect the scrolling, so it must be
978			// factored in for FF.
979			// The caveat is that for IE, you will need to set the LI's height explicitly
980			// REPLACED: this.scrollAmountPerInc = (li.offsetHeight + liPaddingMarginHeight);
981			// WITH:
982			var liHeight = this._getStyleVal(li, "height", true);
983			this.scrollAmountPerInc = (liHeight + liPaddingMarginHeight);
984			
985			var liWidth = this._getStyleVal(li, "width");
986			this.carouselElem.style.width = (liWidth + liPaddingMarginWidth) + "px";			
987			this._clipReg.style.height = 
988					(this.scrollAmountPerInc * numVisible + revealAmt*2 + 
989					ulPaddingHeight) + "px";
990//alert(this._clipReg.style.height);
991			this.carouselElem.style.height = 
992				(this.scrollAmountPerInc * numVisible + revealAmt*2 + navMargin*2 +
993					ulPaddingHeight) + "px";
994
995			// possible that the umt+upt is needed... need to test this.
996			var revealTop = (this._isExtraRevealed()) ? 
997					(revealAmt - (Math.abs(mt-mb)+Math.abs(pt-pb))/2
998					) : 
999					0;
1000			YAHOO.util.Dom.setStyle(this.carouselList, "position", "relative");
1001			YAHOO.util.Dom.setStyle(this.carouselList, "top", "" + revealTop + "px");
1002
1003			// if we set the initial start >
1003 1 then this will adjust the scrolled location
1004			var currY = YAHOO.util.Dom.getY(this.carouselList);	
1005			YAHOO.util.Dom.setY(this.carouselList, currY - this.scrollAmountPerInc*(firstVisible-1));
1006
1007		// --- HORIZONTAL
1008		} else {
1009			YAHOO.util.Dom.addClass(this.carouselList, "carousel-horizontal");
1010
1011			var upl = this._getStyleVal(this.carouselList, "paddingLeft");
1012			var upr = this._getStyleVal(this.carouselList, "paddingRight");
1013			var uml = this._getStyleVal(this.carouselList, "marginLeft")
1014			var umr = this._getStyleVal(this.carouselList, "marginRight")
1015			var ulPaddingWidth = upl + upr + uml + umr;
1016
1017			var liMarginWidth = ml + mr;
1018			var liPaddingMarginWidth = liMarginWidth + pr + pl;
1019			
1020			// try to reveal the amount taking into consideration the margin & padding.
1021			// This guarantees that this.revealAmount of pixels will be shown on both sides
1022			var revealAmt = (this._isExtraRevealed()) ?
1023			 					(this.cfg.getProperty("revealAmount")+(liPaddingMarginWidth)/2) : 0;
1024			
1025			var liWidth = li.offsetWidth; 
1026			this.scrollAmountPerInc = liWidth + liMarginWidth;
1027			
1028			this._clipReg.style.width = 
1029					(this.scrollAmountPerInc*numVisible + revealAmt*2) + "px";
1030			this.carouselElem.style.width =
1031			 		(this.scrollAmountPerInc*numVisible + navMargin*2 + revealAmt*2 + 
1032					ulPaddingWidth) + "px";
1033			
1034			var revealLeft = (this._isExtraRevealed()) ? 
1035					(revealAmt - (Math.abs(mr-ml)+Math.abs(pr-pl))/2 - (uml+upl)
1036					) : 
1037					0;
1038			YAHOO.util.Dom.setStyle(this.carouselList, "position", "relative");
1039			YAHOO.util.Dom.setStyle(this.carouselList, "left", "" + revealLeft + "px");
1040
1041			// if we set the initial start >
1041 1 then this will adjust the scrolled location
1042			var currX = YAHOO.util.Dom.getX(this.carouselList);
1043			YAHOO.util.Dom.setX(this.carouselList, currX - this.scrollAmountPerInc*(firstVisible-1));
1044		}
1045	},
1046	
1047	// Hides the cfg object
1048	setProperty: function(property, value, silent) {
1049		this.cfg.setProperty(property, value, silent);
1050	},
1051	
1052	getProperty: function(property) {
1053		return this.cfg.getProperty(property);
1054	},
1055	
1056	getFirstItemRevealed: function() {
1057		return this._firstItemRevealed;
1058	},
1059	getLastItemRevealed: function() {
1060		return this._lastItemRevealed;
1061	},
1062	
1063	// Just for convenience and to be symmetrical with getFirstVisible
1064	getFirstVisible: function() {
1065		return this.cfg.getProperty("firstVisible");
1066	},
1067	
1068	getLastVisible: function() {
1069		var firstVisible = this.cfg.getProperty("firstVisible");
1070		var numVisible = this.cfg.getProperty("numVisible");
1071		
1072		return firstVisible + numVisible - 1;
1073	},
1074	
1075	// /////////////////// PRIVATE API //////////////////////////////////////////
1076	_getStyleVal : function(li, style, returnFloat) {
1077		var styleValStr = YAHOO.util.Dom.getStyle(li, style);
1078		
1079		var styleVal = returnFloat ? parseFloat(styleValStr) : parseInt(styleValStr, 10);
1080		if(style=="height" && isNaN(styleVal)) {
1081			styleVal = li.offsetHeight;
1082		} else if(isNaN(styleVal)) {
1083			styleVal = 0;
1084		}
1085		return styleVal;
1086	},
1087	
1088	_calculateSize: function(me) {
1089		me.calculateSize();
1090		me.show();
1091		//YAHOO.util.Dom.setStyle(me.carouselElem, "visibility", "visible");
1092	},
1093
1094	// From Mike Chambers: http://weblogs.macromedia.com/mesh/archives/2006/01/removing_html_e.html
1095	_removeChildrenFromNode: function(node)
1096	{
1097		if(!this._isValidObj(node))
1098		{
1099      		return;
1100		}
1101   
1102		var len = node.childNodes.length;
1103   
1104		while (node.hasChildNodes())
1105		{
1106			node.removeChild(node.firstChild);
1107		}
1108	},
1109	
1110	_prebuildLiElem: function(idx) {
1111		if(idx < 1) return;
1112		
1113		
1114		var liElem = document.createElement("li");
1115		liElem.id = this._carouselElemID + "-item-" + idx;
1116		// this is default flag to know that we're not really loaded yet.
1117		liElem.placeholder = true;   
1118		this.carouselList.appendChild(liElem);
1119		
1120		this._lastPrebuiltIdx = (idx > this._lastPrebuiltIdx) ? idx : this._lastPrebuiltIdx;
1121	},
1122	
1123	_createItem: function(idx, innerHTMLOrElem) {
1124		if(idx < 1) return;
1125		
1126		
1127		var liElem = document.createElement("li");
1128		liElem.id = this._carouselElemID + "-item-" + idx;
1129
1130		// if String then assume innerHTML, else an elem object
1131		if(typeof(innerHTMLOrElem) === "string") {
1132			liElem.innerHTML = innerHTMLOrElem;
1133		} else {
1134			liElem.appendChild(innerHTMLOrElem);
1135		}
1136		
1137		return liElem;
1138	},
1139	
1140	// idx is the location to insert after
1141	_insertAfterItem: function(refIdx, innerHTMLOrElem) {
1142		return this._insertBeforeItem(refIdx+1, innerHTMLOrElem);
1143	},
1144	
1145	
1146	_insertBeforeItem: function(refIdx, innerHTMLOrElem) {
1147
1148		var refItem = this.getItem(refIdx);
1149		var size = this.cfg.getProperty("size");
1150		if(size != this.UNBOUNDED_SIZE) {
1151			this.cfg.setProperty("size", size + 1, true);
1152		}
1153				
1154		for(var i=this._lastPrebuiltIdx; i>=refIdx; i--) {
1155			var anItem = this.getItem(i);
1156			if(this._isValidObj(anItem)) {
1157				anItem.id = this._carouselElemID + "-item-" + (i+1);
1158			}
1159		}
1160
1161		var liElem = this._createItem(refIdx, innerHTMLOrElem);
1162		
1163		var insertedItem = this.carouselList.insertBefore(liElem, refItem);
1164		this._lastPrebuiltIdx += 1;
1165		
1166		return liElem;
1167	},
1168	
1169	// TEST THIS... think it has to do with prebuild
1170	insertAfterEnd: function(innerHTMLOrElem) {
1171		return this.insertAfter(this.cfg.getProperty("size"), innerHTMLOrElem);
1172	},
1173		
1174	_position: function(newStart, showAnimation) {
1175		// do we bypass the isAnimated check?
1176		var currStart = this._priorFirstVisible;
1177		if(newStart > currStart) {
1178			var inc = newStart - currStart;
1179			this._scrollNextInc(inc, showAnimation);
1180		} else {
1181			var dec = currStart - newStart;
1182			this._scrollPrevInc(dec, showAnimation);
1183		}
1184	},
1185
1186	_scrollPrev: function(e, carousel) {
1187		if(e !== null) { // event fired this so disable autoplay
1188			carousel.stopAutoPlay();
1189		}
1190		if(carousel._scrollPrevAnim.isAnimated()) {
1191			return false;
1192		}
1193		carousel._scrollPrevInc(carousel.cfg.getProperty("scrollInc"), 
1194							(carousel.cfg.getProperty("animationSpeed") !== 0));
1195	},
1196	
1197	// event handler
1198	_scrollNext: function(e, carousel) {		
1199		if(e !== null) { // event fired this so disable autoplay
1200			carousel.stopAutoPlay();
1201		}
1202		if(carousel._scrollNextAnim.isAnimated()) {
1203			return false; // might be better to set ourself waiting for animation completion and
1204			// then just do this function. that will allow faster scroll responses.
1205		}
1206
1207		carousel._scrollNextInc(carousel.cfg.getProperty("scrollInc"), 
1208								(carousel.cfg.getProperty("animationSpeed") !== 0));
1209	},
1210	
1211	
1212	_handleAnimationComplete: function(type, args, argList) {
1213		var carousel = argList[0];
1214		var direction = argList[1];
1215		
1216		carousel._animationCompleteEvt.fire(direction);
1217
1218		
1219	},
1220	
1221	// If EVERY item is already loaded in the range then return true
1222	// Also prebuild whatever is not already created.
1223	_areAllItemsLoaded: function(first, last) {
1224		var itemsLoaded = true;
1225		for(var i=first; i<=last; i++) {
1226			var liElem = this.getItem(i);
1227			
1228			// If the li elem does not exist, then prebuild it in the correct order
1229			// but still flag as not loaded (just prebuilt the li item.
1230			if(!this._isValidObj(liElem)) {
1231				this._prebuildLiElem(i);
1232				itemsLoaded = false;
1233			// but if the item exists and is a placeholder, then
1234			// note that this item is not loaded (only a placeholder)
1235			} else if(this._isValidObj(liElem.placeholder)) {
1236				itemsLoaded = false;
1237			}
1238		}
1239		return itemsLoaded;
1240	}, 
1241	
1242	_prebuildItems: function(first, last) {
1243		for(var i=first; i<=last; i++) {
1244			var liElem = this.getItem(i);
1245			
1246			// If the li elem does not exist, then prebuild it in the correct order
1247			// but still flag as not loaded (just prebuilt the li item.
1248			if(!this._isValidObj(liElem)) {
1249				this._prebuildLiElem(i);
1250			}
1251		}
1252	}, 
1253	
1254	_isExtraRevealed: function() {
1255		return (this.cfg.getProperty("revealAmount") > 0);
1256	},
1257
1258	// probably no longer need carousel passed in, this should be correct now.
1259	_scrollNextInc: function(inc, showAnimation) {
1260		var numVisible = this.cfg.getProperty("numVisible");
1261		var currStart = this._priorFirstVisible;
1262		var currEnd = this._priorLastVisible;
1263		var size = this.cfg.getProperty("size");
1264
1265		var scrollExtent = this._calculateAllowableScrollExtent();
1266		
1267		if(this.cfg.getProperty("wrap") && currEnd == scrollExtent.end) {
1268			this.scrollTo(scrollExtent.start); // might need to check animation is on or not
1269			return;
1270		}
1271
1272		// increment start by inc
1273		var newStart = currStart + inc;		
1274		var newEnd = newStart + numVisible - 1;
1275
1276		// If we are past the end, adjust or wrap
1277		if(newEnd > scrollExtent.end) {
1278			newEnd = scrollExtent.end;
1279			newStart = newEnd - numVisible + 1;
1280		}
1281
1282		inc = newStart - currStart;
1283
1284		// at this point the following variables are set
1285		// inc... amount to increment by
1286		// newStart... the firstVisible item after the scroll
1287		// newEnd... the last item visible after the scroll
1288
1289		this.cfg.setProperty("firstVisible", newStart, true);
1290
1291
1292		if(inc > 0) {
1293			if(this._isValidObj(this.cfg.getProperty("loadNextHandler"))) {
1294				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
1295				var cacheStart = (currEnd+1) < visibleExtent.start ? (currEnd+1) : visibleExtent.start;						
1296				var alreadyCached = this._areAllItemsLoaded(cacheStart, visibleExtent.end);
1297				this._loadNextHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
1298			}
1299
1300			if(showAnimation) {
1301	 			var nextParams = { points: { by: [-this.scrollAmountPerInc*inc, 0] } };
1302	 			if(this.isVertical()) {
1303	 				nextParams = { points: { by: [0, -this.scrollAmountPerInc*inc] } };
1304	 			}
1305
1306	 			this._scrollNextAnim = new YAHOO.util.Motion(this.carouselList, 
1307	 							nextParams, 
1308   								this.cfg.getProperty("animationSpeed"), 
1309								this.cfg.getProperty("animationMethod"));
1310
1311// is this getting added multiple times?
1312				if(this.cfg.getProperty("animationCompleteHandler")) {
1313					this._scrollNextAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "next"]);
1314				}
1315				this._scrollNextAnim.animate();
1316			} else {
1317				if(this.isVertical()) {
1318					var currY = YAHOO.util.Dom.getY(this.carouselList);
1319
1320					YAHOO.util.Dom.setY(this.carouselList, 
1321								currY - this.scrollAmountPerInc*inc);
1322				} else {
1323					var currX = YAHOO.util.Dom.getX(this.carouselList);
1324					YAHOO.util.Dom.setX(this.carouselList, 
1325								currX - this.scrollAmountPerInc*inc);
1326				}
1327			}
1328
1329		}
1330		this._priorFirstVisible = newStart;
1331		this._priorLastVisible = newEnd;	
1332
1333		this._enableDisableControls();
1334		return false;
1335	},
1336
1337	// firstVisible is already set
1338	_scrollPrevInc: function(dec, showAnimation) {
1339		var numVisible = this.cfg.getProperty("numVisible");
1340		var currStart = this._priorFirstVisible;
1341		var currEnd = this._priorLastVisible;
1342		var size = this.cfg.getProperty("size");
1343
1344		// decrement start by dec
1345		var newStart = currStart - dec;	
1346
1347		var scrollExtent = this._calculateAllowableScrollExtent();
1348	
1349		// How to decide whether to stop at 1 or not
1350		newStart = (newStart < scrollExtent.start) ? scrollExtent.start : newStart;
1351		
1352		// if we are going to extend past the end, then we need to correct the start
1353		var newEnd = newStart + numVisible - 1;
1354		if(newEnd > scrollExtent.end) {
1355			newEnd = scrollExtent.end;
1356			newStart = newEnd - numVisible + 1;
1357		}
1358				
1359		dec = currStart - newStart;
1360
1361		// at this point the following variables are set
1362		// dec... amount to decrement by
1363		// newStart... the firstVisible item after the scroll
1364		// newEnd... the last item visible after the scroll
1365		this.cfg.setProperty("firstVisible", newStart, true);
1366				
1367		// if we are decrementing
1368		if(dec > 0) {			
1369			if(this._isValidObj(this.cfg.getProperty("loadPrevHandler"))) {	
1370				var visibleExtent = this._calculateVisibleExtent(newStart, newEnd);
1371				var cacheEnd = (currStart-1) > visibleExtent.end ? (currStart-1) : visibleExtent.end;						
1372				var alreadyCached = this._areAllItemsLoaded(visibleExtent.start, cacheEnd);
1373				
1374				this._loadPrevHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached);
1375			}
1376
1377			if(showAnimation) {
1378	 			var prevParams = { points: { by: [this.scrollAmountPerInc*dec, 0] } };
1379	 			if(this.isVertical()) {
1380	 				prevParams = { points: { by: [0, this.scrollAmountPerInc*dec] } };
1381	 			}
1382 		
1383	 			this._scrollPrevAnim = new YAHOO.util.Motion(this.carouselList,
1384	 							prevParams, 
1385   								this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod"));
1386				if(this.cfg.getProperty("animationCompleteHandler")) {
1387					this._scrollPrevAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "prev"]);
1388				}
1389				this._scrollPrevAnim.animate();
1390			} else {
1391				if(this.isVertical()) {
1392					var currY = YAHOO.util.Dom.getY(this.carouselList);
1393					YAHOO.util.Dom.setY(this.carouselList, currY + 
1394							this.scrollAmountPerInc*dec);				
1395				} else {
1396					var currX = YAHOO.util.Dom.getX(this.carouselList);
1397					YAHOO.util.Dom.setX(this.carouselList, currX + 
1398							this.scrollAmountPerInc*dec);
1399				}
1400			}
1401		}
1402		this._priorFirstVisible = newStart;
1403		this._priorLastVisible = newEnd;	
1404		
1405		this._enableDisableControls();
1406
1407		return false;
1408	},
1409	
1410	// Check for all cases and enable/disable controls as needed by current state
1411	_enableDisableControls: function() {
1412	
1413		var firstVisible = this.cfg.getProperty("firstVisible");
1414		var lastVisible = this.getLastVisible();
1415		var scrollExtent = this._calculateAllowableScrollExtent();
1416				
1417		// previous arrow is turned on. Check to see if we need to turn it off
1418		if(this._prevEnabled) {
1419			if(firstVisible === scrollExtent.start) {
1420				this._disablePrev();
1421			}
1422		}
1423
1424		// previous arrow is turned off. Check to see if we need to turn it on
1425		if(this._prevEnabled === false) {
1426			if(firstVisible > scrollExtent.start) {
1427				this._enablePrev();
1428			}
1429		}
1430	
1431		// next arrow is turned on. Check to see if we need to turn it off
1432		if(this._nextEnabled) {
1433			if(lastVisible === scrollExtent.end) {
1434				this._disableNext();
1435			}
1436		}
1437
1438		// next arrow is turned off. Check to see if we need to turn it on
1439		if(this._nextEnabled === false) {
1440			if(lastVisible < scrollExtent.end) {
1441				this._enableNext();
1442			}
1443		}	
1444	},
1445	
1446	/**
1447	 * _loadInitial looks at firstItemVisible for the start (not necessarily 1)
1448	 */
1449	_loadInitial: function() {
1450		var firstVisible = this.cfg.getProperty("firstVisible");
1451		this._priorLastVisible = this.getLastVisible();
1452		// Load from 1 to the last visible
1453		// The _calculateSize method will adjust the scroll position
1454		// for starts > 1
1455		if(this._loadInitHandlerEvt) {
1456			var visibleExtent = this._calculateVisibleExtent(firstVisible, this._priorLastVisible);
1457			// still treat the first real item as starting at 1 
1458			var alreadyCached = this._areAllItemsLoaded(1, visibleExtent.end
1458);
1459			
1460			this._loadInitHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached); 
1461		}
1462		
1463		if(this.cfg.getProperty("autoPlay") !== 0) {
1464			this._autoPlayTimer = this.startAutoPlay();
1465		}	
1466		
1467		this._enableDisableControls();	
1468    },
1469	
1470	_calculateAllowableScrollExtent: function() {
1471		var scrollBeforeAmount = this.cfg.getProperty("scrollBeforeAmount");
1472		var scrollAfterAmount = this.cfg.getProperty("scrollAfterAmount");
1473		var size = this.cfg.getProperty("size");
1474		
1475		var extent = {start: 1-scrollBeforeAmount, end: size+scrollAfterAmount};
1476		return extent;
1477		
1478	},
1479	
1480	_calculateVisibleExtent: function(start, end) {
1481		if(!start) {
1482			start = this.cfg.getProperty("firstVisible");
1483			end = this.getLastVisible();
1484		}
1485		
1486		var size = this.cfg.getProperty("size");
1487		
1488		// we ignore the firstItem property... this method is used
1489		// for prebuilding the cache and signaling the developer
1490		// what to render on a given scroll.
1491		start = start<1?1:start;
1492		end = end>size?size:end;
1493		
1494		var extent = {start: start, end: end};
1495		
1496		// set up the indices for revealed items. If there is no item revealed, then set
1497		// the index to -1
1498		this._firstItemRevealed = -1;
1499		this._lastItemRevealed = -1;
1500		if(this._isExtraRevealed()) {
1501			if(start > 1) {
1502				this._firstItemRevealed = start - 1;
1503				extent.start = this._firstItemRevealed;
1504			}
1505			if(end < size) {
1506				this._lastItemRevealed = end + 1;
1507				extent.end = this._lastItemRevealed;
1508			}
1509		}
1510
1511		return extent;
1512	},
1513	
1514	_disablePrev: function() {
1515		this._prevEnabled = false;
1516		if(this._prevButtonStateHandlerEvt) {
1517			this._prevButtonStateHandlerEvt.fire(false, this._carouselPrev);
1518		}
1519		if(this._isValidObj(this._carouselPrev)) {
1520			YAHOO.util.Event.removeListener(this._carouselPrev, "click", this._scrollPrev);
1521		}
1522	},
1523	
1524	_enablePrev: function() {
1525		this._prevEnabled = true;
1526		if(this._prevButtonStateHandlerEvt) {
1527			this._prevButtonStateHandlerEvt.fire(true, this._carouselPrev);
1528		}
1529		if(this._isValidObj(this._carouselPrev)) {
1530			YAHOO.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this);
1531		}
1532	},
1533		
1534	_disableNext: function() {
1535		if(this.cfg.getProperty("wrap")) {
1536			return;
1537		}
1538		this._nextEnabled = false;
1539		if(this._isValidObj(this._nextButtonStateHandlerEvt)) {
1540			this._nextButtonStateHandlerEvt.fire(false, this._carouselNext);
1541		}
1542		if(this._isValidObj(this._carouselNext)) {
1543			YAHOO.util.Event.removeListener(this._carouselNext, "click", this._scrollNext);
1544		}
1545	},
1546	
1547	_enableNext: function() {
1548		this._nextEnabled = true;
1549		if(this._isValidObj(this._nextButtonStateHandlerEvt)) {
1550			this._nextButtonStateHandlerEvt.fire(true, this._carouselNext);
1551		}
1552		if(this._isValidObj(this._carouselNext)) {
1553			YAHOO.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this);
1554		}
1555	},
1556		
1557	_isValidObj: function(obj) {
1558
1559		if (null == obj) {
1560			return false;
1561		}
1562		if ("undefined" == typeof(obj) ) {
1563			return false;
1564		}
1565		return true;
1566	}
1567};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.