diff options
Diffstat (limited to 'js/dojo/dojox/image/SlideShow.js')
| -rw-r--r-- | js/dojo/dojox/image/SlideShow.js | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/js/dojo/dojox/image/SlideShow.js b/js/dojo/dojox/image/SlideShow.js new file mode 100644 index 0000000..e26a722 --- /dev/null +++ b/js/dojo/dojox/image/SlideShow.js @@ -0,0 +1,681 @@ +//>>built +// wrapped by build app +define("dojox/image/SlideShow", ["dijit","dojo","dojox","dojo/require!dojo/string,dojo/fx,dijit/_Widget,dijit/_Templated"], function(dijit,dojo,dojox){ +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 +// +// +// 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\">\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>"), + + // _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(); + dojo.style(this.navNode, "opacity", 0); + }, + + 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"); + + dojo.style(this.navPlay, 'marginLeft', 0); + + this.navPlay._size = dojo.marginBox(this.navPlay); + this.navPrev._size = dojo.marginBox(this.navPrev); + this.navNext._size = dojo.marginBox(this.navNext); + + 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;} + this._calcNavDimensions(); + dojo.style(this.navNode, "marginTop", "0px"); + + + var navPlayPos = dojo.style(this.navNode, "width")/2 - this.navPlay._size.w/2 - this.navPrev._size.w; + console.log('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.position(element, true); + + return (m.x >= bb.x + && m.x <= (bb.x + bb.w) + && m.y >= bb.y + && m.y <= (top + bb.h) + ); // boolean + } +}); + +}); |
