summaryrefslogtreecommitdiff
path: root/js/dojo/dojox/image/Lightbox.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/dojo/dojox/image/Lightbox.js')
-rw-r--r--js/dojo/dojox/image/Lightbox.js608
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;
+
+});
+