diff options
| author | Tristan Zur <tzur@web.web.ccwn.org> | 2014-03-27 22:27:47 +0100 |
|---|---|---|
| committer | Tristan Zur <tzur@web.web.ccwn.org> | 2014-03-27 22:27:47 +0100 |
| commit | b62676ca5d3d6f6ba3f019ea3f99722e165a98d8 (patch) | |
| tree | 86722cb80f07d4569f90088eeaea2fc2f6e2ef94 /js/dojo/dojox/image | |
Diffstat (limited to 'js/dojo/dojox/image')
33 files changed, 3593 insertions, 0 deletions
diff --git a/js/dojo/dojox/image/Badge.js b/js/dojo/dojox/image/Badge.js new file mode 100644 index 0000000..3a84ada --- /dev/null +++ b/js/dojo/dojox/image/Badge.js @@ -0,0 +1,229 @@ +//>>built +define("dojox/image/Badge", ["dojo", "dijit", "dojox/main", "dijit/_Widget", "dijit/_TemplatedMixin", "dojo/fx/easing"], function(dojo, dijit, dojox){ + + dojo.experimental("dojox.image.Badge"); + dojo.getObject("image", true, dojox); + + dojo.declare("dojox.image.Badge", [dijit._Widget, dijit._TemplatedMixin], { + // summary: A simple grid of Images that loops through thumbnails + // + + baseClass: "dojoxBadge", + + templateString:'<div class="dojoxBadge" dojoAttachPoint="containerNode"></div>', + + // children: String + // A CSS3 Selector that determines the node to become a child + children: "div.dojoxBadgeImage", + + // rows: Integer + // Number of Rows to display + rows: 4, + + // cols: Integer + // Number of Columns to display + cols: 5, + + // cellSize: Integer + // Size in PX of each thumbnail + cellSize: 50, + + // cellMargin: Integer + // Size in PX to adjust for cell margins + cellMargin: 1, + + // delay: Integer + // Time (in ms) to show the image before sizing down again + delay: 2000, + + // threads: Integer + // how many cycles will be going "simultaneously" (>2 not reccommended) + threads: 1, + + // easing: Function|String + // An easing function to use when showing the node (does not apply to shrinking) + easing: "dojo.fx.easing.backOut", + + startup: function(){ + if(this._started){ return; } + if(dojo.isString(this.easing)){ + this.easing = dojo.getObject(this.easing); + } + this.inherited(arguments); + this._init(); + }, + + _init: function(){ + // summary: Setup and layout the images + + var _row = 0, + _w = this.cellSize; + + dojo.style(this.domNode, { + width: _w * this.cols + "px", + height: _w * this.rows + "px" + }); + + this._nl = dojo.query(this.children, this.containerNode) + .forEach(function(n, _idx){ + + var _col = _idx % this.cols, + t = _row * _w, + l = _col * _w, + m = this.cellMargin * 2; + + dojo.style(n, { + top: t + "px", + left: l + "px", + width: _w - m + "px", + height: _w - m + "px" + }); + + if(_col == this.cols - 1){ _row++; } + dojo.addClass(n, this.baseClass + "Image"); + + }, this) + ; + + var l = this._nl.length; + while(this.threads--){ + var s = Math.floor(Math.random() * l); + setTimeout(dojo.hitch(this, "_enbiggen", { + target: this._nl[s] + }), this.delay * this.threads); + } + + }, + + _getCell: function(/* DomNode */ n){ + // summary: Return information about the position for a given node + var _pos = this._nl.indexOf(n); + if(_pos >= 0){ + var _col = _pos % this.cols; + var _row = Math.floor(_pos / this.cols); + return { x: _col, y: _row, n: this._nl[_pos], io: _pos }; + }else{ + return undefined; + } + }, + + _getImage: function(){ + // summary: Returns the next image in the list, or the first one if not available + return "url('')"; + }, + + _enbiggen: function(/* Event|DomNode */ e){ + // summary: Show the passed node in the picker + var _pos = this._getCell(e.target || e); + + if (_pos){ + // we have a node, and know where it is + + var m = this.cellMargin, + _cc = (this.cellSize * 2) - (m * 2), + props = { + height: _cc, + width: _cc + } + ; + + var _tehDecider = function(){ + // if we have room, we'll want to decide which direction to go + // let "teh decider" decide. + return Math.round(Math.random()); + }; + + if(_pos.x == this.cols - 1 || (_pos.x > 0 && _tehDecider() )){ + // we have to go left, at right edge (or we want to and not on left edge) + props.left = this.cellSize * (_pos.x - m); + } + + if(_pos.y == this.rows - 1 || (_pos.y > 0 && _tehDecider() )){ + // we have to go up, at bottom edge (or we want to and not at top) + props.top = this.cellSize * (_pos.y - m); + } + + var bc = this.baseClass; + dojo.addClass(_pos.n, bc + "Top"); + dojo.addClass(_pos.n, bc + "Seen"); + + dojo.animateProperty({ node: _pos.n, properties: props, + onEnd: dojo.hitch(this, "_loadUnder", _pos, props), + easing: this.easing + }).play(); + + } + }, + + _loadUnder: function(info, props){ + // summary: figure out which three images are being covered, and + // determine if they need loaded or not + + var idx = info.io; + var nodes = []; + + var isLeft = (props.left >= 0); + var isUp = (props.top >= 0); + + var c = this.cols, + // the three node index's we're allegedly over: + e = idx + (isLeft ? -1 : 1), + f = idx + (isUp ? -c : c), + // don't ask: + g = (isUp ? (isLeft ? e - c : f + 1) : (isLeft ? f - 1 : e + c)), + + bc = this.baseClass; + + dojo.forEach([e, f, g], function(x){ + var n = this._nl[x]; + if(n){ + if(dojo.hasClass(n, bc + "Seen")){ + // change the background image out? + dojo.removeClass(n, bc + "Seen"); + } + } + },this); + + setTimeout(dojo.hitch(this, "_disenbiggen", info, props), this.delay * 1.25); + + }, + + _disenbiggen: function(info, props){ + // summary: Hide the passed node (info.n), passing along properties + // received. + + if(props.top >= 0){ + props.top += this.cellSize; + } + if(props.left >= 0){ + props.left += this.cellSize; + } + var _cc = this.cellSize - (this.cellMargin * 2); + dojo.animateProperty({ + node: info.n, + properties: dojo.mixin(props, { + width:_cc, + height:_cc + }), + onEnd: dojo.hitch(this, "_cycle", info, props) + }).play(5); + }, + + _cycle: function(info, props){ + // summary: Select an un-viewed image from the list, and show it + + var bc = this.baseClass; + dojo.removeClass(info.n, bc + "Top"); + var ns = this._nl.filter(function(n){ + return !dojo.hasClass(n, bc + "Seen") + }); + var c = ns[Math.floor(Math.random() * ns.length)]; + setTimeout(dojo.hitch(this,"_enbiggen", { target: c }), this.delay / 2) + + } + + }); + + return dojox.image.Badge; +}) + diff --git a/js/dojo/dojox/image/FlickrBadge.js b/js/dojo/dojox/image/FlickrBadge.js new file mode 100644 index 0000000..c03408e --- /dev/null +++ b/js/dojo/dojox/image/FlickrBadge.js @@ -0,0 +1,107 @@ +//>>built +define("dojox/image/FlickrBadge", ["dojo", "dojox/main", "dojox/image/Badge", "dojox/data/FlickrRestStore"], function(dojo, dojox){ + + dojo.getObject("image", true, dojox); + return dojo.declare("dojox.image.FlickrBadge", dojox.image.Badge, { + children: "a.flickrImage", + + // userid: String + // If you know your Flickr userid, you can set it to prevent a call to fetch the id + userid: "", + + // username: String + // Your Flickr username + username: "", + + // setid: String + // The id of the set to display + setid: "", + + // tags: String|Array + // A comma separated list of tags or an array of tags to grab from Flickr + tags: "", + + // searchText: String + // Free text search. Photos who's title, description, or tags contain the text will be displayed + searchText: "", + + // target: String + // Where to display the pictures when clicked on. Valid values are the same as the target attribute + // of the A tag. + target: "", + + apikey: "8c6803164dbc395fb7131c9d54843627", + _store: null, + + postCreate: function(){ + if(this.username && !this.userid){ + var def = dojo.io.script.get({ + url: "http://www.flickr.com/services/rest/", + preventCache: true, + content: { + format: "json", + method: "flickr.people.findByUsername", + api_key: this.apikey, + username: this.username + }, + callbackParamName: "jsoncallback" + }); + def.addCallback(this, function(data){ + if(data.user && data.user.nsid){ + this.userid = data.user.nsid; + if(!this._started){ + this.startup(); + } + } + }); + } + }, + + startup: function(){ + if(this._started){ return; } + if(this.userid){ + var query = { + userid: this.userid + }; + if(this.setid){ + query["setid"] = this.setid; + } + if(this.tags){ + query.tags = this.tags; + } + if(this.searchText){ + query.text = this.searchText; + } + var args = arguments; + this._store = new dojox.data.FlickrRestStore({ apikey: this.apikey }); + this._store.fetch({ + count: this.cols * this.rows, + query: query, + onComplete: dojo.hitch(this, function(items){ + dojo.forEach(items, function(item){ + var a = dojo.doc.createElement("a"); + dojo.addClass(a, "flickrImage"); + a.href = this._store.getValue(item, "link"); + if(this.target){ + a.target = this.target; + } + + var img = dojo.doc.createElement("img"); + img.src = this._store.getValue(item, "imageUrlThumb"); + dojo.style(img, { + width: "100%", + height: "100%" + }); + + a.appendChild(img); + this.domNode.appendChild(a); + }, this); + dojox.image.Badge.prototype.startup.call(this, args); + }) + }); + } + } + }); + +}); + diff --git a/js/dojo/dojox/image/Gallery.js b/js/dojo/dojox/image/Gallery.js new file mode 100644 index 0000000..fadbc4a --- /dev/null +++ b/js/dojo/dojox/image/Gallery.js @@ -0,0 +1,194 @@ +//>>built +// wrapped by build app +define("dojox/image/Gallery", ["dijit","dojo","dojox","dojo/require!dojo/fx,dijit/_Widget,dijit/_Templated,dojox/image/ThumbnailPicker,dojox/image/SlideShow"], function(dijit,dojo,dojox){ +dojo.provide("dojox.image.Gallery"); +dojo.experimental("dojox.image.Gallery"); +// +// dojox.image.Gallery courtesy Shane O Sullivan, licensed under a Dojo CLA +// +// For a sample usage, see http://www.skynet.ie/~sos/photos.php +// +// TODO: Make public, document params and privitize non-API conformant methods. +// document topics. + +dojo.require("dojo.fx"); +dojo.require("dijit._Widget"); +dojo.require("dijit._Templated"); +dojo.require("dojox.image.ThumbnailPicker"); +dojo.require("dojox.image.SlideShow"); + +dojo.declare("dojox.image.Gallery", + [dijit._Widget, dijit._Templated], + { + // summary: + // Gallery widget that wraps a dojox.image.ThumbnailPicker and dojox.image.SlideShow widget + // + // imageHeight: Number + // Maximum height of an image in the SlideShow widget + imageHeight: 375, + + // imageWidth: Number + // Maximum width of an image in the SlideShow widget + imageWidth: 500, + + // pageSize: Number + // The number of records to retrieve from the data store per request. + pageSize: dojox.image.SlideShow.prototype.pageSize, + + // autoLoad: Boolean + // If true, images are loaded before the user views them. If false, an + // image is loaded when the user displays it. + autoLoad: true, + + // 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", + + // imageThumbAttr: String + // Defines the name of the attribute to request from the store to retrieve the + // URL to the thumbnail image. + imageThumbAttr: "imageUrlThumb", + + // 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: Integer + // Time, in seconds, between image changes in the slide show. + slideshowInterval: 3, + + templateString: dojo.cache("dojox.image", "resources/Gallery.html", "<div dojoAttachPoint=\"outerNode\" class=\"imageGalleryWrapper\">\n\t<div dojoAttachPoint=\"thumbPickerNode\"></div>\n\t<div dojoAttachPoint=\"slideShowNode\"></div>\n</div>"), + + postCreate: function(){ + // summary: + // Initializes the widget, creates the ThumbnailPicker and SlideShow widgets + this.widgetid = this.id; + this.inherited(arguments) + + this.thumbPicker = new dojox.image.ThumbnailPicker({ + linkAttr: this.linkAttr, + imageLargeAttr: this.imageLargeAttr, + imageThumbAttr: this.imageThumbAttr, + titleAttr: this.titleAttr, + useLoadNotifier: true, + size: this.imageWidth + }, this.thumbPickerNode); + + + this.slideShow = new dojox.image.SlideShow({ + imageHeight: this.imageHeight, + imageWidth: this.imageWidth, + autoLoad: this.autoLoad, + linkAttr: this.linkAttr, + imageLargeAttr: this.imageLargeAttr, + titleAttr: this.titleAttr, + slideshowInterval: this.slideshowInterval, + pageSize: this.pageSize + }, this.slideShowNode); + + var _this = this; + //When an image is shown in the Slideshow, make sure it is visible + //in the ThumbnailPicker + dojo.subscribe(this.slideShow.getShowTopicName(), function(packet){ + _this.thumbPicker._showThumbs(packet.index); + }); + //When the user clicks a thumbnail, show that image + dojo.subscribe(this.thumbPicker.getClickTopicName(), function(evt){ + _this.slideShow.showImage(evt.index); + }); + //When the ThumbnailPicker moves to show a new set of pictures, + //make the Slideshow start loading those pictures first. + dojo.subscribe(this.thumbPicker.getShowTopicName(), function(evt){ + _this.slideShow.moveImageLoadingPointer(evt.index); + }); + //When an image finished loading in the slideshow, update the loading + //notification in the ThumbnailPicker + dojo.subscribe(this.slideShow.getLoadTopicName(), function(index){ + _this.thumbPicker.markImageLoaded(index); + }); + this._centerChildren(); + }, + + 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.thumbPicker.setDataStore(dataStore, request, paramNames); + this.slideShow.setDataStore(dataStore, request, paramNames); + }, + + reset: function(){ + // summary: + // Resets the widget to its initial state + this.slideShow.reset(); + this.thumbPicker.reset(); + }, + + showNextImage: function(inTimer){ + // summary: + // Changes the image being displayed in the SlideShow to the next + // image in the data store + // inTimer: Boolean + // If true, a slideshow is active, otherwise the slideshow is inactive. + this.slideShow.showNextImage(); + }, + + toggleSlideshow: function(){ + dojo.deprecated("dojox.widget.Gallery.toggleSlideshow is deprecated. Use toggleSlideShow instead.", "", "2.0"); + this.toggleSlideShow(); + }, + + toggleSlideShow: function(){ + // summary: + // Switches the slideshow mode on and off. + this.slideShow.toggleSlideShow(); + }, + + showImage: function(index, /*optional*/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. + this.slideShow.showImage(index, callback); + }, + + resize: function(dim){ + this.thumbPicker.resize(dim); + }, + + _centerChildren: function() { + // summary: + // Ensures that the ThumbnailPicker and the SlideShow widgets + // are centered. + var thumbSize = dojo.marginBox(this.thumbPicker.outerNode); + var slideSize = dojo.marginBox(this.slideShow.outerNode); + + var diff = (thumbSize.w - slideSize.w) / 2; + + if(diff > 0) { + dojo.style(this.slideShow.outerNode, "marginLeft", diff + "px"); + } else if(diff < 0) { + dojo.style(this.thumbPicker.outerNode, "marginLeft", (diff * -1) + "px"); + } + } +}); + +}); diff --git a/js/dojo/dojox/image/Lightbox.js b/js/dojo/dojox/image/Lightbox.js new file mode 100644 index 0000000..18a5cd3 --- /dev/null +++ b/js/dojo/dojox/image/Lightbox.js @@ -0,0 +1,608 @@ +//>>built +require({cache:{ +'url:dojox/image/resources/Lightbox.html':"<div class=\"dojoxLightbox\" dojoAttachPoint=\"containerNode\">\n\t<div style=\"position:relative\">\n\t\t<div dojoAttachPoint=\"imageContainer\" class=\"dojoxLightboxContainer\" dojoAttachEvent=\"onclick: _onImageClick\">\n\t\t\t<img dojoAttachPoint=\"imgNode\" src=\"${imgUrl}\" class=\"dojoxLightboxImage\" alt=\"${title}\">\n\t\t\t<div class=\"dojoxLightboxFooter\" dojoAttachPoint=\"titleNode\">\n\t\t\t\t<div class=\"dijitInline LightboxClose\" dojoAttachPoint=\"closeButtonNode\"></div>\n\t\t\t\t<div class=\"dijitInline LightboxNext\" dojoAttachPoint=\"nextButtonNode\"></div>\t\n\t\t\t\t<div class=\"dijitInline LightboxPrev\" dojoAttachPoint=\"prevButtonNode\"></div>\n\t\t\t\t<div class=\"dojoxLightboxText\" dojoAttachPoint=\"titleTextNode\"><span dojoAttachPoint=\"textNode\">${title}</span><span dojoAttachPoint=\"groupCount\" class=\"dojoxLightboxGroupText\"></span></div>\n\t\t\t</div>\n\t\t</div>\n\t</div>\n</div>"}}); +define("dojox/image/Lightbox", ["dojo", "dijit", "dojox", "dojo/text!./resources/Lightbox.html", "dijit/Dialog", "dojox/fx/_base"], function(dojo, dijit, dojox, template){ + + dojo.experimental("dojox.image.Lightbox"); + dojo.getObject("image", true, dojox); + + dojo.declare("dojox.image.Lightbox", dijit._Widget, { + // summary: + // A dojo-based Lightbox implementation. + // + // description: + // An Elegant, keyboard accessible, markup and store capable Lightbox widget to show images + // in a modal dialog-esque format. Can show individual images as Modal dialog, or can group + // images with multiple entry points, all using a single "master" Dialog for visualization + // + // key controls: + // ESC - close + // Down Arrow / Rt Arrow / N - Next Image + // Up Arrow / Lf Arrow / P - Previous Image + // + // example: + // | <a href="image1.jpg" dojoType="dojox.image.Lightbox">show lightbox</a> + // + // example: + // | <a href="image2.jpg" dojoType="dojox.image.Lightbox" group="one">show group lightbox</a> + // | <a href="image3.jpg" dojoType="dojox.image.Lightbox" group="one">show group lightbox</a> + // + // example: + // | not implemented fully yet, though works with basic datastore access. need to manually call + // | widget._attachedDialog.addImage(item,"fromStore") for each item in a store result set. + // | <div dojoType="dojox.image.Lightbox" group="fromStore" store="storeName"></div> + // + // group: String + // Grouping images in a page with similar tags will provide a 'slideshow' like grouping of images + group: "", + + // title: String + // A string of text to be shown in the Lightbox beneath the image (empty if using a store) + title: "", + + // href; String + // Link to image to use for this Lightbox node (empty if using a store). + href: "", + + // duration: Integer + // Generic time in MS to adjust the feel of widget. could possibly add various + // durations for the various actions (dialog fadein, sizeing, img fadein ...) + duration: 500, + + // modal: Boolean + // If true, this Dialog instance will be truly modal and prevent closing until + // explicitly told to by calling hide() or clicking the (x) - Defaults to false + // to preserve previous behaviors. (aka: enable click-to-click on the underlay) + modal: false, + + // _allowPassthru: Boolean + // Privately set this to disable/enable natural link of anchor tags + _allowPassthru: false, + + // _attachedDialg: dojox.image._LightboxDialog + // The pointer to the global lightbox dialog for this widget + _attachedDialog: null, // try to share a single underlay per page? + + startup: function(){ + this.inherited(arguments); + // setup an attachment to the masterDialog (or create the masterDialog) + var tmp = dijit.byId('dojoxLightboxDialog'); + if(tmp){ + this._attachedDialog = tmp; + }else{ + // this is the first instance to start, so we make the masterDialog + this._attachedDialog = new dojox.image.LightboxDialog({ id: "dojoxLightboxDialog" }); + this._attachedDialog.startup(); + } + if(!this.store){ + // FIXME: full store support lacking, have to manually call this._attachedDialog.addImage(imgage,group) as it stands + this._addSelf(); + this.connect(this.domNode, "onclick", "_handleClick"); + } + + }, + + _addSelf: function(){ + // summary: Add this instance to the master LightBoxDialog + this._attachedDialog.addImage({ + href: this.href, + title: this.title + }, this.group || null); + }, + + _handleClick: function(/* Event */e){ + // summary: Handle the click on the link + if(!this._allowPassthru){ e.preventDefault(); } + else{ return; } + this.show(); + }, + + show: function(){ + // summary: Show the Lightbox with this instance as the starting point + this._attachedDialog.show(this); + }, + + hide: function(){ + // summary: Hide the Lightbox currently showing + this._attachedDialog.hide(); + }, + + // FIXME: switch to .attr, deprecate eventually. + disable: function(){ + // summary: Disables event clobbering and dialog, and follows natural link + this._allowPassthru = true; + }, + + enable: function(){ + // summary: Enables the dialog (prevents default link) + this._allowPassthru = false; + }, + + onClick: function(){ + // summary: + // Stub fired when the image in the lightbox is clicked. + }, + + destroy: function(){ + this._attachedDialog.removeImage(this); + this.inherited(arguments); + } + + }); + + dojo.declare("dojox.image.LightboxDialog", + dijit.Dialog, { + // summary: + // The "dialog" shared between any Lightbox instances on the page, publically available + // for programatic manipulation. + // + // description: + // + // A widget that intercepts anchor links (typically around images) + // and displays a modal Dialog. this is the actual Dialog, which you can + // create and populate manually, though should use simple Lightbox's + // unless you need the direct access. + // + // There should only be one of these on a page, so all dojox.image.Lightbox's will us it + // (the first instance of a Lightbox to be show()'n will create me If i do not exist) + // + // example: + // | // show a single image from a url + // | var url = "http://dojotoolkit.org/logo.png"; + // | var dialog = new dojox.image.LightboxDialog().startup(); + // | dialog.show({ href: url, title:"My Remote Image"}); + // + // title: String + // The current title, read from object passed to show() + title: "", + + // FIXME: implement titleTemplate + + // inGroup: Array + // Array of objects. this is populated by from the JSON object _groups, and + // should not be populate manually. it is a placeholder for the currently + // showing group of images in this master dialog + inGroup: null, + + // imgUrl: String + // The src="" attribute of our imageNode (can be null at statup) + imgUrl: dijit._Widget.prototype._blankGif, + + // errorMessage: String + // The text to display when an unreachable image is linked + errorMessage: "Image not found.", + + // adjust: Boolean + // If true, ensure the image always stays within the viewport + // more difficult than necessary to disable, but enabled by default + // seems sane in most use cases. + adjust: true, + + // modal: Boolean + // If true, this Dialog instance will be truly modal and prevent closing until + // explicitly told to by calling hide() or clicking the (x) - Defaults to false + // to preserve previous behaviors. (aka: enable click-to-click on the underlay) + modal: false, + + /*===== + // _groups: Object + // an object of arrays, each array (of objects) being a unique 'group' + _groups: { XnoGroupX: [] }, + + =====*/ + + // errorImg: Url + // Path to the image used when a 404 is encountered + errorImg: dojo.moduleUrl("dojox.image","resources/images/warning.png"), + + templateString: template, + + constructor: function(args){ + this._groups = this._groups || (args && args._groups) || { XnoGroupX:[] }; + }, + + startup: function(){ + // summary: Add some extra event handlers, and startup our superclass. + // + // returns: dijit._Widget + // Perhaps the only `dijit._Widget` that returns itself to allow + // 'chaining' or var referencing with .startup() + + this.inherited(arguments); + + this._animConnects = []; + this.connect(this.nextButtonNode, "onclick", "_nextImage"); + this.connect(this.prevButtonNode, "onclick", "_prevImage"); + this.connect(this.closeButtonNode, "onclick", "hide"); + this._makeAnims(); + this._vp = dojo.window.getBox(); + return this; + }, + + show: function(/* Object */groupData){ + // summary: Show the Master Dialog. Starts the chain of events to show + // an image in the dialog, including showing the dialog if it is + // not already visible + // + // groupData: Object + // needs href and title attributes. the values for this image. + // + // + var _t = this; // size + this._lastGroup = groupData; + + // we only need to call dijit.Dialog.show() if we're not already open. + if(!_t.open){ + _t.inherited(arguments); + _t._modalconnects.push( + dojo.connect(dojo.global, "onscroll", this, "_position"), + dojo.connect(dojo.global, "onresize", this, "_position"), + dojo.connect(dojo.body(), "onkeypress", this, "_handleKey") + ); + if(!groupData.modal){ + _t._modalconnects.push( + dojo.connect(dijit._underlay.domNode, "onclick", this, "onCancel") + ); + } + } + + if(this._wasStyled){ + // ugly fix for IE being stupid. place the new image relative to the old + // image to allow for overriden templates to adjust the location of the + // titlebar. DOM will remain "unchanged" between views. + var tmpImg = dojo.create("img", null, _t.imgNode, "after"); + dojo.destroy(_t.imgNode); + _t.imgNode = tmpImg; + _t._makeAnims(); + _t._wasStyled = false; + } + + dojo.style(_t.imgNode,"opacity","0"); + dojo.style(_t.titleNode,"opacity","0"); + + var src = groupData.href; + + if((groupData.group && groupData !== "XnoGroupX") || _t.inGroup){ + if(!_t.inGroup){ + _t.inGroup = _t._groups[(groupData.group)]; + // determine where we were or are in the show + dojo.forEach(_t.inGroup, function(g, i){ + if(g.href == groupData.href){ + _t._index = i; + //return false; + } + //return true; + }); + } + if(!_t._index){ + _t._index = 0; + var sr = _t.inGroup[_t._index]; + src = (sr && sr.href) || _t.errorImg; + } + // FIXME: implement titleTemplate + _t.groupCount.innerHTML = " (" + (_t._index + 1) + " of " + Math.max(1, _t.inGroup.length) + ")"; + _t.prevButtonNode.style.visibility = "visible"; + _t.nextButtonNode.style.visibility = "visible"; + }else{ + // single images don't have buttons, or counters: + _t.groupCount.innerHTML = ""; + _t.prevButtonNode.style.visibility = "hidden"; + _t.nextButtonNode.style.visibility = "hidden"; + } + if(!groupData.leaveTitle){ + _t.textNode.innerHTML = groupData.title; + } + _t._ready(src); + }, + + _ready: function(src){ + // summary: A function to trigger all 'real' showing of some src + + var _t = this; + + // listen for 404's: + _t._imgError = dojo.connect(_t.imgNode, "error", _t, function(){ + dojo.disconnect(_t._imgError); + // trigger the above onload with a new src: + _t.imgNode.src = _t.errorImg; + _t.textNode.innerHTML = _t.errorMessage; + }); + + // connect to the onload of the image + _t._imgConnect = dojo.connect(_t.imgNode, "load", _t, function(e){ + _t.resizeTo({ + w: _t.imgNode.width, + h: _t.imgNode.height, + duration:_t.duration + }); + // cleanup + dojo.disconnect(_t._imgConnect); + if(_t._imgError){ + dojo.disconnect(_t._imgError); + } + }); + + _t.imgNode.src = src; + }, + + _nextImage: function(){ + // summary: Load next image in group + if(!this.inGroup){ return; } + if(this._index + 1 < this.inGroup.length){ + this._index++; + }else{ + this._index = 0; + } + this._loadImage(); + }, + + _prevImage: function(){ + // summary: Load previous image in group + if(this.inGroup){ + if(this._index == 0){ + this._index = this.inGroup.length - 1; + }else{ + this._index--; + } + this._loadImage(); + } + }, + + _loadImage: function(){ + // summary: Do the prep work before we can show another image + this._loadingAnim.play(1); + }, + + _prepNodes: function(){ + // summary: A localized hook to accompany _loadImage + this._imageReady = false; + if(this.inGroup && this.inGroup[this._index]){ + this.show({ + href: this.inGroup[this._index].href, + title: this.inGroup[this._index].title + }); + }else{ + this.show({ + title: this.errorMessage, + href: this.errorImg + }); + } + + }, + + _calcTitleSize: function(){ + var sizes = dojo.map(dojo.query("> *", this.titleNode).position(), function(s){ return s.h; }); + return { h: Math.max.apply(Math, sizes) }; + }, + + resizeTo: function(/* Object */size, forceTitle){ + // summary: Resize our dialog container, and fire _showImage + + var adjustSize = dojo.boxModel == "border-box" ? + dojo._getBorderExtents(this.domNode).w : 0, + titleSize = forceTitle || this._calcTitleSize() + ; + + this._lastTitleSize = titleSize; + + if(this.adjust && + (size.h + titleSize.h + adjustSize + 80 > this._vp.h || + size.w + adjustSize + 60 > this._vp.w + ) + ){ + this._lastSize = size; + size = this._scaleToFit(size); + } + this._currentSize = size; + + var _sizeAnim = dojox.fx.sizeTo({ + node: this.containerNode, + duration: size.duration||this.duration, + width: size.w + adjustSize, + height: size.h + titleSize.h + adjustSize + }); + this.connect(_sizeAnim, "onEnd", "_showImage"); + _sizeAnim.play(15); + }, + + _scaleToFit: function(/* Object */size){ + // summary: resize an image to fit within the bounds of the viewport + // size: Object + // The 'size' object passed around for this image + + var ns = {}, // New size + nvp = { + w: this._vp.w - 80, + h: this._vp.h - 60 - this._lastTitleSize.h + }; // New viewport + + // Calculate aspect ratio + var viewportAspect = nvp.w / nvp.h, + imageAspect = size.w / size.h; + + // Calculate new image size + if(imageAspect >= viewportAspect){ + ns.h = nvp.w / imageAspect; + ns.w = nvp.w; + }else{ + ns.w = imageAspect * nvp.h; + ns.h = nvp.h; + } + + // we actually have to style this image, it's too big + this._wasStyled = true; + this._setImageSize(ns); + + ns.duration = size.duration; + return ns; // Object + }, + + _setImageSize: function(size){ + // summary: Reset the image size to some actual size. + var s = this.imgNode; + s.height = size.h; + s.width = size.w; + }, + + // clobber inherited function, it is useless. + _size: function(){}, + + _position: function(/* Event */e){ + // summary: we want to know the viewport size any time it changes + this._vp = dojo.window.getBox(); + this.inherited(arguments); + + // determine if we need to scale up or down, if at all. + if(e && e.type == "resize"){ + if(this._wasStyled){ + this._setImageSize(this._lastSize); + this.resizeTo(this._lastSize); + }else{ + if(this.imgNode.height + 80 > this._vp.h || this.imgNode.width + 60 > this._vp.h){ + this.resizeTo({ + w: this.imgNode.width, h: this.imgNode.height + }); + } + } + } + }, + + _showImage: function(){ + // summary: Fade in the image, and fire showNav + this._showImageAnim.play(1); + }, + + _showNav: function(){ + // summary: Fade in the footer, and setup our connections. + var titleSizeNow = dojo.marginBox(this.titleNode); + if(titleSizeNow.h > this._lastTitleSize.h){ + this.resizeTo(this._wasStyled ? this._lastSize : this._currentSize, titleSizeNow); + }else{ + this._showNavAnim.play(1); + } + }, + + hide: function(){ + // summary: Hide the Master Lightbox + dojo.fadeOut({ + node: this.titleNode, + duration: 200, + // #5112 - if you _don't_ change the .src, safari will + // _never_ fire onload for this image + onEnd: dojo.hitch(this, function(){ + this.imgNode.src = this._blankGif; + }) + }).play(5); + + this.inherited(arguments); + + this.inGroup = null; + this._index = null; + }, + + addImage: function(child, group){ + // summary: Add an image to this Master Lightbox + // + // child: Object + // The image information to add. + // href: String - link to image (required) + // title: String - title to display + // + // group: String? + // attach to group of similar tag or null for individual image instance + var g = group; + if(!child.href){ return; } + if(g){ + if(!this._groups[g]){ + this._groups[g] = []; + } + this._groups[g].push(child); + }else{ this._groups["XnoGroupX"].push(child); } + }, + + removeImage: function(/* Widget */child){ + // summary: Remove an image instance from this LightboxDialog. + // child: Object + // A reference to the Lightbox child that was added (or an object literal) + // only the .href member is compared for uniqueness. The object may contain + // a .group member as well. + + var g = child.group || "XnoGroupX"; + dojo.every(this._groups[g], function(item, i, ar){ + if(item.href == child.href){ + ar.splice(i, 1); + return false; + } + return true; + }); + }, + + removeGroup: function(group){ + // summary: Remove all images in a passed group + if(this._groups[group]){ this._groups[group] = []; } + }, + + _handleKey: function(/* Event */e){ + // summary: Handle keyboard navigation internally + if(!this.open){ return; } + + var dk = dojo.keys; + switch(e.charOrCode){ + + case dk.ESCAPE: + this.hide(); + break; + + case dk.DOWN_ARROW: + case dk.RIGHT_ARROW: + case 78: // key "n" + this._nextImage(); + break; + + case dk.UP_ARROW: + case dk.LEFT_ARROW: + case 80: // key "p" + this._prevImage(); + break; + } + }, + + _makeAnims: function(){ + // summary: make and cleanup animation and animation connections + + dojo.forEach(this._animConnects, dojo.disconnect); + this._animConnects = []; + this._showImageAnim = dojo.fadeIn({ + node: this.imgNode, + duration: this.duration + }); + this._animConnects.push(dojo.connect(this._showImageAnim, "onEnd", this, "_showNav")); + this._loadingAnim = dojo.fx.combine([ + dojo.fadeOut({ node:this.imgNode, duration:175 }), + dojo.fadeOut({ node:this.titleNode, duration:175 }) + ]); + this._animConnects.push(dojo.connect(this._loadingAnim, "onEnd", this, "_prepNodes")); + this._showNavAnim = dojo.fadeIn({ node: this.titleNode, duration:225 }); + }, + + onClick: function(groupData){ + // summary: a stub function, called with the currently displayed image as the only argument + }, + + _onImageClick: function(e){ + if(e && e.target == this.imgNode){ + this.onClick(this._lastGroup); + // also fire the onclick for the Lightbox widget which triggered, if you + // aren't working directly with the LBDialog + if(this._lastGroup.declaredClass){ + this._lastGroup.onClick(this._lastGroup); + } + } + } + }); + + + return dojox.image.Lightbox; + +}); + diff --git a/js/dojo/dojox/image/LightboxNano.js b/js/dojo/dojox/image/LightboxNano.js new file mode 100644 index 0000000..d40d221 --- /dev/null +++ b/js/dojo/dojox/image/LightboxNano.js @@ -0,0 +1,310 @@ +//>>built +define("dojox/image/LightboxNano", ["dojo", "dojo/fx"], function(dojo, fx) { + + var abs = "absolute", + vis = "visibility", + getViewport = function(){ + // summary: Returns the dimensions and scroll position of the viewable area of a browser window + var scrollRoot = (dojo.doc.compatMode == "BackCompat") ? dojo.body() : dojo.doc.documentElement, + scroll = dojo._docScroll(); + return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y }; + } + ; + + return dojo.declare("dojox.image.LightboxNano", null, { + // summary: + // A simple "nano" version of the lightbox. + // + // description: + // Very lightweight lightbox which only displays a larger image. There is + // no support for a caption or description. The lightbox can be closed by + // clicking any where or pressing any key. This widget is intended to be + // used on <a> and <img> tags. Upon creation, if the domNode is <img> tag, + // then it is wrapped in an <a> tag, then a <div class="enlarge"> is placed + // inside the <a> and can be styled to display an icon that the original + // can be enlarged. + // + // example: + // | <a dojoType="dojox.image.LightboxNano" href="/path/to/largeimage.jpg"><img src="/path/to/thumbnail.jpg"></a> + // + // example: + // | <img dojoType="dojox.image.LightboxNano" src="/path/to/thumbnail.jpg" href="/path/to/largeimage.jpg"> + + // href: string + // URL to the large image to show in the lightbox. + href: "", + + // duration: int + // The delay in milliseconds of the LightboxNano open and close animation. + duration: 500, + + // preloadDelay: int + // The delay in milliseconds after the LightboxNano is created before preloading the larger image. + preloadDelay: 5000, + + constructor: function(/*Object?*/p, /*DomNode?*/n){ + // summary: Initializes the DOM node and connect onload event + var _this = this; + + dojo.mixin(_this, p); + n = _this._node = dojo.byId(n); + + // if we have a origin node, then prepare it to show the LightboxNano + if(n){ + if(!/a/i.test(n.tagName)){ + var a = dojo.create("a", { href: _this.href, "class": n.className }, n, "after"); + n.className = ""; + a.appendChild(n); + n = a; + } + + dojo.style(n, "position", "relative"); + _this._createDiv("dojoxEnlarge", n); + dojo.setSelectable(n, false); + _this._onClickEvt = dojo.connect(n, "onclick", _this, "_load"); + } + + if(_this.href){ + setTimeout(function(){ + (new Image()).src = _this.href; + _this._hideLoading(); + }, _this.preloadDelay); + } + }, + + destroy: function(){ + // summary: Destroys the LightboxNano and it's DOM node + var a = this._connects || []; + a.push(this._onClickEvt); + dojo.forEach(a, dojo.disconnect); + dojo.destroy(this._node); + }, + + _createDiv: function(/*String*/cssClass, /*DomNode*/refNode, /*boolean*/display){ + // summary: Creates a div for the enlarge icon and loading indicator layers + return dojo.create("div", { // DomNode + "class": cssClass, + style: { + position: abs, + display: display ? "" : "none" + } + }, refNode); + }, + + _load: function(/*Event*/e){ + // summary: Creates the large image and begins to show it + var _this = this; + + e && dojo.stopEvent(e); + + if(!_this._loading){ + _this._loading = true; + _this._reset(); + + var i = _this._img = dojo.create("img", { + style: { + visibility: "hidden", + cursor: "pointer", + position: abs, + top: 0, + left: 0, + zIndex: 9999999 + } + }, dojo.body()), + ln = _this._loadingNode, + n = dojo.query("img", _this._node)[0] || _this._node, + a = dojo.position(n, true), + c = dojo.contentBox(n), + b = dojo._getBorderExtents(n) + ; + + if(ln == null){ + _this._loadingNode = ln = _this._createDiv("dojoxLoading", _this._node, true); + var l = dojo.marginBox(ln); + dojo.style(ln, { + left: parseInt((c.w - l.w) / 2) + "px", + top: parseInt((c.h - l.h) / 2) + "px" + }); + } + + c.x = a.x - 10 + b.l; + c.y = a.y - 10 + b.t; + _this._start = c; + + _this._connects = [dojo.connect(i, "onload", _this, "_show")]; + + i.src = _this.href; + } + }, + + _hideLoading: function(){ + // summary: Hides the animated loading indicator + if(this._loadingNode){ + dojo.style(this._loadingNode, "display", "none"); + } + this._loadingNode = false; + }, + + _show: function(){ + // summary: The image is now loaded, calculate size and display + var _this = this, + vp = getViewport(), + w = _this._img.width, + h = _this._img.height, + vpw = parseInt((vp.w - 20) * 0.9), + vph = parseInt((vp.h - 20) * 0.9), + dd = dojo.doc, + bg = _this._bg = dojo.create("div", { + style: { + backgroundColor: "#000", + opacity: 0.0, + position: abs, + zIndex: 9999998 + } + }, dojo.body()), + ln = _this._loadingNode + ; + + if(_this._loadingNode){ + _this._hideLoading(); + } + dojo.style(_this._img, { + border: "10px solid #fff", + visibility: "visible" + }); + dojo.style(_this._node, vis, "hidden"); + + _this._loading = false; + + _this._connects = _this._connects.concat([ + dojo.connect(dd, "onmousedown", _this, "_hide"), + dojo.connect(dd, "onkeypress", _this, "_key"), + dojo.connect(window, "onresize", _this, "_sizeBg") + ]); + + if(w > vpw){ + h = h * vpw / w; + w = vpw; + } + if(h > vph){ + w = w * vph / h; + h = vph; + } + + _this._end = { + x: (vp.w - 20 - w) / 2 + vp.l, + y: (vp.h - 20 - h) / 2 + vp.t, + w: w, + h: h + }; + + _this._sizeBg(); + + dojo.fx.combine([ + _this._anim(_this._img, _this._coords(_this._start, _this._end)), + _this._anim(bg, { opacity: 0.5 }) + ]).play(); + }, + + _sizeBg: function(){ + // summary: Resize the background to fill the page + var dd = dojo.doc.documentElement; + dojo.style(this._bg, { + top: 0, + left: 0, + width: dd.scrollWidth + "px", + height: dd.scrollHeight + "px" + }); + }, + + _key: function(/*Event*/e){ + // summary: A key was pressed, so hide the lightbox + dojo.stopEvent(e); + this._hide(); + }, + + _coords: function(/*Object*/s, /*Object*/e){ + // summary: Returns animation parameters with the start and end coords + return { // Object + left: { start: s.x, end: e.x }, + top: { start: s.y, end: e.y }, + width: { start: s.w, end: e.w }, + height: { start: s.h, end: e.h } + }; + }, + + _hide: function(){ + // summary: Closes the lightbox + var _this = this; + dojo.forEach(_this._connects, dojo.disconnect); + _this._connects = []; + dojo.fx.combine([ + _this._anim(_this._img, _this._coords(_this._end, _this._start), "_reset"), + _this._anim(_this._bg, {opacity:0}) + ]).play(); + }, + + _reset: function(){ + // summary: Destroys the lightbox + dojo.style(this._node, vis, "visible"); + dojo.destroy(this._img); + dojo.destroy(this._bg); + this._img = this._bg = null; + this._node.focus(); + }, + + _anim: function(/*DomNode*/node, /*Object*/args, /*Function*/onEnd){ + // summary: Creates the lightbox open/close and background fadein/out animations + return dojo.animateProperty({ // dojo.Animation + node: node, + duration: this.duration, + properties: args, + onEnd: onEnd ? dojo.hitch(this, onEnd) : null + }); + }, + + show: function(/*Object?*/args){ + // summary: + // Shows this LightboxNano programatically. Allows passing a new href and + // a programatic origin. + // + // args: Object? + // An object with optional members of `href` and `origin`. + // `origin` can be be a String|Id of a DomNode to use when + // animating the openeing of the image (the 'box' effect starts + // from this origin point. eg: { origin: e.target }) + // If there's no origin, it will use the center of the viewport. + // The `href` member is a string URL for the image to be + // displayed. Omiting either of these members will revert to + // the default href (which could be absent in some cases) and + // the original srcNodeRef for the widget. + args = args || {}; + this.href = args.href || this.href; + + var n = dojo.byId(args.origin), + vp = getViewport(); + + // if we don't have a valid origin node, then create one as a reference + // that is centered in the viewport + this._node = n || dojo.create("div", { + style: { + position: abs, + width: 0, + hieght: 0, + left: (vp.l + (vp.w / 2)) + "px", + top: (vp.t + (vp.h / 2)) + "px" + } + }, dojo.body()) + ; + + this._load(); + + // if we don't have a valid origin node, then destroy the centered reference + // node since load() has already been called and it's not needed anymore. + if(!n){ + dojo.destroy(this._node); + } + } + }); + +}); diff --git a/js/dojo/dojox/image/Magnifier.js b/js/dojo/dojox/image/Magnifier.js new file mode 100644 index 0000000..06a0c8f --- /dev/null +++ b/js/dojo/dojox/image/Magnifier.js @@ -0,0 +1,71 @@ +//>>built +define("dojox/image/Magnifier", ["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/window", "dojox/gfx", "dojox/gfx/canvas", "./MagnifierLite"], function(declare, construct, window, gfx, canvas, MagnifierLite){ + + return declare("dojox.image.Magnifier", MagnifierLite, { + // summary: + // Adds magnification on a portion of an image element, using `dojox.gfx` + // + // description: + // An unobtrusive way to add an unstyled overlay + // above the srcNode image element. The overlay/glass is a + // scaled version of the src image (so larger images sized down + // are clearer). + // + // over-ride the _createGlass method to create your custom surface, + // being sure to create an img node on that surface. + + _createGlass: function(){ + // summary: create the glassNode, and an img on a dojox.gfx surface + + // images are hard to make into workable templates, so just add outer overlay + // and skip using dijit._Templated + this.glassNode = construct.create('div', { + style: { + height: this.glassSize + "px", + width: this.glassSize + "px" + }, + className: "glassNode" + }, window.body()); + this.surfaceNode = construct.create('div', null, this.glassNode); + + gfx.switchTo('canvas'); + this.surface = canvas.createSurface(this.surfaceNode, this.glassSize, this.glassSize); + this.img = this.surface.createImage({ + src: this.domNode.src, + width: this._zoomSize.w, + height: this._zoomSize.h + }); + + }, + + _placeGlass: function(e){ + // summary: position the overlay centered under the cursor + var x = e.pageX - 2, + y = e.pageY - 2, + xMax = this.offset.x + this.offset.w + 2, + yMax = this.offset.y + this.offset.h + 2 + ; + + // with svg, our mouseout connection to the image surface doesn't + // fire, so we'r have to manually calculate offsets + if(x < this.offset.x || y < this.offset.y || x > xMax || y > yMax){ + this._hideGlass(); + }else{ + this.inherited(arguments); + } + }, + + _setImage: function(e){ + // summary: set the image's offset in the clipping window relative to the mouse position + + var xOff = (e.pageX - this.offset.x) / this.offset.w, + yOff = (e.pageY - this.offset.y) / this.offset.h, + x = (this._zoomSize.w * xOff * -1)+(this.glassSize*xOff), + y = (this._zoomSize.h * yOff * -1)+(this.glassSize*yOff) + ; + // set the image offset + this.img.setShape({ x: x, y: y }); + + } + }); +});
\ No newline at end of file diff --git a/js/dojo/dojox/image/MagnifierLite.js b/js/dojo/dojox/image/MagnifierLite.js new file mode 100644 index 0000000..4650f79 --- /dev/null +++ b/js/dojo/dojox/image/MagnifierLite.js @@ -0,0 +1,126 @@ +//>>built +define("dojox/image/MagnifierLite", ["dojo/_base/kernel", "dojo/_base/declare", "dijit/_Widget", "dojo/dom-construct", "dojo/dom-style", "dojo/dom-geometry", "dojo/_base/window", "dojo/_base/lang"], function(kernel, declare, _Widget, construct, style, geometry, window, lang){ + + kernel.experimental("dojox.image.MagnifierLite"); + + return declare("dojox.image.MagnifierLite", _Widget, { + // summary: Adds magnification on a portion of an image element + // + // description: An unobtrusive way to add an unstyled overlay + // above the srcNode image element. The overlay/glass is a + // scaled version of the src image (so larger images sized down + // are clearer). + // + // The logic behind requiring the src image to be large is + // "it's going to be downloaded, anyway" so this method avoids + // having to make thumbnails and 2 http requests among other things. + // + // glassSize: Int + // the width and height of the bounding box + glassSize: 125, + + // scale: Decimal + // the multiplier of the Mangification. + scale: 6, + + postCreate: function(){ + this.inherited(arguments); + + // images are hard to make into workable templates, so just add outer overlay + // and skip using dijit._Templated + this._adjustScale(); + this._createGlass(); + + this.connect(this.domNode,"onmouseenter","_showGlass"); + this.connect(this.glassNode,"onmousemove","_placeGlass"); + this.connect(this.img,"onmouseout","_hideGlass"); + + // when position of domNode changes, _adjustScale needs to run. + // window.resize isn't it always, FIXME: + this.connect(window,"onresize","_adjustScale"); + }, + + _createGlass: function(){ + // summary: make img and glassNode elements as children of the body + + var node = this.glassNode = construct.create('div', { + style: { + height: this.glassSize + "px", + width: this.glassSize + "px" + }, + className: "glassNode" + }, window.body()); + + this.surfaceNode = node.appendChild(construct.create('div')); + + this.img = construct.place(lang.clone(this.domNode), node); + // float the image around inside the .glassNode + style.set(this.img, { + position: "relative", + top: 0, left: 0, + width: this._zoomSize.w + "px", + height: this._zoomSize.h + "px" + }); + }, + + _adjustScale: function(){ + // summary: update the calculations should this.scale change + + this.offset = geometry.position(this.domNode, true); + console.dir(this.offset); + this._imageSize = { w: this.offset.w, h:this.offset.h }; + this._zoomSize = { + w: this._imageSize.w * this.scale, + h: this._imageSize.h * this.scale + }; + }, + + _showGlass: function(e){ + // summary: show the overlay + this._placeGlass(e); + style.set(this.glassNode, { + visibility: "visible", + display:"" + }); + }, + + _hideGlass: function(e){ + // summary: hide the overlay + style.set(this.glassNode, { + visibility: "hidden", + display:"none" + }); + }, + + _placeGlass: function(e){ + // summary: position the overlay centered under the cursor + + this._setImage(e); + var sub = Math.floor(this.glassSize / 2); + style.set(this.glassNode,{ + top: Math.floor(e.pageY - sub) + "px", + left:Math.floor(e.pageX - sub) + "px" + }); + }, + + _setImage: function(e){ + // summary: set the image's offset in the clipping window relative to the mouse position + + var xOff = (e.pageX - this.offset.x) / this.offset.w, + yOff = (e.pageY - this.offset.y) / this.offset.h, + x = (this._zoomSize.w * xOff * -1) + (this.glassSize * xOff), + y = (this._zoomSize.h * yOff * -1) + (this.glassSize * yOff); + + style.set(this.img, { + top: y + "px", + left: x + "px" + }); + }, + + destroy: function(finalize){ + construct.destroy(this.glassNode); + this.inherited(arguments); + } + + }); +}); diff --git a/js/dojo/dojox/image/README b/js/dojo/dojox/image/README new file mode 100644 index 0000000..cbe32f3 --- /dev/null +++ b/js/dojo/dojox/image/README @@ -0,0 +1,88 @@ +------------------------------------------------------------------------------- +dojox.image - Image Related Widgets and Components +------------------------------------------------------------------------------- +Version 1.0 +Release date: 10/31/07 +------------------------------------------------------------------------------- +Project state: + +[Badge] experimental +[FlickrBadge] experimental +[Gallery] experimental +[Lightbox] beta +[Magnifier] experimental +[MagnifierLite] experimental +[SlideShow] experimental +[ThumbnailPicker] experimental + +------------------------------------------------------------------------------- +Credits + +[Badge] Peter Higgins (dante) +[FlickrBadge] Bryan Forbes (BryanForbes) +[Gallery] Shane O'Sullivan (sos) +[Lightbox] Peter Higgins (dante) +[Magnifier] Peter Higgins (dante) +[MagnifierLite] Peter Higgins (dante) +[SlideShow] Shane O'Sullivan (sos) +[ThumbnailPicker] Shane O'Sullivan (sos) + +------------------------------------------------------------------------------- +Project description + + A class to provide a common API for images, and home for image + related Widgets. + +------------------------------------------------------------------------------- +Dependencies: + + LightBox: dojo core, dojox.fx and optionally dojox.data. uses + either tundra or soria theme, no standalone icons. + + SlideShow: dojo core, dojo.fx, and dojo.data (optional + dojox.data store implementations apply) + + ThumbNailPicker: dojo core, dojo.fx and dojo.data. Combined + with a SlideShow, creates a sample Gallery app. + + Gallery: core, dojox.image.SlideShow, dojox.image.ThumbNailPicker + + Magnifier: (prototype) dojo core, dijit._Widget, dojox.gfx + + Badge: (prototype) dojo core, dijit._Widget + _Templated + +------------------------------------------------------------------------------- +Documentation + +------------------------------------------------------------------------------- +Installation instructions + +Grab the following from the Dojo SVN Repository: +http://svn.dojotoolkit.org/dojo/dojox/trunk/image/* + +Install into the following directory structure: +/dojox/image/ + +...which should be at the same level as your Dojo checkout. +------------------------------------------------------------------------------- +Additional Notes + + LightBox: currently works as individual items, and grouped items, + but usage of dojo.data is broken (atm). the API is subject to + change, and is marked accordingly. + + Hoping to implement: Carossel, and Reflect using + a common API provided by dojox.image.Pane (?) + + SlideShow: Shows an image, one by one, from a datastore. Acts + as standing ImagePane implementation, + + Gallery: A combination Thumbnail view and SlideShow, using + a datastore, and providing navigation, and common API. + + Magnifier: Unobtrusive way to attach a hovering window window + when moving the mouse over an image. The window shows a a zoomed + version of the original source. (prototype) + + Badge: (prototype) A Javascript version of a FlickrBadge thingr, + which loops through and sizes children in a loop. 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 + } +}); + +}); diff --git a/js/dojo/dojox/image/ThumbnailPicker.js b/js/dojo/dojox/image/ThumbnailPicker.js new file mode 100644 index 0000000..e0fd3cf --- /dev/null +++ b/js/dojo/dojox/image/ThumbnailPicker.js @@ -0,0 +1,589 @@ +//>>built +// wrapped by build app +define("dojox/image/ThumbnailPicker", ["dijit","dojo","dojox","dojo/require!dojox/fx/scroll,dojo/fx/easing,dojo/fx,dijit/_Widget,dijit/_Templated"], function(dijit,dojo,dojox){ +dojo.provide("dojox.image.ThumbnailPicker"); +dojo.experimental("dojox.image.ThumbnailPicker"); +// +// dojox.image.ThumbnailPicker courtesy Shane O Sullivan, licensed under a Dojo CLA +// +// For a sample usage, see http://www.skynet.ie/~sos/photos.php +// +// document topics. + +dojo.require("dojox.fx.scroll"); // is optional, but don't want to dojo[require] it +dojo.require("dojo.fx.easing"); + +dojo.require("dojo.fx"); +dojo.require("dijit._Widget"); +dojo.require("dijit._Templated"); + +// FIXME: use CSS for size, thumbHeight, and thumbWidth + +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 image + thumbHeight: 75, + + // thumbWidth: Number + // Default width of an image + thumbWidth: 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 pages + isScrollable: true, + + // isHorizontal: Boolean + // If true, the thumbnails are displayed horizontally. Otherwise they are displayed + // vertically + isHorizontal: true, + + //autoLoad: Boolean + autoLoad: true, + + // linkAttr: String + // The attribute name for accessing the url from the data store + linkAttr: "link", + + // imageThumbAttr: String + // The attribute name for accessing the thumbnail image url from the data store + imageThumbAttr: "imageUrlThumb", + + // imageLargeAttr: String + // The attribute name for accessing the large image url from the data store + imageLargeAttr: "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 store + titleAttr: "title", + + templateString: dojo.cache("dojox.image", "resources/ThumbnailPicker.html", "<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>"), + + // 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 listeners + this.widgetid = this.id; + this.inherited(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 widget + dojo.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 link + if(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 nothing + if(!url){return;} + + if(this.hyperlinkTarget == "new"){ + window.open(url); + }else{ + window.location = url; + } + }); + } + + 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 listeners + if(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); + + dojo.attr(this.navNextImg, "src", this._blankGif); + dojo.attr(this.navPrevImg, "src", this._blankGif); + + this.connect(this.navPrev, "onclick", "_prev"); + this.connect(this.navNext, "onclick", "_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.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 visible + return (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 || 0, + 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){ + dojo.forEach(["imageThumbAttr", "imageLargeAttr", "linkAttr", "titleAttr"], function(attrName){ + if(paramNames[attrName]){ this[attrName] = paramNames[attrName]; } + }, this); + } + + this.request.start = 0; + this.request.count = this.pageSize; + this.imageStore = dataStore; + this._loadInProgress = false; + if(!this.init()){this._loadNextPage();} + }, + + reset: function(){ + // summary: + // Resets the widget back to its original state. + this._loadedImages = {}; + dojo.forEach(this._thumbs, function(img){ + if(img && img.parentNode){ + dojo.destroy(img); + } + }); + + this._thumbs = []; + this.isInitialized = false; + this._noImages = true; + }, + + isVisible: function(index) { + // summary: + // Returns true if the image at the specified index is currently visible. False otherwise. + var img = this._thumbs[index]; + 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); + }, + + resize: function(dim){ + var sizeParam = this.isHorizontal ? "w": "h"; + + var total = 0; + + if(this._thumbs.length > 0 && dojo.marginBox(this._thumbs[0]).w == 0){ + // Skip the resize if the widget is not visible + return; + } + + // Calculate the complete size of the thumbnails + dojo.forEach(this._thumbs, dojo.hitch(this, function(imgContainer){ + var mb = dojo.marginBox(imgContainer.firstChild); + var size = mb[sizeParam]; + total += (Number(size) + 10); + + if(this.useLoadNotifier && mb.w > 0){ + dojo.style(imgContainer.lastChild, "width", (mb.w - 4) + "px"); + } + dojo.style(imgContainer, "width", mb.w + "px"); + })); + + dojo.style(this.thumbsNode, this._sizeProperty, total + "px"); + this._updateNavControls(); + }, + + _next: function() { + // summary: + // Displays the next page of images + var 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 index = -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 images + if(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 index = -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, index){ + // summary: + // Checks if an image is loaded. + dojo.publish(this.getShowTopicName(), [{index:index}]); + this._updateNavControls(); + this._loadingImages = {}; + + this._thumbIndex = index; + + //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(index){ + // summary: + // Displays thumbnail images, starting at position 'index' + // index: Number + // The index of the first thumbnail + +//FIXME: When is this be called with an invalid index? Do we need this check at all? +// if(typeof index != "number"){ index = this._thumbIndex; } + index = Math.min(Math.max(index, 0), this._maxPhotos); + + if(index >= this._maxPhotos){ return; } + + var img = this._thumbs[index]; + 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:dojo.fx.easing.easeOut, + onEnd: dojo.hitch(this, "_checkLoad", img, index) + }).play(10); + }else{ + if(this.isHorizontal){ + this.thumbScroller.scrollLeft = left; + }else{ + this.thumbScroller.scrollTop = top; + } + this._checkLoad(img, index); + } + }, + + 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 images + if(this._loadInProgress){return;} + this._loadInProgress = true; + var start = this.request.start + (this._noImages ? 0 : this.pageSize); + + var pos = start; + while(pos < this._thumbs.length && this._thumbs[pos]){pos ++;} + + var store = this.imageStore; + + //Define the function to call when the items have been + //returned from the data store. + var complete = function(items, request){ + if(store != this.imageStore){ + // If the store has been changed, ignore this callback. + return; + } + if(items && items.length){ + var itemCounter = 0; + var loadNext = dojo.hitch(this, 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.log("Error getting items"); + }; + + this.request.onComplete = dojo.hitch(this, complete); + this.request.onError = dojo.hitch(this, 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){ + // summary: + // Loads an image. + + var store = this.imageStore; + var url = store.getValue(data,this.imageThumbAttr); + + var imgContainer = dojo.create("div", { + id: "img_" + this.widgetid + "_" + index + }); + var img = dojo.create("img", {}, imgContainer); + img._index = index; + img._data = data; + + this._thumbs[index] = imgContainer; + var loadingDiv; + if(this.useLoadNotifier){ + loadingDiv = dojo.create("div", { + id: "loadingDiv_" + this.widgetid+"_" + index + }, imgContainer); + + //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"); + } + 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 them + this.thumbScroller.scrollLeft = sl; + this.thumbScroller.scrollTop = st; + this.thumbsNode.appendChild(imgContainer); + + dojo.connect(img, "onload", this, dojo.hitch(this, function(){ + if(store != this.imageStore){ + // If the store has changed, ignore this load event + return false; + } + this.resize(); + + // Have to use a timeout here to prevent a call stack that gets + // so deep that IE throws stack overflow errors + setTimeout(callback, 0); + 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); + } +}); + +}); diff --git a/js/dojo/dojox/image/_base.js b/js/dojo/dojox/image/_base.js new file mode 100644 index 0000000..28d93c0 --- /dev/null +++ b/js/dojo/dojox/image/_base.js @@ -0,0 +1,116 @@ +//>>built +define("dojox/image/_base", ["dojo", "dojox"], function(dojo, dojox){ + + dojo.getObject("image", true, dojox); + var d = dojo; + + var cacheNode; + dojox.image.preload = function(/* Array */urls){ + // summary: Preload a list of images in the dom. + // + // urls: Array + // The list of urls to load. Can be any valid .src attribute. + // + // example: + // Load two images into cache: + // | dojox.image.preload(["foo.png", "bar.gif"]); + // + // example: + // Using djConfig: + // | var djConfig = { + // | preloadImages:["bar.png", "baz.png", "http://example.com/icon.gif"] + // | }; + // + // returns: Array + // An Array of DomNodes that have been cached. + + if(!cacheNode){ + cacheNode = d.create("div", { + style:{ position:"absolute", top:"-9999px", height:"1px", overflow:"hidden" } + }, d.body()); + } + + // place them in the hidden cachenode + return d.map(urls, function(url){ + return d.create("img", { src: url }, cacheNode); + }); + + }; + + /*===== + dojo.mixin(djConfig, { + // preloadImages: Array? + // An optional array of urls to preload immediately upon + // page load. Uses `dojox.image`, and is unused if not present. + preloadImages: [] + }); + =====*/ + + if(d.config.preloadImages){ + d.addOnLoad(function(){ + dojox.image.preload(d.config.preloadImages); + }); + } + +// dojo.declare("dojox.image.Image", dijit._Widget, { +// // summary: an Image widget +// // +// // example: +// // | new dojox.Image({ src:"foo.png", id:"bar" }); +// +// alt: "", +// src: dojo._blankGif, +// title: "", +// +// onLoad: function(e){ +// // summary: Stub fired when this image is really ready. +// }, +// +// _onLoad: function(e){ +// // summary: private function to normalize `onLoad` for this +// // instance. +// this.onLoad(e); +// }, +// +// _setSrcAttr: function(newSrc){ +// // summary: Function so widget.attr('src', someUrl) works +// +// var ts = this.domNode, os = td.src; +// if(os !== newSrc){ +// td.src = newSrc; +// } +// }, +// +// /* Sugar Functions: */ +// +// crossFade: function(newSrc){ +// // summary: Set this Image to a new src with crossfading +// // +// // example: +// // dijit.byId("bar").crossFade("/images/newImage.png"); +// // +// +// d.fadeOut({ +// node: this.domNode, +// onEnd: d.hitch(this, function(){ +// this.attr('src', newSrc); +// d.fadeIn({ +// node: this.domNode, +// delay: 75 +// }).play(); +// }) +// }).play(); +// }, +// +// /* Overrides */ +// +// buildRendering: function(){ +// // override buildrendering to create a real "img" instead of a div +// // when no srcNodeRef is passed. also wire up single onload. +// this.domNode = this.srcNodeRef || d.create('img'); +// this.connect(this.domNode, "onload", "_onload"); +// } +// +// }); + +});
\ No newline at end of file diff --git a/js/dojo/dojox/image/resources/Badge.css b/js/dojo/dojox/image/resources/Badge.css new file mode 100644 index 0000000..482ffa2 --- /dev/null +++ b/js/dojo/dojox/image/resources/Badge.css @@ -0,0 +1,37 @@ +/* dojox.image.Badge definitions */ + +.dojoxBadge { + position:relative; +} + +.dojoxBadge .dojoxBadgeImage { + position:absolute; + top:0; left:0; + margin:0; + padding:0; +} + +.dojoxBadge { + margin:0; padding:0; + border:1px solid #eee; +/* background:#fff; */ +} + +.dojoxBadge .dojoxBadgeImage { + overflow:hidden; + margin-left:1px; + margin-top:1px; + background:#ededed; + z-index:90; +} + +.dojoxBadge .dojoxBadgeSeen { + background-color:#dedede; +} +.dojoxBadge .dojoxBadgeOver { + background-color:green !important; +} + +.dojoxBadge .dojoxBadgeTop { + z-index:99; +} diff --git a/js/dojo/dojox/image/resources/Gallery.css b/js/dojo/dojox/image/resources/Gallery.css new file mode 100644 index 0000000..8e4fd90 --- /dev/null +++ b/js/dojo/dojox/image/resources/Gallery.css @@ -0,0 +1,6 @@ +/* dojox.image.Gallery - too small to be it's own file? */ + +.imageGalleryWrapper { + padding-bottom: 20px; + text-align: center; +} diff --git a/js/dojo/dojox/image/resources/Gallery.html b/js/dojo/dojox/image/resources/Gallery.html new file mode 100644 index 0000000..571dc4f --- /dev/null +++ b/js/dojo/dojox/image/resources/Gallery.html @@ -0,0 +1,4 @@ +<div dojoAttachPoint="outerNode" class="imageGalleryWrapper"> + <div dojoAttachPoint="thumbPickerNode"></div> + <div dojoAttachPoint="slideShowNode"></div> +</div>
\ No newline at end of file diff --git a/js/dojo/dojox/image/resources/Lightbox.css b/js/dojo/dojox/image/resources/Lightbox.css new file mode 100644 index 0000000..22faf48 --- /dev/null +++ b/js/dojo/dojox/image/resources/Lightbox.css @@ -0,0 +1,128 @@ + +/* dojox.image.Lightbox:base */ +/* FIXME: should be be doing this? I want a black underlay, but this sets ALL dialogs to black, + but because it's decendant of body, i can't set this color any other way ... */ +.tundra .dijitDialogUnderlay, +.nihilo .dijitDialogUnderlay, +.soria .dijitDialogUnderlay { + background-color:#000; +} + +.claro .dojoxLightbox .dijitDialogCloseIconHover, +.nihilo .dojoxLightbox .dijitDialogCloseIconHover, +.tundra .dojoxLightbox .dijitDialogCloseIconHover, +.tundra .dojoxLightbox .dijitDialogCloseIconActive, +.nihilo .dojoxLightbox .dijitDialogCloseIconActive, +.claro .dojoxLightbox .dijitDialogCloseIconActive { + background:url('images/close.png') no-repeat 0 0; +} + +/* more specific to override .theme .dijitDialog name. might try baseClass="dojoxLightbox" + but that would require additional overriden CSS on top of the original Dialog.css */ +.claro .dojoxLightbox, +.soria .dojoxLightbox, +.nihilo .dojoxLightbox, +.tundra .dojoxLightbox { + position:absolute; + z-index:999; + overflow:hidden; + width:100px; + height:100px; + border:11px solid #fff !important; /* not happy, but all themes overwrite this to 1px */ + background:#fff url('images/loading.gif') no-repeat center center; + + /* special safari + FF specific rounding + shadows */ + -webkit-box-shadow: 0px 6px 10px #636363; /* #adadad; */ + -webkit-border-radius: 3px; + -moz-border-radius:4px; + border-radius: 4px; +} + +.dojoxLightboxContainer { + position:absolute; + top:0; left:0; + background-color:#fff; +} + +.dojoxLightboxFooter { + padding-bottom:5px; + position:relative; + bottom:0; + left:0; + margin-top:8px; + color:#333; + z-index:1000; + font-size:10pt; +} + +.dojoxLightboxGroupText { + color:#666; + font-size:8pt; +} + +.LightboxNext, +.LightboxPrev, +.LightboxClose { + float:right; + width:16px; + height:16px; + cursor:pointer; +} + +/* dojox.image.Lightbox:tundra:nihilo */ + +.claro .LightboxClose, +.nihilo .LightboxClose, +.tundra .LightboxClose { + background:url('images/close.png') no-repeat center center; +} + +.di_ie6 .claro .LightboxClose, +.di_ie6 .nihilo .LightboxClose, +.dj_ie6 .tundra .LightboxClose { + background:url('images/close.gif') no-repeat center center; +} + +.claro .LightboxNext, +.nihilo .LightboxNext, +.tundra .LightboxNext { + background:url('images/right.png') no-repeat center center; +} + +.dj_ie6 .claro .LightboxNext, +.dj_ie6 .nihilo .LightboxNext, +.dj_ie6 .tundra .LightboxNext { + background:url('images/right.gif') no-repeat center center; +} + +.claro .LightboxPrev, +.nihilo .LightboxPrev, +.tundra .LightboxPrev { + background:url('images/left.png') no-repeat center center; +} + +.dj_ie6 .claro .LightboxPrev, +.dj_ie6 .nihilo .LightboxPrev, +.dj_ie6 .tundra .LightboxPrev { + background:url('images/left.gif') no-repeat center center; +} + +/* dojox.image.Lightbox:soria */ +.soria .LightboxClose, +.soria .LightboxNext, +.soria .LightboxPrev { + width:15px; + height:15px; + background:url('../../../dijit/themes/soria/images/spriteRoundedIconsSmall.png') no-repeat center center; + background-position:-60px; +} +.soria .LightboxNext { + background-position:-30px 0; +} +.soria .LightboxPrev { + background-position:0 0; +} + +.dojoxLightboxText { + margin:0; padding:0; +} diff --git a/js/dojo/dojox/image/resources/Lightbox.html b/js/dojo/dojox/image/resources/Lightbox.html new file mode 100644 index 0000000..aca14ee --- /dev/null +++ b/js/dojo/dojox/image/resources/Lightbox.html @@ -0,0 +1,13 @@ +<div class="dojoxLightbox" dojoAttachPoint="containerNode"> + <div style="position:relative"> + <div dojoAttachPoint="imageContainer" class="dojoxLightboxContainer" dojoAttachEvent="onclick: _onImageClick"> + <img dojoAttachPoint="imgNode" src="${imgUrl}" class="dojoxLightboxImage" alt="${title}"> + <div class="dojoxLightboxFooter" dojoAttachPoint="titleNode"> + <div class="dijitInline LightboxClose" dojoAttachPoint="closeButtonNode"></div> + <div class="dijitInline LightboxNext" dojoAttachPoint="nextButtonNode"></div> + <div class="dijitInline LightboxPrev" dojoAttachPoint="prevButtonNode"></div> + <div class="dojoxLightboxText" dojoAttachPoint="titleTextNode"><span dojoAttachPoint="textNode">${title}</span><span dojoAttachPoint="groupCount" class="dojoxLightboxGroupText"></span></div> + </div> + </div> + </div> +</div>
\ No newline at end of file diff --git a/js/dojo/dojox/image/resources/Magnifier.css b/js/dojo/dojox/image/resources/Magnifier.css new file mode 100644 index 0000000..85eba72 --- /dev/null +++ b/js/dojo/dojox/image/resources/Magnifier.css @@ -0,0 +1,5 @@ +.glassNode { + overflow:hidden; + position:absolute; + visibility:hidden; +} diff --git a/js/dojo/dojox/image/resources/SlideShow.css b/js/dojo/dojox/image/resources/SlideShow.css new file mode 100644 index 0000000..d1d76e7 --- /dev/null +++ b/js/dojo/dojox/image/resources/SlideShow.css @@ -0,0 +1,111 @@ +/* dojox.image.SlideShow */ + +.slideShowWrapper { + position:relative; + background:#fff; + padding:8px; + border:1px solid #333; + padding-bottom:20px; + overflow:hidden; + -moz-border-radius:3pt; + -webkit-border-radius:4pt; + -webkit-drop-shadow:#ccc 4pt; +} +.slideShowNav { + position:absolute; + bottom:-18px; + left:0px; + padding:0px 3px 2px 0px; + background-color:#fff; + width:100%; +} +.slideShowNavWrapper { float:right; } +.slideShowTitle { + float:left; + color:#333; + font-size:10pt; +} +.slideShowTitle .slideShowCounterText { + font-size:6pt; color:#666; +} +.slideShowHidden { + position:absolute; + display: none; + height: 1px; + width: 1px; +} +.slideShowImageWrapper { + position:relative; + text-align: center; + margin-top: -42px; + float: left; + width: 100%; +} +.slideShowImageWrapper img { + border: 0px none; +} +.slideShowNotifier { + background-color: red; + width: 100px; + height: 5px; + font-size: 1%;/*IE hack to get around the Empty-Div bug*/ +} +.slideShowSlideShow { + position:absolute; + top:30px; + padding: 0 5px; + border: 0px; + text-decoration: none; + color: #2e6ab1; +} +.slideShowLoading { background-color: #fad66a; } +.slideShowLoaded { background-color: transparent; } +/* +.sprite-arrowbottom { background-position: 0 -30px; } +.sprite-arrowtop { background-position: 0 -430px; } +*/ +.slideShowCtrlPrev { + background-position: -96px 0px; + float: left; +} +.slideShowCtrlNext { + background-position: -144px 0px; + float: right; +} +.slideShowCtrlPlay { + background-position: -190px 0px; + position: absolute; +} +.slideShowPaused .slideShowCtrlPlay { + background-position: -236px 0px; + position: absolute; +} +.slideShowCtrl span.slideShowCtrlHide { + background-image: url("../../../dojo/resources/blank.gif"); + cursor: auto; +} + +.slideShowCtrl { + height: 50px; + width: 100%; + position: relative; + z-index:999; + float: left; +} +.slideShowCtrl span { + width: 50px; + height: 100%; + background-image: url("images/buttons.png"); + cursor: pointer; +} +.dj_ie .slideShowCtrl span { + background-image: url("images/buttons.gif"); +} + +.dj_ie6 .slideShowPager li.currentpage, +.dj_ie6 .pagination li.disablepage{ + /*IE 6 and below. Adjust non linked LIs slightly to account for bugs*/ + margin-right: 5px; + padding-right: 0; +} + diff --git a/js/dojo/dojox/image/resources/SlideShow.html b/js/dojo/dojox/image/resources/SlideShow.html new file mode 100644 index 0000000..fa4aca6 --- /dev/null +++ b/js/dojo/dojox/image/resources/SlideShow.html @@ -0,0 +1,14 @@ +<div dojoAttachPoint="outerNode" class="slideShowWrapper"> + <div style="position:relative;" dojoAttachPoint="innerWrapper"> + <div class="slideShowNav" dojoAttachEvent="onclick: _handleClick"> + <div class="dijitInline slideShowTitle" dojoAttachPoint="titleNode">${title}</div> + </div> + <div dojoAttachPoint="navNode" class="slideShowCtrl" dojoAttachEvent="onclick: _handleClick"> + <span dojoAttachPoint="navPrev" class="slideShowCtrlPrev"></span> + <span dojoAttachPoint="navPlay" class="slideShowCtrlPlay"></span> + <span dojoAttachPoint="navNext" class="slideShowCtrlNext"></span> + </div> + <div dojoAttachPoint="largeNode" class="slideShowImageWrapper"></div> + <div dojoAttachPoint="hiddenNode" class="slideShowHidden"></div> + </div> +</div>
\ No newline at end of file diff --git a/js/dojo/dojox/image/resources/ThumbnailPicker.css b/js/dojo/dojox/image/resources/ThumbnailPicker.css new file mode 100644 index 0000000..54ace0b --- /dev/null +++ b/js/dojo/dojox/image/resources/ThumbnailPicker.css @@ -0,0 +1,129 @@ +/* dojox.image.ThumbnailPicker */ + +.thumbWrapper .thumbNav { + background-repeat: no-repeat; + background-position: center; + padding-top: 1px; + width: 30px; + height: 100%; +} + +.thumbOuter { + padding-bottom: 2px; +} + +.thumbOuter.thumbHoriz { + width: 500px; + height: 85px; +} + +.thumbOuter.thumbVert { + width: 100px; + height: 400px; +} + +.thumbOuter .enabled { + background: transparent url("images/buttons.png") no-repeat center center; +} +.dj_ie6 .thumbOuter .enabled { background-image: url("images/buttons.gif"); } + +.thumbOuter .thumbNav img { + width: 48px; + height: 75px; +} +.thumbOuter .thumbClickable div { + cursor: pointer; +} +.thumbOuter .prevHoriz { + background-position: -96px 12px; + position: relative; + float: left; + height: 100%; +} + +.thumbOuter .nextHoriz { + background-position: -144px 12px; + position: relative; + float: right; + height: 100%; +/* margin-top: -85px;*/ +} +.thumbOuter .prevVert { + background-position: 0px 0px; + height: 48px; + width:48px; + margin-left:24px; +} + +.thumbOuter .nextVert { + background-position: -48px 0px; + height: 48px; + width:48px; + margin-left:24px; +} + +.thumbWrapper img { + height: 75px; + max-width: 100px; + width: expression(this.width > 100 ? 100: true);/*IE Hack*/ +} + +.thumbWrapper .thumbNav .imageGalleryThumb { + height: 50px; +} + +.thumbWrapper .thumbNotifier { + background-color: red; + width: 0px; + margin-left: 2px; + height: 5px; + font-size: 1%;/*IE hack to get around the Empty-Div bug*/ +} + +.thumbWrapper .thumbLoaded { + background-color: transparent; +} + +.thumbScroller { + overflow-x: hidden; + overflow-y: hidden; + text-align: center; +} + +.thumbHoriz .thumbScroller { + width: 500px; + height: 85px; + float: left; +} + +.thumbVert .thumbScroller { + height: 500px; + width: 100px; +} + +.thumbWrapper { + float: left; +} + +.thumbVert .thumbWrapper { + width: 100px; + height: 10px; +} +.thumbHoriz .thumbWapper { + height:85px; + width: 10px; +} + +.thumbWrapper.thumbHoriz div { + float: left; + padding-right: 2px; +} + +.thumbWrapper.thumbVert { + width: 100px; +} + +.thumbWrapper.thumbVert div { + padding-bottom: 2px; +} + diff --git a/js/dojo/dojox/image/resources/ThumbnailPicker.html b/js/dojo/dojox/image/resources/ThumbnailPicker.html new file mode 100644 index 0000000..561ce2d --- /dev/null +++ b/js/dojo/dojox/image/resources/ThumbnailPicker.html @@ -0,0 +1,11 @@ +<div dojoAttachPoint="outerNode" class="thumbOuter"> + <div dojoAttachPoint="navPrev" class="thumbNav thumbClickable"> + <img src="" dojoAttachPoint="navPrevImg"/> + </div> + <div dojoAttachPoint="thumbScroller" class="thumbScroller"> + <div dojoAttachPoint="thumbsNode" class="thumbWrapper"></div> + </div> + <div dojoAttachPoint="navNext" class="thumbNav thumbClickable"> + <img src="" dojoAttachPoint="navNextImg"/> + </div> +</div>
\ No newline at end of file diff --git a/js/dojo/dojox/image/resources/image.css b/js/dojo/dojox/image/resources/image.css new file mode 100644 index 0000000..c4ea0a2 --- /dev/null +++ b/js/dojo/dojox/image/resources/image.css @@ -0,0 +1,16 @@ +/* + This is the master CSS file for the dojox.image project, and provides all + needed definitions for the DojoX Image Project + + Before build, the files are individual. You can use image.css as a cacheable + single-file rollup, or link only the individual css you need (based on componenet + name) + +*/ +@import "Lightbox.css"; +@import "SlideShow.css"; +@import "ThumbnailPicker.css"; +@import "Gallery.css"; +@import "Badge.css"; +@import "Magnifier.css"; + diff --git a/js/dojo/dojox/image/resources/images/buttons.gif b/js/dojo/dojox/image/resources/images/buttons.gif Binary files differnew file mode 100644 index 0000000..5f9081f --- /dev/null +++ b/js/dojo/dojox/image/resources/images/buttons.gif diff --git a/js/dojo/dojox/image/resources/images/buttons.png b/js/dojo/dojox/image/resources/images/buttons.png Binary files differnew file mode 100644 index 0000000..306e2f8 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/buttons.png diff --git a/js/dojo/dojox/image/resources/images/close.gif b/js/dojo/dojox/image/resources/images/close.gif Binary files differnew file mode 100644 index 0000000..ca97265 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/close.gif diff --git a/js/dojo/dojox/image/resources/images/close.png b/js/dojo/dojox/image/resources/images/close.png Binary files differnew file mode 100644 index 0000000..1ac9d10 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/close.png diff --git a/js/dojo/dojox/image/resources/images/close_dark.png b/js/dojo/dojox/image/resources/images/close_dark.png Binary files differnew file mode 100644 index 0000000..105fe55 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/close_dark.png diff --git a/js/dojo/dojox/image/resources/images/left.gif b/js/dojo/dojox/image/resources/images/left.gif Binary files differnew file mode 100644 index 0000000..8db89c4 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/left.gif diff --git a/js/dojo/dojox/image/resources/images/left.png b/js/dojo/dojox/image/resources/images/left.png Binary files differnew file mode 100644 index 0000000..0848dba --- /dev/null +++ b/js/dojo/dojox/image/resources/images/left.png diff --git a/js/dojo/dojox/image/resources/images/loading.gif b/js/dojo/dojox/image/resources/images/loading.gif Binary files differnew file mode 100644 index 0000000..e4ab783 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/loading.gif diff --git a/js/dojo/dojox/image/resources/images/right.gif b/js/dojo/dojox/image/resources/images/right.gif Binary files differnew file mode 100644 index 0000000..adfc443 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/right.gif diff --git a/js/dojo/dojox/image/resources/images/right.png b/js/dojo/dojox/image/resources/images/right.png Binary files differnew file mode 100644 index 0000000..7cab686 --- /dev/null +++ b/js/dojo/dojox/image/resources/images/right.png diff --git a/js/dojo/dojox/image/resources/images/warning.png b/js/dojo/dojox/image/resources/images/warning.png Binary files differnew file mode 100644 index 0000000..a52a55f --- /dev/null +++ b/js/dojo/dojox/image/resources/images/warning.png |
