/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.widget.Standby"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.widget.Standby"] = true;
dojo.provide("dojox.widget.Standby");
dojo.require("dojo.window");
dojo.require("dojo.fx");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.experimental("dojox.widget.Standby");
dojo.declare("dojox.widget.Standby",[dijit._Widget, dijit._Templated],{
// 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: dojo.moduleUrl("dojox", "widget/Standby/images/loading.gif").toString(),
// imageText: String
// Text for the ALT tag.
imageText: "Please Wait...",
// 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 = dijit.byId(this.target);
if(w){
this.target = w.domNode;
}else{
this.target = dojo.byId(this.target);
}
}
if(this.text){
this._textNode.innerHTML = this.text;
}
if(this.centerIndicator === "image"){
this._centerNode = this._imageNode;
dojo.attr(this._imageNode, "src", this.image);
dojo.attr(this._imageNode, "alt", this.imageText);
}else{
this._centerNode = this._textNode;
}
dojo.style(this._underlayNode, {
display: "none",
backgroundColor: this.color
});
dojo.style(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 != dojo.body()){
dojo.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(dojo.isIE == 7){
this._ieFixNode = dojo.doc.createElement("div");
dojo.style(this._ieFixNode, {
opacity: "0",
zIndex: "-1000",
position: "absolute",
top: "-1000px"
});
dojo.body().appendChild(this._ieFixNode);
}
}
},
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);
}
dojo.style(this._centerNode, "display", "none");
dojo.style(this._underlayNode, "display", "none");
if(dojo.isIE == 7){
dojo.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 = dojo.attr(dojo.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 = dojo.style(this._centerNode, "display");
dojo.style(this._centerNode, "display", "block");
var box = dojo.position(target, true);
if(target === dojo.body() || target === dojo.doc){
// Target is the whole doc, so scale to viewport.
box = dojo.window.getBox();
box.x = box.l;
box.y = box.t;
}
var cntrIndicator = dojo.marginBox(this._centerNode);
dojo.style(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 = dojo.style(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 !== dojo.body()){
zi = dojo.style(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;
}
dojo.style(this._centerNode, "zIndex", ziIn);
dojo.style(this._underlayNode, "zIndex", ziUl);
var pn = target.parentNode;
if(pn && pn !== dojo.body() &&
target !== dojo.body() &&
target !== dojo.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 = dojo.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(dojo.isOpera){
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(dojo.isIE){
pnBox.x += pn.scrollHeight > pn.clientHeight &&
pn.clientHeight > 0 ? scrollers.v: 0;
}else if(dojo.isWebKit){
//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.
dojo.style(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(!dojo.isIE){
//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){
dojo.style(this._centerNode, {
top: (cntrIndicatorTop + box.y) + "px",
left: (cntrIndicatorLeft + box.x) + "px",
display: "block"
});
}else{
dojo.style(this._centerNode, "display", "none");
}
}else{
//Target has no size, display nothing on it!
dojo.style(this._underlayNode, "display", "none");
dojo.style(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
dojo.forEach(list, function(style){
dojo.style(this._underlayNode,style,dojo.style(this.target,style));
}, this);
},
_fadeIn: function(){
// summary:
// Internal function that does the opacity style fade in animation.
// tags:
// private
var self = this;
var underlayNodeAnim = dojo.animateProperty({
duration: self.duration,
node: self._underlayNode,
properties: {opacity: {start: 0, end: 0.75}}
});
var imageAnim = dojo.animateProperty({
duration: self.duration,
node: self._centerNode,
properties: {opacity: {start: 0, end: 1}},
onEnd: function(){
self.onShow();
delete self._anim;
}
});
this._anim = dojo.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 = dojo.animateProperty({
duration: self.duration,
node: self._underlayNode,
properties: {opacity: {start: 0.75, end: 0}},
onEnd: function(){
dojo.style(this.node,{"display":"none", "zIndex": "-1000"});
}
});
var imageAnim = dojo.animateProperty({
duration: self.duration,
node: self._centerNode,
properties: {opacity: {start: 1, end: 0}},
onEnd: function(){
dojo.style(this.node,{"display":"none", "zIndex": "-1000"});
self.onHide();
self._enableOverflow();
delete self._anim;
}
});
this._anim = dojo.fx.combine([underlayNodeAnim,imageAnim]);
this._anim.play();
},
_ignore: function(event){
// summary:
// Function to ignore events that occur on the overlay.
// event: Event
// The event to halt
// tags:
// private
if(event){
dojo.stopEvent(event);
}
},
_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 = dojo.doc.createElement("div");
dojo.style(div, {
position: "absolute",
opacity: 0,
overflow: "hidden",
width: "50px",
height: "50px",
zIndex: "-100",
top: "-200px",
left: "-200px",
padding: "0px",
margin: "0px"
});
var iDiv = dojo.doc.createElement("div");
dojo.style(iDiv, {
width: "200px",
height: "10px"
});
div.appendChild(iDiv);
dojo.body().appendChild(div);
//Figure out content size before and after
//scrollbars are there, then just subtract to
//get width.
var b = dojo.contentBox(div);
dojo.style(div, "overflow", "scroll");
var a = dojo.contentBox(div);
dojo.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..
dojo.style(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.
dojo.attr(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.
dojo.attr(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;
dojo.style(this._textNode, "display", "none");
}else{
this._centerNode = this._textNode;
dojo.style(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 === dojo.body() || this.target === dojo.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 = dojo.body();
if(body.style && body.style.overflow){
this._oldOverflow = dojo.style(body, "overflow");
}else{
this._oldOverflow = "";
}
if(dojo.isIE && !dojo.isQuirks){
// 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 = dojo.style(body.parentNode, "overflow");
}catch(e){
this._oldBodyParentOverflow = "scroll";
}
}
dojo.style(body.parentNode, "overflow", "hidden");
}
dojo.style(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 = dojo.body();
// Restore all the overflow.
if(dojo.isIE && !dojo.isQuirks){
body.parentNode.style.overflow = this._oldBodyParentOverflow;
delete this._oldBodyParentOverflow;
}
dojo.style(body, "overflow", this._oldOverflow);
if(dojo.isWebKit){
//Gotta poke WebKit, or scrollers don't come back. :-(
var div = dojo.create("div", { style: {
height: "2px"
}
});
body.appendChild(div);
setTimeout(function(){
body.removeChild(div);
}, 0);
}
delete this._oldOverflow;
}
}
});
}