YE = YAHOO.util.Event; YD = YAHOO.util.Dom; YL = YAHOO.util.Lang; YUA = YAHOO.env.ua; CWS = { debug: false, log: function() { if(CWS.debug && typeof console === 'object' && typeof console.log !== 'undefined') { if(YUA.gecko > 0) console.log.apply(this, arguments); else if (YUA.ie > 0 || YUA.webkit) console.log(CWS.sprintf.apply(this, arguments)); } }, hasAllProperties: function(o, properties) { var hasAll = true; for(var i = 0; i < properties.length; i++) { if(!(properties[i] in o)) { CWS.log('Missing property: %s', properties[i]); hasAll = false; } } return hasAll; }, sprintf: function() //from http://code.google.com/p/sprintf/ { var i = 0, a, f = arguments[i++], o = [], m, p, c, x; while (f) { if (m = /^[^\x25]+/.exec(f)) o.push(m[0]); else if (m = /^\x25{2}/.exec(f)) o.push('%'); else if (m = /^\x25(?:(\d+)\$)?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(f)) { if (((a = arguments[m[1] || i++]) == null) || (a == undefined)) throw("Too few arguments."); if (/[^s]/.test(m[7]) && (typeof(a) != 'number')) throw("Expecting number but found " + typeof(a)); switch (m[7]) { case 'b': a = a.toString(2); break; case 'c': a = String.fromCharCode(a); break; case 'd': a = parseInt(a); break; case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; case 'o': a = a.toString(8); break; case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; case 'u': a = Math.abs(a); break; case 'x': a = a.toString(16); break; case 'X': a = a.toString(16).toUpperCase(); break; } a = (/[def]/.test(m[7]) && m[2] && a > 0 ? '+' + a : a); c = m[3] ? m[3] == '0' ? '0' : m[3].charAt(1) : ' '; x = m[5] - String(a).length; p = m[5] ? CWS.str_repeat(c, x) : ''; o.push(m[4] ? a + p : p + a); } else throw ("Huh ?!"); f = f.substring(m[0].length); } return o.join(''); }, str_repeat: function(i, m) { for (var o = []; m > 0; o[--m] = i); return(o.join('')); } }; /** * CWS.Carousel fades images in and out and can display arbitrary html over * each image at arbitrary positions. * * You should only need to call the init function. init() will add * a listener for the canvas id being added to the dom so you can call * init() whether the canvas div is present in the dom or not. The same is * true of the nav unordered list. * * Example: * * * * * * * * * * * * * * * * * * * */ CWS.Carousel = function() { // private properties of CWS.Carousel var that = null, imageInstances = [], readyImages = [], items = [], imagePath = null, width = null, height = null, canvas = null, currentIndex = null, fadeinImage = null, fadeinOverlay = null, navTemplate = null, nav = null, stop = true, a = null, b = null, current = null, notCurrent = null, showNextDelayTimeout = null; /** * Called once the animation displaying a new image is finished. */ var delayShowNext = function() { CWS.log('Showing: index=%d, duration=%dms', currentIndex, items[currentIndex].displayDuration); YD.setStyle(notCurrent.image, 'opacity', 0); YD.setStyle(notCurrent.overlay, 'opacity', 0); showNextDelayTimeout = setTimeout(showNext, items[currentIndex].displayDuration); }; /** * Called once displayDuration has passed. */ var showNext = function() { if(stop) { CWS.log('Aborting show next'); return; } var nextI = nextIndex(); if(readyImages[nextI] !== true) { CWS.log('Delaying showNext until image %d loaded.', nextI); YE.on(imageInstances[nextI], 'load', function() { readyImages[nextI] = true showNext(); }); return; } var firstShowNext = (currentIndex == -1); increment(); swapCurrent(); current.image.innerHTML = getImageTag(currentIndex); current.overlay.innerHTML = items[currentIndex].overlay; if (YUA.ie > 0) { // internet explorer, text gets ugly if there's no background YD.setStyle(current.overlay, 'background-image', 'url('+getImageSrc(currentIndex)+')'); } positionOverlay(); preloadImage(nextIndex()); if(firstShowNext) { delayShowNext(); return; } var animationType = 'YAHOO.util.Easing.'+items[currentIndex].fadeType; var animDuration = items[currentIndex].transitionDuration; CWS.log('Fading in: index=%d, src=%s, duration=%dms, type=%s', currentIndex, getImageSrc(currentIndex), animDuration, animationType); animDuration = animDuration/1000; animationType = window[animationType]; fadeinImage = new YAHOO.util.Anim( current.image, {opacity: {from: 0, to: 1}}, animDuration, animationType ); fadeinImage.onComplete.subscribe(delayShowNext); fadeinOverlay = new YAHOO.util.Anim( current.overlay, {opacity: {from: 0, to: 1}}, animDuration, animationType ); fadeinImage.animate(); fadeinOverlay.animate(); setTimeout(setActiveNav, items[currentIndex].transitionDuration/2); }; /** * Called when a nav item is clicked. */ var showIndex = function(i) { CWS.log('Show index (nav clicked): index=%d', i); currentIndex = i; preloadImage(i); swapCurrent(); current.image.innerHTML = getImageTag(i); current.overlay.innerHTML = items[i].overlay; positionOverlay(); YD.setStyle(current.image, 'opacity', 1); YD.setStyle(notCurrent.image, 'opacity', 0); YD.setStyle(current.overlay, 'opacity', 1); YD.setStyle(notCurrent.overlay, 'opacity', 0); setActiveNav(); preloadImage(nextIndex()); }; var setActiveNav = function() { //currentIndex will be -1 if initNav happens before initCanvas var i = (currentIndex == -1) ? 0 : currentIndex; that.onChange.fire(items[i]); if(typeof items[0].nav === 'undefined') return; for(var j = 0; j < items.length; j++) { YD.removeClass(items[j].nav, 'carousel-nav-active'); } YD.addClass(items[i].nav, 'carousel-nav-active'); } var swapCurrent = function() { YE.removeListener(current.image, 'click'); YD.setStyle(current.image, 'cursor', 'default'); var t = current; current = notCurrent; notCurrent = t; if(YL.isString(items[currentIndex].url) && items[currentIndex].url.length > 0) { YE.on(current.image, 'click', function(){document.location = items[currentIndex].url}); YD.setStyle(current.image, 'cursor', 'pointer'); } YD.setStyle(current.image, 'z-index', 22); YD.setStyle(current.overlay, 'z-index', 24); YD.setStyle(notCurrent.image, 'z-index', 12); YD.setStyle(notCurrent.overlay, 'z-index', 14); }; var positionOverlay = function() { YD.setStyle(current.overlay, 'left', items[currentIndex].overlayPosition[0]); YD.setStyle(current.overlay, 'top', items[currentIndex].overlayPosition[1]); if (YUA.ie > 0) { // internet explorer YD.setStyle(current.overlay, 'background-position', '-'+items[currentIndex].overlayPosition[0] + ' -' + items[currentIndex].overlayPosition[1]); } }; var preloadImage = function(i) { if(YD.get(getImageId(i))) { CWS.log('Image id %s present already?', getImageId(i)); } if(YL.isObject(imageInstances[i])) { CWS.log('Image already preloaded: index=%d', i); return; } CWS.log('Preloading image: index=%d', i); imageInstances[i] = new Image(); readyImages[i] = false; YE.on(imageInstances[i], 'load', function() { CWS.log('Image %d ready', i); readyImages[i] = true }); imageInstances[i].src = getImageSrc(i); }; var increment = function() { currentIndex = nextIndex(); }; var nextIndex = function() { return (currentIndex+1) % items.length; } var getImageId = function(i) { return 'carousel-image-'+i; }; var getImageTag = function(i) { return ''; }; var getImageSrc = function(i) { var img = items[i].image; return imagePath+'/' + img; }; var initCanvas = function() { canvas = YD.get(canvas); canvas.innerHTML = ''+ ''+ ''+ ''+ ''; notCurrent = a = { image: YD.get('carousel-image-a'), overlay: YD.get('carousel-overlay-a') }; current = b = { image: YD.get('carousel-image-b'), overlay: YD.get('carousel-overlay-b') }; var containers = [a.image, b.image, canvas]; YD.setStyle(containers, 'width', width); YD.setStyle(containers, 'height', height); preloadImage(0); that.play(); }; var initNav = function() { nav = YD.get(nav); var navHTML = ''; for(var i = 0; i < items.length; i++) { var id = 'carousel-nav-item-'+i; items[i].nav = id; navHTML += '
  • '+navTemplate.replace('{content}', items[i].navContent).replace('{id}', id)+'
  • '; } nav.innerHTML = navHTML; for(i = 0; i < items.length; i++) { items[i].nav = YD.get(items[i].nav); YE.on(items[i].nav, 'click', setIndex); if(i === items.length-1) YD.addClass(items[i].nav, 'last'); } setActiveNav(0); }; var setIndex = function(e) { YE.preventDefault(e); for(var i = 0; i < items.length; i++) { if(items[i].nav==this) { that.stopAndShow(i); } } return false; }; return { // public properties of CWS.Carousel stopAndShow: function(i) { stop = true; //prevents further changes to canvas if(YL.isObject(fadeinOverlay) && fadeinOverlay.isAnimated()) { CWS.log('Stopping animation'); fadeinOverlay.stop(); fadeinImage.stop(); } that.onStop.fire(); showIndex(i); }, play: function() { if(stop) { stop = false; that.onPlay.fire(); clearTimeout(showNextDelayTimeout); showNext(); } }, onChange: null, onPlay: null, onStop: null, init: function(config) { that = this; this.onChange = new YAHOO.util.CustomEvent('onChange'); this.onPlay = new YAHOO.util.CustomEvent('onPlay'); this.onStop = new YAHOO.util.CustomEvent('onStop'); var configProperties = [ 'width', 'height', 'items', 'imagePath', 'canvas', 'navTemplate', 'nav' ]; if(!CWS.hasAllProperties(config, configProperties)) { CWS.log('Invalid configuration, stopping.'); return; } width = config.width; height = config.height; items = config.items; imagePath = config.imagePath; canvas = config.canvas; navTemplate = config.navTemplate; nav = config.nav; if(items.length < 1) { CWS.log('Empty carousel, will not attempt to load carousel.'); return; } var itemProperties = [ 'image', 'overlay', 'overlayPosition', 'navContent', 'transitionDuration', 'displayDuration', 'fadeType', 'url', 'name' ]; for(var i = 0; i < items.length; i++) { if(!CWS.hasAllProperties(items[i], itemProperties)) { CWS.log('Invalid item at index %d, stopping.', i); return; } } currentIndex = -1; YE.onContentReady(canvas, initCanvas); YE.onContentReady(nav, initNav); } }; }();