Blame | Last modification | View Log | RSS feed
if(!dojo._hasResource["dojox.image.ThumbnailPicker"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.dojo._hasResource["dojox.image.ThumbnailPicker"] = true;dojo.provide("dojox.image.ThumbnailPicker");dojo.experimental("dojox.image.ThumbnailPicker");//// dojox.image.ThumbnailPicker courtesy Shane O Sullivan, licensed under a Dojo CLA// @author Copyright 2007 Shane O Sullivan (shaneosullivan1@gmail.com)//// For a sample usage, see http://www.skynet.ie/~sos/photos.php//// document topics.dojo.require("dojo.fx");dojo.require("dijit._Widget");dojo.require("dijit._Templated");dojo.declare("dojox.image.ThumbnailPicker",[dijit._Widget, dijit._Templated],{// summary: A scrolling Thumbnail Picker widget//// imageStore: Object// A data store that implements the dojo.data Read API.imageStore: null,// request: Object// A dojo.data Read API Request object.request: null,// size: Number// Width or height in pixels, depending if horizontal or vertical.size: 500,// thumbHeight: Number// Default height of a thumbnail imagethumbHeight: 75,// thumbWidth: Number// Default width of an imagethumbWidth: 100,// useLoadNotifier: Boolean// Setting useLoadNotifier to true makes a colored DIV appear under each// thumbnail image, which is used to display the loading status of each// image in the data store.useLoadNotifier: false,// useHyperlink: boolean// Setting useHyperlink to true causes a click on a thumbnail to open a link.useHyperlink: false,// hyperlinkTarget: String// If hyperlinkTarget is set to "new", clicking on a thumb will open a new window// If it is set to anything else, clicking a thumbnail will open the url in the// current window.hyperlinkTarget: "new",// isClickable: Boolean// When set to true, the cursor over a thumbnail changes.isClickable: true,// isScrollable: Boolean// When true, uses smoothScroll to move between pagesisScrollable: true,// isHorizontal: Boolean// If true, the thumbnails are displayed horizontally. Otherwise they are displayed// verticallyisHorizontal: true,//autoLoad: BooleanautoLoad: true,// linkAttr: String// The attribute name for accessing the url from the data storelinkAttr: "link",// imageThumbAttr: String// The attribute name for accessing the thumbnail image url from the data storeimageThumbAttr: "imageUrlThumb",// imageLargeAttr: String// The attribute name for accessing the large image url from the data storeimageLargeAttr: "imageUrl",// pageSize: Number// The number of images to request each time.pageSize: 20,// titleAttr: String// The attribute name for accessing the title from the data storetitleAttr: "title",templateString:"<div dojoAttachPoint=\"outerNode\" class=\"thumbOuter\">\n\t<div dojoAttachPoint=\"navPrev\" class=\"thumbNav thumbClickable\">\n\t <img src=\"\" dojoAttachPoint=\"navPrevImg\"/> \n\t</div>\n\t<div dojoAttachPoint=\"thumbScroller\" class=\"thumbScroller\">\n\t <div dojoAttachPoint=\"thumbsNode\" class=\"thumbWrapper\"></div>\n\t</div>\n\t<div dojoAttachPoint=\"navNext\" class=\"thumbNav thumbClickable\">\n\t <img src=\"\" dojoAttachPoint=\"navNextImg\"/> \n\t</div>\n</div>\n",tempImgPath: dojo.moduleUrl("dojox.image", "resources/images/1pixel.gif"),// thumbs: Array// Stores the image nodes for the thumbnails._thumbs: [],// _thumbIndex: Number// The index of the first thumbnail shown_thumbIndex: 0,// _maxPhotos: Number// The total number of photos in the image store_maxPhotos: 0,// _loadedImages: Object// Stores the indices of images that have been marked as loaded using the// markImageLoaded function._loadedImages: {},postCreate: function(){// summary: Initializes styles and listenersthis.widgetid = this.id;this.inherited("postCreate",arguments);this.pageSize = Number(this.pageSize);this._scrollerSize = this.size - (51 * 2);var sizeProp = this._sizeProperty = this.isHorizontal ? "width" : "height";// FIXME: do this via css? calculate the correct width for the widgetdojo.style(this.outerNode, "textAlign","center");dojo.style(this.outerNode, sizeProp, this.size+"px");dojo.style(this.thumbScroller, sizeProp, this._scrollerSize + "px");//If useHyperlink is true, then listen for a click on a thumbnail, and//open the linkif(this.useHyperlink){dojo.subscribe(this.getClickTopicName(), this, function(packet){var index = packet.index;var url = this.imageStore.getValue(packet.data,this.linkAttr);//If the data item doesn't contain a URL, do nothingif(!url){return;}if(this.hyperlinkTarget == "new"){window.open(url);}else{window.location = url;}});}if(this.isScrollable) {dojo.require("dojox.fx.scroll");dojo.require("dojox.fx.easing");}if(this.isClickable){dojo.addClass(this.thumbsNode, "thumbClickable");}this._totalSize = 0;this.init();},init: function(){// summary: Creates DOM nodes for thumbnail images and initializes their listenersif(this.isInitialized) {return false;}var classExt = this.isHorizontal ? "Horiz" : "Vert";// FIXME: can we setup a listener around the whole element and determine based on e.target?dojo.addClass(this.navPrev, "prev" + classExt);dojo.addClass(this.navNext, "next" + classExt);dojo.addClass(this.thumbsNode, "thumb"+classExt);dojo.addClass(this.outerNode, "thumb"+classExt);this.navNextImg.setAttribute("src", this.tempImgPath);this.navPrevImg.setAttribute("src", this.tempImgPath);dojo.connect(this.navPrev, "onclick", this, "_prev");dojo.connect(this.navNext, "onclick", this, "_next");this.isInitialized = true;if(this.isHorizontal){this._offsetAttr = "offsetLeft";this._sizeAttr = "offsetWidth";this._scrollAttr = "scrollLeft";}else{this._offsetAttr = "offsetTop";this._sizeAttr = "offsetHeight";this._scrollAttr = "scrollTop";}this._updateNavControls();if(this.imageStore && this.request){this._loadNextPage();}return true;},getClickTopicName: function(){// summary: Returns the name of the dojo topic that can be// subscribed to in order to receive notifications on// which thumbnail was selected.return (this.widgetId ? this.widgetId : this.id) + "/select"; // String},getShowTopicName: function(){// summary: Returns the name of the dojo topic that can be// subscribed to in order to receive notifications on// which thumbnail is now visiblereturn (this.widgetId ? this.widgetId : this.id) + "/show"; // String},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 four attributes allowed are 'linkAttr', 'imageLargeAttr',// 'imageThumbAttr' and 'titleAttr'this.reset();this.request = {query: {},start: request.start ? request.start : 0,count: request.count ? request.count : 10,onBegin: dojo.hitch(this, function(total){this._maxPhotos = total;})};if(request.query){ dojo.mixin(this.request.query, request.query);}if(paramNames && paramNames.imageThumbAttr){var attrNames = ["imageThumbAttr", "imageLargeAttr", "linkAttr", "titleAttr"];for(var i = 0; i< attrNames.length; i++){if(paramNames[attrNames[i]]){this[attrNames[i]] = paramNames[attrNames[i]];}}}this.request.start = 0;this.request.count = this.pageSize;this.imageStore = dataStore;if(!this.init()){this._loadNextPage();}},reset: function(){// summary: Resets the widget back to its original state.this._loadedImages = {};var img;for(var pos = 0; pos < this._thumbs.length; pos++){img = this._thumbs[pos];if(img){// dojo.event.browser.clean(img);if(img.parentNode){img.parentNode.removeChild(img);}}}this._thumbs = [];this.isInitialized = false;this._noImages = true;},isVisible: function(idx) {// summary: Returns true if the image at the specified index is currently visible. False otherwise.var img = this._thumbs[idx];;if(!img){return false;}var pos = this.isHorizontal ? "offsetLeft" : "offsetTop";var size = this.isHorizontal ? "offsetWidth" : "offsetHeight";var scrollAttr = this.isHorizontal ? "scrollLeft" : "scrollTop";var offset = img[pos] - this.thumbsNode[pos];return (offset >= this.thumbScroller[scrollAttr]&& offset + img[size] <= this.thumbScroller[scrollAttr] + this._scrollerSize);},_next: function() {// summary: Displays the next page of imagesvar pos = this.isHorizontal ? "offsetLeft" : "offsetTop";var size = this.isHorizontal ? "offsetWidth" : "offsetHeight";var baseOffset = this.thumbsNode[pos];var firstThumb = this._thumbs[this._thumbIndex];var origOffset = firstThumb[pos] - baseOffset;var idx = -1, img;for(var i = this._thumbIndex + 1; i < this._thumbs.length; i++){img = this._thumbs[i];if(img[pos] - baseOffset + img[size] - origOffset > this._scrollerSize){this._showThumbs(i);return;}}},_prev: function(){// summary: Displays the next page of imagesif(this.thumbScroller[this.isHorizontal ? "scrollLeft" : "scrollTop"] == 0){return;}var pos = this.isHorizontal ? "offsetLeft" : "offsetTop";var size = this.isHorizontal ? "offsetWidth" : "offsetHeight";var firstThumb = this._thumbs[this._thumbIndex];var origOffset = firstThumb[pos] - this.thumbsNode[pos];var idx = -1, img;for(var i = this._thumbIndex - 1; i > -1; i--) {img = this._thumbs[i];if(origOffset - img[pos] > this._scrollerSize){this._showThumbs(i + 1);return;}}this._showThumbs(0);},_checkLoad: function(img, idx){dojo.publish(this.getShowTopicName(), [{index:idx}]);this._updateNavControls();this._loadingImages = {};this._thumbIndex = idx;//If we have not already requested the data from the store, do so.if(this.thumbsNode.offsetWidth - img.offsetLeft < (this._scrollerSize * 2)){this._loadNextPage();}},_showThumbs: function(idx){// summary: Displays thumbnail images, starting at position 'idx'// idx: Number// The index of the first thumbnailvar _this = this;var idx = arguments.length == 0 ? this._thumbIndex : arguments[0];idx = Math.min(Math.max(idx, 0), this._maxPhotos);if(idx >= this._maxPhotos){ return; }var img = this._thumbs[idx];if(!img){ return; }var left = img.offsetLeft - this.thumbsNode.offsetLeft;var top = img.offsetTop - this.thumbsNode.offsetTop;var offset = this.isHorizontal ? left : top;if( (offset >= this.thumbScroller[this._scrollAttr]) &&(offset + img[this._sizeAttr] <= this.thumbScroller[this._scrollAttr] + this._scrollerSize)){// FIXME: WTF is this checking for?return;}if(this.isScrollable){var target = this.isHorizontal ? {x: left, y: 0} : { x:0, y:top};dojox.fx.smoothScroll({target: target,win: this.thumbScroller,duration:300,easing:dojox.fx.easing.easeOut,onEnd: dojo.hitch(this, "_checkLoad", img, idx)}).play(10);}else{if(this.isHorizontal){this.thumbScroller.scrollLeft = left;}else{this.thumbScroller.scrollTop = top;}this._checkLoad(img, idx);}},markImageLoaded: function(index){// summary: Changes a visual cue to show the image is loaded// description: If 'useLoadNotifier' is set to true, then a visual cue is// given to state whether the image is loaded or not. Calling this function// marks an image as loaded.var thumbNotifier = dojo.byId("loadingDiv_"+this.widgetid+"_"+index);if(thumbNotifier){this._setThumbClass(thumbNotifier, "thumbLoaded");}this._loadedImages[index] = true;},_setThumbClass: function(thumb, className){// summary: Adds a CSS class to a thumbnail, only if 'autoLoad' is true// thumb: DomNode// The thumbnail DOM node to set the class on// className: String// The CSS class to add to the DOM node.if(!this.autoLoad){ return; }dojo.addClass(thumb, className);},_loadNextPage: function(){// summary: Loads the next page of thumbnail imagesif(this._loadInProgress){return;}this._loadInProgress = true;var start = this.request.start + (this._noImages == true ? 0 : this.pageSize);var pos = start;while(pos < this._thumbs.length && this._thumbs[pos]){pos ++;}var _this = this;//Define the function to call when the items have been//returned from the data store.var complete = function(items, request){if(items && items.length) {var itemCounter = 0;var loadNext = function(){if(itemCounter >= items.length){_this._loadInProgress = false;return;}var counter = itemCounter++;_this._loadImage(items[counter], pos + counter, loadNext);}loadNext();//Show or hide the navigation arrows on the thumbnails,//depending on whether or not the widget is at the start,//end, or middle of the list of images._this._updateNavControls();}else{_this._loadInProgress = false;}};//Define the function to call if the store reports an error.var error = function(){_this._loadInProgress = false;console.debug("Error getting items");};this.request.onComplete = complete;this.request.onError = error;//Increment the start parameter. This is the dojo.data API's//version of paging.this.request.start = start;this._noImages = false;//Execute the request for data.this.imageStore.fetch(this.request);},_loadImage: function(data, index, callback){var url = this.imageStore.getValue(data,this.imageThumbAttr);var img = document.createElement("img");var imgContainer = document.createElement("div");imgContainer.setAttribute("id","img_" + this.widgetid+"_"+index);imgContainer.appendChild(img);img._index = index;img._data = data;this._thumbs[index] = imgContainer;var loadingDiv;if(this.useLoadNotifier){loadingDiv = document.createElement("div");loadingDiv.setAttribute("id","loadingDiv_" + this.widgetid+"_"+index);//If this widget was previously told that the main image for this//thumb has been loaded, make the loading indicator transparent.this._setThumbClass(loadingDiv,this._loadedImages[index] ? "thumbLoaded":"thumbNotifier");imgContainer.appendChild(loadingDiv);}var size = dojo.marginBox(this.thumbsNode);var defaultSize;var sizeParam;if(this.isHorizontal){defaultSize = this.thumbWidth;sizeParam = 'w';} else{defaultSize = this.thumbHeight;sizeParam = 'h';}size = size[sizeParam];var sl = this.thumbScroller.scrollLeft, st = this.thumbScroller.scrollTop;dojo.style(this.thumbsNode, this._sizeProperty, (size + defaultSize + 20) + "px");//Remember the scroll values, as changing the size can alter themthis.thumbScroller.scrollLeft = sl;this.thumbScroller.scrollTop = st;this.thumbsNode.appendChild(imgContainer);dojo.connect(img, "onload", this, function(){var realSize = dojo.marginBox(img)[sizeParam];this._totalSize += (Number(realSize) + 4);dojo.style(this.thumbsNode, this._sizeProperty, this._totalSize + "px");if(this.useLoadNotifier){dojo.style(loadingDiv, "width", (img.width - 4) + "px"); }callback();return false;});dojo.connect(img, "onclick", this, function(evt){dojo.publish(this.getClickTopicName(), [{index: evt.target._index,data: evt.target._data,url: img.getAttribute("src"),largeUrl: this.imageStore.getValue(data,this.imageLargeAttr),title: this.imageStore.getValue(data,this.titleAttr),link: this.imageStore.getValue(data,this.linkAttr)}]);return false;});dojo.addClass(img, "imageGalleryThumb");img.setAttribute("src", url);var title = this.imageStore.getValue(data, this.titleAttr);if(title){ img.setAttribute("title",title); }this._updateNavControls();},_updateNavControls: function(){// summary: Updates the navigation controls to hide/show them when at// the first or last images.var cells = [];var change = function(node, add){var fn = add ? "addClass" : "removeClass";dojo[fn](node,"enabled");dojo[fn](node,"thumbClickable");};var pos = this.isHorizontal ? "scrollLeft" : "scrollTop";var size = this.isHorizontal ? "offsetWidth" : "offsetHeight";change(this.navPrev, (this.thumbScroller[pos] > 0));var last = this._thumbs[this._thumbs.length - 1];var addClass = (this.thumbScroller[pos] + this._scrollerSize < this.thumbsNode[size]);change(this.navNext, addClass);}});}