New file |
0,0 → 1,590 |
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) |
// @license Licensed under the Academic Free License 3.0 http://www.opensource.org/licenses/afl-3.0.php |
// |
// TODO: more cleanups |
// |
dojo.require("dojo.fx"); |
dojo.require("dijit._Widget"); |
dojo.require("dijit._Templated"); |
|
dojo.declare("dojox.image.SlideShow", |
[dijit._Widget, dijit._Templated], |
{ |
// 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 tags 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, |
|
// 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("dojox.image", "resources/images/1pixel.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("postCreate",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){} |
}); |
dojo.connect(this.outerNode, "onmouseout", function(evt){ |
try{_this._hideNav(evt);} |
catch(e){} |
}); |
} |
|
this.outerNode.style.width = this.imageWidth + "px"; |
|
img.setAttribute("src", this._tempImgPath); |
var _this = this; |
|
this.largeNode.appendChild(img); |
this._tmpImage = img; |
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) ? request.start : 0), |
count: ((request.count) ? request.count : this.pageSize), |
onBegin: function(count, request){ |
_this.maxPhotos = count; |
} |
}; |
if(request.query){ dojo.mixin(this._request.query, request.query); } |
if(paramNames && paramNames.imageLargeAttr){ |
this.imageLargeAttr = paramNames.imageLargeAttr; |
} |
|
var _this = this; |
var _complete = function(items){ |
_this.showImage(0); |
_this._request.onComplete = null; |
}; |
|
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); |
} |
var img; |
for(var pos = 0; pos < this.images.length; pos++){ |
img = this.images[pos]; |
if(img && img.parentNode){ img.parentNode.removeChild(img); } |
} |
this.images = []; |
this.isInitialized = false; |
this._imageCounter = 0; |
}, |
|
isImageLoaded: function(idx){ |
// summary: Returns true if image at the specified index is loaded, false otherwise. |
// idx: |
// The number index in the data store to check if it is loaded. |
return this.images && this.images.length > index && this.images[idx]; |
}, |
|
moveImageLoadingPointer: function(idx){ |
// summary: If 'autoload' is true, this tells the widget to start loading |
// images from the specified pointer. |
// idx: |
// The number index in the data store to start loading images from. |
this._imageCounter = idx; |
}, |
|
destroy: function(){ |
// summary: Cleans up the widget when it is being destroyed |
if(this._slideId) { this._stop(); } |
this.inherited("destroy",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.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(idx, /* Function? */callback){ |
// summary: Shows the image at index 'idx'. |
// idx: 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 = idx; |
|
var showOrLoadIt = function() { |
//If the image is already loaded, then show it. |
if(_this.images[idx]){ |
while(_this.largeNode.firstChild){ |
_this.largeNode.removeChild(_this.largeNode.firstChild); |
} |
_this.images[idx].style.opacity = 0; |
_this.largeNode.appendChild(_this.images[idx]); |
_this._currentImage = _this.images[idx]._img; |
_this._fitSize(); |
|
var onEnd = function(a,b,c) { |
var img = _this.images[idx].firstChild; |
if(img.tagName.toLowerCase() != "img"){img = img.firstChild;} |
title = img.getAttribute("title"); |
|
if(_this._navShowing){ |
_this._showNav(true); |
} |
dojo.publish(_this.getShowTopicName(), [{ |
index: idx, |
title: title, |
url: img.getAttribute("src") |
}]); |
if(callback) { callback(a,b,c); } |
_this._setTitle(title); |
}; |
|
dojo.fadeIn({ |
node: _this.images[idx], |
duration: 300, |
onEnd: onEnd |
}).play(); |
}else{ |
//If the image is not loaded yet, load it first, then show it. |
_this._loadImage(idx, function(){ |
dojo.publish(_this.getLoadTopicName(), [idx]); |
_this.showImage(idx, 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(idx, callbackFn){ |
// summary: Load image at specified index |
// description: |
// This function loads the image at position 'idx' into the |
// internal cache of images. This does not cause the image to be displayed. |
// idx: |
// 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[idx] || !this._request) { return; } |
|
var pageStart = idx - (idx % this.pageSize); |
|
this._request.start = pageStart; |
|
this._request.onComplete = function(items){ |
var diff = idx - 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" + idx); |
dojo.connect(img, "onload", function(){ |
_this._fitImage(img); |
div.setAttribute("width",_this.imageWidth); |
div.setAttribute("height",_this.imageHeight); |
|
dojo.publish(_this.getLoadTopicName(), [idx]); |
_this._loadNextImage(); |
if(callbackFn){ callbackFn(); } |
}); |
_this.hiddenNode.appendChild(div); |
|
var titleDiv = document.createElement("div"); |
dojo.addClass(titleDiv, "slideShowTitle"); |
div.appendChild(titleDiv); |
|
_this.images[idx] = 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 |
this._slideId = setTimeout("dijit.byId('"+this.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, "left", "-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, "left", ""); |
}, |
|
_setTitle: function(title){ |
// summary: Sets the title of the image to be displayed |
// title: String |
// The String title of the image |
this.titleNode.innerHTML = this.titleTemplate.replace('@title',title) |
.replace('@current', String(Number(this.imageIndex) + 1)) |
.replace('@total',String(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 |
} |
}); |
|
} |