diff options
Diffstat (limited to 'js/dojo/dojox/widget/Standby.js')
| -rw-r--r-- | js/dojo/dojox/widget/Standby.js | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/js/dojo/dojox/widget/Standby.js b/js/dojo/dojox/widget/Standby.js new file mode 100644 index 0000000..a21bec6 --- /dev/null +++ b/js/dojo/dojox/widget/Standby.js @@ -0,0 +1,777 @@ +//>>built +define("dojox/widget/Standby", ["dojo/_base/kernel", + "dojo/_base/declare", + "dojo/_base/array", + "dojo/_base/event", + "dojo/_base/sniff", + "dojo/dom", + "dojo/dom-attr", + "dojo/dom-construct", + "dojo/dom-geometry", + "dojo/dom-style", + "dojo/window", + "dojo/_base/window", + "dojo/_base/fx", + "dojo/fx", + "dijit/_Widget", + "dijit/_TemplatedMixin", + "dijit/registry"], + +function(kernel, + declare, + array, + event, + has, + dom, + attr, + construct, + geometry, + domStyle, + window, + baseWindow, + baseFx, + fx, + _Widget, + _TemplatedMixin, + registry) { + +kernel.experimental("dojox.widget.Standby"); + +return declare("dojox.widget.Standby", [_Widget, _TemplatedMixin],{ + // summary: + // A widget designed to act as a Standby/Busy/Disable/Blocking widget to indicate a + // particular DOM node is processing and cannot be clicked on at this time. + // This widget uses absolute positioning to apply the overlay and image. + // + // image: + // A URL to an image to center within the blocking overlay. + // The default is a basic spinner. + // + // imageText: + // Text to set on the ALT tag of the image. + // The default is 'Please wait...' + // + // text: + // Text to display in the center instead of an image. + // Defaults to 'Please Wait...' + // + // centerIndicator: + // Which to use as the center info, the text or the image. + // Defaults to image. + // + // color: + // The color to use for the translucent overlay. + // Text string such as: darkblue, #FE02FD, etc. + // + // duration: + // How long the fade in and out effects should run in milliseconds. + // Default is 500ms + // + // zIndex: + // Control that lets you specify if the zIndex for the overlay + // should be auto-computed based off parent zIndex, or should be set + // to a particular value. This is useful when you want to overlay + // things in digit.Dialogs, you can specify a base zIndex to append from. + // Default is 'auto'. + + // templateString: [protected] String + // The template string defining out the basics of the widget. No need for an external + // file. + templateString: + "<div>" + + "<div style=\"display: none; opacity: 0; z-index: 9999; " + + "position: absolute; cursor:wait;\" dojoAttachPoint=\"_underlayNode\"></div>" + + "<img src=\"${image}\" style=\"opacity: 0; display: none; z-index: -10000; " + + "position: absolute; top: 0px; left: 0px; cursor:wait;\" "+ + "dojoAttachPoint=\"_imageNode\">" + + "<div style=\"opacity: 0; display: none; z-index: -10000; position: absolute; " + + "top: 0px;\" dojoAttachPoint=\"_textNode\"></div>" + + "</div>", + + // _underlayNode: [private] DOMNode + // The node that is the translucent underlay for the + // image that blocks access to the target. + _underlayNode: null, + + // _imageNode: [private] DOMNode + // The image node where we attach and define the image to display. + _imageNode: null, + + // _textNode: [private] DOMNode + // The div to attach text/HTML in the overlay center item. + _textNode: null, + + // _centerNode: [private] DOMNode + // Which node to use as the center node, the image or the text node. + _centerNode: null, + + // image: String + // The URL to the image to center in the overlay. + image: require.toUrl("dojox/widget/Standby/images/loading.gif").toString(), + + // imageText: String + // Text for the ALT tag. + imageText: "Please Wait...", // TODO: i18n + + // text: String + // Text/HTML to display in the center of the overlay + // This is used if image center is disabled. + text: "Please wait...", + + // centerIndicator: String + // Property to define if the image and its alt text should be used, or + // a simple Text/HTML node should be used. Allowable values are 'image' + // and 'text'. + // Default is 'image'. + centerIndicator: "image", + + // _displayed: [private] Boolean + // Flag to indicate if the overlay is displayed or not. + _displayed: false, + + // _resizeCheck: [private] Object + // Handle to interval function that checks the target for changes. + _resizeCheck: null, + + // target: DOMNode||DOMID(String)||WidgetID(String) + // The target to overlay when active. Can be a widget id, a + // dom id, or a direct node reference. + target: "", + + // color: String + // The color to set the overlay. Should be in #XXXXXX form. + // Default color for the translucent overlay is light gray. + color: "#C0C0C0", + + // duration: integer + // Integer defining how long the show and hide effects should take. + duration: 500, + + // _started: [private] Boolean + // Trap flag to ensure startup only processes once. + _started: false, + + // _parent: [private] DOMNode + // Wrapping div for the widget, also used for IE 7 in dealing with the + // zoom issue. + _parent: null, + + // zIndex: String + // Control that lets you specify if the zIndex for the overlay + // should be auto-computed based off parent zIndex, or should be set + // to a particular value. This is useful when you want to overlay + // things in digit.Dialogs, you can specify a base zIndex to append from. + zIndex: "auto", + + startup: function(args){ + // summary: + // Over-ride of the basic widget startup function. + // Configures the target node and sets the image to use. + if(!this._started){ + if(typeof this.target === "string"){ + var w = registry.byId(this.target); + this.target = w ? w.domNode : dom.byId(this.target); + } + + if(this.text){ + this._textNode.innerHTML = this.text; + } + if(this.centerIndicator === "image"){ + this._centerNode = this._imageNode; + attr.set(this._imageNode, "src", this.image); + attr.set(this._imageNode, "alt", this.imageText); + }else{ + this._centerNode = this._textNode; + } + domStyle.set(this._underlayNode, { + display: "none", + backgroundColor: this.color + }); + domStyle.set(this._centerNode, "display", "none"); + this.connect(this._underlayNode, "onclick", "_ignore"); + + //Last thing to do is move the widgets parent, if any, to the current document body. + //Avoids having to deal with parent relative/absolute mess. Otherwise positioning + //tends to go goofy. + if(this.domNode.parentNode && this.domNode.parentNode != baseWindow.body()){ + baseWindow.body().appendChild(this.domNode); + } + + //IE 7 has a horrible bug with zoom, so we have to create this node + //to cross-check later. Sigh. + if(has("ie") == 7){ + this._ieFixNode = construct.create("div"); + domStyle.set(this._ieFixNode, { + opacity: "0", + zIndex: "-1000", + position: "absolute", + top: "-1000px" + }); + baseWindow.body().appendChild(this._ieFixNode); + } + this.inherited(arguments); + } + }, + + show: function(){ + // summary: + // Function to display the blocking overlay and busy/status icon or text. + if(!this._displayed){ + if(this._anim){ + this._anim.stop(); + delete this._anim; + } + this._displayed = true; + this._size(); + this._disableOverflow(); + this._fadeIn(); + } + }, + + hide: function(){ + // summary: + // Function to hide the blocking overlay and status icon or text. + if(this._displayed){ + if(this._anim){ + this._anim.stop(); + delete this._anim; + } + this._size(); + this._fadeOut(); + this._displayed = false; + if(this._resizeCheck !== null){ + clearInterval(this._resizeCheck); + this._resizeCheck = null; + } + } + }, + + isVisible: function(){ + // summary: + // Helper function so you can test if the widget is already visible or not. + // returns: + // boolean indicating if the widget is in 'show' state or not. + return this._displayed; // boolean + }, + + onShow: function(){ + // summary: + // Event that fires when the display of the Standby completes. + }, + + onHide: function(){ + // summary: + // Event that fires when the display of the Standby completes. + }, + + uninitialize: function(){ + // summary: + // Over-ride to hide the widget, which clears intervals, before cleanup. + this._displayed = false; + if(this._resizeCheck){ + clearInterval(this._resizeCheck); + } + domStyle.set(this._centerNode, "display", "none"); + domStyle.set(this._underlayNode, "display", "none"); + if(has("ie") == 7 && this._ieFixNode){ + baseWindow.body().removeChild(this._ieFixNode); + delete this._ieFixNode; + } + if(this._anim){ + this._anim.stop(); + delete this._anim; + } + this.target = null; + this._imageNode = null; + this._textNode = null; + this._centerNode = null; + this.inherited(arguments); + }, + + _size: function(){ + // summary: + // Internal function that handles resizing the overlay and + // centering of the image on window resizing. + // tags: + // private + if(this._displayed){ + var dir = attr.get(baseWindow.body(), "dir"); + if(dir){dir = dir.toLowerCase();} + var _ie7zoom; + var scrollers = this._scrollerWidths(); + + var target = this.target; + + //Show the image and make sure the zIndex is set high. + var curStyle = domStyle.get(this._centerNode, "display"); + domStyle.set(this._centerNode, "display", "block"); + var box = geometry.position(target, true); + if(target === baseWindow.body() || target === baseWindow.doc){ + // Target is the whole doc, so scale to viewport. + box = window.getBox(); + box.x = box.l; + box.y = box.t; + } + + var cntrIndicator = geometry.getMarginBox(this._centerNode); + domStyle.set(this._centerNode, "display", curStyle); + + //IE has a horrible zoom bug. So, we have to try and account for + //it and fix up the scaling. + if(this._ieFixNode){ + _ie7zoom = -this._ieFixNode.offsetTop / 1000; + box.x = Math.floor((box.x + 0.9) / _ie7zoom); + box.y = Math.floor((box.y + 0.9) / _ie7zoom); + box.w = Math.floor((box.w + 0.9) / _ie7zoom); + box.h = Math.floor((box.h + 0.9) / _ie7zoom); + } + + //Figure out how to zIndex this thing over the target. + var zi = domStyle.get(target, "zIndex"); + var ziUl = zi; + var ziIn = zi; + + if(this.zIndex === "auto"){ + if(zi != "auto"){ + ziUl = parseInt(ziUl, 10) + 1; + ziIn = parseInt(ziIn, 10) + 2; + }else{ + //We need to search up the chain to see if there + //are any parent zIndexs to overlay. + var cNode = target.parentNode; + var oldZi = -100000; + while(cNode && cNode !== baseWindow.body()){ + zi = domStyle.get(cNode, "zIndex"); + if(!zi || zi === "auto"){ + cNode = cNode.parentNode; + }else{ + var newZi = parseInt(zi, 10); + if(oldZi < newZi){ + oldZi = newZi; + ziUl = newZi + 1; + ziIn = newZi + 2; + } + // Keep looking until we run out, we want the highest zIndex. + cNode = cNode.parentNode; + } + } + } + }else{ + ziUl = parseInt(this.zIndex, 10) + 1; + ziIn = parseInt(this.zIndex, 10) + 2; + } + + domStyle.set(this._centerNode, "zIndex", ziIn); + domStyle.set(this._underlayNode, "zIndex", ziUl); + + + var pn = target.parentNode; + if(pn && pn !== baseWindow.body() && + target !== baseWindow.body() && + target !== baseWindow.doc){ + + // If the parent is the body tag itself, + // we can avoid all this, the body takes + // care of overflow for me. Besides, browser + // weirdness with height and width on body causes + // problems with this sort of intersect testing + // anyway. + var obh = box.h; + var obw = box.w; + var pnBox = geometry.position(pn, true); + + //More IE zoom corrections. Grr. + if(this._ieFixNode){ + _ie7zoom = -this._ieFixNode.offsetTop / 1000; + pnBox.x = Math.floor((pnBox.x + 0.9) / _ie7zoom); + pnBox.y = Math.floor((pnBox.y + 0.9) / _ie7zoom); + pnBox.w = Math.floor((pnBox.w + 0.9) / _ie7zoom); + pnBox.h = Math.floor((pnBox.h + 0.9) / _ie7zoom); + } + + //Shift the parent width/height a bit if scollers are present. + pnBox.w -= pn.scrollHeight > pn.clientHeight && + pn.clientHeight > 0 ? scrollers.v: 0; + pnBox.h -= pn.scrollWidth > pn.clientWidth && + pn.clientWidth > 0 ? scrollers.h: 0; + + //RTL requires a bit of massaging in some cases + //(and differently depending on browser, ugh!) + //WebKit and others still need work. + if(dir === "rtl"){ + if(has("opera")){ + box.x += pn.scrollHeight > pn.clientHeight && + pn.clientHeight > 0 ? scrollers.v: 0; + pnBox.x += pn.scrollHeight > pn.clientHeight && + pn.clientHeight > 0 ? scrollers.v: 0; + }else if(has("ie")){ + pnBox.x += pn.scrollHeight > pn.clientHeight && + pn.clientHeight > 0 ? scrollers.v: 0; + }else if(has("webkit")){ + //TODO: FIX THIS! + } + } + + //Figure out if we need to adjust the overlay to fit a viewable + //area, then resize it, we saved the original height/width above. + //This is causing issues on IE. Argh! + if(pnBox.w < box.w){ + //Scale down the width if necessary. + box.w = box.w - pnBox.w; + } + if(pnBox.h < box.h){ + //Scale down the width if necessary. + box.h = box.h - pnBox.h; + } + + //Look at the y positions and see if we intersect with the + //viewport borders. Will have to do computations off it. + var vpTop = pnBox.y; + var vpBottom = pnBox.y + pnBox.h; + var bTop = box.y; + var bBottom = box.y + obh; + var vpLeft = pnBox.x; + var vpRight = pnBox.x + pnBox.w; + var bLeft = box.x; + var bRight = box.x + obw; + var delta; + //Adjust the height now + if(bBottom > vpTop && + bTop < vpTop){ + box.y = pnBox.y; + //intersecting top, need to do some shifting. + delta = vpTop - bTop; + var visHeight = obh - delta; + //If the visible height < viewport height, + //We need to shift it. + if(visHeight < pnBox.h){ + box.h = visHeight; + }else{ + //Deal with horizontal scrollbars if necessary. + box.h -= 2*(pn.scrollWidth > pn.clientWidth && + pn.clientWidth > 0? scrollers.h: 0); + } + }else if(bTop < vpBottom && bBottom > vpBottom){ + //Intersecting bottom, just figure out how much + //overlay to show. + box.h = vpBottom - bTop; + }else if(bBottom <= vpTop || bTop >= vpBottom){ + //Outside view, hide it. + box.h = 0; + } + + //adjust width + if(bRight > vpLeft && bLeft < vpLeft){ + box.x = pnBox.x; + //intersecting left, need to do some shifting. + delta = vpLeft - bLeft; + var visWidth = obw - delta; + //If the visible width < viewport width, + //We need to shift it. + if(visWidth < pnBox.w){ + box.w = visWidth; + }else{ + //Deal with horizontal scrollbars if necessary. + box.w -= 2*(pn.scrollHeight > pn.clientHeight && + pn.clientHeight > 0? scrollers.w:0); + } + }else if(bLeft < vpRight && bRight > vpRight){ + //Intersecting right, just figure out how much + //overlay to show. + box.w = vpRight - bLeft; + }else if(bRight <= vpLeft || bLeft >= vpRight){ + //Outside view, hide it. + box.w = 0; + } + } + + if(box.h > 0 && box.w > 0){ + //Set position and size of the blocking div overlay. + domStyle.set(this._underlayNode, { + display: "block", + width: box.w + "px", + height: box.h + "px", + top: box.y + "px", + left: box.x + "px" + }); + + var styles = ["borderRadius", "borderTopLeftRadius", + "borderTopRightRadius","borderBottomLeftRadius", + "borderBottomRightRadius"]; + this._cloneStyles(styles); + if(!has("ie")){ + //Browser specific styles to try and clone if non-IE. + styles = ["MozBorderRadius", "MozBorderRadiusTopleft", + "MozBorderRadiusTopright","MozBorderRadiusBottomleft", + "MozBorderRadiusBottomright","WebkitBorderRadius", + "WebkitBorderTopLeftRadius", "WebkitBorderTopRightRadius", + "WebkitBorderBottomLeftRadius","WebkitBorderBottomRightRadius" + ]; + this._cloneStyles(styles, this); + } + var cntrIndicatorTop = (box.h/2) - (cntrIndicator.h/2); + var cntrIndicatorLeft = (box.w/2) - (cntrIndicator.w/2); + //Only show the image if there is height and width room. + if(box.h >= cntrIndicator.h && box.w >= cntrIndicator.w){ + domStyle.set(this._centerNode, { + top: (cntrIndicatorTop + box.y) + "px", + left: (cntrIndicatorLeft + box.x) + "px", + display: "block" + }); + }else{ + domStyle.set(this._centerNode, "display", "none"); + } + }else{ + //Target has no size, display nothing on it! + domStyle.set(this._underlayNode, "display", "none"); + domStyle.set(this._centerNode, "display", "none"); + } + if(this._resizeCheck === null){ + //Set an interval timer that checks the target size and scales as needed. + //Checking every 10th of a second seems to generate a fairly smooth update. + var self = this; + this._resizeCheck = setInterval(function(){self._size();}, 100); + } + } + }, + + _cloneStyles: function(list){ + // summary: + // Internal function to clone a set of styles from the target to + // the underlay. + // list: Array + // An array of style names to clone. + // + // tags: + // private + array.forEach(list, function(s){ + domStyle.set(this._underlayNode, s, domStyle.get(this.target, s)); + }, this); + }, + + _fadeIn: function(){ + // summary: + // Internal function that does the opacity style fade in animation. + // tags: + // private + var self = this; + var underlayNodeAnim = baseFx.animateProperty({ + duration: self.duration, + node: self._underlayNode, + properties: {opacity: {start: 0, end: 0.75}} + }); + var imageAnim = baseFx.animateProperty({ + duration: self.duration, + node: self._centerNode, + properties: {opacity: {start: 0, end: 1}}, + onEnd: function(){ + self.onShow(); + delete self._anim; + } + }); + this._anim = fx.combine([underlayNodeAnim,imageAnim]); + this._anim.play(); + }, + + _fadeOut: function(){ + // summary: + // Internal function that does the opacity style fade out animation. + // tags: + // private + var self = this; + var underlayNodeAnim = baseFx.animateProperty({ + duration: self.duration, + node: self._underlayNode, + properties: {opacity: {start: 0.75, end: 0}}, + onEnd: function(){ + domStyle.set(this.node,{"display":"none", "zIndex": "-1000"}); + } + }); + var imageAnim = baseFx.animateProperty({ + duration: self.duration, + node: self._centerNode, + properties: {opacity: {start: 1, end: 0}}, + onEnd: function(){ + domStyle.set(this.node,{"display":"none", "zIndex": "-1000"}); + self.onHide(); + self._enableOverflow(); + delete self._anim; + } + }); + this._anim = fx.combine([underlayNodeAnim,imageAnim]); + this._anim.play(); + }, + + _ignore: function(e){ + // summary: + // Function to ignore events that occur on the overlay. + // event: Event + // The event to halt + // tags: + // private + if(e){ + event.stop(e); + } + }, + + _scrollerWidths: function(){ + // summary: + // This function will calculate the size of the vertical and + // horizontaol scrollbars. + // returns: + // Object of form: {v: Number, h: Number} where v is vertical scrollbar width + // and h is horizontal scrollbar width. + // tags: + // private + var div = construct.create("div"); + domStyle.set(div, { + position: "absolute", + opacity: 0, + overflow: "hidden", + width: "50px", + height: "50px", + zIndex: "-100", + top: "-200px", + padding: "0px", + margin: "0px" + }); + var iDiv = construct.create("div"); + domStyle.set(iDiv, { + width: "200px", + height: "10px" + }); + div.appendChild(iDiv); + baseWindow.body().appendChild(div); + + //Figure out content size before and after + //scrollbars are there, then just subtract to + //get width. + var b = geometry.getContentBox(div); + domStyle.set(div, "overflow", "scroll"); + var a = geometry.getContentBox(div); + baseWindow.body().removeChild(div); + return { v: b.w - a.w, h: b.h - a.h }; + }, + + /* The following are functions that tie into _Widget.attr() */ + + _setTextAttr: function(text){ + // summary: + // Function to allow widget.attr to set the text displayed in center + // if using text display. + // text: String + // The text to set. + this._textNode.innerHTML = text; + this.text = text; + }, + + _setColorAttr: function(c){ + // summary: + // Function to allow widget.attr to set the color used for the translucent + // div overlay. + // c: String + // The color to set the background underlay to in #XXXXXX format.. + domStyle.set(this._underlayNode, "backgroundColor", c); + this.color = c; + }, + + _setImageTextAttr: function(text){ + // summary: + // Function to allow widget.attr to set the ALT text text displayed for + // the image (if using image center display). + // text: String + // The text to set. + attr.set(this._imageNode, "alt", text); + this.imageText = text; + }, + + _setImageAttr: function(url){ + // summary: + // Function to allow widget.attr to set the url source for the center image + // text: String + // The url to set for the image. + attr.set(this._imageNode, "src", url); + this.image = url; + }, + + _setCenterIndicatorAttr: function(indicator){ + // summary: + // Function to allow widget.attr to set the node used for the center indicator, + // either the image or the text. + // indicator: String + // The indicator to use, either 'image' or 'text'. + this.centerIndicator = indicator; + if(indicator === "image"){ + this._centerNode = this._imageNode; + domStyle.set(this._textNode, "display", "none"); + }else{ + this._centerNode = this._textNode; + domStyle.set(this._imageNode, "display", "none"); + } + }, + + _disableOverflow: function(){ + // summary: + // Function to disable scrollbars on the body. Only used if the overlay + // targets the body or the document. + if(this.target === baseWindow.body() || this.target === baseWindow.doc){ + // Store the overflow state we have to restore later. + // IE had issues, so have to check that it's defined. Ugh. + this._overflowDisabled = true; + var body = baseWindow.body(); + if(body.style && body.style.overflow){ + this._oldOverflow = domStyle.set(body, "overflow"); + }else{ + this._oldOverflow = ""; + } + if(has("ie") && !has("quirks")){ + // IE will put scrollbars in anyway, html (parent of body) + // also controls them in standards mode, so we have to + // remove them, argh. + if(body.parentNode && + body.parentNode.style && + body.parentNode.style.overflow){ + this._oldBodyParentOverflow = body.parentNode.style.overflow; + }else{ + try{ + this._oldBodyParentOverflow = domStyle.set(body.parentNode, "overflow"); + }catch(e){ + this._oldBodyParentOverflow = "scroll"; + } + } + domStyle.set(body.parentNode, "overflow", "hidden"); + } + domStyle.set(body, "overflow", "hidden"); + } + }, + + _enableOverflow: function(){ + // summary: + // Function to restore scrollbars on the body. Only used if the overlay + // targets the body or the document. + if(this._overflowDisabled){ + delete this._overflowDisabled; + var body = baseWindow.body(); + // Restore all the overflow. + if(has("ie") && !has("quirks")){ + body.parentNode.style.overflow = this._oldBodyParentOverflow; + delete this._oldBodyParentOverflow; + } + domStyle.set(body, "overflow", this._oldOverflow); + if(has("webkit")){ + //Gotta poke WebKit, or scrollers don't come back. :-( + var div = construct.create("div", { style: { + height: "2px" + } + }); + body.appendChild(div); + setTimeout(function(){ + body.removeChild(div); + }, 0); + } + delete this._oldOverflow; + } + } +}); + +}); |
