", this.contentWidget.domNode, "after");
// wrapper div's first child is the button widget (ie, the title bar)
var child = this.contentWidget,
cls = dojo.getObject(this.buttonWidget);
this.button = child._buttonWidget = (new cls({
contentWidget: child,
label: child.title,
title: child.tooltip,
dir: child.dir,
lang: child.lang,
iconClass: child.iconClass,
id: child.id + "_button",
parent: this.parent
})).placeAt(this.domNode);
// and then the actual content widget (changing it from prior-sibling to last-child)
dojo.place(this.contentWidget.domNode, this.domNode);
},
postCreate: function(){
this.inherited(arguments);
this.connect(this.contentWidget, 'set', function(name, value){
var mappedName = {title: "label", tooltip: "title", iconClass: "iconClass"}[name];
if(mappedName){
this.button.set(mappedName, value);
}
}, this);
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this.selected = isSelected;
this.button.set("selected", isSelected);
if(isSelected){
var cw = this.contentWidget;
if(cw.onSelected){ cw.onSelected(); }
}
},
startup: function(){
// Called by _Container.addChild()
this.contentWidget.startup();
},
destroy: function(){
this.button.destroyRecursive();
delete this.contentWidget._buttonWidget;
delete this.contentWidget._wrapperWidget;
this.inherited(arguments);
},
destroyDescendants: function(){
// since getChildren isn't working for me, have to code this manually
this.contentWidget.destroyRecursive();
}
});
dojo.declare("dijit.layout._AccordionButton",
[dijit._Widget, dijit._Templated, dijit._CssStateMixin],
{
// summary:
// The title bar to click to open up an accordion pane.
// Internal widget used by AccordionContainer.
// tags:
// private
templateString: dojo.cache("dijit.layout", "templates/AccordionButton.html", "
\n\t
+-\n\t\t
\n\t
\n
\n"),
attributeMap: dojo.mixin(dojo.clone(dijit.layout.ContentPane.prototype.attributeMap), {
label: {node: "titleTextNode", type: "innerHTML" },
title: {node: "titleTextNode", type: "attribute", attribute: "title"},
iconClass: { node: "iconNode", type: "class" }
}),
baseClass: "dijitAccordionTitle",
getParent: function(){
// summary:
// Returns the AccordionContainer parent.
// tags:
// private
return this.parent;
},
postCreate: function(){
this.inherited(arguments);
dojo.setSelectable(this.domNode, false);
var titleTextNodeId = dojo.attr(this.domNode,'id').replace(' ','_');
dojo.attr(this.titleTextNode, "id", titleTextNodeId+"_title");
dijit.setWaiState(this.focusNode, "labelledby", dojo.attr(this.titleTextNode, "id"));
},
getTitleHeight: function(){
// summary:
// Returns the height of the title dom node.
return dojo.marginBox(this.domNode).h; // Integer
},
// TODO: maybe the parent should set these methods directly rather than forcing the code
// into the button widget?
_onTitleClick: function(){
// summary:
// Callback when someone clicks my title.
var parent = this.getParent();
if(!parent._inTransition){
parent.selectChild(this.contentWidget, true);
dijit.focus(this.focusNode);
}
},
_onTitleKeyPress: function(/*Event*/ evt){
return this.getParent()._onKeyPress(evt, this.contentWidget);
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this.selected = isSelected;
dijit.setWaiState(this.focusNode, "expanded", isSelected);
dijit.setWaiState(this.focusNode, "selected", isSelected);
this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
}
});
}
if(!dojo._hasResource["dijit.layout.BorderContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.BorderContainer"] = true;
dojo.provide("dijit.layout.BorderContainer");
dojo.declare(
"dijit.layout.BorderContainer",
dijit.layout._LayoutWidget,
{
// summary:
// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
//
// description:
// A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
// that contains a child widget marked region="center" and optionally children widgets marked
// region equal to "top", "bottom", "leading", "trailing", "left" or "right".
// Children along the edges will be laid out according to width or height dimensions and may
// include optional splitters (splitter="true") to make them resizable by the user. The remaining
// space is designated for the center region.
//
// NOTE: Splitters must not be more than 50 pixels in width.
//
// The outer size must be specified on the BorderContainer node. Width must be specified for the sides
// and height for the top and bottom, respectively. No dimensions should be specified on the center;
// it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
// "left" and "right" except that they will be reversed in right-to-left environments.
//
// example:
// |
// |
header text
// |
table of contents
// |
client area
// |
// design: String
// Which design is used for the layout:
// - "headline" (default) where the top and bottom extend
// the full width of the container
// - "sidebar" where the left and right sides extend from top to bottom.
design: "headline",
// gutters: Boolean
// Give each pane a border and margin.
// Margin determined by domNode.paddingLeft.
// When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
gutters: true,
// liveSplitters: Boolean
// Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
liveSplitters: true,
// persist: Boolean
// Save splitter positions in a cookie.
persist: false,
baseClass: "dijitBorderContainer",
// _splitterClass: String
// Optional hook to override the default Splitter widget used by BorderContainer
_splitterClass: "dijit.layout._Splitter",
postMixInProperties: function(){
// change class name to indicate that BorderContainer is being used purely for
// layout (like LayoutContainer) rather than for pretty formatting.
if(!this.gutters){
this.baseClass += "NoGutter";
}
this.inherited(arguments);
},
postCreate: function(){
this.inherited(arguments);
this._splitters = {};
this._splitterThickness = {};
},
startup: function(){
if(this._started){ return; }
dojo.forEach(this.getChildren(), this._setupChild, this);
this.inherited(arguments);
},
_setupChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget._setupChild().
var region = child.region;
if(region){
this.inherited(arguments);
dojo.addClass(child.domNode, this.baseClass+"Pane");
var ltr = this.isLeftToRight();
if(region == "leading"){ region = ltr ? "left" : "right"; }
if(region == "trailing"){ region = ltr ? "right" : "left"; }
//FIXME: redundant?
this["_"+region] = child.domNode;
this["_"+region+"Widget"] = child;
// Create draggable splitter for resizing pane,
// or alternately if splitter=false but BorderContainer.gutters=true then
// insert dummy div just for spacing
if((child.splitter || this.gutters) && !this._splitters[region]){
var _Splitter = dojo.getObject(child.splitter ? this._splitterClass : "dijit.layout._Gutter");
var splitter = new _Splitter({
id: child.id + "_splitter",
container: this,
child: child,
region: region,
live: this.liveSplitters
});
splitter.isSplitter = true;
this._splitters[region] = splitter.domNode;
dojo.place(this._splitters[region], child.domNode, "after");
// Splitters arent added as Contained children, so we need to call startup explicitly
splitter.startup();
}
child.region = region;
}
},
_computeSplitterThickness: function(region){
this._splitterThickness[region] = this._splitterThickness[region] ||
dojo.marginBox(this._splitters[region])[(/top|bottom/.test(region) ? 'h' : 'w')];
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
for(var region in this._splitters){ this._computeSplitterThickness(region); }
this._layoutChildren();
},
addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){
// Override _LayoutWidget.addChild().
this.inherited(arguments);
if(this._started){
this.layout(); //OPT
}
},
removeChild: function(/*dijit._Widget*/ child){
// Override _LayoutWidget.removeChild().
var region = child.region;
var splitter = this._splitters[region];
if(splitter){
dijit.byNode(splitter).destroy();
delete this._splitters[region];
delete this._splitterThickness[region];
}
this.inherited(arguments);
delete this["_"+region];
delete this["_" +region+"Widget"];
if(this._started){
this._layoutChildren();
}
dojo.removeClass(child.domNode, this.baseClass+"Pane");
},
getChildren: function(){
// Override _LayoutWidget.getChildren() to only return real children, not the splitters.
return dojo.filter(this.inherited(arguments), function(widget){
return !widget.isSplitter;
});
},
getSplitter: function(/*String*/region){
// summary:
// Returns the widget responsible for rendering the splitter associated with region
var splitter = this._splitters[region];
return splitter ? dijit.byNode(splitter) : null;
},
resize: function(newSize, currentSize){
// Overrides _LayoutWidget.resize().
// resetting potential padding to 0px to provide support for 100% width/height + padding
// TODO: this hack doesn't respect the box model and is a temporary fix
if(!this.cs || !this.pe){
var node = this.domNode;
this.cs = dojo.getComputedStyle(node);
this.pe = dojo._getPadExtents(node, this.cs);
this.pe.r = dojo._toPixelValue(node, this.cs.paddingRight);
this.pe.b = dojo._toPixelValue(node, this.cs.paddingBottom);
dojo.style(node, "padding", "0px");
}
this.inherited(arguments);
},
_layoutChildren: function(/*String?*/changedRegion, /*Number?*/ changedRegionSize){
// summary:
// This is the main routine for setting size/position of each child.
// description:
// With no arguments, measures the height of top/bottom panes, the width
// of left/right panes, and then sizes all panes accordingly.
//
// With changedRegion specified (as "left", "top", "bottom", or "right"),
// it changes that region's width/height to changedRegionSize and
// then resizes other regions that were affected.
// changedRegion:
// The region should be changed because splitter was dragged.
// "left", "right", "top", or "bottom".
// changedRegionSize:
// The new width/height (in pixels) to make changedRegion
if(!this._borderBox || !this._borderBox.h){
// We are currently hidden, or we haven't been sized by our parent yet.
// Abort. Someone will resize us later.
return;
}
var sidebarLayout = (this.design == "sidebar");
var topHeight = 0, bottomHeight = 0, leftWidth = 0, rightWidth = 0;
var topStyle = {}, leftStyle = {}, rightStyle = {}, bottomStyle = {},
centerStyle = (this._center && this._center.style) || {};
var changedSide = /left|right/.test(changedRegion);
var layoutSides = !changedRegion || (!changedSide && !sidebarLayout);
var layoutTopBottom = !changedRegion || (changedSide && sidebarLayout);
// Ask browser for width/height of side panes.
// Would be nice to cache this but height can change according to width
// (because words wrap around). I don't think width will ever change though
// (except when the user drags a splitter).
if(this._top){
topStyle = (changedRegion == "top" || layoutTopBottom) && this._top.style;
topHeight = changedRegion == "top" ? changedRegionSize : dojo.marginBox(this._top).h;
}
if(this._left){
leftStyle = (changedRegion == "left" || layoutSides) && this._left.style;
leftWidth = changedRegion == "left" ? changedRegionSize : dojo.marginBox(this._left).w;
}
if(this._right){
rightStyle = (changedRegion == "right" || layoutSides) && this._right.style;
rightWidth = changedRegion == "right" ? changedRegionSize : dojo.marginBox(this._right).w;
}
if(this._bottom){
bottomStyle = (changedRegion == "bottom" || layoutTopBottom) && this._bottom.style;
bottomHeight = changedRegion == "bottom" ? changedRegionSize : dojo.marginBox(this._bottom).h;
}
var splitters = this._splitters;
var topSplitter = splitters.top, bottomSplitter = splitters.bottom,
leftSplitter = splitters.left, rightSplitter = splitters.right;
var splitterThickness = this._splitterThickness;
var topSplitterThickness = splitterThickness.top || 0,
leftSplitterThickness = splitterThickness.left || 0,
rightSplitterThickness = splitterThickness.right || 0,
bottomSplitterThickness = splitterThickness.bottom || 0;
// Check for race condition where CSS hasn't finished loading, so
// the splitter width == the viewport width (#5824)
if(leftSplitterThickness > 50 || rightSplitterThickness > 50){
setTimeout(dojo.hitch(this, function(){
// Results are invalid. Clear them out.
this._splitterThickness = {};
for(var region in this._splitters){
this._computeSplitterThickness(region);
}
this._layoutChildren();
}), 50);
return false;
}
var pe = this.pe;
var splitterBounds = {
left: (sidebarLayout ? leftWidth + leftSplitterThickness: 0) + pe.l + "px",
right: (sidebarLayout ? rightWidth + rightSplitterThickness: 0) + pe.r + "px"
};
if(topSplitter){
dojo.mixin(topSplitter.style, splitterBounds);
topSplitter.style.top = topHeight + pe.t + "px";
}
if(bottomSplitter){
dojo.mixin(bottomSplitter.style, splitterBounds);
bottomSplitter.style.bottom = bottomHeight + pe.b + "px";
}
splitterBounds = {
top: (sidebarLayout ? 0 : topHeight + topSplitterThickness) + pe.t + "px",
bottom: (sidebarLayout ? 0 : bottomHeight + bottomSplitterThickness) + pe.b + "px"
};
if(leftSplitter){
dojo.mixin(leftSplitter.style, splitterBounds);
leftSplitter.style.left = leftWidth + pe.l + "px";
}
if(rightSplitter){
dojo.mixin(rightSplitter.style, splitterBounds);
rightSplitter.style.right = rightWidth + pe.r + "px";
}
dojo.mixin(centerStyle, {
top: pe.t + topHeight + topSplitterThickness + "px",
left: pe.l + leftWidth + leftSplitterThickness + "px",
right: pe.r + rightWidth + rightSplitterThickness + "px",
bottom: pe.b + bottomHeight + bottomSplitterThickness + "px"
});
var bounds = {
top: sidebarLayout ? pe.t + "px" : centerStyle.top,
bottom: sidebarLayout ? pe.b + "px" : centerStyle.bottom
};
dojo.mixin(leftStyle, bounds);
dojo.mixin(rightStyle, bounds);
leftStyle.left = pe.l + "px"; rightStyle.right = pe.r + "px"; topStyle.top = pe.t + "px"; bottomStyle.bottom = pe.b + "px";
if(sidebarLayout){
topStyle.left = bottomStyle.left = leftWidth + leftSplitterThickness + pe.l + "px";
topStyle.right = bottomStyle.right = rightWidth + rightSplitterThickness + pe.r + "px";
}else{
topStyle.left = bottomStyle.left = pe.l + "px";
topStyle.right = bottomStyle.right = pe.r + "px";
}
// More calculations about sizes of panes
var containerHeight = this._borderBox.h - pe.t - pe.b,
middleHeight = containerHeight - ( topHeight + topSplitterThickness + bottomHeight + bottomSplitterThickness),
sidebarHeight = sidebarLayout ? containerHeight : middleHeight;
var containerWidth = this._borderBox.w - pe.l - pe.r,
middleWidth = containerWidth - (leftWidth + leftSplitterThickness + rightWidth + rightSplitterThickness),
sidebarWidth = sidebarLayout ? middleWidth : containerWidth;
// New margin-box size of each pane
var dim = {
top: { w: sidebarWidth, h: topHeight },
bottom: { w: sidebarWidth, h: bottomHeight },
left: { w: leftWidth, h: sidebarHeight },
right: { w: rightWidth, h: sidebarHeight },
center: { h: middleHeight, w: middleWidth }
};
if(changedRegion){
// Respond to splitter drag event by changing changedRegion's width or height
var child = this["_" + changedRegion + "Widget"],
mb = {};
mb[ /top|bottom/.test(changedRegion) ? "h" : "w"] = changedRegionSize;
child.resize ? child.resize(mb, dim[child.region]) : dojo.marginBox(child.domNode, mb);
}
// Nodes in IE<8 don't respond to t/l/b/r, and TEXTAREA doesn't respond in any browser
var janky = dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.some(this.getChildren(), function(child){
return child.domNode.tagName == "TEXTAREA" || child.domNode.tagName == "INPUT";
});
if(janky){
// Set the size of the children the old fashioned way, by setting
// CSS width and height
var resizeWidget = function(widget, changes, result){
if(widget){
(widget.resize ? widget.resize(changes, result) : dojo.marginBox(widget.domNode, changes));
}
};
if(leftSplitter){ leftSplitter.style.height = sidebarHeight; }
if(rightSplitter){ rightSplitter.style.height = sidebarHeight; }
resizeWidget(this._leftWidget, {h: sidebarHeight}, dim.left);
resizeWidget(this._rightWidget, {h: sidebarHeight}, dim.right);
if(topSplitter){ topSplitter.style.width = sidebarWidth; }
if(bottomSplitter){ bottomSplitter.style.width = sidebarWidth; }
resizeWidget(this._topWidget, {w: sidebarWidth}, dim.top);
resizeWidget(this._bottomWidget, {w: sidebarWidth}, dim.bottom);
resizeWidget(this._centerWidget, dim.center);
}else{
// Calculate which panes need a notification that their size has been changed
// (we've already set style.top/bottom/left/right on those other panes).
var notifySides = !changedRegion || (/top|bottom/.test(changedRegion) && this.design != "sidebar"),
notifyTopBottom = !changedRegion || (/left|right/.test(changedRegion) && this.design == "sidebar"),
notifyList = {
center: true,
left: notifySides,
right: notifySides,
top: notifyTopBottom,
bottom: notifyTopBottom
};
// Send notification to those panes that have changed size
dojo.forEach(this.getChildren(), function(child){
if(child.resize && notifyList[child.region]){
child.resize(null, dim[child.region]);
}
}, this);
}
},
destroy: function(){
for(var region in this._splitters){
var splitter = this._splitters[region];
dijit.byNode(splitter).destroy();
dojo.destroy(splitter);
}
delete this._splitters;
delete this._splitterThickness;
this.inherited(arguments);
}
});
// This argument can be specified for the children of a BorderContainer.
// Since any widget can be specified as a LayoutContainer child, mix it
// into the base widget class. (This is a hack, but it's effective.)
dojo.extend(dijit._Widget, {
// region: [const] String
// Parameter for children of `dijit.layout.BorderContainer`.
// Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
// See the `dijit.layout.BorderContainer` description for details.
region: '',
// splitter: [const] Boolean
// Parameter for child of `dijit.layout.BorderContainer` where region != "center".
// If true, enables user to resize the widget by putting a draggable splitter between
// this widget and the region=center widget.
splitter: false,
// minSize: [const] Number
// Parameter for children of `dijit.layout.BorderContainer`.
// Specifies a minimum size (in pixels) for this widget when resized by a splitter.
minSize: 0,
// maxSize: [const] Number
// Parameter for children of `dijit.layout.BorderContainer`.
// Specifies a maximum size (in pixels) for this widget when resized by a splitter.
maxSize: Infinity
});
dojo.declare("dijit.layout._Splitter", [ dijit._Widget, dijit._Templated ],
{
// summary:
// A draggable spacer between two items in a `dijit.layout.BorderContainer`.
// description:
// This is instantiated by `dijit.layout.BorderContainer`. Users should not
// create it directly.
// tags:
// private
/*=====
// container: [const] dijit.layout.BorderContainer
// Pointer to the parent BorderContainer
container: null,
// child: [const] dijit.layout._LayoutWidget
// Pointer to the pane associated with this splitter
child: null,
// region: String
// Region of pane associated with this splitter.
// "top", "bottom", "left", "right".
region: null,
=====*/
// live: [const] Boolean
// If true, the child's size changes and the child widget is redrawn as you drag the splitter;
// otherwise, the size doesn't change until you drop the splitter (by mouse-up)
live: true,
templateString: '
',
postCreate: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
// dojo.addClass(this.child.domNode, "dijitSplitterPane");
// dojo.setSelectable(this.domNode, false); //TODO is this necessary?
this._factor = /top|left/.test(this.region) ? 1 : -1;
this._cookieName = this.container.id + "_" + this.region;
if(this.container.persist){
// restore old size
var persistSize = dojo.cookie(this._cookieName);
if(persistSize){
this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
}
}
},
_computeMaxSize: function(){
// summary:
// Compute the maximum size that my corresponding pane can be set to
var dim = this.horizontal ? 'h' : 'w',
thickness = this.container._splitterThickness[this.region];
// Get DOMNode of opposite pane, if an opposite pane exists.
// Ex: if I am the _Splitter for the left pane, then get the right pane.
var flip = {left:'right', right:'left', top:'bottom', bottom:'top', leading:'trailing', trailing:'leading'},
oppNode = this.container["_" + flip[this.region]];
// I can expand up to the edge of the opposite pane, or if there's no opposite pane, then to
// edge of BorderContainer
var available = dojo.contentBox(this.container.domNode)[dim] -
(oppNode ? dojo.marginBox(oppNode)[dim] : 0) -
20 - thickness * 2;
return Math.min(this.child.maxSize, available);
},
_startDrag: function(e){
if(!this.cover){
this.cover = dojo.doc.createElement('div');
dojo.addClass(this.cover, "dijitSplitterCover");
dojo.place(this.cover, this.child.domNode, "after");
}
dojo.addClass(this.cover, "dijitSplitterCoverActive");
// Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
if(this.fake){ dojo.destroy(this.fake); }
if(!(this._resize = this.live)){ //TODO: disable live for IE6?
// create fake splitter to display at old position while we drag
(this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
dojo.addClass(this.domNode, "dijitSplitterShadow");
dojo.place(this.fake, this.domNode, "after");
}
dojo.addClass(this.domNode, "dijitSplitterActive");
dojo.addClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
if(this.fake){
dojo.removeClass(this.fake, "dijitSplitterHover");
dojo.removeClass(this.fake, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
}
//Performance: load data info local vars for onmousevent function closure
var factor = this._factor,
max = this._computeMaxSize(),
min = this.child.minSize || 20,
isHorizontal = this.horizontal,
axis = isHorizontal ? "pageY" : "pageX",
pageStart = e[axis],
splitterStyle = this.domNode.style,
dim = isHorizontal ? 'h' : 'w',
childStart = dojo.marginBox(this.child.domNode)[dim],
region = this.region,
splitterStart = parseInt(this.domNode.style[region], 10),
resize = this._resize,
childNode = this.child.domNode,
layoutFunc = dojo.hitch(this.container, this.container._layoutChildren),
de = dojo.doc;
this._handlers = (this._handlers || []).concat([
dojo.connect(de, "onmousemove", this._drag = function(e, forceResize){
var delta = e[axis] - pageStart,
childSize = factor * delta + childStart,
boundChildSize = Math.max(Math.min(childSize, max), min);
if(resize || forceResize){
layoutFunc(region, boundChildSize);
}
splitterStyle[region] = factor * delta + splitterStart + (boundChildSize - childSize) + "px";
}),
dojo.connect(de, "ondragstart", dojo.stopEvent),
dojo.connect(dojo.body(), "onselectstart", dojo.stopEvent),
dojo.connect(de, "onmouseup", this, "_stopDrag")
]);
dojo.stopEvent(e);
},
_onMouse: function(e){
var o = (e.type == "mouseover" || e.type == "mouseenter");
dojo.toggleClass(this.domNode, "dijitSplitterHover", o);
dojo.toggleClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
},
_stopDrag: function(e){
try{
if(this.cover){
dojo.removeClass(this.cover, "dijitSplitterCoverActive");
}
if(this.fake){ dojo.destroy(this.fake); }
dojo.removeClass(this.domNode, "dijitSplitterActive");
dojo.removeClass(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
dojo.removeClass(this.domNode, "dijitSplitterShadow");
this._drag(e); //TODO: redundant with onmousemove?
this._drag(e, true);
}finally{
this._cleanupHandlers();
delete this._drag;
}
if(this.container.persist){
dojo.cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
}
},
_cleanupHandlers: function(){
dojo.forEach(this._handlers, dojo.disconnect);
delete this._handlers;
},
_onKeyPress: function(/*Event*/ e){
// should we apply typematic to this?
this._resize = true;
var horizontal = this.horizontal;
var tick = 1;
var dk = dojo.keys;
switch(e.charOrCode){
case horizontal ? dk.UP_ARROW : dk.LEFT_ARROW:
tick *= -1;
// break;
case horizontal ? dk.DOWN_ARROW : dk.RIGHT_ARROW:
break;
default:
// this.inherited(arguments);
return;
}
var childSize = dojo.marginBox(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
this.container._layoutChildren(this.region, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
dojo.stopEvent(e);
},
destroy: function(){
this._cleanupHandlers();
delete this.child;
delete this.container;
delete this.cover;
delete this.fake;
this.inherited(arguments);
}
});
dojo.declare("dijit.layout._Gutter", [dijit._Widget, dijit._Templated ],
{
// summary:
// Just a spacer div to separate side pane from center pane.
// Basically a trick to lookup the gutter/splitter width from the theme.
// description:
// Instantiated by `dijit.layout.BorderContainer`. Users should not
// create directly.
// tags:
// private
templateString: '
',
postCreate: function(){
this.horizontal = /top|bottom/.test(this.region);
dojo.addClass(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
}
});
}
if(!dojo._hasResource["dijit.layout._TabContainerBase"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout._TabContainerBase"] = true;
dojo.provide("dijit.layout._TabContainerBase");
dojo.declare("dijit.layout._TabContainerBase",
[dijit.layout.StackContainer, dijit._Templated],
{
// summary:
// Abstract base class for TabContainer. Must define _makeController() to instantiate
// and return the widget that displays the tab labels
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// tabPosition: String
// Defines where tabs go relative to tab content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
baseClass: "dijitTabContainer",
// tabStrip: Boolean
// Defines whether the tablist gets an extra class for layouting, putting a border/shading
// around the set of tabs.
tabStrip: false,
// nested: Boolean
// If true, use styling for a TabContainer nested inside another TabContainer.
// For tundra etc., makes tabs look like links, and hides the outer
// border since the outer TabContainer already has a border.
nested: false,
templateString: dojo.cache("dijit.layout", "templates/TabContainer.html", "
\n"),
postMixInProperties: function(){
// set class name according to tab position, ex: dijitTabContainerTop
this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
this.srcNodeRef && dojo.style(this.srcNodeRef, "visibility", "hidden");
this.inherited(arguments);
},
postCreate: function(){
this.inherited(arguments);
// Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
this.tablist = this._makeController(this.tablistNode);
if(!this.doLayout){ dojo.addClass(this.domNode, "dijitTabContainerNoLayout"); }
if(this.nested){
/* workaround IE's lack of support for "a > b" selectors by
* tagging each node in the template.
*/
dojo.addClass(this.domNode, "dijitTabContainerNested");
dojo.addClass(this.tablist.containerNode, "dijitTabContainerTabListNested");
dojo.addClass(this.tablistSpacer, "dijitTabContainerSpacerNested");
dojo.addClass(this.containerNode, "dijitTabPaneWrapperNested");
}else{
dojo.addClass(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
}
},
_setupChild: function(/*dijit._Widget*/ tab){
// Overrides StackContainer._setupChild().
dojo.addClass(tab.domNode, "dijitTabPane");
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
// wire up the tablist and its tabs
this.tablist.startup();
this.inherited(arguments);
},
layout: function(){
// Overrides StackContainer.layout().
// Configure the content pane to take up all the space except for where the tabs are
if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
var sc = this.selectedChildWidget;
if(this.doLayout){
// position and size the titles and the container node
var titleAlign = this.tabPosition.replace(/-h/, "");
this.tablist.layoutAlign = titleAlign;
var children = [this.tablist, {
domNode: this.tablistSpacer,
layoutAlign: titleAlign
}, {
domNode: this.containerNode,
layoutAlign: "client"
}];
dijit.layout.layoutChildren(this.domNode, this._contentBox, children);
// Compute size to make each of my children.
// children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
this._containerContentBox = dijit.layout.marginBox2contentBox(this.containerNode, children[2]);
if(sc && sc.resize){
sc.resize(this._containerContentBox);
}
}else{
// just layout the tab controller, so it can position left/right buttons etc.
if(this.tablist.resize){
this.tablist.resize({w: dojo.contentBox(this.domNode).w});
}
// and call resize() on the selected pane just to tell it that it's been made visible
if(sc && sc.resize){
sc.resize();
}
}
},
destroy: function(){
if(this.tablist){
this.tablist.destroy();
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.layout.TabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.TabController"] = true;
dojo.provide("dijit.layout.TabController");
// Menu is used for an accessible close button, would be nice to have a lighter-weight solution
dojo.declare("dijit.layout.TabController",
dijit.layout.StackController,
{
// summary:
// Set of tabs (the things with titles and a close button, that you click to show a tab panel).
// Used internally by `dijit.layout.TabContainer`.
// description:
// Lets the user select the currently shown pane in a TabContainer or StackContainer.
// TabController also monitors the TabContainer, and whenever a pane is
// added or deleted updates itself accordingly.
// tags:
// private
templateString: "
",
// tabPosition: String
// Defines where tabs go relative to the content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
// buttonWidget: String
// The name of the tab widget to create to correspond to each page
buttonWidget: "dijit.layout._TabButton",
_rectifyRtlTabList: function(){
// summary:
// For left/right TabContainer when page is RTL mode, rectify the width of all tabs to be equal, otherwise the tab widths are different in IE
if(0 >= this.tabPosition.indexOf('-h')){ return; }
if(!this.pane2button){ return; }
var maxWidth = 0;
for(var pane in this.pane2button){
var ow = this.pane2button[pane].innerDiv.scrollWidth;
maxWidth = Math.max(maxWidth, ow);
}
//unify the length of all the tabs
for(pane in this.pane2button){
this.pane2button[pane].innerDiv.style.width = maxWidth + 'px';
}
}
});
dojo.declare("dijit.layout._TabButton",
dijit.layout._StackButton,
{
// summary:
// A tab (the thing you click to select a pane).
// description:
// Contains the title of the pane, and optionally a close-button to destroy the pane.
// This is an internal widget and should not be instantiated directly.
// tags:
// private
// baseClass: String
// The CSS class applied to the domNode.
baseClass: "dijitTab",
// Apply dijitTabCloseButtonHover when close button is hovered
cssStateNodes: {
closeNode: "dijitTabCloseButton"
},
templateString: dojo.cache("dijit.layout", "templates/_TabButton.html", "
\n
\n
\n \t
\n\t\t
\n\t\t
\n\t\t
\n\t\t x\n\t\t\t
\n
\n
\n
\n"),
// Override _FormWidget.scrollOnFocus.
// Don't scroll the whole tab container into view when the button is focused.
scrollOnFocus: false,
postMixInProperties: function(){
// Override blank iconClass from Button to do tab height adjustment on IE6,
// to make sure that tabs with and w/out close icons are same height
if(!this.iconClass){
this.iconClass = "dijitTabButtonIcon";
}
},
postCreate: function(){
this.inherited(arguments);
dojo.setSelectable(this.containerNode, false);
// If a custom icon class has not been set for the
// tab icon, set its width to one pixel. This ensures
// that the height styling of the tab is maintained,
// as it is based on the height of the icon.
// TODO: I still think we can just set dijitTabButtonIcon to 1px in CSS
if(this.iconNode.className == "dijitTabButtonIcon"){
dojo.style(this.iconNode, "width", "1px");
}
},
startup: function(){
this.inherited(arguments);
var n = this.domNode;
// Required to give IE6 a kick, as it initially hides the
// tabs until they are focused on.
setTimeout(function(){
n.className = n.className;
}, 1);
},
_setCloseButtonAttr: function(disp){
this.closeButton = disp;
dojo.toggleClass(this.innerDiv, "dijitClosable", disp);
this.closeNode.style.display = disp ? "" : "none";
if(disp){
var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
if(this.closeNode){
dojo.attr(this.closeNode,"title", _nlsResources.itemClose);
}
// add context menu onto title button
var _nlsResources = dojo.i18n.getLocalization("dijit", "common");
this._closeMenu = new dijit.Menu({
id: this.id+"_Menu",
dir: this.dir,
lang: this.lang,
targetNodeIds: [this.domNode]
});
this._closeMenu.addChild(new dijit.MenuItem({
label: _nlsResources.itemClose,
dir: this.dir,
lang: this.lang,
onClick: dojo.hitch(this, "onClickCloseButton")
}));
}else{
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
}
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for attr('label', ...) to work.
// description:
// takes an HTML string.
// Inherited ToggleButton implementation will Set the label (text) of the button;
// Need to set the alt attribute of icon on tab buttons if no label displayed
this.inherited(arguments);
if(this.showLabel == false && !this.params.title){
this.iconNode.alt = dojo.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
},
destroy: function(){
if(this._closeMenu){
this._closeMenu.destroyRecursive();
delete this._closeMenu;
}
this.inherited(arguments);
}
});
}
if(!dojo._hasResource["dijit.layout.ScrollingTabController"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.ScrollingTabController"] = true;
dojo.provide("dijit.layout.ScrollingTabController");
dojo.declare("dijit.layout.ScrollingTabController",
dijit.layout.TabController,
{
// summary:
// Set of tabs with left/right arrow keys and a menu to switch between tabs not
// all fitting on a single row.
// Works only for horizontal tabs (either above or below the content, not to the left
// or right).
// tags:
// private
templateString: dojo.cache("dijit.layout", "templates/ScrollingTabController.html", "\n"),
// useMenu:[const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// tabStripClass: String
// The css class to apply to the tab strip, if it is visible.
tabStripClass: "",
widgetsInTemplate: true,
// _minScroll: Number
// The distance in pixels from the edge of the tab strip which,
// if a scroll animation is less than, forces the scroll to
// go all the way to the left/right.
_minScroll: 5,
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
"class": "containerNode"
}),
postCreate: function(){
this.inherited(arguments);
var n = this.domNode;
this.scrollNode = this.tablistWrapper;
this._initButtons();
if(!this.tabStripClass){
this.tabStripClass = "dijitTabContainer" +
this.tabPosition.charAt(0).toUpperCase() +
this.tabPosition.substr(1).replace(/-.*/, "") +
"None";
dojo.addClass(n, "tabStrip-disabled")
}
dojo.addClass(this.tablistWrapper, this.tabStripClass);
},
onStartup: function(){
this.inherited(arguments);
// Do not show the TabController until the related
// StackController has added it's children. This gives
// a less visually jumpy instantiation.
dojo.style(this.domNode, "visibility", "visible");
this._postStartup = true;
},
onAddChild: function(page, insertIndex){
this.inherited(arguments);
var menuItem;
if(this.useMenu){
var containerId = this.containerId;
menuItem = new dijit.MenuItem({
id: page.id + "_stcMi",
label: page.title,
dir: page.dir,
lang: page.lang,
onClick: dojo.hitch(this, function(){
var container = dijit.byId(containerId);
container.selectChild(page);
})
});
this._menuChildren[page.id] = menuItem;
this._menu.addChild(menuItem, insertIndex);
}
// update the menuItem label when the button label is updated
this.pane2handles[page.id].push(
this.connect(this.pane2button[page.id], "set", function(name, value){
if(this._postStartup){
if(name == "label"){
if(menuItem){
menuItem.set(name, value);
}
// The changed label will have changed the width of the
// buttons, so do a resize
if(this._dim){
this.resize(this._dim);
}
}
}
})
);
// Increment the width of the wrapper when a tab is added
// This makes sure that the buttons never wrap.
// The value 200 is chosen as it should be bigger than most
// Tab button widths.
dojo.style(this.containerNode, "width",
(dojo.style(this.containerNode, "width") + 200) + "px");
},
onRemoveChild: function(page, insertIndex){
// null out _selectedTab because we are about to delete that dom node
var button = this.pane2button[page.id];
if(this._selectedTab === button.domNode){
this._selectedTab = null;
}
// delete menu entry corresponding to pane that was removed from TabContainer
if(this.useMenu && page && page.id && this._menuChildren[page.id]){
this._menu.removeChild(this._menuChildren[page.id]);
this._menuChildren[page.id].destroy();
delete this._menuChildren[page.id];
}
this.inherited(arguments);
},
_initButtons: function(){
// summary:
// Creates the buttons used to scroll to view tabs that
// may not be visible if the TabContainer is too narrow.
this._menuChildren = {};
// Make a list of the buttons to display when the tab labels become
// wider than the TabContainer, and hide the other buttons.
// Also gets the total width of the displayed buttons.
this._btnWidth = 0;
this._buttons = dojo.query("> .tabStripButton", this.domNode).filter(function(btn){
if((this.useMenu && btn == this._menuBtn.domNode) ||
(this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
this._btnWidth += dojo.marginBox(btn).w;
return true;
}else{
dojo.style(btn, "display", "none");
return false;
}
}, this);
if(this.useMenu){
// Create the menu that is used to select tabs.
this._menu = new dijit.Menu({
id: this.id + "_menu",
dir: this.dir,
lang: this.lang,
targetNodeIds: [this._menuBtn.domNode],
leftClickToOpen: true,
refocus: false // selecting a menu item sets focus to a TabButton
});
this._supportingWidgets.push(this._menu);
}
},
_getTabsWidth: function(){
var children = this.getChildren();
if(children.length){
var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
return rightTab.offsetLeft + dojo.style(rightTab, "width") - leftTab.offsetLeft;
}else{
return 0;
}
},
_enableBtn: function(width){
// summary:
// Determines if the tabs are wider than the width of the TabContainer, and
// thus that we need to display left/right/menu navigation buttons.
var tabsWidth = this._getTabsWidth();
width = width || dojo.style(this.scrollNode, "width");
return tabsWidth > 0 && width < tabsWidth;
},
resize: function(dim){
// summary:
// Hides or displays the buttons used to scroll the tab list and launch the menu
// that selects tabs.
if(this.domNode.offsetWidth == 0){
return;
}
// Save the dimensions to be used when a child is renamed.
this._dim = dim;
// Set my height to be my natural height (tall enough for one row of tab labels),
// and my content-box width based on margin-box width specified in dim parameter.
// But first reset scrollNode.height in case it was set by layoutChildren() call
// in a previous run of this method.
this.scrollNode.style.height = "auto";
this._contentBox = dijit.layout.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
this._contentBox.h = this.scrollNode.offsetHeight;
dojo.contentBox(this.domNode, this._contentBox);
// Show/hide the left/right/menu navigation buttons depending on whether or not they
// are needed.
var enable = this._enableBtn(this._contentBox.w);
this._buttons.style("display", enable ? "" : "none");
// Position and size the navigation buttons and the tablist
this._leftBtn.layoutAlign = "left";
this._rightBtn.layoutAlign = "right";
this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
dijit.layout.layoutChildren(this.domNode, this._contentBox,
[this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
// set proper scroll so that selected tab is visible
if(this._selectedTab){
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var w = this.scrollNode,
sl = this._convertToScrollLeft(this._getScrollForSelectedTab());
w.scrollLeft = sl;
}
// Enable/disabled left right buttons depending on whether or not user can scroll to left or right
this._setButtonClass(this._getScroll());
this._postResize = true;
},
_getScroll: function(){
// summary:
// Returns the current scroll of the tabs where 0 means
// "scrolled all the way to the left" and some positive number, based on #
// of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
var sl = (this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit) ? this.scrollNode.scrollLeft :
dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width")
+ (dojo.isIE == 8 ? -1 : 1) * this.scrollNode.scrollLeft;
return sl;
},
_convertToScrollLeft: function(val){
// summary:
// Given a scroll value where 0 means "scrolled all the way to the left"
// and some positive number, based on # of pixels of possible scroll (ex: 1000)
// means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
// to achieve that scroll.
//
// This method is to adjust for RTL funniness in various browsers and versions.
if(this.isLeftToRight() || dojo.isIE < 8 || (dojo.isIE && dojo.isQuirks) || dojo.isWebKit){
return val;
}else{
var maxScroll = dojo.style(this.containerNode, "width") - dojo.style(this.scrollNode, "width");
return (dojo.isIE == 8 ? -1 : 1) * (val - maxScroll);
}
},
onSelectChild: function(/*dijit._Widget*/ page){
// summary:
// Smoothly scrolls to a tab when it is selected.
var tab = this.pane2button[page.id];
if(!tab || !page){return;}
// Scroll to the selected tab, except on startup, when scrolling is handled in resize()
var node = tab.domNode;
if(this._postResize && node != this._selectedTab){
this._selectedTab = node;
var sl = this._getScroll();
if(sl > node.offsetLeft ||
sl + dojo.style(this.scrollNode, "width") <
node.offsetLeft + dojo.style(node, "width")){
this.createSmoothScroll().play();
}
}
this.inherited(arguments);
},
_getScrollBounds: function(){
// summary:
// Returns the minimum and maximum scroll setting to show the leftmost and rightmost
// tabs (respectively)
var children = this.getChildren(),
scrollNodeWidth = dojo.style(this.scrollNode, "width"), // about 500px
containerWidth = dojo.style(this.containerNode, "width"), // 50,000px
maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
tabsWidth = this._getTabsWidth();
if(children.length && tabsWidth > scrollNodeWidth){
// Scrolling should happen
return {
min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
max: this.isLeftToRight() ?
(children[children.length-1].domNode.offsetLeft + dojo.style(children[children.length-1].domNode, "width")) - scrollNodeWidth :
maxPossibleScroll
};
}else{
// No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
return {
min: onlyScrollPosition,
max: onlyScrollPosition
};
}
},
_getScrollForSelectedTab: function(){
// summary:
// Returns the scroll value setting so that the selected tab
// will appear in the center
var w = this.scrollNode,
n = this._selectedTab,
scrollNodeWidth = dojo.style(this.scrollNode, "width"),
scrollBounds = this._getScrollBounds();
// TODO: scroll minimal amount (to either right or left) so that
// selected tab is fully visible, and just return if it's already visible?
var pos = (n.offsetLeft + dojo.style(n, "width")/2) - scrollNodeWidth/2;
pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
// TODO:
// If scrolling close to the left side or right side, scroll
// all the way to the left or right. See this._minScroll.
// (But need to make sure that doesn't scroll the tab out of view...)
return pos;
},
createSmoothScroll : function(x){
// summary:
// Creates a dojo._Animation object that smoothly scrolls the tab list
// either to a fixed horizontal pixel value, or to the selected tab.
// description:
// If an number argument is passed to the function, that horizontal
// pixel position is scrolled to. Otherwise the currently selected
// tab is scrolled to.
// x: Integer?
// An optional pixel value to scroll to, indicating distance from left.
// Calculate position to scroll to
if(arguments.length > 0){
// position specified by caller, just make sure it's within bounds
var scrollBounds = this._getScrollBounds();
x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
}else{
// scroll to center the current tab
x = this._getScrollForSelectedTab();
}
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var self = this,
w = this.scrollNode,
anim = new dojo._Animation({
beforeBegin: function(){
if(this.curve){ delete this.curve; }
var oldS = w.scrollLeft,
newS = self._convertToScrollLeft(x);
anim.curve = new dojo._Line(oldS, newS);
},
onAnimate: function(val){
w.scrollLeft = val;
}
});
this._anim = anim;
// Disable/enable left/right buttons according to new scroll position
this._setButtonClass(x);
return anim; // dojo._Animation
},
_getBtnNode: function(e){
// summary:
// Gets a button DOM node from a mouse click event.
// e:
// The mouse click event.
var n = e.target;
while(n && !dojo.hasClass(n, "tabStripButton")){
n = n.parentNode;
}
return n;
},
doSlideRight: function(e){
// summary:
// Scrolls the menu to the right.
// e:
// The mouse click event.
this.doSlide(1, this._getBtnNode(e));
},
doSlideLeft: function(e){
// summary:
// Scrolls the menu to the left.
// e:
// The mouse click event.
this.doSlide(-1,this._getBtnNode(e));
},
doSlide: function(direction, node){
// summary:
// Scrolls the tab list to the left or right by 75% of the widget width.
// direction:
// If the direction is 1, the widget scrolls to the right, if it is
// -1, it scrolls to the left.
if(node && dojo.hasClass(node, "dijitTabDisabled")){return;}
var sWidth = dojo.style(this.scrollNode, "width");
var d = (sWidth * 0.75) * direction;
var to = this._getScroll() + d;
this._setButtonClass(to);
this.createSmoothScroll(to).play();
},
_setButtonClass: function(scroll){
// summary:
// Disables the left scroll button if the tabs are scrolled all the way to the left,
// or the right scroll button in the opposite case.
// scroll: Integer
// amount of horizontal scroll
var scrollBounds = this._getScrollBounds();
this._leftBtn.set("disabled", scroll <= scrollBounds.min);
this._rightBtn.set("disabled", scroll >= scrollBounds.max);
}
});
dojo.declare("dijit.layout._ScrollingTabControllerButton",
dijit.form.Button,
{
baseClass: "dijitTab tabStripButton",
templateString: dojo.cache("dijit.layout", "templates/_ScrollingTabControllerButton.html", "\n\t
\n\t\t
\n\t\t\t
\n\t\t\t
\n\t\t
\n\t
\n
\n"),
// Override inherited tabIndex: 0 from dijit.form.Button, because user shouldn't be
// able to tab to the left/right/menu buttons
tabIndex: "-1"
}
);
}
if(!dojo._hasResource["dijit.layout.TabContainer"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.layout.TabContainer"] = true;
dojo.provide("dijit.layout.TabContainer");
dojo.declare("dijit.layout.TabContainer",
dijit.layout._TabContainerBase,
{
// summary:
// A Container with tabs to select each child (only one of which is displayed at a time).
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// controllerWidget: String
// An optional parameter to override the widget used to display the tab labels
controllerWidget: "",
_makeController: function(/*DomNode*/ srcNode){
// summary:
// Instantiate tablist controller widget and return reference to it.
// Callback from _TabContainerBase.postCreate().
// tags:
// protected extension
var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
TabController = dojo.getObject(this.controllerWidget);
return new TabController({
id: this.id + "_tablist",
dir: this.dir,
lang: this.lang,
tabPosition: this.tabPosition,
doLayout: this.doLayout,
containerId: this.id,
"class": cls,
nested: this.nested,
useMenu: this.useMenu,
useSlider: this.useSlider,
tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
}, srcNode);
},
postMixInProperties: function(){
this.inherited(arguments);
// Scrolling controller only works for horizontal non-nested tabs
if(!this.controllerWidget){
this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
"dijit.layout.ScrollingTabController" : "dijit.layout.TabController";
}
}
});
}
if(!dojo._hasResource["dojo.number"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.number"] = true;
dojo.provide("dojo.number");
/*=====
dojo.number = {
// summary: localized formatting and parsing routines for Number
}
dojo.number.__FormatOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// places: Number?
// fixed number of decimal places to show. This overrides any
// information in the provided pattern.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means do not round.
// locale: String?
// override the locale used to determine formatting rules
// fractional: Boolean?
// If false, show no decimal places, overriding places and pattern settings.
this.pattern = pattern;
this.type = type;
this.places = places;
this.round = round;
this.locale = locale;
this.fractional = fractional;
}
=====*/
dojo.number.format = function(/*Number*/value, /*dojo.number.__FormatOptions?*/options){
// summary:
// Format a Number as a String, using locale-specific settings
// description:
// Create a string from a Number using a known localized pattern.
// Formatting patterns appropriate to the locale are chosen from the
// [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
// delimiters.
// If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
// value:
// the number to be formatted
options = dojo.mixin({}, options || {});
var locale = dojo.i18n.normalizeLocale(options.locale),
bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale);
options.customs = bundle;
var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
return dojo.number._applyPattern(value, pattern, options); // String
};
//dojo.number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
dojo.number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
dojo.number._applyPattern = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatOptions?*/options){
// summary:
// Apply pattern to format value as a string using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted.
// pattern:
// a pattern string as described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// options: dojo.number.__FormatOptions?
// _applyPattern is usually called via `dojo.number.format()` which
// populates an extra property in the options parameter, "customs".
// The customs object specifies group and decimal parameters if set.
//TODO: support escapes
options = options || {};
var group = options.customs.group,
decimal = options.customs.decimal,
patternList = pattern.split(';'),
positivePattern = patternList[0];
pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
//TODO: only test against unescaped
if(pattern.indexOf('%') != -1){
value *= 100;
}else if(pattern.indexOf('\u2030') != -1){
value *= 1000; // per mille
}else if(pattern.indexOf('\u00a4') != -1){
group = options.customs.currencyGroup || group;//mixins instead?
decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
pattern = pattern.replace(/\u00a4{1,3}/, function(match){
var prop = ["symbol", "currency", "displayName"][match.length-1];
return options[prop] || options.currency || "";
});
}else if(pattern.indexOf('E') != -1){
throw new Error("exponential notation not supported");
}
//TODO: support @ sig figs?
var numberPatternRE = dojo.number._numberPatternRE;
var numberPattern = positivePattern.match(numberPatternRE);
if(!numberPattern){
throw new Error("unable to find a number expression in pattern: "+pattern);
}
if(options.fractional === false){ options.places = 0; }
return pattern.replace(numberPatternRE,
dojo.number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
}
dojo.number.round = function(/*Number*/value, /*Number?*/places, /*Number?*/increment){
// summary:
// Rounds to the nearest value with the given number of decimal places, away from zero
// description:
// Rounds to the nearest value with the given number of decimal places, away from zero if equal.
// Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
// fractional increments also, such as the nearest quarter.
// NOTE: Subject to floating point errors. See dojox.math.round for experimental workaround.
// value:
// The number to round
// places:
// The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
// Must be non-negative.
// increment:
// Rounds next place to nearest value of increment/10. 10 by default.
// example:
// >>> dojo.number.round(-0.5)
// -1
// >>> dojo.number.round(162.295, 2)
// 162.29 // note floating point error. Should be 162.3
// >>> dojo.number.round(10.71, 0, 2.5)
// 10.75
var factor = 10 / (increment || 10);
return (factor * +value).toFixed(places) / factor; // Number
}
if((0.9).toFixed() == 0){
// (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
// is just after the rounding place and is >=5
(function(){
var round = dojo.number.round;
dojo.number.round = function(v, p, m){
var d = Math.pow(10, -p || 0), a = Math.abs(v);
if(!v || a >= d || a * Math.pow(10, p + 1) < 5){
d = 0;
}
return round(v, p, m) + (v > 0 ? d : -d);
}
})();
}
/*=====
dojo.number.__FormatAbsoluteOptions = function(){
// decimal: String?
// the decimal separator
// group: String?
// the group separator
// places: Number?|String?
// number of decimal places. the range "n,m" will format to m places.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means don't round.
this.decimal = decimal;
this.group = group;
this.places = places;
this.round = round;
}
=====*/
dojo.number._formatAbsolute = function(/*Number*/value, /*String*/pattern, /*dojo.number.__FormatAbsoluteOptions?*/options){
// summary:
// Apply numeric pattern to absolute value using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted, ignores sign
// pattern:
// the number portion of a pattern (e.g. `#,##0.00`)
options = options || {};
if(options.places === true){options.places=0;}
if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
var patternParts = pattern.split("."),
comma = typeof options.places == "string" && options.places.indexOf(","),
maxPlaces = options.places;
if(comma){
maxPlaces = options.places.substring(comma + 1);
}else if(!(maxPlaces >= 0)){
maxPlaces = (patternParts[1] || []).length;
}
if(!(options.round < 0)){
value = dojo.number.round(value, maxPlaces, options.round);
}
var valueParts = String(Math.abs(value)).split("."),
fractional = valueParts[1] || "";
if(patternParts[1] || options.places){
if(comma){
options.places = options.places.substring(0, comma);
}
// Pad fractional with trailing zeros
var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
if(pad > fractional.length){
valueParts[1] = dojo.string.pad(fractional, pad, '0', true);
}
// Truncate fractional
if(maxPlaces < fractional.length){
valueParts[1] = fractional.substr(0, maxPlaces);
}
}else{
if(valueParts[1]){ valueParts.pop(); }
}
// Pad whole with leading zeros
var patternDigits = patternParts[0].replace(',', '');
pad = patternDigits.indexOf("0");
if(pad != -1){
pad = patternDigits.length - pad;
if(pad > valueParts[0].length){
valueParts[0] = dojo.string.pad(valueParts[0], pad);
}
// Truncate whole
if(patternDigits.indexOf("#") == -1){
valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
}
}
// Add group separators
var index = patternParts[0].lastIndexOf(','),
groupSize, groupSize2;
if(index != -1){
groupSize = patternParts[0].length - index - 1;
var remainder = patternParts[0].substr(0, index);
index = remainder.lastIndexOf(',');
if(index != -1){
groupSize2 = remainder.length - index - 1;
}
}
var pieces = [];
for(var whole = valueParts[0]; whole;){
var off = whole.length - groupSize;
pieces.push((off > 0) ? whole.substr(off) : whole);
whole = (off > 0) ? whole.slice(0, off) : "";
if(groupSize2){
groupSize = groupSize2;
delete groupSize2;
}
}
valueParts[0] = pieces.reverse().join(options.group || ",");
return valueParts.join(options.decimal || ".");
};
/*=====
dojo.number.__RegexpOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// places: Number|String?
// number of decimal places to accept: Infinity, a positive number, or
// a range "n,m". Defined by pattern or Infinity if pattern not provided.
this.pattern = pattern;
this.type = type;
this.locale = locale;
this.strict = strict;
this.places = places;
}
=====*/
dojo.number.regexp = function(/*dojo.number.__RegexpOptions?*/options){
// summary:
// Builds the regular needed to parse a number
// description:
// Returns regular expression with positive and negative match, group
// and decimal separators
return dojo.number._parseInfo(options).regexp; // String
}
dojo.number._parseInfo = function(/*Object?*/options){
options = options || {};
var locale = dojo.i18n.normalizeLocale(options.locale),
bundle = dojo.i18n.getLocalization("dojo.cldr", "number", locale),
pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
//TODO: memoize?
group = bundle.group,
decimal = bundle.decimal,
factor = 1;
if(pattern.indexOf('%') != -1){
factor /= 100;
}else if(pattern.indexOf('\u2030') != -1){
factor /= 1000; // per mille
}else{
var isCurrency = pattern.indexOf('\u00a4') != -1;
if(isCurrency){
group = bundle.currencyGroup || group;
decimal = bundle.currencyDecimal || decimal;
}
}
//TODO: handle quoted escapes
var patternList = pattern.split(';');
if(patternList.length == 1){
patternList.push("-" + patternList[0]);
}
var re = dojo.regexp.buildGroupRE(patternList, function(pattern){
pattern = "(?:"+dojo.regexp.escapeString(pattern, '.')+")";
return pattern.replace(dojo.number._numberPatternRE, function(format){
var flags = {
signed: false,
separator: options.strict ? group : [group,""],
fractional: options.fractional,
decimal: decimal,
exponent: false
},
parts = format.split('.'),
places = options.places;
// special condition for percent (factor != 1)
// allow decimal places even if not specified in pattern
if(parts.length == 1 && factor != 1){
parts[1] = "###";
}
if(parts.length == 1 || places === 0){
flags.fractional = false;
}else{
if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
flags.places = places;
}
var groups = parts[0].split(',');
if(groups.length > 1){
flags.groupSize = groups.pop().length;
if(groups.length > 1){
flags.groupSize2 = groups.pop().length;
}
}
return "("+dojo.number._realNumberRegexp(flags)+")";
});
}, true);
if(isCurrency){
// substitute the currency symbol for the placeholder in the pattern
re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
var prop = ["symbol", "currency", "displayName"][target.length-1],
symbol = dojo.regexp.escapeString(options[prop] || options.currency || "");
before = before ? "[\\s\\xa0]" : "";
after = after ? "[\\s\\xa0]" : "";
if(!options.strict){
if(before){before += "*";}
if(after){after += "*";}
return "(?:"+before+symbol+after+")?";
}
return before+symbol+after;
});
}
//TODO: substitute localized sign/percent/permille/etc.?
// normalize whitespace and return
return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
}
/*=====
dojo.number.__ParseOptions = function(){
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// fractional: Boolean?|Array?
// Whether to include the fractional portion, where the number of decimal places are implied by pattern
// or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
this.pattern = pattern;
this.type = type;
this.locale = locale;
this.strict = strict;
this.fractional = fractional;
}
=====*/
dojo.number.parse = function(/*String*/expression, /*dojo.number.__ParseOptions?*/options){
// summary:
// Convert a properly formatted string to a primitive Number, using
// locale-specific settings.
// description:
// Create a Number from a string using a known localized pattern.
// Formatting patterns are chosen appropriate to the locale
// and follow the syntax described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// Note that literal characters in patterns are not supported.
// expression:
// A string representation of a Number
var info = dojo.number._parseInfo(options),
results = (new RegExp("^"+info.regexp+"$")).exec(expression);
if(!results){
return NaN; //NaN
}
var absoluteMatch = results[1]; // match for the positive expression
if(!results[1]){
if(!results[2]){
return NaN; //NaN
}
// matched the negative pattern
absoluteMatch =results[2];
info.factor *= -1;
}
// Transform it to something Javascript can parse as a number. Normalize
// decimal point and strip out group separators or alternate forms of whitespace
absoluteMatch = absoluteMatch.
replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
replace(info.decimal, ".");
// Adjust for negative sign, percent, etc. as necessary
return absoluteMatch * info.factor; //Number
};
/*=====
dojo.number.__RealNumberRegexpFlags = function(){
// places: Number?
// The integer number of decimal places or a range given as "n,m". If
// not given, the decimal part is optional and the number of places is
// unlimited.
// decimal: String?
// A string for the character used as the decimal point. Default
// is ".".
// fractional: Boolean?|Array?
// Whether decimal places are used. Can be true, false, or [true,
// false]. Default is [true, false] which means optional.
// exponent: Boolean?|Array?
// Express in exponential notation. Can be true, false, or [true,
// false]. Default is [true, false], (i.e. will match if the
// exponential part is present are not).
// eSigned: Boolean?|Array?
// The leading plus-or-minus sign on the exponent. Can be true,
// false, or [true, false]. Default is [true, false], (i.e. will
// match if it is signed or unsigned). flags in regexp.integer can be
// applied.
this.places = places;
this.decimal = decimal;
this.fractional = fractional;
this.exponent = exponent;
this.eSigned = eSigned;
}
=====*/
dojo.number._realNumberRegexp = function(/*dojo.number.__RealNumberRegexpFlags?*/flags){
// summary:
// Builds a regular expression to match a real number in exponential
// notation
// assign default values to missing parameters
flags = flags || {};
//TODO: use mixin instead?
if(!("places" in flags)){ flags.places = Infinity; }
if(typeof flags.decimal != "string"){ flags.decimal = "."; }
if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
if(!("exponent" in flags)){ flags.exponent = [true, false]; }
if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
var integerRE = dojo.number._integerRegexp(flags),
decimalRE = dojo.regexp.buildGroupRE(flags.fractional,
function(q){
var re = "";
if(q && (flags.places!==0)){
re = "\\" + flags.decimal;
if(flags.places == Infinity){
re = "(?:" + re + "\\d+)?";
}else{
re += "\\d{" + flags.places + "}";
}
}
return re;
},
true
);
var exponentRE = dojo.regexp.buildGroupRE(flags.exponent,
function(q){
if(q){ return "([eE]" + dojo.number._integerRegexp({ signed: flags.eSigned}) + ")"; }
return "";
}
);
var realRE = integerRE + decimalRE;
// allow for decimals without integers, e.g. .25
if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
return realRE + exponentRE; // String
};
/*=====
dojo.number.__IntegerRegexpFlags = function(){
// signed: Boolean?
// The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
// Default is `[true, false]`, (i.e. will match if it is signed
// or unsigned).
// separator: String?
// The character used as the thousands separator. Default is no
// separator. For more than one symbol use an array, e.g. `[",", ""]`,
// makes ',' optional.
// groupSize: Number?
// group size between separators
// groupSize2: Number?
// second grouping, where separators 2..n have a different interval than the first separator (for India)
this.signed = signed;
this.separator = separator;
this.groupSize = groupSize;
this.groupSize2 = groupSize2;
}
=====*/
dojo.number._integerRegexp = function(/*dojo.number.__IntegerRegexpFlags?*/flags){
// summary:
// Builds a regular expression that matches an integer
// assign default values to missing parameters
flags = flags || {};
if(!("signed" in flags)){ flags.signed = [true, false]; }
if(!("separator" in flags)){
flags.separator = "";
}else if(!("groupSize" in flags)){
flags.groupSize = 3;
}
var signRE = dojo.regexp.buildGroupRE(flags.signed,
function(q){ return q ? "[-+]" : ""; },
true
);
var numberRE = dojo.regexp.buildGroupRE(flags.separator,
function(sep){
if(!sep){
return "(?:\\d+)";
}
sep = dojo.regexp.escapeString(sep);
if(sep == " "){ sep = "\\s"; }
else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
var grp = flags.groupSize, grp2 = flags.groupSize2;
//TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
if(grp2){
var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
}
return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
},
true
);
return signRE + numberRE; // String
}
}
if(!dojo._hasResource["dijit.ProgressBar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.ProgressBar"] = true;
dojo.provide("dijit.ProgressBar");
dojo.declare("dijit.ProgressBar", [dijit._Widget, dijit._Templated], {
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
//
// example:
// |
// |
//
// description:
// Note that the progress bar is updated via (a non-standard)
// update() method, rather than via attr() like other widgets.
// progress: [const] String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// With "%": percentage value, 0% <= progress <= 100%, or
// without "%": absolute value, 0 <= progress <= maximum
// TODO: rename to value for 2.0
progress: "0",
// maximum: [const] Float
// Max sample number
maximum: 100,
// places: [const] Number
// Number of places to show in values; 0 by default
places: 0,
// indeterminate: [const] Boolean
// If false: show progress value (number or percentage).
// If true: show that a process is underway but that the amount completed is unknown.
indeterminate: false,
// name: String
// this is the field name (for a form) if set. This needs to be set if you want to use
// this widget in a dijit.form.Form widget (such as dijit.Dialog)
name: '',
templateString: dojo.cache("dijit", "templates/ProgressBar.html", "\n"),
// _indeterminateHighContrastImagePath: [private] dojo._URL
// URL to image to use for indeterminate progress bar when display is in high contrast mode
_indeterminateHighContrastImagePath:
dojo.moduleUrl("dijit", "themes/a11y/indeterminate_progress.gif"),
// public functions
postCreate: function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",
this._indeterminateHighContrastImagePath.toString());
this.update();
},
update: function(/*Object?*/attributes){
// summary:
// Change attributes of ProgressBar, similar to attr(hash).
//
// attributes:
// May provide progress and/or maximum properties on this parameter;
// see attribute specs for details.
//
// example:
// | myProgressBar.update({'indeterminate': true});
// | myProgressBar.update({'progress': 80});
// TODO: deprecate this method and use set() instead
dojo.mixin(this, attributes || {});
var tip = this.internalProgress;
var percent = 1, classFunc;
if(this.indeterminate){
classFunc = "addClass";
dijit.removeWaiState(tip, "valuenow");
dijit.removeWaiState(tip, "valuemin");
dijit.removeWaiState(tip, "valuemax");
}else{
classFunc = "removeClass";
if(String(this.progress).indexOf("%") != -1){
percent = Math.min(parseFloat(this.progress)/100, 1);
this.progress = percent * this.maximum;
}else{
this.progress = Math.min(this.progress, this.maximum);
percent = this.progress / this.maximum;
}
var text = this.report(percent);
this.label.firstChild.nodeValue = text;
dijit.setWaiState(tip, "describedby", this.label.id);
dijit.setWaiState(tip, "valuenow", this.progress);
dijit.setWaiState(tip, "valuemin", 0);
dijit.setWaiState(tip, "valuemax", this.maximum);
}
dojo[classFunc](this.domNode, "dijitProgressBarIndeterminate");
tip.style.width = (percent * 100) + "%";
this.onChange();
},
_setValueAttr: function(v){
if(v == Infinity){
this.update({indeterminate:true});
}else{
this.update({indeterminate:false, progress:v});
}
},
_getValueAttr: function(){
return this.progress;
},
report: function(/*float*/percent){
// summary:
// Generates message to show inside progress bar (normally indicating amount of task completed).
// May be overridden.
// tags:
// extension
return dojo.number.format(percent, { type: "percent", places: this.places, locale: this.lang });
},
onChange: function(){
// summary:
// Callback fired when progress updates.
// tags:
// progress
}
});
}
if(!dojo._hasResource["dijit.ToolbarSeparator"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.ToolbarSeparator"] = true;
dojo.provide("dijit.ToolbarSeparator");
dojo.declare("dijit.ToolbarSeparator",
[ dijit._Widget, dijit._Templated ],
{
// summary:
// A spacer between two `dijit.Toolbar` items
templateString: '',
postCreate: function(){ dojo.setSelectable(this.domNode, false); },
isFocusable: function(){
// summary:
// This widget isn't focusable, so pass along that fact.
// tags:
// protected
return false;
}
});
}
if(!dojo._hasResource["dijit.Toolbar"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.Toolbar"] = true;
dojo.provide("dijit.Toolbar");
dojo.declare("dijit.Toolbar",
[dijit._Widget, dijit._Templated, dijit._KeyNavContainer],
{
// summary:
// A Toolbar widget, used to hold things like `dijit.Editor` buttons
templateString:
'',
baseClass: "dijitToolbar",
postCreate: function(){
this.connectKeyNavHandlers(
this.isLeftToRight() ? [dojo.keys.LEFT_ARROW] : [dojo.keys.RIGHT_ARROW],
this.isLeftToRight() ? [dojo.keys.RIGHT_ARROW] : [dojo.keys.LEFT_ARROW]
);
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
this.startupKeyNavChildren();
this.inherited(arguments);
}
}
);
// For back-compat, remove for 2.0
}
if(!dojo._hasResource["dojo.DeferredList"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.DeferredList"] = true;
dojo.provide("dojo.DeferredList");
dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
// summary:
// Provides event handling for a group of Deferred objects.
// description:
// DeferredList takes an array of existing deferreds and returns a new deferred of its own
// this new deferred will typically have its callback fired when all of the deferreds in
// the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
// fireOnOneErrback, will fire before all the deferreds as appropriate
//
// list:
// The list of deferreds to be synchronizied with this DeferredList
// fireOnOneCallback:
// Will cause the DeferredLists callback to be fired as soon as any
// of the deferreds in its list have been fired instead of waiting until
// the entire list has finished
// fireonOneErrback:
// Will cause the errback to fire upon any of the deferreds errback
// canceller:
// A deferred canceller function, see dojo.Deferred
var resultList = [];
dojo.Deferred.call(this);
var self = this;
if(list.length === 0 && !fireOnOneCallback){
this.resolve([0, []]);
}
var finished = 0;
dojo.forEach(list, function(item, i){
item.then(function(result){
if(fireOnOneCallback){
self.resolve([i, result]);
}else{
addResult(true, result);
}
},function(error){
if(fireOnOneErrback){
self.reject(error);
}else{
addResult(false, error);
}
if(consumeErrors){
return null;
}
throw error;
});
function addResult(succeeded, result){
resultList[i] = [succeeded, result];
finished++;
if(finished === list.length){
self.resolve(resultList);
}
}
});
};
dojo.DeferredList.prototype = new dojo.Deferred();
dojo.DeferredList.prototype.gatherResults= function(deferredList){
// summary:
// Gathers the results of the deferreds for packaging
// as the parameters to the Deferred Lists' callback
var d = new dojo.DeferredList(deferredList, false, true, false);
d.addCallback(function(results){
var ret = [];
dojo.forEach(results, function(result){
ret.push(result[1]);
});
return ret;
});
return d;
};
}
if(!dojo._hasResource["dijit.tree.TreeStoreModel"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dijit.tree.TreeStoreModel"] = true;
dojo.provide("dijit.tree.TreeStoreModel");
dojo.declare(
"dijit.tree.TreeStoreModel",
null,
{
// summary:
// Implements dijit.Tree.model connecting to a store with a single
// root item. Any methods passed into the constructor will override
// the ones defined here.
// store: dojo.data.Store
// Underlying store
store: null,
// childrenAttrs: String[]
// One or more attribute names (attributes in the dojo.data item) that specify that item's children
childrenAttrs: ["children"],
// newItemIdAttr: String
// Name of attribute in the Object passed to newItem() that specifies the id.
//
// If newItemIdAttr is set then it's used when newItem() is called to see if an
// item with the same id already exists, and if so just links to the old item
// (so that the old item ends up with two parents).
//
// Setting this to null or "" will make every drop create a new item.
newItemIdAttr: "id",
// labelAttr: String
// If specified, get label for tree node from this attribute, rather
// than by calling store.getLabel()
labelAttr: "",
// root: [readonly] dojo.data.Item
// Pointer to the root item (read only, not a parameter)
root: null,
// query: anything
// Specifies datastore query to return the root item for the tree.
// Must only return a single item. Alternately can just pass in pointer
// to root item.
// example:
// | {id:'ROOT'}
query: null,
// deferItemLoadingUntilExpand: Boolean
// Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
// until they are expanded. This allows for lazying loading where only one
// loadItem (and generally one network call, consequently) per expansion
// (rather than one for each child).
// This relies on partial loading of the children items; each children item of a
// fully loaded item should contain the label and info about having children.
deferItemLoadingUntilExpand: false,
constructor: function(/* Object */ args){
// summary:
// Passed the arguments listed above (store, etc)
// tags:
// private
dojo.mixin(this, args);
this.connects = [];
var store = this.store;
if(!store.getFeatures()['dojo.data.api.Identity']){
throw new Error("dijit.Tree: store must support dojo.data.Identity");
}
// if the store supports Notification, subscribe to the notification events
if(store.getFeatures()['dojo.data.api.Notification']){
this.connects = this.connects.concat([
dojo.connect(store, "onNew", this, "onNewItem"),
dojo.connect(store, "onDelete", this, "onDeleteItem"),
dojo.connect(store, "onSet", this, "onSetItem")
]);
}
},
destroy: function(){
dojo.forEach(this.connects, dojo.disconnect);
// TODO: should cancel any in-progress processing of getRoot(), getChildren()
},
// =======================================================================
// Methods for traversing hierarchy
getRoot: function(onItem, onError){
// summary:
// Calls onItem with the root item for the tree, possibly a fabricated item.
// Calls onError on error.
if(this.root){
onItem(this.root);
}else{
this.store.fetch({
query: this.query,
onComplete: dojo.hitch(this, function(items){
if(items.length != 1){
throw new Error(this.declaredClass + ": query " + dojo.toJson(this.query) + " returned " + items.length +
" items, but must return exactly one item");
}
this.root = items[0];
onItem(this.root);
}),
onError: onError
});
}
},
mayHaveChildren: function(/*dojo.data.Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
return dojo.some(this.childrenAttrs, function(attr){
return this.store.hasAttribute(item, attr);
}, this);
},
getChildren: function(/*dojo.data.Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
var store = this.store;
if(!store.isItemLoaded(parentItem)){
// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
// mode, so we will load it and just return the children (without loading each
// child item)
var getChildren = dojo.hitch(this, arguments.callee);
store.loadItem({
item: parentItem,
onItem: function(parentItem){
getChildren(parentItem, onComplete, onError);
},
onError: onError
});
return;
}
// get children of specified item
var childItems = [];
for(var i=0; i\n\t\t\t\n\t\t \n\t\n \n"),
baseClass: "dijitTreeNode",
// For hover effect for tree node, and focus effect for label
cssStateNodes: {
rowNode: "dijitTreeRow",
labelNode: "dijitTreeLabel"
},
attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, {
label: {node: "labelNode", type: "innerText"},
tooltip: {node: "rowNode", type: "attribute", attribute: "title"}
}),
postCreate: function(){
this.inherited(arguments);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
dijit.setWaiState(this.labelNode, "expanded", this.isExpanded);
}
},
_setIndentAttr: function(indent){
// summary:
// Tell this node how many levels it should be indented
// description:
// 0 for top level nodes, 1 for their children, 2 for their
// grandchildren, etc.
this.indent = indent;
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
dojo.style(this.domNode, "backgroundPosition", pixels + " 0px");
dojo.style(this.rowNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
dojo.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
},
markProcessing: function(){
// summary:
// Visually denote that tree is loading data, etc.
// tags:
// private
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary:
// Clear markup from markProcessing() call
// tags:
// private
this._setExpando(false);
},
_updateItemClasses: function(item){
// summary:
// Set appropriate CSS classes for icon and label dom node
// (used to allow for item updates to change respective CSS)
// tags:
// private
var tree = this.tree, model = tree.model;
if(tree._v10Compat && item === model.root){
// For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
item = null;
}
this._applyClassAndStyle(item, "icon", "Icon");
this._applyClassAndStyle(item, "label", "Label");
this._applyClassAndStyle(item, "row", "Row");
},
_applyClassAndStyle: function(item, lower, upper){
// summary:
// Set the appropriate CSS classes and styles for labels, icons and rows.
//
// item:
// The data item.
//
// lower:
// The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
//
// upper:
// The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
//
// tags:
// private
var clsName = "_" + lower + "Class";
var nodeName = lower + "Node";
if(this[clsName]){
dojo.removeClass(this[nodeName], this[clsName]);
}
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
if(this[clsName]){
dojo.addClass(this[nodeName], this[clsName]);
}
dojo.style(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
// summary:
// Set appropriate CSS classes for this.domNode
// tags:
// private
var parent = this.getParent();
if(!parent || parent.rowNode.style.display == "none"){
/* if we are hiding the root node then make every first level child look like a root node */
dojo.addClass(this.domNode, "dijitTreeIsRoot");
}else{
dojo.toggleClass(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary:
// Set the right image for the expando node
// tags:
// private
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
_a11yStates = ["*","-","+","*"],
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
dojo.removeClass(this.expandoNode, styles);
dojo.addClass(this.expandoNode, styles[idx]);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
},
expand: function(){
// summary:
// Show my children
// returns:
// Deferred that fires when expansion is complete
// If there's already an expand in progress or we are already expanded, just return
if(this._expandDeferred){
return this._expandDeferred; // dojo.Deferred
}
// cancel in progress collapse operation
this._wipeOut && this._wipeOut.stop();
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
dijit.setWaiState(this.labelNode, "expanded", "true");
dijit.setWaiRole(this.containerNode, "group");
dojo.addClass(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode){
dijit.setWaiState(this.tree.domNode, "expanded", "true");
}
var def,
wipeIn = dojo.fx.wipeIn({
node: this.containerNode, duration: dijit.defaultDuration,
onEnd: function(){
def.callback(true);
}
});
// Deferred that fires when expand is complete
def = (this._expandDeferred = new dojo.Deferred(function(){
// Canceller
wipeIn.stop();
}));
wipeIn.play();
return def; // dojo.Deferred
},
collapse: function(){
// summary:
// Collapse this node (if it's expanded)
if(!this.isExpanded){ return; }
// cancel in progress expand operation
if(this._expandDeferred){
this._expandDeferred.cancel();
delete this._expandDeferred;
}
this.isExpanded = false;
dijit.setWaiState(this.labelNode, "expanded", "false");
if(this == this.tree.rootNode){
dijit.setWaiState(this.tree.domNode, "expanded", "false");
}
dojo.removeClass(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(!this._wipeOut){
this._wipeOut = dojo.fx.wipeOut({
node: this.containerNode, duration: dijit.defaultDuration
});
}
this._wipeOut.play();
},
// indent: Integer
// Levels from this node to the root node
indent: 0,
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
// Also, if this.persist == true, expands any children that were previously
// opened.
// returns:
// Deferred object that fires after all previously opened children
// have been expanded again (or fires instantly if there are no such children).
var tree = this.tree,
model = tree.model,
defs = []; // list of deferreds that need to fire before I am complete
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
dojo.forEach(this.getChildren(), function(child){
dijit._Container.prototype.removeChild.call(this, child);
}, this);
this.state = "LOADED";
if(items && items.length > 0){
this.isExpandable = true;
// Create _TreeNode widget for each specified tree node, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
dojo.forEach(items, function(item){
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
if(existingNodes){
for(var i=0;i