diff options
Diffstat (limited to 'includes/js/dojox/image/SlideShow.js')
-rw-r--r-- | includes/js/dojox/image/SlideShow.js | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/includes/js/dojox/image/SlideShow.js b/includes/js/dojox/image/SlideShow.js new file mode 100644 index 0000000..4767cf1 --- /dev/null +++ b/includes/js/dojox/image/SlideShow.js @@ -0,0 +1,598 @@ +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:"<div dojoAttachPoint=\"outerNode\" class=\"slideShowWrapper\">\n\t<div style=\"position:relative;\" dojoAttachPoint=\"innerWrapper\">\n\t\t<div class=\"slideShowNav\" dojoAttachEvent=\"onclick: _handleClick\">\n\t\t\t<div class=\"dijitInline slideShowTitle\" dojoAttachPoint=\"titleNode\">${title}</div>\n\t\t</div>\n\t\t<div dojoAttachPoint=\"navNode\" class=\"slideShowCtrl\" dojoAttachEvent=\"onclick: _handleClick\">\n\t\t\t<span dojoAttachPoint=\"navPrev\" class=\"slideShowCtrlPrev\"></span>\n\t\t\t<span dojoAttachPoint=\"navPlay\" class=\"slideShowCtrlPlay\"></span>\n\t\t\t<span dojoAttachPoint=\"navNext\" class=\"slideShowCtrlNext\"></span>\n\t\t</div>\n\t\t<div dojoAttachPoint=\"largeNode\" class=\"slideShowImageWrapper\"></div>\t\t\n\t\t<div dojoAttachPoint=\"hiddenNode\" class=\"slideShowHidden\"></div>\n\t</div>\n</div>\n", + + // _tempImgPath: URL + // URL to the image to display when an image is not yet fully loaded. + _tempImgPath: dojo.moduleUrl("dojo", "resources/blank.gif"), + + // _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", function(evt){ + try{_this._showNav();} + catch(e){} //TODO: remove try/catch + }); + dojo.connect(this.outerNode, "onmouseout", function(evt){ + try{_this._hideNav(evt);} + catch(e){} //TODO: remove try/catch + }); + } + + this.outerNode.style.width = this.imageWidth + "px"; + + img.setAttribute("src", this._tempImgPath); + var _this = this; + + this.largeNode.appendChild(img); + this._tmpImage = this._currentImage = img; + this._fitSize(true); + + this._loadImage(0, function(){ + _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){ + _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){ + _this.showImage(0); + _this._request.onComplete = null; + if(_this.autoStart){ + _this.toggleSlideShow(); + } + }; + + 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. + while(this.largeNode.firstChild){ + this.largeNode.removeChild(this.largeNode.firstChild); + } + this.largeNode.appendChild(this._tmpImage); + while(this.hiddenNode.firstChild){ + this.hiddenNode.removeChild(this.hiddenNode.firstChild); + } + 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; + } + } + var _this = this; + this.showImage(this.imageIndex + 1, function(){ + if(inTimer){ _this._startTimer(); } + }); + return true; + }, + + toggleSlideShow: function(){ + // summary: Switches the slideshow mode on and off. + if(this._slideId){ + this._stop(); + }else{ + dojo.toggleClass(this.domNode,"slideShowPaused"); + this._timerCancelled = false; + var success = this.showNextImage(true, true); + if(!success){ + this._stop(); + } + } + }, + + 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); + } + _this.images[index].style.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;} + 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(){ + dojo.publish(_this.getLoadTopicName(), [index]); + _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(){ + if(!this.fixedHeight){return 0;} + // summary: Returns the padding to place at the top of the image to center it vertically. + 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.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 loadIt = function(item){ + var url = _this.imageStore.getValue(item, _this.imageLargeAttr); + var img = document.createElement("img"); + var div = document.createElement("div"); + div._img = img; + + var link = _this.imageStore.getValue(item,_this.linkAttr); + if(!link || _this.noLink){ div.appendChild(img); + }else{ + var a = document.createElement("a"); + a.setAttribute("href", link); + a.setAttribute("target","_blank"); + div.appendChild(a); + a.appendChild(img); + } + + div.setAttribute("id",_this.id + "_imageDiv" + index); + dojo.connect(img, "onload", function(){ + _this._fitImage(img); + div.setAttribute("width",_this.imageWidth); + div.setAttribute("height",_this.imageHeight); + + dojo.publish(_this.getLoadTopicName(), [index]); + _this._loadNextImage(); + if(callbackFn){ callbackFn(); } + }); + _this.hiddenNode.appendChild(div); + + var titleDiv = document.createElement("div"); + dojo.addClass(titleDiv, "slideShowTitle"); + div.appendChild(titleDiv); + + _this.images[index] = div; + img.setAttribute("src", url); + + var title = _this.imageStore.getValue(item,_this.titleAttr); + if(title){ img.setAttribute("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", ""); + dojo.style(this.navNode, "top", ""); + }, + + _setTitle: function(title){ + // summary: Sets the title of the image to be 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.setAttribute("height", height + "px"); + img.setAttribute("width", this.imageWidth + "px"); + } + if(height > this.imageHeight){ + width = Math.floor(width * (this.imageHeight / height)); + img.setAttribute("height", this.imageHeight + "px"); + img.setAttribute("width", width + "px"); + } + }, + + _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"); + dojo.style(this.navPlay, "marginLeft", "0px"); + 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 + } +}); + +} |