text: /*
  	Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
  	Available via Academic Free License >= 2.1 OR the modified BSD license.
  	see: http://dojotoolkit.org/license for details
  */
  
  
  if(!dojo._hasResource["dojox.image.SlideShow"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  dojo._hasResource["dojox.image.SlideShow"] = true;
  dojo.provide("dojox.image.SlideShow");
  //
  // dojox.image.SlideShow courtesy Shane O Sullivan, licensed under a Dojo CLA 
  // For a sample usage, see http://www.skynet.ie/~sos/photos.php
  //
  // @author  Copyright 2007 Shane O Sullivan (shaneosullivan1@gmail.com)
  //
  //	TODO: more cleanups
  //
  dojo.require("dojo.string");
  dojo.require("dojo.fx");
  dojo.require("dijit._Widget");
  dojo.require("dijit._Templated");
  
  dojo.declare("dojox.image.SlideShow",
  	[dijit._Widget, dijit._Templated],
  	{
  	// summary: 
  	//		A Slideshow Widget
  
  	// imageHeight: Number
  	//		The maximum height of an image
  	imageHeight: 375,
  	
  	// imageWidth: Number
  	//		The maximum width of an image.
  	imageWidth: 500,
  
  	// title: String
  	//		The initial title of the SlideShow 
  	title: "",
  
  	// titleTemplate: String
  	//	a way to customize the wording in the title. supported parameters to be populated are:
  	//		${title} = the passed title of the image
  	//		${current} = the current index of the image
  	//		${total} = the total number of images in the SlideShow
  	//
  	//	should add more?
  	titleTemplate: '${title} <span class="slideShowCounterText">(${current} of ${total})</span>',
  
  	// noLink: Boolean
  	//	Prevents the slideshow from putting an anchor link around the displayed image
  	//	enables if true, though still will not link in absence of a url to link to
  	noLink: false,
  
  	// loop: Boolean
  	//	true/false - make the slideshow loop
  	loop: true,
  
  	// hasNav: Boolean
  	//	toggle to enable/disable the visual navigation controls
  	hasNav: true,
  
  	// images: Array
  	// Contains the DOM nodes that individual images are stored in when loaded or loading.
  	images: [],
  	
  	// pageSize: Number
  	//	The number of images to request each time.
  	pageSize: 20,
  		
  	// autoLoad: Boolean
  	//	If true, then images are preloaded, before the user navigates to view them.
  	//	If false, an image is not loaded until the user views it.
  	autoLoad: true,
  
  	// autoStart: Boolean
  	//	If true, the SlideShow begins playing immediately
  	autoStart: false,
  	
  	// fixedHeight: Boolean
  	// If true, the widget does not resize itself to fix the displayed image.
  	fixedHeight: false,
  
  	// imageStore: Object
  	//	Implementation of the dojo.data.api.Read API, which provides data on the images
  	//	to be displayed.
  	imageStore: null,
  		
  	// linkAttr: String
  	//	Defines the name of the attribute to request from the store to retrieve the
  	//	URL to link to from an image, if any.
  	linkAttr: "link",
  	
  	// imageLargeAttr: String
  	//	Defines the name of the attribute to request from the store to retrieve the
  	//	URL to the image.
  	imageLargeAttr: "imageUrl",
  	
  	// titleAttr: String
  	//	Defines the name of the attribute to request from the store to retrieve the
  	//	title of the picture, if any.
  	titleAttr: "title",
  
  	// slideshowInterval: Number
  	// Time, in seconds, between image transitions during a slideshow.
  	slideshowInterval: 3,
  	
  	templateString: dojo.cache("dojox.image", "resources/SlideShow.html", "<div dojoAttachPoint=\"outerNode\" class=\"slideShowWrapper\">\r\n\t<div style=\"position:relative;\" dojoAttachPoint=\"innerWrapper\">\r\n\t\t<div class=\"slideShowNav\" dojoAttachEvent=\"onclick: _handleClick\">\r\n\t\t\t<div class=\"dijitInline slideShowTitle\" dojoAttachPoint=\"titleNode\">${title}</div>\r\n\t\t</div>\r\n\t\t<div dojoAttachPoint=\"navNode\" class=\"slideShowCtrl\" dojoAttachEvent=\"onclick: _handleClick\">\r\n\t\t\t<span dojoAttachPoint=\"navPrev\" class=\"slideShowCtrlPrev\"></span>\r\n\t\t\t<span dojoAttachPoint=\"navPlay\" class=\"slideShowCtrlPlay\"></span>\r\n\t\t\t<span dojoAttachPoint=\"navNext\" class=\"slideShowCtrlNext\"></span>\r\n\t\t</div>\r\n\t\t<div dojoAttachPoint=\"largeNode\" class=\"slideShowImageWrapper\"></div>\t\t\r\n\t\t<div dojoAttachPoint=\"hiddenNode\" class=\"slideShowHidden\"></div>\r\n\t</div>\r\n</div>\r\n"),
  	
  	// _imageCounter: Number
  	//	A counter to keep track of which index image is to be loaded next
  	_imageCounter: 0,
  	
  	// _tmpImage: DomNode
  	//	The temporary image to show when a picture is loading.
  	_tmpImage: null,
  	
  	// _request: Object
  	//	Implementation of the dojo.data.api.Request API, which defines the query 
  	//	parameters for accessing the store.
  	_request: null,
  
  	postCreate: function(){
  		// summary: Initilizes the widget, sets up listeners and shows the first image
  		this.inherited(arguments);
  		var img = document.createElement("img");
  
  		// FIXME: should API be to normalize an image to fit in the specified height/width?
  		img.setAttribute("width", this.imageWidth);
  		img.setAttribute("height", this.imageHeight);
  
  		if(this.hasNav){
  			dojo.connect(this.outerNode, "onmouseover", this, function(evt){
  				try{ this._showNav();}
  				catch(e){} //TODO: remove try/catch
  			});		
  			dojo.connect(this.outerNode, "onmouseout", this, function(evt){
  				try{ this._hideNav(evt);}
  				catch(e){} //TODO: remove try/catch
  			});
  		}
  		
  		this.outerNode.style.width = this.imageWidth + "px";
  
  		img.setAttribute("src", this._blankGif);
  		var _this = this;
  		
  		this.largeNode.appendChild(img);
  		this._tmpImage = this._currentImage = img;
  		this._fitSize(true);
  		
  		this._loadImage(0, dojo.hitch(this, "showImage", 0));
  		this._calcNavDimensions();
  	},
  
  	setDataStore: function(dataStore, request, /*optional*/paramNames){
  		// summary: 
  		//		Sets the data store and request objects to read data from.
  		// dataStore:
  		//		An implementation of the dojo.data.api.Read API. This accesses the image
  		//		data.
  		// request:
  		//		An implementation of the dojo.data.api.Request API. This specifies the
  		//		query and paging information to be used by the data store
  		// paramNames:
  		//		An object defining the names of the item attributes to fetch from the
  		//		data store.  The three attributes allowed are 'linkAttr', 'imageLargeAttr' and 'titleAttr'
  		this.reset();
  		var _this = this;
  
  		this._request = {
  			query: {},
  			start: request.start || 0,
  			count: request.count || this.pageSize,
  			onBegin: function(count, request){
  				// FIXME: fires too often?!?
  				_this.maxPhotos = count;
  			}
  		};
  		if(request.query){ 
  			dojo.mixin(this._request.query, request.query); 
  		}
  		if(paramNames){
  			dojo.forEach(["imageLargeAttr", "linkAttr", "titleAttr"], function(attrName){
  				if(paramNames[attrName]){ 
  					this[attrName] = paramNames[attrName]; 
  				}
  			}, this);
  		}
  	
  		var _complete = function(items){
  			// FIXME: onBegin above used to work for maxPhotos:
  			_this.maxPhotos = items.length;
  			_this._request.onComplete = null;
  			if(_this.autoStart){
  				_this.imageIndex = -1;
  				_this.toggleSlideShow(); 
  			} else {
  				_this.showImage(0);
  			}
  			
  		};
  
  		this.imageStore = dataStore;
  		this._request.onComplete = _complete;
  		this._request.start = 0;
  		this.imageStore.fetch(this._request);
  	},
  
  	reset: function(){
  		// summary: 
  		//		Resets the widget to its initial state
  		// description: 
  		//		Removes all previously loaded images, and clears all caches.
  		dojo.query("> *", this.largeNode).orphan();
  		this.largeNode.appendChild(this._tmpImage);
  		
  		dojo.query("> *", this.hiddenNode).orphan();
  		dojo.forEach(this.images, function(img){
  			if(img && img.parentNode){ img.parentNode.removeChild(img); }
  		});
  		this.images = [];
  		this.isInitialized = false;
  		this._imageCounter = 0;
  	},
  
  	isImageLoaded: function(index){
  		// summary: 
  		//		Returns true if image at the specified index is loaded, false otherwise.
  		// index:
  		//		The number index in the data store to check if it is loaded.
  		return this.images && this.images.length > index && this.images[index];
  	},
  
  	moveImageLoadingPointer: function(index){
  		// summary: 
  		//		If 'autoload' is true, this tells the widget to start loading
  		//		images from the specified pointer.
  		// index:
  		//		The number index in the data store to start loading images from.
  		this._imageCounter = index;
  	},
  	
  	destroy: function(){
  		// summary:
  		//		Cleans up the widget when it is being destroyed
  		if(this._slideId) { this._stop(); }
  		this.inherited(arguments);
  	},
  
  	showNextImage: function(inTimer, forceLoop){
  		// summary:
  		//		Changes the image being displayed to the next image in the data store
  		// inTimer: Boolean
  		//		If true, a slideshow is active, otherwise the slideshow is inactive.
  		if(inTimer && this._timerCancelled){ return false; }
  		
  		if(this.imageIndex + 1 >= this.maxPhotos){
  			if(inTimer && (this.loop || forceLoop)){ 
  				this.imageIndex = -1; 
  			}else{
  				if(this._slideId){ this._stop(); }
  				return false;
  			}
  		}
  
  		this.showImage(this.imageIndex + 1, dojo.hitch(this,function(){
  			if(inTimer){ this._startTimer(); }
  		}));
  		return true;
  	},
  
  	toggleSlideShow: function(){
  		// summary: 
  		//		Switches the slideshow mode on and off.
  		
  		// If the slideshow is already running, stop it.
  		if(this._slideId){
  			this._stop();
  		}else{
  			dojo.toggleClass(this.domNode,"slideShowPaused");
  			this._timerCancelled = false;
  			var idx = this.imageIndex;
  
  			if(idx < 0 || (this.images[idx] && this.images[idx]._img.complete)){
  				var success = this.showNextImage(true, true);
  
  				if(!success){
  					this._stop();
  				}
  			}else{
  				var handle = dojo.subscribe(this.getShowTopicName(), dojo.hitch(this, function(info){
  					setTimeout(dojo.hitch(this, function(){
  						if(info.index == idx){
  							var success = this.showNextImage(true, true);
  							if(!success){
  								this._stop();
  							}
  							dojo.unsubscribe(handle);
  						}}),
  						this.slideshowInterval * 1000);
  				}));
  				dojo.publish(this.getShowTopicName(),
  				  [{index: idx,	title: "", url: ""}]);
  			}
  		}
  	},
  
  	getShowTopicName: function(){
  		// summary: 
  		//		Returns the topic id published to when an image is shown
  		// description:
  		//		The information published is: index, title and url
  		return (this.widgetId || this.id) + "/imageShow";
  	},
  
  	getLoadTopicName: function(){
  		// summary: 
  		//		Returns the topic id published to when an image finishes loading.
  		// description:
  		//		The information published is the index position of the image loaded.
  		return (this.widgetId ? this.widgetId : this.id) + "/imageLoad";
  	},
  
  	showImage: function(index, /* Function? */callback){
  		// summary: 
  		//		Shows the image at index 'index'.
  		// index: Number
  		//		The position of the image in the data store to display
  		// callback: Function
  		//		Optional callback function to call when the image has finished displaying.
  
  		if(!callback && this._slideId){ 
  			this.toggleSlideShow(); 
  		}
  		var _this = this;
  		var current = this.largeNode.getElementsByTagName("div");
  		this.imageIndex = index;
  
  		var showOrLoadIt = function() {
  			//If the image is already loaded, then show it. 
  			if(_this.images[index]){
  				while(_this.largeNode.firstChild){
  					_this.largeNode.removeChild(_this.largeNode.firstChild);
  				}
  				dojo.style(_this.images[index],"opacity", 0);
  				_this.largeNode.appendChild(_this.images[index]);
  				_this._currentImage = _this.images[index]._img;
  				_this._fitSize();
  								
  				var onEnd = function(a,b,c){
  
  					var img = _this.images[index].firstChild;
  					if(img.tagName.toLowerCase() != "img"){ img = img.firstChild; }
  					var title = img.getAttribute("title") || "";
  					if(_this._navShowing){
  						_this._showNav(true);
  					}
  					dojo.publish(_this.getShowTopicName(), [{
  						index: index,	
  						title: title,
  						url: img.getAttribute("src")
  					}]);
  
          				if(callback) { 
          					callback(a,b,c);
          				}
  					_this._setTitle(title);
  	        		};
  				
  				dojo.fadeIn({
  					node: _this.images[index],
  					duration: 300,
  					onEnd: onEnd
  				}).play();
  				
  			}else{
  				//If the image is not loaded yet, load it first, then show it.
  				_this._loadImage(index, function(){
  					_this.showImage(index, callback);	
  				});
  			}
  		};
  
  		//If an image is currently showing, fade it out, then show
  		//the new image. Otherwise, just show the new image. 	
  		if(current && current.length > 0){
  			dojo.fadeOut({
  				node: current[0],
  				duration: 300,
  				onEnd: function(){
  					_this.hiddenNode.appendChild(current[0]);
  					showOrLoadIt();
  				}
  			}).play();
  		}else{
  			showOrLoadIt();
  		}
  	},
  	
  	_fitSize: function(force){
  		// summary: 
  		//		Fits the widget size to the size of the image being shown,
  		//		or centers the image, depending on the value of 'fixedHeight'
  		// force: Boolean
  		//		If true, the widget is always resized, regardless of the value of 'fixedHeight'
  		if(!this.fixedHeight || force){
  			var height = (this._currentImage.height + (this.hasNav ? 20:0));
  			dojo.style(this.innerWrapper, "height", height + "px");
  			return;
  		}
  		dojo.style(this.largeNode, "paddingTop", this._getTopPadding() + "px");
  	},
  	
  	_getTopPadding: function(){
  		// summary: 
  		//		Returns the padding to place at the top of the image to center it vertically.
  		if(!this.fixedHeight){ return 0; }
  		return (this.imageHeight - this._currentImage.height) / 2;
  	},
  	
  	_loadNextImage: function(){
  		// summary: 
  		//		Load the next unloaded image.
  
  		if(!this.autoLoad){
  			return;
  		}
  		while(this.images.length >= this._imageCounter && this.images[this._imageCounter]){
  			this._imageCounter++;
  		}
  		this._loadImage(this._imageCounter);
  	},
  	
  	_loadImage: function(index, callbackFn){
  		// summary: 
  		//		Load image at specified index
  		// description:
  		//		This function loads the image at position 'index' into the
  		//		internal cache of images.  This does not cause the image to be displayed.
  		// index:
  		//		The position in the data store to load an image from.
  		// callbackFn:
  		//		An optional function to execute when the image has finished loading.
  
  		if(this.images[index] || !this._request) { 
  			return; 
  		}
  		
  		var pageStart = index - (index % (this._request.count || this.pageSize));
  
  		this._request.start = pageStart;
  
  		this._request.onComplete = function(items){
  			var diff = index - pageStart;
  			
  			if(items && items.length > diff){
  				loadIt(items[diff]);
  			}else{ /* Squelch - console.log("Got an empty set of items"); */ }
  		}
  
  		var _this = this;
  		var store = this.imageStore;
  		var loadIt = function(item){			
  			var url = _this.imageStore.getValue(item, _this.imageLargeAttr);
  			
  			var img = new Image();	// when creating img with "createElement" IE doesnt has width and height, so use the Image object
  			var div = dojo.create("div", {
  				id: _this.id + "_imageDiv" + index
  			});
  			div._img = img;
  
  			var link = _this.imageStore.getValue(item,_this.linkAttr);
  			if(!link || _this.noLink){ 
  				div.appendChild(img); 
  			}else{
  				var a = dojo.create("a", {
  					"href": link,
  					"target": "_blank"
  				}, div);
  				a.appendChild(img);
  			}
  
  			dojo.connect(img, "onload", function(){
  				if(store != _this.imageStore){
  					// If the store has changed, ignore this load event.
  					return;
  				}
  				_this._fitImage(img);
  				dojo.attr(div, {"width": _this.imageWidth, "height": _this.imageHeight});
  				
  				// make a short timeout to prevent IE6/7 stack overflow at line 0 ~ still occuring though for first image 
  				dojo.publish(_this.getLoadTopicName(), [index]);
  
  				setTimeout(function(){_this._loadNextImage();}, 1);	
  				if(callbackFn){ callbackFn(); }
  			});
  			_this.hiddenNode.appendChild(div);
  
  			var titleDiv = dojo.create("div", {
  				className: "slideShowTitle"
  			}, div);
  
  			_this.images[index] = div;
  			dojo.attr(img, "src", url);
  			
  			var title = _this.imageStore.getValue(item, _this.titleAttr);
  			if(title){ dojo.attr(img, "title", title); } 
  		}
  		this.imageStore.fetch(this._request);
  	},
  
  	_stop: function(){
  		// summary: 
  		//		Stops a running slide show.
  		if(this._slideId){ clearTimeout(this._slideId); }
  		this._slideId = null;
  		this._timerCancelled = true;
  		dojo.removeClass(this.domNode,"slideShowPaused");
  	},
  
  	_prev: function(){
  		// summary: 
  		//		Show the previous image.
  		// FIXME: either pull code from showNext/prev, or call it here
  		if(this.imageIndex < 1){ return; }
  		this.showImage(this.imageIndex - 1);
  	},
  
  	_next: function(){
  		// summary: 
  		//		Show the next image
  		this.showNextImage();
  	},
  
  	_startTimer: function(){
  		// summary: 
  		//		Starts a timeout to show the next image when a slide show is active
  		var id = this.id;
  		this._slideId = setTimeout(function(){
  			dijit.byId(id).showNextImage(true);
  		}, this.slideshowInterval * 1000);
  	},
  	
  	_calcNavDimensions: function() {
  		// summary:
  		//		Calculates the dimensions of the navigation controls
  		dojo.style(this.navNode, "position", "absolute");
  		
  		//Place the navigation controls far off screen
  		dojo.style(this.navNode, "top", "-10000px");
  		
  		//Make the navigation controls visible
  		dojo._setOpacity(this.navNode, 99);
  		
  		this.navPlay._size = dojo.marginBox(this.navPlay);
  		this.navPrev._size = dojo.marginBox(this.navPrev);
  		this.navNext._size = dojo.marginBox(this.navNext);
  		
  		dojo._setOpacity(this.navNode, 0);
  		
  		dojo.style(this.navNode, {"position": "", top: ""});
  	},
  
  	_setTitle: function(title){
  		// summary: 
  		//		Sets the title to the image being displayed
  		// title: String
  		//		The String title of the image
  
  		this.titleNode.innerHTML = dojo.string.substitute(this.titleTemplate,{ 
  			title: title, 
  			current: 1 + this.imageIndex, 
  			total: this.maxPhotos || ""
  		});
  	},
  	
  	_fitImage: function(img) {
  		// summary: 
  		//		Ensures that the image width and height do not exceed the maximum.
  		// img: Node
  		//		The image DOM node to optionally resize
  		var width = img.width;
  		var height = img.height;
  		
  		if(width > this.imageWidth){
  			height = Math.floor(height * (this.imageWidth / width));
  			img.height = height;
  			img.width = width = this.imageWidth;
  		}
  		if(height > this.imageHeight){
  			width = Math.floor(width * (this.imageHeight / height));
  			img.height = this.imageHeight;
  			img.width = width;
  		}
  	},
  	
  	_handleClick: function(/* Event */e){
  		// summary:
  		//		Performs navigation on the images based on users mouse clicks
  		// e:
  		//		An Event object
  		switch(e.target){
  			case this.navNext: this._next(); break;
  			case this.navPrev: this._prev(); break;
  			case this.navPlay: this.toggleSlideShow(); break;
  		}
  	},
  	
  	_showNav: function(force){
  		// summary:
  		//		Shows the navigation controls
  		// force: Boolean
  		//		If true, the navigation controls are repositioned even if they are
  		//		currently visible.
  		if(this._navShowing && !force){return;}
  		dojo.style(this.navNode, "marginTop", "0px");
  		
  		var navPlayPos = dojo.style(this.navNode, "width")/2 - this.navPlay._size.w/2 - this.navPrev._size.w;
  		
  		dojo.style(this.navPlay, "marginLeft", navPlayPos + "px");
  		var wrapperSize = dojo.marginBox(this.outerNode);
  		
  		var margin = this._currentImage.height - this.navPlay._size.h - 10 + this._getTopPadding();
  		
  		if(margin > this._currentImage.height){margin += 10;}
  		dojo[this.imageIndex < 1 ? "addClass":"removeClass"](this.navPrev, "slideShowCtrlHide");
  		dojo[this.imageIndex + 1 >= this.maxPhotos ? "addClass":"removeClass"](this.navNext, "slideShowCtrlHide");
  	
  		var _this = this;
  		if(this._navAnim) {
  			this._navAnim.stop();
  		}
  		if(this._navShowing){ return; }
  		this._navAnim = dojo.fadeIn({
  			node: this.navNode, 
  			duration: 300,
  			onEnd: function(){ _this._navAnim = null; }
  		});
  		this._navAnim.play();
  		this._navShowing = true;
  	},
  	
  	_hideNav: function(/* Event */e){
  		// summary:
  		//		Hides the navigation controls
  		// e: Event
  		//		The DOM Event that triggered this function
  		if(!e || !this._overElement(this.outerNode, e)){
  			var _this = this;
  			if(this._navAnim){
  				this._navAnim.stop();
  			}
  			this._navAnim = dojo.fadeOut({
  				node: this.navNode,
  				duration:300,
  				onEnd: function(){ _this._navAnim = null; }
  			});
  			this._navAnim.play();
  			this._navShowing = false;
  		}
  	},
  	
  	_overElement: function(/*DomNode*/element, /*Event*/e){
  		// summary:
  		//		Returns whether the mouse is over the passed element.
  		//		Element must be display:block (ie, not a <span>)
  		
  		//When the page is unloading, if this method runs it will throw an
  		//exception.
  		if(typeof(dojo) == "undefined"){ return false; }
  		element = dojo.byId(element);
  		var m = { x: e.pageX, y: e.pageY };
  		var bb = dojo._getBorderBox(element);
  		var absl = dojo.coords(element, true);
  		var left = absl.x;
  
  		return (m.x >= left
  			&& m.x <= (left + bb.w)
  			&& m.y >= absl.y
  			&& m.y <= (top + bb.h)
  		);	//	boolean
  	}
  });
  
  }
  