diff options
Diffstat (limited to 'js/dojo/dojox/image/Lightbox.js')
| -rw-r--r-- | js/dojo/dojox/image/Lightbox.js | 608 |
1 files changed, 608 insertions, 0 deletions
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; + +}); + |
