diff options
Diffstat (limited to 'lib/dijit/dijit.js.uncompressed.js')
-rw-r--r-- | lib/dijit/dijit.js.uncompressed.js | 5961 |
1 files changed, 5961 insertions, 0 deletions
diff --git a/lib/dijit/dijit.js.uncompressed.js b/lib/dijit/dijit.js.uncompressed.js new file mode 100644 index 000000000..076a03208 --- /dev/null +++ b/lib/dijit/dijit.js.uncompressed.js @@ -0,0 +1,5961 @@ +/* + Copyright (c) 2004-2010, 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 +*/ + +/* + This is an optimized version of Dojo, built for deployment and not for + development. To get sources and documentation, please visit: + + http://dojotoolkit.org +*/ + +if(!dojo._hasResource["dojo.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.window"] = true; +dojo.provide("dojo.window"); + +dojo.window.getBox = function(){ + // summary: + // Returns the dimensions and scroll position of the viewable area of a browser window + + var scrollRoot = (dojo.doc.compatMode == 'BackCompat') ? dojo.body() : dojo.doc.documentElement; + + // get scroll position + var scroll = dojo._docScroll(); // scrollRoot.scrollTop/Left should work + return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y }; +}; + +dojo.window.get = function(doc){ + // summary: + // Get window object associated with document doc + + // In some IE versions (at least 6.0), document.parentWindow does not return a + // reference to the real window object (maybe a copy), so we must fix it as well + // We use IE specific execScript to attach the real window reference to + // document._parentWindow for later use + if(dojo.isIE && window !== document.parentWindow){ + /* + In IE 6, only the variable "window" can be used to connect events (others + may be only copies). + */ + doc.parentWindow.execScript("document._parentWindow = window;", "Javascript"); + //to prevent memory leak, unset it after use + //another possibility is to add an onUnload handler which seems overkill to me (liucougar) + var win = doc._parentWindow; + doc._parentWindow = null; + return win; // Window + } + + return doc.parentWindow || doc.defaultView; // Window +}; + +dojo.window.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){ + // summary: + // Scroll the passed node into view, if it is not already. + + // don't rely on node.scrollIntoView working just because the function is there + + try{ // catch unexpected/unrecreatable errors (#7808) since we can recover using a semi-acceptable native method + node = dojo.byId(node); + var doc = node.ownerDocument || dojo.doc, + body = doc.body || dojo.body(), + html = doc.documentElement || body.parentNode, + isIE = dojo.isIE, isWK = dojo.isWebKit; + // if an untested browser, then use the native method + if((!(dojo.isMoz || isIE || isWK || dojo.isOpera) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){ + node.scrollIntoView(false); // short-circuit to native if possible + return; + } + var backCompat = doc.compatMode == 'BackCompat', + clientAreaRoot = backCompat? body : html, + scrollRoot = isWK ? body : clientAreaRoot, + rootWidth = clientAreaRoot.clientWidth, + rootHeight = clientAreaRoot.clientHeight, + rtl = !dojo._isBodyLtr(), + nodePos = pos || dojo.position(node), + el = node.parentNode, + isFixed = function(el){ + return ((isIE <= 6 || (isIE && backCompat))? false : (dojo.style(el, 'position').toLowerCase() == "fixed")); + }; + if(isFixed(node)){ return; } // nothing to do + + while(el){ + if(el == body){ el = scrollRoot; } + var elPos = dojo.position(el), + fixedPos = isFixed(el); + + if(el == scrollRoot){ + elPos.w = rootWidth; elPos.h = rootHeight; + if(scrollRoot == html && isIE && rtl){ elPos.x += scrollRoot.offsetWidth-elPos.w; } // IE workaround where scrollbar causes negative x + if(elPos.x < 0 || !isIE){ elPos.x = 0; } // IE can have values > 0 + if(elPos.y < 0 || !isIE){ elPos.y = 0; } + }else{ + var pb = dojo._getPadBorderExtents(el); + elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t; + } + + if(el != scrollRoot){ // body, html sizes already have the scrollbar removed + var clientSize = el.clientWidth, + scrollBarSize = elPos.w - clientSize; + if(clientSize > 0 && scrollBarSize > 0){ + elPos.w = clientSize; + if(isIE && rtl){ elPos.x += scrollBarSize; } + } + clientSize = el.clientHeight; + scrollBarSize = elPos.h - clientSize; + if(clientSize > 0 && scrollBarSize > 0){ + elPos.h = clientSize; + } + } + if(fixedPos){ // bounded by viewport, not parents + if(elPos.y < 0){ + elPos.h += elPos.y; elPos.y = 0; + } + if(elPos.x < 0){ + elPos.w += elPos.x; elPos.x = 0; + } + if(elPos.y + elPos.h > rootHeight){ + elPos.h = rootHeight - elPos.y; + } + if(elPos.x + elPos.w > rootWidth){ + elPos.w = rootWidth - elPos.x; + } + } + // calculate overflow in all 4 directions + var l = nodePos.x - elPos.x, // beyond left: < 0 + t = nodePos.y - Math.max(elPos.y, 0), // beyond top: < 0 + r = l + nodePos.w - elPos.w, // beyond right: > 0 + bot = t + nodePos.h - elPos.h; // beyond bottom: > 0 + if(r * l > 0){ + var s = Math[l < 0? "max" : "min"](l, r); + nodePos.x += el.scrollLeft; + el.scrollLeft += (isIE >= 8 && !backCompat && rtl)? -s : s; + nodePos.x -= el.scrollLeft; + } + if(bot * t > 0){ + nodePos.y += el.scrollTop; + el.scrollTop += Math[t < 0? "max" : "min"](t, bot); + nodePos.y -= el.scrollTop; + } + el = (el != scrollRoot) && !fixedPos && el.parentNode; + } + }catch(error){ + console.error('scrollIntoView: ' + error); + node.scrollIntoView(false); + } +}; + +} + +if(!dojo._hasResource["dijit._base.manager"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.manager"] = true; +dojo.provide("dijit._base.manager"); + +dojo.declare("dijit.WidgetSet", null, { + // summary: + // A set of widgets indexed by id. A default instance of this class is + // available as `dijit.registry` + // + // example: + // Create a small list of widgets: + // | var ws = new dijit.WidgetSet(); + // | ws.add(dijit.byId("one")); + // | ws.add(dijit.byId("two")); + // | // destroy both: + // | ws.forEach(function(w){ w.destroy(); }); + // + // example: + // Using dijit.registry: + // | dijit.registry.forEach(function(w){ /* do something */ }); + + constructor: function(){ + this._hash = {}; + this.length = 0; + }, + + add: function(/*dijit._Widget*/ widget){ + // summary: + // Add a widget to this list. If a duplicate ID is detected, a error is thrown. + // + // widget: dijit._Widget + // Any dijit._Widget subclass. + if(this._hash[widget.id]){ + throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered"); + } + this._hash[widget.id] = widget; + this.length++; + }, + + remove: function(/*String*/ id){ + // summary: + // Remove a widget from this WidgetSet. Does not destroy the widget; simply + // removes the reference. + if(this._hash[id]){ + delete this._hash[id]; + this.length--; + } + }, + + forEach: function(/*Function*/ func, /* Object? */thisObj){ + // summary: + // Call specified function for each widget in this set. + // + // func: + // A callback function to run for each item. Is passed the widget, the index + // in the iteration, and the full hash, similar to `dojo.forEach`. + // + // thisObj: + // An optional scope parameter + // + // example: + // Using the default `dijit.registry` instance: + // | dijit.registry.forEach(function(widget){ + // | console.log(widget.declaredClass); + // | }); + // + // returns: + // Returns self, in order to allow for further chaining. + + thisObj = thisObj || dojo.global; + var i = 0, id; + for(id in this._hash){ + func.call(thisObj, this._hash[id], i++, this._hash); + } + return this; // dijit.WidgetSet + }, + + filter: function(/*Function*/ filter, /* Object? */thisObj){ + // summary: + // Filter down this WidgetSet to a smaller new WidgetSet + // Works the same as `dojo.filter` and `dojo.NodeList.filter` + // + // filter: + // Callback function to test truthiness. Is passed the widget + // reference and the pseudo-index in the object. + // + // thisObj: Object? + // Option scope to use for the filter function. + // + // example: + // Arbitrary: select the odd widgets in this list + // | dijit.registry.filter(function(w, i){ + // | return i % 2 == 0; + // | }).forEach(function(w){ /* odd ones */ }); + + thisObj = thisObj || dojo.global; + var res = new dijit.WidgetSet(), i = 0, id; + for(id in this._hash){ + var w = this._hash[id]; + if(filter.call(thisObj, w, i++, this._hash)){ + res.add(w); + } + } + return res; // dijit.WidgetSet + }, + + byId: function(/*String*/ id){ + // summary: + // Find a widget in this list by it's id. + // example: + // Test if an id is in a particular WidgetSet + // | var ws = new dijit.WidgetSet(); + // | ws.add(dijit.byId("bar")); + // | var t = ws.byId("bar") // returns a widget + // | var x = ws.byId("foo"); // returns undefined + + return this._hash[id]; // dijit._Widget + }, + + byClass: function(/*String*/ cls){ + // summary: + // Reduce this widgetset to a new WidgetSet of a particular `declaredClass` + // + // cls: String + // The Class to scan for. Full dot-notated string. + // + // example: + // Find all `dijit.TitlePane`s in a page: + // | dijit.registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); }); + + var res = new dijit.WidgetSet(), id, widget; + for(id in this._hash){ + widget = this._hash[id]; + if(widget.declaredClass == cls){ + res.add(widget); + } + } + return res; // dijit.WidgetSet +}, + + toArray: function(){ + // summary: + // Convert this WidgetSet into a true Array + // + // example: + // Work with the widget .domNodes in a real Array + // | dojo.map(dijit.registry.toArray(), function(w){ return w.domNode; }); + + var ar = []; + for(var id in this._hash){ + ar.push(this._hash[id]); + } + return ar; // dijit._Widget[] +}, + + map: function(/* Function */func, /* Object? */thisObj){ + // summary: + // Create a new Array from this WidgetSet, following the same rules as `dojo.map` + // example: + // | var nodes = dijit.registry.map(function(w){ return w.domNode; }); + // + // returns: + // A new array of the returned values. + return dojo.map(this.toArray(), func, thisObj); // Array + }, + + every: function(func, thisObj){ + // summary: + // A synthetic clone of `dojo.every` acting explicitly on this WidgetSet + // + // func: Function + // A callback function run for every widget in this list. Exits loop + // when the first false return is encountered. + // + // thisObj: Object? + // Optional scope parameter to use for the callback + + thisObj = thisObj || dojo.global; + var x = 0, i; + for(i in this._hash){ + if(!func.call(thisObj, this._hash[i], x++, this._hash)){ + return false; // Boolean + } + } + return true; // Boolean + }, + + some: function(func, thisObj){ + // summary: + // A synthetic clone of `dojo.some` acting explictly on this WidgetSet + // + // func: Function + // A callback function run for every widget in this list. Exits loop + // when the first true return is encountered. + // + // thisObj: Object? + // Optional scope parameter to use for the callback + + thisObj = thisObj || dojo.global; + var x = 0, i; + for(i in this._hash){ + if(func.call(thisObj, this._hash[i], x++, this._hash)){ + return true; // Boolean + } + } + return false; // Boolean + } + +}); + +(function(){ + + /*===== + dijit.registry = { + // summary: + // A list of widgets on a page. + // description: + // Is an instance of `dijit.WidgetSet` + }; + =====*/ + dijit.registry = new dijit.WidgetSet(); + + var hash = dijit.registry._hash, + attr = dojo.attr, + hasAttr = dojo.hasAttr, + style = dojo.style; + + dijit.byId = function(/*String|dijit._Widget*/ id){ + // summary: + // Returns a widget by it's id, or if passed a widget, no-op (like dojo.byId()) + return typeof id == "string" ? hash[id] : id; // dijit._Widget + }; + + var _widgetTypeCtr = {}; + dijit.getUniqueId = function(/*String*/widgetType){ + // summary: + // Generates a unique id for a given widgetType + + var id; + do{ + id = widgetType + "_" + + (widgetType in _widgetTypeCtr ? + ++_widgetTypeCtr[widgetType] : _widgetTypeCtr[widgetType] = 0); + }while(hash[id]); + return dijit._scopeName == "dijit" ? id : dijit._scopeName + "_" + id; // String + }; + + dijit.findWidgets = function(/*DomNode*/ root){ + // summary: + // Search subtree under root returning widgets found. + // Doesn't search for nested widgets (ie, widgets inside other widgets). + + var outAry = []; + + function getChildrenHelper(root){ + for(var node = root.firstChild; node; node = node.nextSibling){ + if(node.nodeType == 1){ + var widgetId = node.getAttribute("widgetId"); + if(widgetId){ + outAry.push(hash[widgetId]); + }else{ + getChildrenHelper(node); + } + } + } + } + + getChildrenHelper(root); + return outAry; + }; + + dijit._destroyAll = function(){ + // summary: + // Code to destroy all widgets and do other cleanup on page unload + + // Clean up focus manager lingering references to widgets and nodes + dijit._curFocus = null; + dijit._prevFocus = null; + dijit._activeStack = []; + + // Destroy all the widgets, top down + dojo.forEach(dijit.findWidgets(dojo.body()), function(widget){ + // Avoid double destroy of widgets like Menu that are attached to <body> + // even though they are logically children of other widgets. + if(!widget._destroyed){ + if(widget.destroyRecursive){ + widget.destroyRecursive(); + }else if(widget.destroy){ + widget.destroy(); + } + } + }); + }; + + if(dojo.isIE){ + // Only run _destroyAll() for IE because we think it's only necessary in that case, + // and because it causes problems on FF. See bug #3531 for details. + dojo.addOnWindowUnload(function(){ + dijit._destroyAll(); + }); + } + + dijit.byNode = function(/*DOMNode*/ node){ + // summary: + // Returns the widget corresponding to the given DOMNode + return hash[node.getAttribute("widgetId")]; // dijit._Widget + }; + + dijit.getEnclosingWidget = function(/*DOMNode*/ node){ + // summary: + // Returns the widget whose DOM tree contains the specified DOMNode, or null if + // the node is not contained within the DOM tree of any widget + while(node){ + var id = node.getAttribute && node.getAttribute("widgetId"); + if(id){ + return hash[id]; + } + node = node.parentNode; + } + return null; + }; + + var shown = (dijit._isElementShown = function(/*Element*/ elem){ + var s = style(elem); + return (s.visibility != "hidden") + && (s.visibility != "collapsed") + && (s.display != "none") + && (attr(elem, "type") != "hidden"); + }); + + dijit.hasDefaultTabStop = function(/*Element*/ elem){ + // summary: + // Tests if element is tab-navigable even without an explicit tabIndex setting + + // No explicit tabIndex setting, need to investigate node type + switch(elem.nodeName.toLowerCase()){ + case "a": + // An <a> w/out a tabindex is only navigable if it has an href + return hasAttr(elem, "href"); + case "area": + case "button": + case "input": + case "object": + case "select": + case "textarea": + // These are navigable by default + return true; + case "iframe": + // If it's an editor <iframe> then it's tab navigable. + //TODO: feature detect "designMode" in elem.contentDocument? + if(dojo.isMoz){ + try{ + return elem.contentDocument.designMode == "on"; + }catch(err){ + return false; + } + }else if(dojo.isWebKit){ + var doc = elem.contentDocument, + body = doc && doc.body; + return body && body.contentEditable == 'true'; + }else{ + // contentWindow.document isn't accessible within IE7/8 + // if the iframe.src points to a foreign url and this + // page contains an element, that could get focus + try{ + doc = elem.contentWindow.document; + body = doc && doc.body; + return body && body.firstChild && body.firstChild.contentEditable == 'true'; + }catch(e){ + return false; + } + } + default: + return elem.contentEditable == 'true'; + } + }; + + var isTabNavigable = (dijit.isTabNavigable = function(/*Element*/ elem){ + // summary: + // Tests if an element is tab-navigable + + // TODO: convert (and rename method) to return effective tabIndex; will save time in _getTabNavigable() + if(attr(elem, "disabled")){ + return false; + }else if(hasAttr(elem, "tabIndex")){ + // Explicit tab index setting + return attr(elem, "tabIndex") >= 0; // boolean + }else{ + // No explicit tabIndex setting, so depends on node type + return dijit.hasDefaultTabStop(elem); + } + }); + + dijit._getTabNavigable = function(/*DOMNode*/ root){ + // summary: + // Finds descendants of the specified root node. + // + // description: + // Finds the following descendants of the specified root node: + // * the first tab-navigable element in document order + // without a tabIndex or with tabIndex="0" + // * the last tab-navigable element in document order + // without a tabIndex or with tabIndex="0" + // * the first element in document order with the lowest + // positive tabIndex value + // * the last element in document order with the highest + // positive tabIndex value + var first, last, lowest, lowestTabindex, highest, highestTabindex; + var walkTree = function(/*DOMNode*/parent){ + dojo.query("> *", parent).forEach(function(child){ + // Skip hidden elements, and also non-HTML elements (those in custom namespaces) in IE, + // since show() invokes getAttribute("type"), which crash on VML nodes in IE. + if((dojo.isIE && child.scopeName!=="HTML") || !shown(child)){ + return; + } + + if(isTabNavigable(child)){ + var tabindex = attr(child, "tabIndex"); + if(!hasAttr(child, "tabIndex") || tabindex == 0){ + if(!first){ first = child; } + last = child; + }else if(tabindex > 0){ + if(!lowest || tabindex < lowestTabindex){ + lowestTabindex = tabindex; + lowest = child; + } + if(!highest || tabindex >= highestTabindex){ + highestTabindex = tabindex; + highest = child; + } + } + } + if(child.nodeName.toUpperCase() != 'SELECT'){ + walkTree(child); + } + }); + }; + if(shown(root)){ walkTree(root) } + return { first: first, last: last, lowest: lowest, highest: highest }; + } + dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root){ + // summary: + // Finds the descendant of the specified root node + // that is first in the tabbing order + var elems = dijit._getTabNavigable(dojo.byId(root)); + return elems.lowest ? elems.lowest : elems.first; // DomNode + }; + + dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root){ + // summary: + // Finds the descendant of the specified root node + // that is last in the tabbing order + var elems = dijit._getTabNavigable(dojo.byId(root)); + return elems.last ? elems.last : elems.highest; // DomNode + }; + + /*===== + dojo.mixin(dijit, { + // defaultDuration: Integer + // The default animation speed (in ms) to use for all Dijit + // transitional animations, unless otherwise specified + // on a per-instance basis. Defaults to 200, overrided by + // `djConfig.defaultDuration` + defaultDuration: 200 + }); + =====*/ + + dijit.defaultDuration = dojo.config["defaultDuration"] || 200; + +})(); + +} + +if(!dojo._hasResource["dijit._base.focus"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.focus"] = true; +dojo.provide("dijit._base.focus"); + + + // for dijit.isTabNavigable() + +// summary: +// These functions are used to query or set the focus and selection. +// +// Also, they trace when widgets become activated/deactivated, +// so that the widget can fire _onFocus/_onBlur events. +// "Active" here means something similar to "focused", but +// "focus" isn't quite the right word because we keep track of +// a whole stack of "active" widgets. Example: ComboButton --> Menu --> +// MenuItem. The onBlur event for ComboButton doesn't fire due to focusing +// on the Menu or a MenuItem, since they are considered part of the +// ComboButton widget. It only happens when focus is shifted +// somewhere completely different. + +dojo.mixin(dijit, { + // _curFocus: DomNode + // Currently focused item on screen + _curFocus: null, + + // _prevFocus: DomNode + // Previously focused item on screen + _prevFocus: null, + + isCollapsed: function(){ + // summary: + // Returns true if there is no text selected + return dijit.getBookmark().isCollapsed; + }, + + getBookmark: function(){ + // summary: + // Retrieves a bookmark that can be used with moveToBookmark to return to the same range + var bm, rg, tg, sel = dojo.doc.selection, cf = dijit._curFocus; + + if(dojo.global.getSelection){ + //W3C Range API for selections. + sel = dojo.global.getSelection(); + if(sel){ + if(sel.isCollapsed){ + tg = cf? cf.tagName : ""; + if(tg){ + //Create a fake rangelike item to restore selections. + tg = tg.toLowerCase(); + if(tg == "textarea" || + (tg == "input" && (!cf.type || cf.type.toLowerCase() == "text"))){ + sel = { + start: cf.selectionStart, + end: cf.selectionEnd, + node: cf, + pRange: true + }; + return {isCollapsed: (sel.end <= sel.start), mark: sel}; //Object. + } + } + bm = {isCollapsed:true}; + }else{ + rg = sel.getRangeAt(0); + bm = {isCollapsed: false, mark: rg.cloneRange()}; + } + } + }else if(sel){ + // If the current focus was a input of some sort and no selection, don't bother saving + // a native bookmark. This is because it causes issues with dialog/page selection restore. + // So, we need to create psuedo bookmarks to work with. + tg = cf ? cf.tagName : ""; + tg = tg.toLowerCase(); + if(cf && tg && (tg == "button" || tg == "textarea" || tg == "input")){ + if(sel.type && sel.type.toLowerCase() == "none"){ + return { + isCollapsed: true, + mark: null + } + }else{ + rg = sel.createRange(); + return { + isCollapsed: rg.text && rg.text.length?false:true, + mark: { + range: rg, + pRange: true + } + }; + } + } + bm = {}; + + //'IE' way for selections. + try{ + // createRange() throws exception when dojo in iframe + //and nothing selected, see #9632 + rg = sel.createRange(); + bm.isCollapsed = !(sel.type == 'Text' ? rg.htmlText.length : rg.length); + }catch(e){ + bm.isCollapsed = true; + return bm; + } + if(sel.type.toUpperCase() == 'CONTROL'){ + if(rg.length){ + bm.mark=[]; + var i=0,len=rg.length; + while(i<len){ + bm.mark.push(rg.item(i++)); + } + }else{ + bm.isCollapsed = true; + bm.mark = null; + } + }else{ + bm.mark = rg.getBookmark(); + } + }else{ + console.warn("No idea how to store the current selection for this browser!"); + } + return bm; // Object + }, + + moveToBookmark: function(/*Object*/bookmark){ + // summary: + // Moves current selection to a bookmark + // bookmark: + // This should be a returned object from dijit.getBookmark() + + var _doc = dojo.doc, + mark = bookmark.mark; + if(mark){ + if(dojo.global.getSelection){ + //W3C Rangi API (FF, WebKit, Opera, etc) + var sel = dojo.global.getSelection(); + if(sel && sel.removeAllRanges){ + if(mark.pRange){ + var r = mark; + var n = r.node; + n.selectionStart = r.start; + n.selectionEnd = r.end; + }else{ + sel.removeAllRanges(); + sel.addRange(mark); + } + }else{ + console.warn("No idea how to restore selection for this browser!"); + } + }else if(_doc.selection && mark){ + //'IE' way. + var rg; + if(mark.pRange){ + rg = mark.range; + }else if(dojo.isArray(mark)){ + rg = _doc.body.createControlRange(); + //rg.addElement does not have call/apply method, so can not call it directly + //rg is not available in "range.addElement(item)", so can't use that either + dojo.forEach(mark, function(n){ + rg.addElement(n); + }); + }else{ + rg = _doc.body.createTextRange(); + rg.moveToBookmark(mark); + } + rg.select(); + } + } + }, + + getFocus: function(/*Widget?*/ menu, /*Window?*/ openedForWindow){ + // summary: + // Called as getFocus(), this returns an Object showing the current focus + // and selected text. + // + // Called as getFocus(widget), where widget is a (widget representing) a button + // that was just pressed, it returns where focus was before that button + // was pressed. (Pressing the button may have either shifted focus to the button, + // or removed focus altogether.) In this case the selected text is not returned, + // since it can't be accurately determined. + // + // menu: dijit._Widget or {domNode: DomNode} structure + // The button that was just pressed. If focus has disappeared or moved + // to this button, returns the previous focus. In this case the bookmark + // information is already lost, and null is returned. + // + // openedForWindow: + // iframe in which menu was opened + // + // returns: + // A handle to restore focus/selection, to be passed to `dijit.focus` + var node = !dijit._curFocus || (menu && dojo.isDescendant(dijit._curFocus, menu.domNode)) ? dijit._prevFocus : dijit._curFocus; + return { + node: node, + bookmark: (node == dijit._curFocus) && dojo.withGlobal(openedForWindow || dojo.global, dijit.getBookmark), + openedForWindow: openedForWindow + }; // Object + }, + + focus: function(/*Object || DomNode */ handle){ + // summary: + // Sets the focused node and the selection according to argument. + // To set focus to an iframe's content, pass in the iframe itself. + // handle: + // object returned by get(), or a DomNode + + if(!handle){ return; } + + var node = "node" in handle ? handle.node : handle, // because handle is either DomNode or a composite object + bookmark = handle.bookmark, + openedForWindow = handle.openedForWindow, + collapsed = bookmark ? bookmark.isCollapsed : false; + + // Set the focus + // Note that for iframe's we need to use the <iframe> to follow the parentNode chain, + // but we need to set focus to iframe.contentWindow + if(node){ + var focusNode = (node.tagName.toLowerCase() == "iframe") ? node.contentWindow : node; + if(focusNode && focusNode.focus){ + try{ + // Gecko throws sometimes if setting focus is impossible, + // node not displayed or something like that + focusNode.focus(); + }catch(e){/*quiet*/} + } + dijit._onFocusNode(node); + } + + // set the selection + // do not need to restore if current selection is not empty + // (use keyboard to select a menu item) or if previous selection was collapsed + // as it may cause focus shift (Esp in IE). + if(bookmark && dojo.withGlobal(openedForWindow || dojo.global, dijit.isCollapsed) && !collapsed){ + if(openedForWindow){ + openedForWindow.focus(); + } + try{ + dojo.withGlobal(openedForWindow || dojo.global, dijit.moveToBookmark, null, [bookmark]); + }catch(e2){ + /*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */ + } + } + }, + + // _activeStack: dijit._Widget[] + // List of currently active widgets (focused widget and it's ancestors) + _activeStack: [], + + registerIframe: function(/*DomNode*/ iframe){ + // summary: + // Registers listeners on the specified iframe so that any click + // or focus event on that iframe (or anything in it) is reported + // as a focus/click event on the <iframe> itself. + // description: + // Currently only used by editor. + // returns: + // Handle to pass to unregisterIframe() + return dijit.registerWin(iframe.contentWindow, iframe); + }, + + unregisterIframe: function(/*Object*/ handle){ + // summary: + // Unregisters listeners on the specified iframe created by registerIframe. + // After calling be sure to delete or null out the handle itself. + // handle: + // Handle returned by registerIframe() + + dijit.unregisterWin(handle); + }, + + registerWin: function(/*Window?*/targetWindow, /*DomNode?*/ effectiveNode){ + // summary: + // Registers listeners on the specified window (either the main + // window or an iframe's window) to detect when the user has clicked somewhere + // or focused somewhere. + // description: + // Users should call registerIframe() instead of this method. + // targetWindow: + // If specified this is the window associated with the iframe, + // i.e. iframe.contentWindow. + // effectiveNode: + // If specified, report any focus events inside targetWindow as + // an event on effectiveNode, rather than on evt.target. + // returns: + // Handle to pass to unregisterWin() + + // TODO: make this function private in 2.0; Editor/users should call registerIframe(), + + var mousedownListener = function(evt){ + dijit._justMouseDowned = true; + setTimeout(function(){ dijit._justMouseDowned = false; }, 0); + + // workaround weird IE bug where the click is on an orphaned node + // (first time clicking a Select/DropDownButton inside a TooltipDialog) + if(dojo.isIE && evt && evt.srcElement && evt.srcElement.parentNode == null){ + return; + } + + dijit._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse"); + }; + //dojo.connect(targetWindow, "onscroll", ???); + + // Listen for blur and focus events on targetWindow's document. + // IIRC, I'm using attachEvent() rather than dojo.connect() because focus/blur events don't bubble + // through dojo.connect(), and also maybe to catch the focus events early, before onfocus handlers + // fire. + // Connect to <html> (rather than document) on IE to avoid memory leaks, but document on other browsers because + // (at least for FF) the focus event doesn't fire on <html> or <body>. + var doc = dojo.isIE ? targetWindow.document.documentElement : targetWindow.document; + if(doc){ + if(dojo.isIE){ + doc.attachEvent('onmousedown', mousedownListener); + var activateListener = function(evt){ + // IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1, + // Should consider those more like a mouse-click than a focus.... + if(evt.srcElement.tagName.toLowerCase() != "#document" && + dijit.isTabNavigable(evt.srcElement)){ + dijit._onFocusNode(effectiveNode || evt.srcElement); + }else{ + dijit._onTouchNode(effectiveNode || evt.srcElement); + } + }; + doc.attachEvent('onactivate', activateListener); + var deactivateListener = function(evt){ + dijit._onBlurNode(effectiveNode || evt.srcElement); + }; + doc.attachEvent('ondeactivate', deactivateListener); + + return function(){ + doc.detachEvent('onmousedown', mousedownListener); + doc.detachEvent('onactivate', activateListener); + doc.detachEvent('ondeactivate', deactivateListener); + doc = null; // prevent memory leak (apparent circular reference via closure) + }; + }else{ + doc.addEventListener('mousedown', mousedownListener, true); + var focusListener = function(evt){ + dijit._onFocusNode(effectiveNode || evt.target); + }; + doc.addEventListener('focus', focusListener, true); + var blurListener = function(evt){ + dijit._onBlurNode(effectiveNode || evt.target); + }; + doc.addEventListener('blur', blurListener, true); + + return function(){ + doc.removeEventListener('mousedown', mousedownListener, true); + doc.removeEventListener('focus', focusListener, true); + doc.removeEventListener('blur', blurListener, true); + doc = null; // prevent memory leak (apparent circular reference via closure) + }; + } + } + }, + + unregisterWin: function(/*Handle*/ handle){ + // summary: + // Unregisters listeners on the specified window (either the main + // window or an iframe's window) according to handle returned from registerWin(). + // After calling be sure to delete or null out the handle itself. + + // Currently our handle is actually a function + handle && handle(); + }, + + _onBlurNode: function(/*DomNode*/ node){ + // summary: + // Called when focus leaves a node. + // Usually ignored, _unless_ it *isn't* follwed by touching another node, + // which indicates that we tabbed off the last field on the page, + // in which case every widget is marked inactive + dijit._prevFocus = dijit._curFocus; + dijit._curFocus = null; + + if(dijit._justMouseDowned){ + // the mouse down caused a new widget to be marked as active; this blur event + // is coming late, so ignore it. + return; + } + + // if the blur event isn't followed by a focus event then mark all widgets as inactive. + if(dijit._clearActiveWidgetsTimer){ + clearTimeout(dijit._clearActiveWidgetsTimer); + } + dijit._clearActiveWidgetsTimer = setTimeout(function(){ + delete dijit._clearActiveWidgetsTimer; + dijit._setStack([]); + dijit._prevFocus = null; + }, 100); + }, + + _onTouchNode: function(/*DomNode*/ node, /*String*/ by){ + // summary: + // Callback when node is focused or mouse-downed + // node: + // The node that was touched. + // by: + // "mouse" if the focus/touch was caused by a mouse down event + + // ignore the recent blurNode event + if(dijit._clearActiveWidgetsTimer){ + clearTimeout(dijit._clearActiveWidgetsTimer); + delete dijit._clearActiveWidgetsTimer; + } + + // compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem) + var newStack=[]; + try{ + while(node){ + var popupParent = dojo.attr(node, "dijitPopupParent"); + if(popupParent){ + node=dijit.byId(popupParent).domNode; + }else if(node.tagName && node.tagName.toLowerCase() == "body"){ + // is this the root of the document or just the root of an iframe? + if(node === dojo.body()){ + // node is the root of the main document + break; + } + // otherwise, find the iframe this node refers to (can't access it via parentNode, + // need to do this trick instead). window.frameElement is supported in IE/FF/Webkit + node=dojo.window.get(node.ownerDocument).frameElement; + }else{ + // if this node is the root node of a widget, then add widget id to stack, + // except ignore clicks on disabled widgets (actually focusing a disabled widget still works, + // to support MenuItem) + var id = node.getAttribute && node.getAttribute("widgetId"), + widget = id && dijit.byId(id); + if(widget && !(by == "mouse" && widget.get("disabled"))){ + newStack.unshift(id); + } + node=node.parentNode; + } + } + }catch(e){ /* squelch */ } + + dijit._setStack(newStack, by); + }, + + _onFocusNode: function(/*DomNode*/ node){ + // summary: + // Callback when node is focused + + if(!node){ + return; + } + + if(node.nodeType == 9){ + // Ignore focus events on the document itself. This is here so that + // (for example) clicking the up/down arrows of a spinner + // (which don't get focus) won't cause that widget to blur. (FF issue) + return; + } + + dijit._onTouchNode(node); + + if(node == dijit._curFocus){ return; } + if(dijit._curFocus){ + dijit._prevFocus = dijit._curFocus; + } + dijit._curFocus = node; + dojo.publish("focusNode", [node]); + }, + + _setStack: function(/*String[]*/ newStack, /*String*/ by){ + // summary: + // The stack of active widgets has changed. Send out appropriate events and records new stack. + // newStack: + // array of widget id's, starting from the top (outermost) widget + // by: + // "mouse" if the focus/touch was caused by a mouse down event + + var oldStack = dijit._activeStack; + dijit._activeStack = newStack; + + // compare old stack to new stack to see how many elements they have in common + for(var nCommon=0; nCommon<Math.min(oldStack.length, newStack.length); nCommon++){ + if(oldStack[nCommon] != newStack[nCommon]){ + break; + } + } + + var widget; + // for all elements that have gone out of focus, send blur event + for(var i=oldStack.length-1; i>=nCommon; i--){ + widget = dijit.byId(oldStack[i]); + if(widget){ + widget._focused = false; + widget._hasBeenBlurred = true; + if(widget._onBlur){ + widget._onBlur(by); + } + dojo.publish("widgetBlur", [widget, by]); + } + } + + // for all element that have come into focus, send focus event + for(i=nCommon; i<newStack.length; i++){ + widget = dijit.byId(newStack[i]); + if(widget){ + widget._focused = true; + if(widget._onFocus){ + widget._onFocus(by); + } + dojo.publish("widgetFocus", [widget, by]); + } + } + } +}); + +// register top window and all the iframes it contains +dojo.addOnLoad(function(){ + var handle = dijit.registerWin(window); + if(dojo.isIE){ + dojo.addOnWindowUnload(function(){ + dijit.unregisterWin(handle); + handle = null; + }) + } +}); + +} + +if(!dojo._hasResource["dojo.AdapterRegistry"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.AdapterRegistry"] = true; +dojo.provide("dojo.AdapterRegistry"); + +dojo.AdapterRegistry = function(/*Boolean?*/ returnWrappers){ + // summary: + // A registry to make contextual calling/searching easier. + // description: + // Objects of this class keep list of arrays in the form [name, check, + // wrap, directReturn] that are used to determine what the contextual + // result of a set of checked arguments is. All check/wrap functions + // in this registry should be of the same arity. + // example: + // | // create a new registry + // | var reg = new dojo.AdapterRegistry(); + // | reg.register("handleString", + // | dojo.isString, + // | function(str){ + // | // do something with the string here + // | } + // | ); + // | reg.register("handleArr", + // | dojo.isArray, + // | function(arr){ + // | // do something with the array here + // | } + // | ); + // | + // | // now we can pass reg.match() *either* an array or a string and + // | // the value we pass will get handled by the right function + // | reg.match("someValue"); // will call the first function + // | reg.match(["someValue"]); // will call the second + + this.pairs = []; + this.returnWrappers = returnWrappers || false; // Boolean +} + +dojo.extend(dojo.AdapterRegistry, { + register: function(/*String*/ name, /*Function*/ check, /*Function*/ wrap, /*Boolean?*/ directReturn, /*Boolean?*/ override){ + // summary: + // register a check function to determine if the wrap function or + // object gets selected + // name: + // a way to identify this matcher. + // check: + // a function that arguments are passed to from the adapter's + // match() function. The check function should return true if the + // given arguments are appropriate for the wrap function. + // directReturn: + // If directReturn is true, the value passed in for wrap will be + // returned instead of being called. Alternately, the + // AdapterRegistry can be set globally to "return not call" using + // the returnWrappers property. Either way, this behavior allows + // the registry to act as a "search" function instead of a + // function interception library. + // override: + // If override is given and true, the check function will be given + // highest priority. Otherwise, it will be the lowest priority + // adapter. + this.pairs[((override) ? "unshift" : "push")]([name, check, wrap, directReturn]); + }, + + match: function(/* ... */){ + // summary: + // Find an adapter for the given arguments. If no suitable adapter + // is found, throws an exception. match() accepts any number of + // arguments, all of which are passed to all matching functions + // from the registered pairs. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[1].apply(this, arguments)){ + if((pair[3])||(this.returnWrappers)){ + return pair[2]; + }else{ + return pair[2].apply(this, arguments); + } + } + } + throw new Error("No match found"); + }, + + unregister: function(name){ + // summary: Remove a named adapter from the registry + + // FIXME: this is kind of a dumb way to handle this. On a large + // registry this will be slow-ish and we can use the name as a lookup + // should we choose to trade memory for speed. + for(var i = 0; i < this.pairs.length; i++){ + var pair = this.pairs[i]; + if(pair[0] == name){ + this.pairs.splice(i, 1); + return true; + } + } + return false; + } +}); + +} + +if(!dojo._hasResource["dijit._base.place"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.place"] = true; +dojo.provide("dijit._base.place"); + + + + + +dijit.getViewport = function(){ + // summary: + // Returns the dimensions and scroll position of the viewable area of a browser window + + return dojo.window.getBox(); +}; + +/*===== +dijit.__Position = function(){ + // x: Integer + // horizontal coordinate in pixels, relative to document body + // y: Integer + // vertical coordinate in pixels, relative to document body + + thix.x = x; + this.y = y; +} +=====*/ + + +dijit.placeOnScreen = function( + /* DomNode */ node, + /* dijit.__Position */ pos, + /* String[] */ corners, + /* dijit.__Position? */ padding){ + // summary: + // Positions one of the node's corners at specified position + // such that node is fully visible in viewport. + // description: + // NOTE: node is assumed to be absolutely or relatively positioned. + // pos: + // Object like {x: 10, y: 20} + // corners: + // Array of Strings representing order to try corners in, like ["TR", "BL"]. + // Possible values are: + // * "BL" - bottom left + // * "BR" - bottom right + // * "TL" - top left + // * "TR" - top right + // padding: + // set padding to put some buffer around the element you want to position. + // example: + // Try to place node's top right corner at (10,20). + // If that makes node go (partially) off screen, then try placing + // bottom left corner at (10,20). + // | placeOnScreen(node, {x: 10, y: 20}, ["TR", "BL"]) + + var choices = dojo.map(corners, function(corner){ + var c = { corner: corner, pos: {x:pos.x,y:pos.y} }; + if(padding){ + c.pos.x += corner.charAt(1) == 'L' ? padding.x : -padding.x; + c.pos.y += corner.charAt(0) == 'T' ? padding.y : -padding.y; + } + return c; + }); + + return dijit._place(node, choices); +} + +dijit._place = function(/*DomNode*/ node, /* Array */ choices, /* Function */ layoutNode){ + // summary: + // Given a list of spots to put node, put it at the first spot where it fits, + // of if it doesn't fit anywhere then the place with the least overflow + // choices: Array + // Array of elements like: {corner: 'TL', pos: {x: 10, y: 20} } + // Above example says to put the top-left corner of the node at (10,20) + // layoutNode: Function(node, aroundNodeCorner, nodeCorner) + // for things like tooltip, they are displayed differently (and have different dimensions) + // based on their orientation relative to the parent. This adjusts the popup based on orientation. + + // get {x: 10, y: 10, w: 100, h:100} type obj representing position of + // viewport over document + var view = dojo.window.getBox(); + + // This won't work if the node is inside a <div style="position: relative">, + // so reattach it to dojo.doc.body. (Otherwise, the positioning will be wrong + // and also it might get cutoff) + if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){ + dojo.body().appendChild(node); + } + + var best = null; + dojo.some(choices, function(choice){ + var corner = choice.corner; + var pos = choice.pos; + + // configure node to be displayed in given position relative to button + // (need to do this in order to get an accurate size for the node, because + // a tooltips size changes based on position, due to triangle) + if(layoutNode){ + layoutNode(node, choice.aroundCorner, corner); + } + + // get node's size + var style = node.style; + var oldDisplay = style.display; + var oldVis = style.visibility; + style.visibility = "hidden"; + style.display = ""; + var mb = dojo.marginBox(node); + style.display = oldDisplay; + style.visibility = oldVis; + + // coordinates and size of node with specified corner placed at pos, + // and clipped by viewport + var startX = Math.max(view.l, corner.charAt(1) == 'L' ? pos.x : (pos.x - mb.w)), + startY = Math.max(view.t, corner.charAt(0) == 'T' ? pos.y : (pos.y - mb.h)), + endX = Math.min(view.l + view.w, corner.charAt(1) == 'L' ? (startX + mb.w) : pos.x), + endY = Math.min(view.t + view.h, corner.charAt(0) == 'T' ? (startY + mb.h) : pos.y), + width = endX - startX, + height = endY - startY, + overflow = (mb.w - width) + (mb.h - height); + + if(best == null || overflow < best.overflow){ + best = { + corner: corner, + aroundCorner: choice.aroundCorner, + x: startX, + y: startY, + w: width, + h: height, + overflow: overflow + }; + } + return !overflow; + }); + + node.style.left = best.x + "px"; + node.style.top = best.y + "px"; + if(best.overflow && layoutNode){ + layoutNode(node, best.aroundCorner, best.corner); + } + return best; +} + +dijit.placeOnScreenAroundNode = function( + /* DomNode */ node, + /* DomNode */ aroundNode, + /* Object */ aroundCorners, + /* Function? */ layoutNode){ + + // summary: + // Position node adjacent or kitty-corner to aroundNode + // such that it's fully visible in viewport. + // + // description: + // Place node such that corner of node touches a corner of + // aroundNode, and that node is fully visible. + // + // aroundCorners: + // Ordered list of pairs of corners to try matching up. + // Each pair of corners is represented as a key/value in the hash, + // where the key corresponds to the aroundNode's corner, and + // the value corresponds to the node's corner: + // + // | { aroundNodeCorner1: nodeCorner1, aroundNodeCorner2: nodeCorner2, ...} + // + // The following strings are used to represent the four corners: + // * "BL" - bottom left + // * "BR" - bottom right + // * "TL" - top left + // * "TR" - top right + // + // layoutNode: Function(node, aroundNodeCorner, nodeCorner) + // For things like tooltip, they are displayed differently (and have different dimensions) + // based on their orientation relative to the parent. This adjusts the popup based on orientation. + // + // example: + // | dijit.placeOnScreenAroundNode(node, aroundNode, {'BL':'TL', 'TR':'BR'}); + // This will try to position node such that node's top-left corner is at the same position + // as the bottom left corner of the aroundNode (ie, put node below + // aroundNode, with left edges aligned). If that fails it will try to put + // the bottom-right corner of node where the top right corner of aroundNode is + // (ie, put node above aroundNode, with right edges aligned) + // + + // get coordinates of aroundNode + aroundNode = dojo.byId(aroundNode); + var oldDisplay = aroundNode.style.display; + aroundNode.style.display=""; + // #3172: use the slightly tighter border box instead of marginBox + var aroundNodePos = dojo.position(aroundNode, true); + aroundNode.style.display=oldDisplay; + + // place the node around the calculated rectangle + return dijit._placeOnScreenAroundRect(node, + aroundNodePos.x, aroundNodePos.y, aroundNodePos.w, aroundNodePos.h, // rectangle + aroundCorners, layoutNode); +}; + +/*===== +dijit.__Rectangle = function(){ + // x: Integer + // horizontal offset in pixels, relative to document body + // y: Integer + // vertical offset in pixels, relative to document body + // width: Integer + // width in pixels + // height: Integer + // height in pixels + + this.x = x; + this.y = y; + this.width = width; + this.height = height; +} +=====*/ + + +dijit.placeOnScreenAroundRectangle = function( + /* DomNode */ node, + /* dijit.__Rectangle */ aroundRect, + /* Object */ aroundCorners, + /* Function */ layoutNode){ + + // summary: + // Like dijit.placeOnScreenAroundNode(), except that the "around" + // parameter is an arbitrary rectangle on the screen (x, y, width, height) + // instead of a dom node. + + return dijit._placeOnScreenAroundRect(node, + aroundRect.x, aroundRect.y, aroundRect.width, aroundRect.height, // rectangle + aroundCorners, layoutNode); +}; + +dijit._placeOnScreenAroundRect = function( + /* DomNode */ node, + /* Number */ x, + /* Number */ y, + /* Number */ width, + /* Number */ height, + /* Object */ aroundCorners, + /* Function */ layoutNode){ + + // summary: + // Like dijit.placeOnScreenAroundNode(), except it accepts coordinates + // of a rectangle to place node adjacent to. + + // TODO: combine with placeOnScreenAroundRectangle() + + // Generate list of possible positions for node + var choices = []; + for(var nodeCorner in aroundCorners){ + choices.push( { + aroundCorner: nodeCorner, + corner: aroundCorners[nodeCorner], + pos: { + x: x + (nodeCorner.charAt(1) == 'L' ? 0 : width), + y: y + (nodeCorner.charAt(0) == 'T' ? 0 : height) + } + }); + } + + return dijit._place(node, choices, layoutNode); +}; + +dijit.placementRegistry= new dojo.AdapterRegistry(); +dijit.placementRegistry.register("node", + function(n, x){ + return typeof x == "object" && + typeof x.offsetWidth != "undefined" && typeof x.offsetHeight != "undefined"; + }, + dijit.placeOnScreenAroundNode); +dijit.placementRegistry.register("rect", + function(n, x){ + return typeof x == "object" && + "x" in x && "y" in x && "width" in x && "height" in x; + }, + dijit.placeOnScreenAroundRectangle); + +dijit.placeOnScreenAroundElement = function( + /* DomNode */ node, + /* Object */ aroundElement, + /* Object */ aroundCorners, + /* Function */ layoutNode){ + + // summary: + // Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object + // for the "around" argument and finds a proper processor to place a node. + + return dijit.placementRegistry.match.apply(dijit.placementRegistry, arguments); +}; + +dijit.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){ + // summary: + // Transforms the passed array of preferred positions into a format suitable for passing as the aroundCorners argument to dijit.placeOnScreenAroundElement. + // + // position: String[] + // This variable controls the position of the drop down. + // It's an array of strings with the following values: + // + // * before: places drop down to the left of the target node/widget, or to the right in + // the case of RTL scripts like Hebrew and Arabic + // * after: places drop down to the right of the target node/widget, or to the left in + // the case of RTL scripts like Hebrew and Arabic + // * above: drop down goes above target node + // * below: drop down goes below target node + // + // The list is positions is tried, in order, until a position is found where the drop down fits + // within the viewport. + // + // leftToRight: Boolean + // Whether the popup will be displaying in leftToRight mode. + // + var align = {}; + dojo.forEach(position, function(pos){ + switch(pos){ + case "after": + align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR"; + break; + case "before": + align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL"; + break; + case "below": + // first try to align left borders, next try to align right borders (or reverse for RTL mode) + align[leftToRight ? "BL" : "BR"] = leftToRight ? "TL" : "TR"; + align[leftToRight ? "BR" : "BL"] = leftToRight ? "TR" : "TL"; + break; + case "above": + default: + // first try to align left borders, next try to align right borders (or reverse for RTL mode) + align[leftToRight ? "TL" : "TR"] = leftToRight ? "BL" : "BR"; + align[leftToRight ? "TR" : "TL"] = leftToRight ? "BR" : "BL"; + break; + } + }); + return align; +}; + +} + +if(!dojo._hasResource["dijit._base.window"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.window"] = true; +dojo.provide("dijit._base.window"); + + + +dijit.getDocumentWindow = function(doc){ + return dojo.window.get(doc); +}; + +} + +if(!dojo._hasResource["dijit._base.popup"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.popup"] = true; +dojo.provide("dijit._base.popup"); + + + + + +/*===== +dijit.popup.__OpenArgs = function(){ + // popup: Widget + // widget to display + // parent: Widget + // the button etc. that is displaying this popup + // around: DomNode + // DOM node (typically a button); place popup relative to this node. (Specify this *or* "x" and "y" parameters.) + // x: Integer + // Absolute horizontal position (in pixels) to place node at. (Specify this *or* "around" parameter.) + // y: Integer + // Absolute vertical position (in pixels) to place node at. (Specify this *or* "around" parameter.) + // orient: Object|String + // When the around parameter is specified, orient should be an + // ordered list of tuples of the form (around-node-corner, popup-node-corner). + // dijit.popup.open() tries to position the popup according to each tuple in the list, in order, + // until the popup appears fully within the viewport. + // + // The default value is {BL:'TL', TL:'BL'}, which represents a list of two tuples: + // 1. (BL, TL) + // 2. (TL, BL) + // where BL means "bottom left" and "TL" means "top left". + // So by default, it first tries putting the popup below the around node, left-aligning them, + // and then tries to put it above the around node, still left-aligning them. Note that the + // default is horizontally reversed when in RTL mode. + // + // When an (x,y) position is specified rather than an around node, orient is either + // "R" or "L". R (for right) means that it tries to put the popup to the right of the mouse, + // specifically positioning the popup's top-right corner at the mouse position, and if that doesn't + // fit in the viewport, then it tries, in order, the bottom-right corner, the top left corner, + // and the top-right corner. + // onCancel: Function + // callback when user has canceled the popup by + // 1. hitting ESC or + // 2. by using the popup widget's proprietary cancel mechanism (like a cancel button in a dialog); + // i.e. whenever popupWidget.onCancel() is called, args.onCancel is called + // onClose: Function + // callback whenever this popup is closed + // onExecute: Function + // callback when user "executed" on the popup/sub-popup by selecting a menu choice, etc. (top menu only) + // padding: dijit.__Position + // adding a buffer around the opening position. This is only useful when around is not set. + this.popup = popup; + this.parent = parent; + this.around = around; + this.x = x; + this.y = y; + this.orient = orient; + this.onCancel = onCancel; + this.onClose = onClose; + this.onExecute = onExecute; + this.padding = padding; +} +=====*/ + +dijit.popup = { + // summary: + // This singleton is used to show/hide widgets as popups. + + // _stack: dijit._Widget[] + // Stack of currently popped up widgets. + // (someone opened _stack[0], and then it opened _stack[1], etc.) + _stack: [], + + // _beginZIndex: Number + // Z-index of the first popup. (If first popup opens other + // popups they get a higher z-index.) + _beginZIndex: 1000, + + _idGen: 1, + + moveOffScreen: function(/*DomNode*/ node){ + // summary: + // Initialization for nodes that will be used as popups + // + // description: + // Puts node inside a wrapper <div>, and + // positions wrapper div off screen, but not display:none, so that + // the widget doesn't appear in the page flow and/or cause a blank + // area at the bottom of the viewport (making scrollbar longer), but + // initialization of contained widgets works correctly + + var wrapper = node.parentNode; + + // Create a wrapper widget for when this node (in the future) will be used as a popup. + // This is done early because of IE bugs where creating/moving DOM nodes causes focus + // to go wonky, see tests/robot/Toolbar.html to reproduce + if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){ + wrapper = dojo.create("div",{ + "class":"dijitPopup", + style:{ + visibility:"hidden", + top: "-9999px" + } + }, dojo.body()); + dijit.setWaiRole(wrapper, "presentation"); + wrapper.appendChild(node); + } + + + var s = node.style; + s.display = ""; + s.visibility = ""; + s.position = ""; + s.top = "0px"; + + dojo.style(wrapper, { + visibility: "hidden", + // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111) + top: "-9999px" + }); + }, + + getTopPopup: function(){ + // summary: + // Compute the closest ancestor popup that's *not* a child of another popup. + // Ex: For a TooltipDialog with a button that spawns a tree of menus, find the popup of the button. + var stack = this._stack; + for(var pi=stack.length-1; pi > 0 && stack[pi].parent === stack[pi-1].widget; pi--){ + /* do nothing, just trying to get right value for pi */ + } + return stack[pi]; + }, + + open: function(/*dijit.popup.__OpenArgs*/ args){ + // summary: + // Popup the widget at the specified position + // + // example: + // opening at the mouse position + // | dijit.popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY}); + // + // example: + // opening the widget as a dropdown + // | dijit.popup.open({parent: this, popup: menuWidget, around: this.domNode, onClose: function(){...}}); + // + // Note that whatever widget called dijit.popup.open() should also listen to its own _onBlur callback + // (fired from _base/focus.js) to know that focus has moved somewhere else and thus the popup should be closed. + + var stack = this._stack, + widget = args.popup, + orient = args.orient || ( + (args.parent ? args.parent.isLeftToRight() : dojo._isBodyLtr()) ? + {'BL':'TL', 'BR':'TR', 'TL':'BL', 'TR':'BR'} : + {'BR':'TR', 'BL':'TL', 'TR':'BR', 'TL':'BL'} + ), + around = args.around, + id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++); + + + // The wrapper may have already been created, but in case it wasn't, create here + var wrapper = widget.domNode.parentNode; + if(!wrapper || !dojo.hasClass(wrapper, "dijitPopup")){ + this.moveOffScreen(widget.domNode); + wrapper = widget.domNode.parentNode; + } + + dojo.attr(wrapper, { + id: id, + style: { + zIndex: this._beginZIndex + stack.length + }, + "class": "dijitPopup " + (widget.baseClass || widget["class"] || "").split(" ")[0] +"Popup", + dijitPopupParent: args.parent ? args.parent.id : "" + }); + + if(dojo.isIE || dojo.isMoz){ + var iframe = wrapper.childNodes[1]; + if(!iframe){ + iframe = new dijit.BackgroundIframe(wrapper); + } + } + + // position the wrapper node and make it visible + var best = around ? + dijit.placeOnScreenAroundElement(wrapper, around, orient, widget.orient ? dojo.hitch(widget, "orient") : null) : + dijit.placeOnScreen(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding); + + wrapper.style.visibility = "visible"; + widget.domNode.style.visibility = "visible"; // counteract effects from _HasDropDown + + var handlers = []; + + // provide default escape and tab key handling + // (this will work for any widget, not just menu) + handlers.push(dojo.connect(wrapper, "onkeypress", this, function(evt){ + if(evt.charOrCode == dojo.keys.ESCAPE && args.onCancel){ + dojo.stopEvent(evt); + args.onCancel(); + }else if(evt.charOrCode === dojo.keys.TAB){ + dojo.stopEvent(evt); + var topPopup = this.getTopPopup(); + if(topPopup && topPopup.onCancel){ + topPopup.onCancel(); + } + } + })); + + // watch for cancel/execute events on the popup and notify the caller + // (for a menu, "execute" means clicking an item) + if(widget.onCancel){ + handlers.push(dojo.connect(widget, "onCancel", args.onCancel)); + } + + handlers.push(dojo.connect(widget, widget.onExecute ? "onExecute" : "onChange", this, function(){ + var topPopup = this.getTopPopup(); + if(topPopup && topPopup.onExecute){ + topPopup.onExecute(); + } + })); + + stack.push({ + wrapper: wrapper, + iframe: iframe, + widget: widget, + parent: args.parent, + onExecute: args.onExecute, + onCancel: args.onCancel, + onClose: args.onClose, + handlers: handlers + }); + + if(widget.onOpen){ + // TODO: in 2.0 standardize onShow() (used by StackContainer) and onOpen() (used here) + widget.onOpen(best); + } + + return best; + }, + + close: function(/*dijit._Widget*/ popup){ + // summary: + // Close specified popup and any popups that it parented + + var stack = this._stack; + + // Basically work backwards from the top of the stack closing popups + // until we hit the specified popup, but IIRC there was some issue where closing + // a popup would cause others to close too. Thus if we are trying to close B in [A,B,C] + // closing C might close B indirectly and then the while() condition will run where stack==[A]... + // so the while condition is constructed defensively. + while(dojo.some(stack, function(elem){return elem.widget == popup;})){ + var top = stack.pop(), + wrapper = top.wrapper, + iframe = top.iframe, + widget = top.widget, + onClose = top.onClose; + + if(widget.onClose){ + // TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here) + widget.onClose(); + } + dojo.forEach(top.handlers, dojo.disconnect); + + // Move the widget plus it's wrapper off screen, unless it has already been destroyed in above onClose() etc. + if(widget && widget.domNode){ + this.moveOffScreen(widget.domNode); + }else{ + dojo.destroy(wrapper); + } + + if(onClose){ + onClose(); + } + } + } +}; + +dijit._frames = new function(){ + // summary: + // cache of iframes + var queue = []; + + this.pop = function(){ + var iframe; + if(queue.length){ + iframe = queue.pop(); + iframe.style.display=""; + }else{ + if(dojo.isIE){ + var burl = dojo.config["dojoBlankHtmlUrl"] || (dojo.moduleUrl("dojo", "resources/blank.html")+"") || "javascript:\"\""; + var html="<iframe src='" + burl + "'" + + " style='position: absolute; left: 0px; top: 0px;" + + "z-index: -1; filter:Alpha(Opacity=\"0\");'>"; + iframe = dojo.doc.createElement(html); + }else{ + iframe = dojo.create("iframe"); + iframe.src = 'javascript:""'; + iframe.className = "dijitBackgroundIframe"; + dojo.style(iframe, "opacity", 0.1); + } + iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didnt work. + dijit.setWaiRole(iframe,"presentation"); + } + return iframe; + }; + + this.push = function(iframe){ + iframe.style.display="none"; + queue.push(iframe); + } +}(); + + +dijit.BackgroundIframe = function(/* DomNode */node){ + // summary: + // For IE/FF z-index schenanigans. id attribute is required. + // + // description: + // new dijit.BackgroundIframe(node) + // Makes a background iframe as a child of node, that fills + // area (and position) of node + + if(!node.id){ throw new Error("no id"); } + if(dojo.isIE || dojo.isMoz){ + var iframe = dijit._frames.pop(); + node.appendChild(iframe); + if(dojo.isIE<7){ + this.resize(node); + this._conn = dojo.connect(node, 'onresize', this, function(){ + this.resize(node); + }); + }else{ + dojo.style(iframe, { + width: '100%', + height: '100%' + }); + } + this.iframe = iframe; + } +}; + +dojo.extend(dijit.BackgroundIframe, { + resize: function(node){ + // summary: + // resize the iframe so its the same size as node + // description: + // this function is a no-op in all browsers except + // IE6, which does not support 100% width/height + // of absolute positioned iframes + if(this.iframe && dojo.isIE<7){ + dojo.style(this.iframe, { + width: node.offsetWidth + 'px', + height: node.offsetHeight + 'px' + }); + } + }, + destroy: function(){ + // summary: + // destroy the iframe + if(this._conn){ + dojo.disconnect(this._conn); + this._conn = null; + } + if(this.iframe){ + dijit._frames.push(this.iframe); + delete this.iframe; + } + } +}); + +} + +if(!dojo._hasResource["dijit._base.scroll"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.scroll"] = true; +dojo.provide("dijit._base.scroll"); + + + +dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){ + // summary: + // Scroll the passed node into view, if it is not already. + // Deprecated, use `dojo.window.scrollIntoView` instead. + + dojo.window.scrollIntoView(node, pos); +}; + +} + +if(!dojo._hasResource["dojo.uacss"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.uacss"] = true; +dojo.provide("dojo.uacss"); + +(function(){ + // summary: + // Applies pre-set CSS classes to the top-level HTML node, based on: + // - browser (ex: dj_ie) + // - browser version (ex: dj_ie6) + // - box model (ex: dj_contentBox) + // - text direction (ex: dijitRtl) + // + // In addition, browser, browser version, and box model are + // combined with an RTL flag when browser text is RTL. ex: dj_ie-rtl. + + var d = dojo, + html = d.doc.documentElement, + ie = d.isIE, + opera = d.isOpera, + maj = Math.floor, + ff = d.isFF, + boxModel = d.boxModel.replace(/-/,''), + + classes = { + dj_ie: ie, + dj_ie6: maj(ie) == 6, + dj_ie7: maj(ie) == 7, + dj_ie8: maj(ie) == 8, + dj_quirks: d.isQuirks, + dj_iequirks: ie && d.isQuirks, + + // NOTE: Opera not supported by dijit + dj_opera: opera, + + dj_khtml: d.isKhtml, + + dj_webkit: d.isWebKit, + dj_safari: d.isSafari, + dj_chrome: d.isChrome, + + dj_gecko: d.isMozilla, + dj_ff3: maj(ff) == 3 + }; // no dojo unsupported browsers + + classes["dj_" + boxModel] = true; + + // apply browser, browser version, and box model class names + var classStr = ""; + for(var clz in classes){ + if(classes[clz]){ + classStr += clz + " "; + } + } + html.className = d.trim(html.className + " " + classStr); + + // If RTL mode, then add dj_rtl flag plus repeat existing classes with -rtl extension. + // We can't run the code below until the <body> tag has loaded (so we can check for dir=rtl). + // Unshift() is to run sniff code before the parser. + dojo._loaders.unshift(function(){ + if(!dojo._isBodyLtr()){ + var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ") + html.className = d.trim(html.className + " " + rtlClassStr); + } + }); +})(); + +} + +if(!dojo._hasResource["dijit._base.sniff"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.sniff"] = true; +// summary: +// Applies pre-set CSS classes to the top-level HTML node, see +// `dojo.uacss` for details. +// +// Simply doing a require on this module will +// establish this CSS. Modified version of Morris' CSS hack. + +dojo.provide("dijit._base.sniff"); + + + +} + +if(!dojo._hasResource["dijit._base.typematic"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.typematic"] = true; +dojo.provide("dijit._base.typematic"); + +dijit.typematic = { + // summary: + // These functions are used to repetitively call a user specified callback + // method when a specific key or mouse click over a specific DOM node is + // held down for a specific amount of time. + // Only 1 such event is allowed to occur on the browser page at 1 time. + + _fireEventAndReload: function(){ + this._timer = null; + this._callback(++this._count, this._node, this._evt); + + // Schedule next event, timer is at most minDelay (default 10ms) to avoid + // browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup) + this._currentTimeout = Math.max( + this._currentTimeout < 0 ? this._initialDelay : + (this._subsequentDelay > 1 ? this._subsequentDelay : Math.round(this._currentTimeout * this._subsequentDelay)), + this._minDelay); + this._timer = setTimeout(dojo.hitch(this, "_fireEventAndReload"), this._currentTimeout); + }, + + trigger: function(/*Event*/ evt, /*Object*/ _this, /*DOMNode*/ node, /*Function*/ callback, /*Object*/ obj, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ + // summary: + // Start a timed, repeating callback sequence. + // If already started, the function call is ignored. + // This method is not normally called by the user but can be + // when the normal listener code is insufficient. + // evt: + // key or mouse event object to pass to the user callback + // _this: + // pointer to the user's widget space. + // node: + // the DOM node object to pass the the callback function + // callback: + // function to call until the sequence is stopped called with 3 parameters: + // count: + // integer representing number of repeated calls (0..n) with -1 indicating the iteration has stopped + // node: + // the DOM node object passed in + // evt: + // key or mouse event object + // obj: + // user space object used to uniquely identify each typematic sequence + // subsequentDelay (optional): + // if > 1, the number of milliseconds until the 3->n events occur + // or else the fractional time multiplier for the next event's delay, default=0.9 + // initialDelay (optional): + // the number of milliseconds until the 2nd event occurs, default=500ms + // minDelay (optional): + // the maximum delay in milliseconds for event to fire, default=10ms + if(obj != this._obj){ + this.stop(); + this._initialDelay = initialDelay || 500; + this._subsequentDelay = subsequentDelay || 0.90; + this._minDelay = minDelay || 10; + this._obj = obj; + this._evt = evt; + this._node = node; + this._currentTimeout = -1; + this._count = -1; + this._callback = dojo.hitch(_this, callback); + this._fireEventAndReload(); + this._evt = dojo.mixin({faux: true}, evt); + } + }, + + stop: function(){ + // summary: + // Stop an ongoing timed, repeating callback sequence. + if(this._timer){ + clearTimeout(this._timer); + this._timer = null; + } + if(this._obj){ + this._callback(-1, this._node, this._evt); + this._obj = null; + } + }, + + addKeyListener: function(/*DOMNode*/ node, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ + // summary: + // Start listening for a specific typematic key. + // See also the trigger method for other parameters. + // keyObject: + // an object defining the key to listen for: + // charOrCode: + // the printable character (string) or keyCode (number) to listen for. + // keyCode: + // (deprecated - use charOrCode) the keyCode (number) to listen for (implies charCode = 0). + // charCode: + // (deprecated - use charOrCode) the charCode (number) to listen for. + // ctrlKey: + // desired ctrl key state to initiate the callback sequence: + // - pressed (true) + // - released (false) + // - either (unspecified) + // altKey: + // same as ctrlKey but for the alt key + // shiftKey: + // same as ctrlKey but for the shift key + // returns: + // an array of dojo.connect handles + if(keyObject.keyCode){ + keyObject.charOrCode = keyObject.keyCode; + dojo.deprecated("keyCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0"); + }else if(keyObject.charCode){ + keyObject.charOrCode = String.fromCharCode(keyObject.charCode); + dojo.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0"); + } + return [ + dojo.connect(node, "onkeypress", this, function(evt){ + if(evt.charOrCode == keyObject.charOrCode && + (keyObject.ctrlKey === undefined || keyObject.ctrlKey == evt.ctrlKey) && + (keyObject.altKey === undefined || keyObject.altKey == evt.altKey) && + (keyObject.metaKey === undefined || keyObject.metaKey == (evt.metaKey || false)) && // IE doesn't even set metaKey + (keyObject.shiftKey === undefined || keyObject.shiftKey == evt.shiftKey)){ + dojo.stopEvent(evt); + dijit.typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay); + }else if(dijit.typematic._obj == keyObject){ + dijit.typematic.stop(); + } + }), + dojo.connect(node, "onkeyup", this, function(evt){ + if(dijit.typematic._obj == keyObject){ + dijit.typematic.stop(); + } + }) + ]; + }, + + addMouseListener: function(/*DOMNode*/ node, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ + // summary: + // Start listening for a typematic mouse click. + // See the trigger method for other parameters. + // returns: + // an array of dojo.connect handles + var dc = dojo.connect; + return [ + dc(node, "mousedown", this, function(evt){ + dojo.stopEvent(evt); + dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay); + }), + dc(node, "mouseup", this, function(evt){ + dojo.stopEvent(evt); + dijit.typematic.stop(); + }), + dc(node, "mouseout", this, function(evt){ + dojo.stopEvent(evt); + dijit.typematic.stop(); + }), + dc(node, "mousemove", this, function(evt){ + evt.preventDefault(); + }), + dc(node, "dblclick", this, function(evt){ + dojo.stopEvent(evt); + if(dojo.isIE){ + dijit.typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay); + setTimeout(dojo.hitch(this, dijit.typematic.stop), 50); + } + }) + ]; + }, + + addListener: function(/*Node*/ mouseNode, /*Node*/ keyNode, /*Object*/ keyObject, /*Object*/ _this, /*Function*/ callback, /*Number*/ subsequentDelay, /*Number*/ initialDelay, /*Number?*/ minDelay){ + // summary: + // Start listening for a specific typematic key and mouseclick. + // This is a thin wrapper to addKeyListener and addMouseListener. + // See the addMouseListener and addKeyListener methods for other parameters. + // mouseNode: + // the DOM node object to listen on for mouse events. + // keyNode: + // the DOM node object to listen on for key events. + // returns: + // an array of dojo.connect handles + return this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay).concat( + this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)); + } +}; + +} + +if(!dojo._hasResource["dijit._base.wai"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base.wai"] = true; +dojo.provide("dijit._base.wai"); + +dijit.wai = { + onload: function(){ + // summary: + // Detects if we are in high-contrast mode or not + + // This must be a named function and not an anonymous + // function, so that the widget parsing code can make sure it + // registers its onload function after this function. + // DO NOT USE "this" within this function. + + // create div for testing if high contrast mode is on or images are turned off + var div = dojo.create("div",{ + id: "a11yTestNode", + style:{ + cssText:'border: 1px solid;' + + 'border-color:red green;' + + 'position: absolute;' + + 'height: 5px;' + + 'top: -999px;' + + 'background-image: url("' + (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")) + '");' + } + }, dojo.body()); + + // test it + var cs = dojo.getComputedStyle(div); + if(cs){ + var bkImg = cs.backgroundImage; + var needsA11y = (cs.borderTopColor == cs.borderRightColor) || (bkImg != null && (bkImg == "none" || bkImg == "url(invalid-url:)" )); + dojo[needsA11y ? "addClass" : "removeClass"](dojo.body(), "dijit_a11y"); + if(dojo.isIE){ + div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014 + }else{ + dojo.body().removeChild(div); + } + } + } +}; + +// Test if computer is in high contrast mode. +// Make sure the a11y test runs first, before widgets are instantiated. +if(dojo.isIE || dojo.isMoz){ // NOTE: checking in Safari messes things up + dojo._loaders.unshift(dijit.wai.onload); +} + +dojo.mixin(dijit, { + _XhtmlRoles: /banner|contentinfo|definition|main|navigation|search|note|secondary|seealso/, + + hasWaiRole: function(/*Element*/ elem, /*String*/ role){ + // summary: + // Determines if an element has a particular non-XHTML role. + // returns: + // True if elem has the specific non-XHTML role attribute and false if not. + // For backwards compatibility if role parameter not provided, + // returns true if has non XHTML role + var waiRole = this.getWaiRole(elem); + return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0); + }, + + getWaiRole: function(/*Element*/ elem){ + // summary: + // Gets the non-XHTML role for an element (which should be a wai role). + // returns: + // The non-XHTML role of elem or an empty string if elem + // does not have a role. + return dojo.trim((dojo.attr(elem, "role") || "").replace(this._XhtmlRoles,"").replace("wairole:","")); + }, + + setWaiRole: function(/*Element*/ elem, /*String*/ role){ + // summary: + // Sets the role on an element. + // description: + // Replace existing role attribute with new role. + // If elem already has an XHTML role, append this role to XHTML role + // and remove other ARIA roles. + + var curRole = dojo.attr(elem, "role") || ""; + if(!this._XhtmlRoles.test(curRole)){ + dojo.attr(elem, "role", role); + }else{ + if((" "+ curRole +" ").indexOf(" " + role + " ") < 0){ + var clearXhtml = dojo.trim(curRole.replace(this._XhtmlRoles, "")); + var cleanRole = dojo.trim(curRole.replace(clearXhtml, "")); + dojo.attr(elem, "role", cleanRole + (cleanRole ? ' ' : '') + role); + } + } + }, + + removeWaiRole: function(/*Element*/ elem, /*String*/ role){ + // summary: + // Removes the specified non-XHTML role from an element. + // Removes role attribute if no specific role provided (for backwards compat.) + + var roleValue = dojo.attr(elem, "role"); + if(!roleValue){ return; } + if(role){ + var t = dojo.trim((" " + roleValue + " ").replace(" " + role + " ", " ")); + dojo.attr(elem, "role", t); + }else{ + elem.removeAttribute("role"); + } + }, + + hasWaiState: function(/*Element*/ elem, /*String*/ state){ + // summary: + // Determines if an element has a given state. + // description: + // Checks for an attribute called "aria-"+state. + // returns: + // true if elem has a value for the given state and + // false if it does not. + + return elem.hasAttribute ? elem.hasAttribute("aria-"+state) : !!elem.getAttribute("aria-"+state); + }, + + getWaiState: function(/*Element*/ elem, /*String*/ state){ + // summary: + // Gets the value of a state on an element. + // description: + // Checks for an attribute called "aria-"+state. + // returns: + // The value of the requested state on elem + // or an empty string if elem has no value for state. + + return elem.getAttribute("aria-"+state) || ""; + }, + + setWaiState: function(/*Element*/ elem, /*String*/ state, /*String*/ value){ + // summary: + // Sets a state on an element. + // description: + // Sets an attribute called "aria-"+state. + + elem.setAttribute("aria-"+state, value); + }, + + removeWaiState: function(/*Element*/ elem, /*String*/ state){ + // summary: + // Removes a state from an element. + // description: + // Sets an attribute called "aria-"+state. + + elem.removeAttribute("aria-"+state); + } +}); + +} + +if(!dojo._hasResource["dijit._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._base"] = true; +dojo.provide("dijit._base"); + + + + + + + + + + + +} + +if(!dojo._hasResource["dojo.date.stamp"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.date.stamp"] = true; +dojo.provide("dojo.date.stamp"); + +// Methods to convert dates to or from a wire (string) format using well-known conventions + +dojo.date.stamp.fromISOString = function(/*String*/formattedString, /*Number?*/defaultTime){ + // summary: + // Returns a Date object given a string formatted according to a subset of the ISO-8601 standard. + // + // description: + // Accepts a string formatted according to a profile of ISO8601 as defined by + // [RFC3339](http://www.ietf.org/rfc/rfc3339.txt), except that partial input is allowed. + // Can also process dates as specified [by the W3C](http://www.w3.org/TR/NOTE-datetime) + // The following combinations are valid: + // + // * dates only + // | * yyyy + // | * yyyy-MM + // | * yyyy-MM-dd + // * times only, with an optional time zone appended + // | * THH:mm + // | * THH:mm:ss + // | * THH:mm:ss.SSS + // * and "datetimes" which could be any combination of the above + // + // timezones may be specified as Z (for UTC) or +/- followed by a time expression HH:mm + // Assumes the local time zone if not specified. Does not validate. Improperly formatted + // input may return null. Arguments which are out of bounds will be handled + // by the Date constructor (e.g. January 32nd typically gets resolved to February 1st) + // Only years between 100 and 9999 are supported. + // + // formattedString: + // A string such as 2005-06-30T08:05:00-07:00 or 2005-06-30 or T08:05:00 + // + // defaultTime: + // Used for defaults for fields omitted in the formattedString. + // Uses 1970-01-01T00:00:00.0Z by default. + + if(!dojo.date.stamp._isoRegExp){ + dojo.date.stamp._isoRegExp = +//TODO: could be more restrictive and check for 00-59, etc. + /^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(.\d+)?)?((?:[+-](\d{2}):(\d{2}))|Z)?)?$/; + } + + var match = dojo.date.stamp._isoRegExp.exec(formattedString), + result = null; + + if(match){ + match.shift(); + if(match[1]){match[1]--;} // Javascript Date months are 0-based + if(match[6]){match[6] *= 1000;} // Javascript Date expects fractional seconds as milliseconds + + if(defaultTime){ + // mix in defaultTime. Relatively expensive, so use || operators for the fast path of defaultTime === 0 + defaultTime = new Date(defaultTime); + dojo.forEach(dojo.map(["FullYear", "Month", "Date", "Hours", "Minutes", "Seconds", "Milliseconds"], function(prop){ + return defaultTime["get" + prop](); + }), function(value, index){ + match[index] = match[index] || value; + }); + } + result = new Date(match[0]||1970, match[1]||0, match[2]||1, match[3]||0, match[4]||0, match[5]||0, match[6]||0); //TODO: UTC defaults + if(match[0] < 100){ + result.setFullYear(match[0] || 1970); + } + + var offset = 0, + zoneSign = match[7] && match[7].charAt(0); + if(zoneSign != 'Z'){ + offset = ((match[8] || 0) * 60) + (Number(match[9]) || 0); + if(zoneSign != '-'){ offset *= -1; } + } + if(zoneSign){ + offset -= result.getTimezoneOffset(); + } + if(offset){ + result.setTime(result.getTime() + offset * 60000); + } + } + + return result; // Date or null +} + +/*===== + dojo.date.stamp.__Options = function(){ + // selector: String + // "date" or "time" for partial formatting of the Date object. + // Both date and time will be formatted by default. + // zulu: Boolean + // if true, UTC/GMT is used for a timezone + // milliseconds: Boolean + // if true, output milliseconds + this.selector = selector; + this.zulu = zulu; + this.milliseconds = milliseconds; + } +=====*/ + +dojo.date.stamp.toISOString = function(/*Date*/dateObject, /*dojo.date.stamp.__Options?*/options){ + // summary: + // Format a Date object as a string according a subset of the ISO-8601 standard + // + // description: + // When options.selector is omitted, output follows [RFC3339](http://www.ietf.org/rfc/rfc3339.txt) + // The local time zone is included as an offset from GMT, except when selector=='time' (time without a date) + // Does not check bounds. Only years between 100 and 9999 are supported. + // + // dateObject: + // A Date object + + var _ = function(n){ return (n < 10) ? "0" + n : n; }; + options = options || {}; + var formattedDate = [], + getter = options.zulu ? "getUTC" : "get", + date = ""; + if(options.selector != "time"){ + var year = dateObject[getter+"FullYear"](); + date = ["0000".substr((year+"").length)+year, _(dateObject[getter+"Month"]()+1), _(dateObject[getter+"Date"]())].join('-'); + } + formattedDate.push(date); + if(options.selector != "date"){ + var time = [_(dateObject[getter+"Hours"]()), _(dateObject[getter+"Minutes"]()), _(dateObject[getter+"Seconds"]())].join(':'); + var millis = dateObject[getter+"Milliseconds"](); + if(options.milliseconds){ + time += "."+ (millis < 100 ? "0" : "") + _(millis); + } + if(options.zulu){ + time += "Z"; + }else if(options.selector != "time"){ + var timezoneOffset = dateObject.getTimezoneOffset(); + var absOffset = Math.abs(timezoneOffset); + time += (timezoneOffset > 0 ? "-" : "+") + + _(Math.floor(absOffset/60)) + ":" + _(absOffset%60); + } + formattedDate.push(time); + } + return formattedDate.join('T'); // String +} + +} + +if(!dojo._hasResource["dojo.parser"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.parser"] = true; +dojo.provide("dojo.parser"); + + +new Date("X"); // workaround for #11279, new Date("") == NaN + +dojo.parser = new function(){ + // summary: The Dom/Widget parsing package + + var d = dojo; + this._attrName = d._scopeName + "Type"; + this._query = "[" + this._attrName + "]"; + + function val2type(/*Object*/ value){ + // summary: + // Returns name of type of given value. + + if(d.isString(value)){ return "string"; } + if(typeof value == "number"){ return "number"; } + if(typeof value == "boolean"){ return "boolean"; } + if(d.isFunction(value)){ return "function"; } + if(d.isArray(value)){ return "array"; } // typeof [] == "object" + if(value instanceof Date) { return "date"; } // assume timestamp + if(value instanceof d._Url){ return "url"; } + return "object"; + } + + function str2obj(/*String*/ value, /*String*/ type){ + // summary: + // Convert given string value to given type + switch(type){ + case "string": + return value; + case "number": + return value.length ? Number(value) : NaN; + case "boolean": + // for checked/disabled value might be "" or "checked". interpret as true. + return typeof value == "boolean" ? value : !(value.toLowerCase()=="false"); + case "function": + if(d.isFunction(value)){ + // IE gives us a function, even when we say something like onClick="foo" + // (in which case it gives us an invalid function "function(){ foo }"). + // Therefore, convert to string + value=value.toString(); + value=d.trim(value.substring(value.indexOf('{')+1, value.length-1)); + } + try{ + if(value === "" || value.search(/[^\w\.]+/i) != -1){ + // The user has specified some text for a function like "return x+5" + return new Function(value); + }else{ + // The user has specified the name of a function like "myOnClick" + // or a single word function "return" + return d.getObject(value, false) || new Function(value); + } + }catch(e){ return new Function(); } + case "array": + return value ? value.split(/\s*,\s*/) : []; + case "date": + switch(value){ + case "": return new Date(""); // the NaN of dates + case "now": return new Date(); // current date + default: return d.date.stamp.fromISOString(value); + } + case "url": + return d.baseUrl + value; + default: + return d.fromJson(value); + } + } + + var instanceClasses = { + // map from fully qualified name (like "dijit.Button") to structure like + // { cls: dijit.Button, params: {label: "string", disabled: "boolean"} } + }; + + // Widgets like BorderContainer add properties to _Widget via dojo.extend(). + // If BorderContainer is loaded after _Widget's parameter list has been cached, + // we need to refresh that parameter list (for _Widget and all widgets that extend _Widget). + dojo.connect(dojo, "extend", function(){ + instanceClasses = {}; + }); + + function getClassInfo(/*String*/ className){ + // className: + // fully qualified name (like "dijit.form.Button") + // returns: + // structure like + // { + // cls: dijit.Button, + // params: { label: "string", disabled: "boolean"} + // } + + if(!instanceClasses[className]){ + // get pointer to widget class + var cls = d.getObject(className); + if(!cls){ return null; } // class not defined [yet] + + var proto = cls.prototype; + + // get table of parameter names & types + var params = {}, dummyClass = {}; + for(var name in proto){ + if(name.charAt(0)=="_"){ continue; } // skip internal properties + if(name in dummyClass){ continue; } // skip "constructor" and "toString" + var defVal = proto[name]; + params[name]=val2type(defVal); + } + + instanceClasses[className] = { cls: cls, params: params }; + } + return instanceClasses[className]; + } + + this._functionFromScript = function(script){ + var preamble = ""; + var suffix = ""; + var argsStr = script.getAttribute("args"); + if(argsStr){ + d.forEach(argsStr.split(/\s*,\s*/), function(part, idx){ + preamble += "var "+part+" = arguments["+idx+"]; "; + }); + } + var withStr = script.getAttribute("with"); + if(withStr && withStr.length){ + d.forEach(withStr.split(/\s*,\s*/), function(part){ + preamble += "with("+part+"){"; + suffix += "}"; + }); + } + return new Function(preamble+script.innerHTML+suffix); + } + + this.instantiate = function(/* Array */nodes, /* Object? */mixin, /* Object? */args){ + // summary: + // Takes array of nodes, and turns them into class instances and + // potentially calls a startup method to allow them to connect with + // any children. + // nodes: Array + // Array of nodes or objects like + // | { + // | type: "dijit.form.Button", + // | node: DOMNode, + // | scripts: [ ... ], // array of <script type="dojo/..."> children of node + // | inherited: { ... } // settings inherited from ancestors like dir, theme, etc. + // | } + // mixin: Object? + // An object that will be mixed in with each node in the array. + // Values in the mixin will override values in the node, if they + // exist. + // args: Object? + // An object used to hold kwArgs for instantiation. + // Supports 'noStart' and inherited. + var thelist = [], dp = dojo.parser; + mixin = mixin||{}; + args = args||{}; + + d.forEach(nodes, function(obj){ + if(!obj){ return; } + + // Get pointers to DOMNode, dojoType string, and clsInfo (metadata about the dojoType), etc.s + var node, type, clsInfo, clazz, scripts; + if(obj.node){ + // new format of nodes[] array, object w/lots of properties pre-computed for me + node = obj.node; + type = obj.type; + clsInfo = obj.clsInfo || (type && getClassInfo(type)); + clazz = clsInfo && clsInfo.cls; + scripts = obj.scripts; + }else{ + // old (backwards compatible) format of nodes[] array, simple array of DOMNodes + node = obj; + type = dp._attrName in mixin ? mixin[dp._attrName] : node.getAttribute(dp._attrName); + clsInfo = type && getClassInfo(type); + clazz = clsInfo && clsInfo.cls; + scripts = (clazz && (clazz._noScript || clazz.prototype._noScript) ? [] : + d.query("> script[type^='dojo/']", node)); + } + if(!clsInfo){ + throw new Error("Could not load class '" + type); + } + + // Setup hash to hold parameter settings for this widget. Start with the parameter + // settings inherited from ancestors ("dir" and "lang"). + // Inherited setting may later be overridden by explicit settings on node itself. + var params = {}, + attributes = node.attributes; + if(args.defaults){ + // settings for the document itself (or whatever subtree is being parsed) + dojo.mixin(params, args.defaults); + } + if(obj.inherited){ + // settings from dir=rtl or lang=... on a node above this node + dojo.mixin(params, obj.inherited); + } + + // read parameters (ie, attributes) specified on DOMNode + // clsInfo.params lists expected params like {"checked": "boolean", "n": "number"} + for(var name in clsInfo.params){ + var item = name in mixin?{value:mixin[name],specified:true}:attributes.getNamedItem(name); + if(!item || (!item.specified && (!dojo.isIE || name.toLowerCase()!="value"))){ continue; } + var value = item.value; + // Deal with IE quirks for 'class' and 'style' + switch(name){ + case "class": + value = "className" in mixin?mixin.className:node.className; + break; + case "style": + value = "style" in mixin?mixin.style:(node.style && node.style.cssText); // FIXME: Opera? + } + var _type = clsInfo.params[name]; + if(typeof value == "string"){ + params[name] = str2obj(value, _type); + }else{ + params[name] = value; + } + } + + // Process <script type="dojo/*"> script tags + // <script type="dojo/method" event="foo"> tags are added to params, and passed to + // the widget on instantiation. + // <script type="dojo/method"> tags (with no event) are executed after instantiation + // <script type="dojo/connect" event="foo"> tags are dojo.connected after instantiation + // note: dojo/* script tags cannot exist in self closing widgets, like <input /> + var connects = [], // functions to connect after instantiation + calls = []; // functions to call after instantiation + + d.forEach(scripts, function(script){ + node.removeChild(script); + var event = script.getAttribute("event"), + type = script.getAttribute("type"), + nf = d.parser._functionFromScript(script); + if(event){ + if(type == "dojo/connect"){ + connects.push({event: event, func: nf}); + }else{ + params[event] = nf; + } + }else{ + calls.push(nf); + } + }); + + var markupFactory = clazz.markupFactory || clazz.prototype && clazz.prototype.markupFactory; + // create the instance + var instance = markupFactory ? markupFactory(params, node, clazz) : new clazz(params, node); + thelist.push(instance); + + // map it to the JS namespace if that makes sense + var jsname = node.getAttribute("jsId"); + if(jsname){ + d.setObject(jsname, instance); + } + + // process connections and startup functions + d.forEach(connects, function(connect){ + d.connect(instance, connect.event, null, connect.func); + }); + d.forEach(calls, function(func){ + func.call(instance); + }); + }); + + // Call startup on each top level instance if it makes sense (as for + // widgets). Parent widgets will recursively call startup on their + // (non-top level) children + if(!mixin._started){ + // TODO: for 2.0, when old instantiate() API is desupported, store parent-child + // relationships in the nodes[] array so that no getParent() call is needed. + // Note that will require a parse() call from ContentPane setting a param that the + // ContentPane is the parent widget (so that the parse doesn't call startup() on the + // ContentPane's children) + d.forEach(thelist, function(instance){ + if( !args.noStart && instance && + instance.startup && + !instance._started && + (!instance.getParent || !instance.getParent()) + ){ + instance.startup(); + } + }); + } + return thelist; + }; + + this.parse = function(/*DomNode?*/ rootNode, /* Object? */ args){ + // summary: + // Scan the DOM for class instances, and instantiate them. + // + // description: + // Search specified node (or root node) recursively for class instances, + // and instantiate them Searches for + // dojoType="qualified.class.name" + // + // rootNode: DomNode? + // A default starting root node from which to start the parsing. Can be + // omitted, defaulting to the entire document. If omitted, the `args` + // object can be passed in this place. If the `args` object has a + // `rootNode` member, that is used. + // + // args: + // a kwArgs object passed along to instantiate() + // + // * noStart: Boolean? + // when set will prevent the parser from calling .startup() + // when locating the nodes. + // * rootNode: DomNode? + // identical to the function's `rootNode` argument, though + // allowed to be passed in via this `args object. + // * inherited: Object + // Hash possibly containing dir and lang settings to be applied to + // parsed widgets, unless there's another setting on a sub-node that overrides + // + // + // example: + // Parse all widgets on a page: + // | dojo.parser.parse(); + // + // example: + // Parse all classes within the node with id="foo" + // | dojo.parser.parse(dojo.byId(foo)); + // + // example: + // Parse all classes in a page, but do not call .startup() on any + // child + // | dojo.parser.parse({ noStart: true }) + // + // example: + // Parse all classes in a node, but do not call .startup() + // | dojo.parser.parse(someNode, { noStart:true }); + // | // or + // | dojo.parser.parse({ noStart:true, rootNode: someNode }); + + // determine the root node based on the passed arguments. + var root; + if(!args && rootNode && rootNode.rootNode){ + args = rootNode; + root = args.rootNode; + }else{ + root = rootNode; + } + + var attrName = this._attrName; + function scan(parent, list){ + // summary: + // Parent is an Object representing a DOMNode, with or without a dojoType specified. + // Scan parent's children looking for nodes with dojoType specified, storing in list[]. + // If parent has a dojoType, also collects <script type=dojo/*> children and stores in parent.scripts[]. + // parent: Object + // Object representing the parent node, like + // | { + // | node: DomNode, // scan children of this node + // | inherited: {dir: "rtl"}, // dir/lang setting inherited from above node + // | + // | // attributes only set if node has dojoType specified + // | scripts: [], // empty array, put <script type=dojo/*> in here + // | clsInfo: { cls: dijit.form.Button, ...} + // | } + // list: DomNode[] + // Output array of objects (same format as parent) representing nodes to be turned into widgets + + // Effective dir and lang settings on parent node, either set directly or inherited from grandparent + var inherited = dojo.clone(parent.inherited); + dojo.forEach(["dir", "lang"], function(name){ + var val = parent.node.getAttribute(name); + if(val){ + inherited[name] = val; + } + }); + + // if parent is a widget, then search for <script type=dojo/*> tags and put them in scripts[]. + var scripts = parent.scripts; + + // unless parent is a widget with the stopParser flag set, continue search for dojoType, recursively + var recurse = !parent.clsInfo || !parent.clsInfo.cls.prototype.stopParser; + + // scan parent's children looking for dojoType and <script type=dojo/*> + for(var child = parent.node.firstChild; child; child = child.nextSibling){ + if(child.nodeType == 1){ + var type = recurse && child.getAttribute(attrName); + if(type){ + // if dojoType specified, add to output array of nodes to instantiate + var params = { + "type": type, + clsInfo: getClassInfo(type), // note: won't find classes declared via dojo.Declaration + node: child, + scripts: [], // <script> nodes that are parent's children + inherited: inherited // dir & lang attributes inherited from parent + }; + list.push(params); + + // Recurse, collecting <script type="dojo/..."> children, and also looking for + // descendant nodes with dojoType specified (unless the widget has the stopParser flag), + scan(params, list); + }else if(scripts && child.nodeName.toLowerCase() == "script"){ + // if <script type="dojo/...">, save in scripts[] + type = child.getAttribute("type"); + if (type && /^dojo\//i.test(type)) { + scripts.push(child); + } + }else if(recurse){ + // Recurse, looking for grandchild nodes with dojoType specified + scan({ + node: child, + inherited: inherited + }, list); + } + } + } + } + + // Make list of all nodes on page w/dojoType specified + var list = []; + scan({ + node: root ? dojo.byId(root) : dojo.body(), + inherited: (args && args.inherited) || { + dir: dojo._isBodyLtr() ? "ltr" : "rtl" + } + }, list); + + // go build the object instances + return this.instantiate(list, null, args); // Array + }; +}(); + +//Register the parser callback. It should be the first callback +//after the a11y test. + +(function(){ + var parseRunner = function(){ + if(dojo.config.parseOnLoad){ + dojo.parser.parse(); + } + }; + + // FIXME: need to clobber cross-dependency!! + if(dojo.exists("dijit.wai.onload") && (dijit.wai.onload === dojo._loaders[0])){ + dojo._loaders.splice(1, 0, parseRunner); + }else{ + dojo._loaders.unshift(parseRunner); + } +})(); + +} + +if(!dojo._hasResource["dijit._Widget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._Widget"] = true; +dojo.provide("dijit._Widget"); + +dojo.require( "dijit._base" ); + + +// This code is to assist deferring dojo.connect() calls in widgets (connecting to events on the widgets' +// DOM nodes) until someone actually needs to monitor that event. +dojo.connect(dojo, "_connect", + function(/*dijit._Widget*/ widget, /*String*/ event){ + if(widget && dojo.isFunction(widget._onConnect)){ + widget._onConnect(event); + } + }); + +dijit._connectOnUseEventHandler = function(/*Event*/ event){}; + +// Keep track of where the last keydown event was, to help avoid generating +// spurious ondijitclick events when: +// 1. focus is on a <button> or <a> +// 2. user presses then releases the ENTER key +// 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler +// 4. onkeyup event fires, causing the ondijitclick handler to fire +dijit._lastKeyDownNode = null; +if(dojo.isIE){ + (function(){ + var keydownCallback = function(evt){ + dijit._lastKeyDownNode = evt.srcElement; + }; + dojo.doc.attachEvent('onkeydown', keydownCallback); + dojo.addOnWindowUnload(function(){ + dojo.doc.detachEvent('onkeydown', keydownCallback); + }); + })(); +}else{ + dojo.doc.addEventListener('keydown', function(evt){ + dijit._lastKeyDownNode = evt.target; + }, true); +} + +(function(){ + +var _attrReg = {}, // cached results from getSetterAttributes + getSetterAttributes = function(widget){ + // summary: + // Returns list of attributes with custom setters for specified widget + var dc = widget.declaredClass; + if(!_attrReg[dc]){ + var r = [], + attrs, + proto = widget.constructor.prototype; + for(var fxName in proto){ + if(dojo.isFunction(proto[fxName]) && (attrs = fxName.match(/^_set([a-zA-Z]*)Attr$/)) && attrs[1]){ + r.push(attrs[1].charAt(0).toLowerCase() + attrs[1].substr(1)); + } + } + _attrReg[dc] = r; + } + return _attrReg[dc] || []; // String[] + }; + +dojo.declare("dijit._Widget", null, { + // summary: + // Base class for all Dijit widgets. + + // id: [const] String + // A unique, opaque ID string that can be assigned by users or by the + // system. If the developer passes an ID which is known not to be + // unique, the specified ID is ignored and the system-generated ID is + // used instead. + id: "", + + // lang: [const] String + // Rarely used. Overrides the default Dojo locale used to render this widget, + // as defined by the [HTML LANG](http://www.w3.org/TR/html401/struct/dirlang.html#adef-lang) attribute. + // Value must be among the list of locales specified during by the Dojo bootstrap, + // formatted according to [RFC 3066](http://www.ietf.org/rfc/rfc3066.txt) (like en-us). + lang: "", + + // dir: [const] String + // Bi-directional support, as defined by the [HTML DIR](http://www.w3.org/TR/html401/struct/dirlang.html#adef-dir) + // attribute. Either left-to-right "ltr" or right-to-left "rtl". If undefined, widgets renders in page's + // default direction. + dir: "", + + // class: String + // HTML class attribute + "class": "", + + // style: String||Object + // HTML style attributes as cssText string or name/value hash + style: "", + + // title: String + // HTML title attribute. + // + // For form widgets this specifies a tooltip to display when hovering over + // the widget (just like the native HTML title attribute). + // + // For TitlePane or for when this widget is a child of a TabContainer, AccordionContainer, + // etc., it's used to specify the tab label, accordion pane title, etc. + title: "", + + // tooltip: String + // When this widget's title attribute is used to for a tab label, accordion pane title, etc., + // this specifies the tooltip to appear when the mouse is hovered over that text. + tooltip: "", + + // baseClass: [protected] String + // Root CSS class of the widget (ex: dijitTextBox), used to construct CSS classes to indicate + // widget state. + baseClass: "", + + // srcNodeRef: [readonly] DomNode + // pointer to original DOM node + srcNodeRef: null, + + // domNode: [readonly] DomNode + // This is our visible representation of the widget! Other DOM + // Nodes may by assigned to other properties, usually through the + // template system's dojoAttachPoint syntax, but the domNode + // property is the canonical "top level" node in widget UI. + domNode: null, + + // containerNode: [readonly] DomNode + // Designates where children of the source DOM node will be placed. + // "Children" in this case refers to both DOM nodes and widgets. + // For example, for myWidget: + // + // | <div dojoType=myWidget> + // | <b> here's a plain DOM node + // | <span dojoType=subWidget>and a widget</span> + // | <i> and another plain DOM node </i> + // | </div> + // + // containerNode would point to: + // + // | <b> here's a plain DOM node + // | <span dojoType=subWidget>and a widget</span> + // | <i> and another plain DOM node </i> + // + // In templated widgets, "containerNode" is set via a + // dojoAttachPoint assignment. + // + // containerNode must be defined for any widget that accepts innerHTML + // (like ContentPane or BorderContainer or even Button), and conversely + // is null for widgets that don't, like TextBox. + containerNode: null, + +/*===== + // _started: Boolean + // startup() has completed. + _started: false, +=====*/ + + // attributeMap: [protected] Object + // attributeMap sets up a "binding" between attributes (aka properties) + // of the widget and the widget's DOM. + // Changes to widget attributes listed in attributeMap will be + // reflected into the DOM. + // + // For example, calling attr('title', 'hello') + // on a TitlePane will automatically cause the TitlePane's DOM to update + // with the new title. + // + // attributeMap is a hash where the key is an attribute of the widget, + // and the value reflects a binding to a: + // + // - DOM node attribute + // | focus: {node: "focusNode", type: "attribute"} + // Maps this.focus to this.focusNode.focus + // + // - DOM node innerHTML + // | title: { node: "titleNode", type: "innerHTML" } + // Maps this.title to this.titleNode.innerHTML + // + // - DOM node innerText + // | title: { node: "titleNode", type: "innerText" } + // Maps this.title to this.titleNode.innerText + // + // - DOM node CSS class + // | myClass: { node: "domNode", type: "class" } + // Maps this.myClass to this.domNode.className + // + // If the value is an array, then each element in the array matches one of the + // formats of the above list. + // + // There are also some shorthands for backwards compatibility: + // - string --> { node: string, type: "attribute" }, for example: + // | "focusNode" ---> { node: "focusNode", type: "attribute" } + // - "" --> { node: "domNode", type: "attribute" } + attributeMap: {id:"", dir:"", lang:"", "class":"", style:"", title:""}, + + // _deferredConnects: [protected] Object + // attributeMap addendum for event handlers that should be connected only on first use + _deferredConnects: { + onClick: "", + onDblClick: "", + onKeyDown: "", + onKeyPress: "", + onKeyUp: "", + onMouseMove: "", + onMouseDown: "", + onMouseOut: "", + onMouseOver: "", + onMouseLeave: "", + onMouseEnter: "", + onMouseUp: "" + }, + + onClick: dijit._connectOnUseEventHandler, + /*===== + onClick: function(event){ + // summary: + // Connect to this function to receive notifications of mouse click events. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onDblClick: dijit._connectOnUseEventHandler, + /*===== + onDblClick: function(event){ + // summary: + // Connect to this function to receive notifications of mouse double click events. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onKeyDown: dijit._connectOnUseEventHandler, + /*===== + onKeyDown: function(event){ + // summary: + // Connect to this function to receive notifications of keys being pressed down. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onKeyPress: dijit._connectOnUseEventHandler, + /*===== + onKeyPress: function(event){ + // summary: + // Connect to this function to receive notifications of printable keys being typed. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onKeyUp: dijit._connectOnUseEventHandler, + /*===== + onKeyUp: function(event){ + // summary: + // Connect to this function to receive notifications of keys being released. + // event: + // key Event + // tags: + // callback + }, + =====*/ + onMouseDown: dijit._connectOnUseEventHandler, + /*===== + onMouseDown: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse button is pressed down. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseMove: dijit._connectOnUseEventHandler, + /*===== + onMouseMove: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves over nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseOut: dijit._connectOnUseEventHandler, + /*===== + onMouseOut: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves off of nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseOver: dijit._connectOnUseEventHandler, + /*===== + onMouseOver: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves onto nodes contained within this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseLeave: dijit._connectOnUseEventHandler, + /*===== + onMouseLeave: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves off of this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseEnter: dijit._connectOnUseEventHandler, + /*===== + onMouseEnter: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse moves onto this widget. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + onMouseUp: dijit._connectOnUseEventHandler, + /*===== + onMouseUp: function(event){ + // summary: + // Connect to this function to receive notifications of when the mouse button is released. + // event: + // mouse Event + // tags: + // callback + }, + =====*/ + + // Constants used in templates + + // _blankGif: [protected] String + // Path to a blank 1x1 image. + // Used by <img> nodes in templates that really get their image via CSS background-image. + _blankGif: (dojo.config.blankGif || dojo.moduleUrl("dojo", "resources/blank.gif")).toString(), + + //////////// INITIALIZATION METHODS /////////////////////////////////////// + + postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){ + // summary: + // Kicks off widget instantiation. See create() for details. + // tags: + // private + this.create(params, srcNodeRef); + }, + + create: function(/*Object?*/params, /*DomNode|String?*/srcNodeRef){ + // summary: + // Kick off the life-cycle of a widget + // params: + // Hash of initialization parameters for widget, including + // scalar values (like title, duration etc.) and functions, + // typically callbacks like onClick. + // srcNodeRef: + // If a srcNodeRef (DOM node) is specified: + // - use srcNodeRef.innerHTML as my contents + // - if this is a behavioral widget then apply behavior + // to that srcNodeRef + // - otherwise, replace srcNodeRef with my generated DOM + // tree + // description: + // Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate, + // etc.), some of which of you'll want to override. See http://docs.dojocampus.org/dijit/_Widget + // for a discussion of the widget creation lifecycle. + // + // Of course, adventurous developers could override create entirely, but this should + // only be done as a last resort. + // tags: + // private + + // store pointer to original DOM tree + this.srcNodeRef = dojo.byId(srcNodeRef); + + // For garbage collection. An array of handles returned by Widget.connect() + // Each handle returned from Widget.connect() is an array of handles from dojo.connect() + this._connects = []; + + // For garbage collection. An array of handles returned by Widget.subscribe() + // The handle returned from Widget.subscribe() is the handle returned from dojo.subscribe() + this._subscribes = []; + + // To avoid double-connects, remove entries from _deferredConnects + // that have been setup manually by a subclass (ex, by dojoAttachEvent). + // If a subclass has redefined a callback (ex: onClick) then assume it's being + // connected to manually. + this._deferredConnects = dojo.clone(this._deferredConnects); + for(var attr in this.attributeMap){ + delete this._deferredConnects[attr]; // can't be in both attributeMap and _deferredConnects + } + for(attr in this._deferredConnects){ + if(this[attr] !== dijit._connectOnUseEventHandler){ + delete this._deferredConnects[attr]; // redefined, probably dojoAttachEvent exists + } + } + + //mixin our passed parameters + if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; } + if(params){ + this.params = params; + dojo.mixin(this,params); + } + this.postMixInProperties(); + + // generate an id for the widget if one wasn't specified + // (be sure to do this before buildRendering() because that function might + // expect the id to be there.) + if(!this.id){ + this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_")); + } + dijit.registry.add(this); + + this.buildRendering(); + + if(this.domNode){ + // Copy attributes listed in attributeMap into the [newly created] DOM for the widget. + this._applyAttributes(); + + var source = this.srcNodeRef; + if(source && source.parentNode){ + source.parentNode.replaceChild(this.domNode, source); + } + + // If the developer has specified a handler as a widget parameter + // (ex: new Button({onClick: ...}) + // then naturally need to connect from DOM node to that handler immediately, + for(attr in this.params){ + this._onConnect(attr); + } + } + + if(this.domNode){ + this.domNode.setAttribute("widgetId", this.id); + } + this.postCreate(); + + // If srcNodeRef has been processed and removed from the DOM (e.g. TemplatedWidget) then delete it to allow GC. + if(this.srcNodeRef && !this.srcNodeRef.parentNode){ + delete this.srcNodeRef; + } + + this._created = true; + }, + + _applyAttributes: function(){ + // summary: + // Step during widget creation to copy all widget attributes to the + // DOM as per attributeMap and _setXXXAttr functions. + // description: + // Skips over blank/false attribute values, unless they were explicitly specified + // as parameters to the widget, since those are the default anyway, + // and setting tabIndex="" is different than not setting tabIndex at all. + // + // It processes the attributes in the attribute map first, and then + // it goes through and processes the attributes for the _setXXXAttr + // functions that have been specified + // tags: + // private + var condAttrApply = function(attr, scope){ + if((scope.params && attr in scope.params) || scope[attr]){ + scope.set(attr, scope[attr]); + } + }; + + // Do the attributes in attributeMap + for(var attr in this.attributeMap){ + condAttrApply(attr, this); + } + + // And also any attributes with custom setters + dojo.forEach(getSetterAttributes(this), function(a){ + if(!(a in this.attributeMap)){ + condAttrApply(a, this); + } + }, this); + }, + + postMixInProperties: function(){ + // summary: + // Called after the parameters to the widget have been read-in, + // but before the widget template is instantiated. Especially + // useful to set properties that are referenced in the widget + // template. + // tags: + // protected + }, + + buildRendering: function(){ + // summary: + // Construct the UI for this widget, setting this.domNode + // description: + // Most widgets will mixin `dijit._Templated`, which implements this + // method. + // tags: + // protected + this.domNode = this.srcNodeRef || dojo.create('div'); + }, + + postCreate: function(){ + // summary: + // Processing after the DOM fragment is created + // description: + // Called after the DOM fragment has been created, but not necessarily + // added to the document. Do not include any operations which rely on + // node dimensions or placement. + // tags: + // protected + + // baseClass is a single class name or occasionally a space-separated list of names. + // Add those classes to the DOMNod. If RTL mode then also add with Rtl suffix. + if(this.baseClass){ + var classes = this.baseClass.split(" "); + if(!this.isLeftToRight()){ + classes = classes.concat( dojo.map(classes, function(name){ return name+"Rtl"; })); + } + dojo.addClass(this.domNode, classes); + } + }, + + startup: function(){ + // summary: + // Processing after the DOM fragment is added to the document + // description: + // Called after a widget and its children have been created and added to the page, + // and all related widgets have finished their create() cycle, up through postCreate(). + // This is useful for composite widgets that need to control or layout sub-widgets. + // Many layout widgets can use this as a wiring phase. + this._started = true; + }, + + //////////// DESTROY FUNCTIONS //////////////////////////////// + + destroyRecursive: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroy this widget and its descendants + // description: + // This is the generic "destructor" function that all widget users + // should call to cleanly discard with a widget. Once a widget is + // destroyed, it is removed from the manager object. + // preserveDom: + // If true, this method will leave the original DOM structure + // alone of descendant Widgets. Note: This will NOT work with + // dijit._Templated widgets. + + this._beingDestroyed = true; + this.destroyDescendants(preserveDom); + this.destroy(preserveDom); + }, + + destroy: function(/*Boolean*/ preserveDom){ + // summary: + // Destroy this widget, but not its descendants. + // This method will, however, destroy internal widgets such as those used within a template. + // preserveDom: Boolean + // If true, this method will leave the original DOM structure alone. + // Note: This will not yet work with _Templated widgets + + this._beingDestroyed = true; + this.uninitialize(); + var d = dojo, + dfe = d.forEach, + dun = d.unsubscribe; + dfe(this._connects, function(array){ + dfe(array, d.disconnect); + }); + dfe(this._subscribes, function(handle){ + dun(handle); + }); + + // destroy widgets created as part of template, etc. + dfe(this._supportingWidgets || [], function(w){ + if(w.destroyRecursive){ + w.destroyRecursive(); + }else if(w.destroy){ + w.destroy(); + } + }); + + this.destroyRendering(preserveDom); + dijit.registry.remove(this.id); + this._destroyed = true; + }, + + destroyRendering: function(/*Boolean?*/ preserveDom){ + // summary: + // Destroys the DOM nodes associated with this widget + // preserveDom: + // If true, this method will leave the original DOM structure alone + // during tear-down. Note: this will not work with _Templated + // widgets yet. + // tags: + // protected + + if(this.bgIframe){ + this.bgIframe.destroy(preserveDom); + delete this.bgIframe; + } + + if(this.domNode){ + if(preserveDom){ + dojo.removeAttr(this.domNode, "widgetId"); + }else{ + dojo.destroy(this.domNode); + } + delete this.domNode; + } + + if(this.srcNodeRef){ + if(!preserveDom){ + dojo.destroy(this.srcNodeRef); + } + delete this.srcNodeRef; + } + }, + + destroyDescendants: function(/*Boolean?*/ preserveDom){ + // summary: + // Recursively destroy the children of this widget and their + // descendants. + // preserveDom: + // If true, the preserveDom attribute is passed to all descendant + // widget's .destroy() method. Not for use with _Templated + // widgets. + + // get all direct descendants and destroy them recursively + dojo.forEach(this.getChildren(), function(widget){ + if(widget.destroyRecursive){ + widget.destroyRecursive(preserveDom); + } + }); + }, + + + uninitialize: function(){ + // summary: + // Stub function. Override to implement custom widget tear-down + // behavior. + // tags: + // protected + return false; + }, + + ////////////////// MISCELLANEOUS METHODS /////////////////// + + onFocus: function(){ + // summary: + // Called when the widget becomes "active" because + // it or a widget inside of it either has focus, or has recently + // been clicked. + // tags: + // callback + }, + + onBlur: function(){ + // summary: + // Called when the widget stops being "active" because + // focus moved to something outside of it, or the user + // clicked somewhere outside of it, or the widget was + // hidden. + // tags: + // callback + }, + + _onFocus: function(e){ + // summary: + // This is where widgets do processing for when they are active, + // such as changing CSS classes. See onFocus() for more details. + // tags: + // protected + this.onFocus(); + }, + + _onBlur: function(){ + // summary: + // This is where widgets do processing for when they stop being active, + // such as changing CSS classes. See onBlur() for more details. + // tags: + // protected + this.onBlur(); + }, + + _onConnect: function(/*String*/ event){ + // summary: + // Called when someone connects to one of my handlers. + // "Turn on" that handler if it isn't active yet. + // + // This is also called for every single initialization parameter + // so need to do nothing for parameters like "id". + // tags: + // private + if(event in this._deferredConnects){ + var mapNode = this[this._deferredConnects[event] || 'domNode']; + this.connect(mapNode, event.toLowerCase(), event); + delete this._deferredConnects[event]; + } + }, + + _setClassAttr: function(/*String*/ value){ + // summary: + // Custom setter for the CSS "class" attribute + // tags: + // protected + var mapNode = this[this.attributeMap["class"] || 'domNode']; + dojo.removeClass(mapNode, this["class"]) + this["class"] = value; + dojo.addClass(mapNode, value); + }, + + _setStyleAttr: function(/*String||Object*/ value){ + // summary: + // Sets the style attribut of the widget according to value, + // which is either a hash like {height: "5px", width: "3px"} + // or a plain string + // description: + // Determines which node to set the style on based on style setting + // in attributeMap. + // tags: + // protected + + var mapNode = this[this.attributeMap.style || 'domNode']; + + // Note: technically we should revert any style setting made in a previous call + // to his method, but that's difficult to keep track of. + + if(dojo.isObject(value)){ + dojo.style(mapNode, value); + }else{ + if(mapNode.style.cssText){ + mapNode.style.cssText += "; " + value; + }else{ + mapNode.style.cssText = value; + } + } + + this.style = value; + }, + + setAttribute: function(/*String*/ attr, /*anything*/ value){ + // summary: + // Deprecated. Use set() instead. + // tags: + // deprecated + dojo.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0"); + this.set(attr, value); + }, + + _attrToDom: function(/*String*/ attr, /*String*/ value){ + // summary: + // Reflect a widget attribute (title, tabIndex, duration etc.) to + // the widget DOM, as specified in attributeMap. + // + // description: + // Also sets this["attr"] to the new value. + // Note some attributes like "type" + // cannot be processed this way as they are not mutable. + // + // tags: + // private + + var commands = this.attributeMap[attr]; + dojo.forEach(dojo.isArray(commands) ? commands : [commands], function(command){ + + // Get target node and what we are doing to that node + var mapNode = this[command.node || command || "domNode"]; // DOM node + var type = command.type || "attribute"; // class, innerHTML, innerText, or attribute + + switch(type){ + case "attribute": + if(dojo.isFunction(value)){ // functions execute in the context of the widget + value = dojo.hitch(this, value); + } + + // Get the name of the DOM node attribute; usually it's the same + // as the name of the attribute in the widget (attr), but can be overridden. + // Also maps handler names to lowercase, like onSubmit --> onsubmit + var attrName = command.attribute ? command.attribute : + (/^on[A-Z][a-zA-Z]*$/.test(attr) ? attr.toLowerCase() : attr); + + dojo.attr(mapNode, attrName, value); + break; + case "innerText": + mapNode.innerHTML = ""; + mapNode.appendChild(dojo.doc.createTextNode(value)); + break; + case "innerHTML": + mapNode.innerHTML = value; + break; + case "class": + dojo.removeClass(mapNode, this[attr]); + dojo.addClass(mapNode, value); + break; + } + }, this); + this[attr] = value; + }, + + attr: function(/*String|Object*/name, /*Object?*/value){ + // summary: + // Set or get properties on a widget instance. + // name: + // The property to get or set. If an object is passed here and not + // a string, its keys are used as names of attributes to be set + // and the value of the object as values to set in the widget. + // value: + // Optional. If provided, attr() operates as a setter. If omitted, + // the current value of the named property is returned. + // description: + // This method is deprecated, use get() or set() directly. + + // Print deprecation warning but only once per calling function + if(dojo.config.isDebug){ + var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}), + caller = (arguments.callee.caller || "unknown caller").toString(); + if(!alreadyCalledHash[caller]){ + dojo.deprecated(this.declaredClass + "::attr() is deprecated. Use get() or set() instead, called from " + + caller, "", "2.0"); + alreadyCalledHash[caller] = true; + } + } + + var args = arguments.length; + if(args >= 2 || typeof name === "object"){ // setter + return this.set.apply(this, arguments); + }else{ // getter + return this.get(name); + } + }, + + get: function(name){ + // summary: + // Get a property from a widget. + // name: + // The property to get. + // description: + // Get a named property from a widget. The property may + // potentially be retrieved via a getter method. If no getter is defined, this + // just retrieves the object's property. + // For example, if the widget has a properties "foo" + // and "bar" and a method named "_getFooAttr", calling: + // | myWidget.get("foo"); + // would be equivalent to writing: + // | widget._getFooAttr(); + // and: + // | myWidget.get("bar"); + // would be equivalent to writing: + // | widget.bar; + var names = this._getAttrNames(name); + return this[names.g] ? this[names.g]() : this[name]; + }, + + set: function(name, value){ + // summary: + // Set a property on a widget + // name: + // The property to set. + // value: + // The value to set in the property. + // description: + // Sets named properties on a widget which may potentially be handled by a + // setter in the widget. + // For example, if the widget has a properties "foo" + // and "bar" and a method named "_setFooAttr", calling: + // | myWidget.set("foo", "Howdy!"); + // would be equivalent to writing: + // | widget._setFooAttr("Howdy!"); + // and: + // | myWidget.set("bar", 3); + // would be equivalent to writing: + // | widget.bar = 3; + // + // set() may also be called with a hash of name/value pairs, ex: + // | myWidget.set({ + // | foo: "Howdy", + // | bar: 3 + // | }) + // This is equivalent to calling set(foo, "Howdy") and set(bar, 3) + + if(typeof name === "object"){ + for(var x in name){ + this.set(x, name[x]); + } + return this; + } + var names = this._getAttrNames(name); + if(this[names.s]){ + // use the explicit setter + var result = this[names.s].apply(this, Array.prototype.slice.call(arguments, 1)); + }else{ + // if param is specified as DOM node attribute, copy it + if(name in this.attributeMap){ + this._attrToDom(name, value); + } + var oldValue = this[name]; + // FIXME: what about function assignments? Any way to connect() here? + this[name] = value; + } + return result || this; + }, + + _attrPairNames: {}, // shared between all widgets + _getAttrNames: function(name){ + // summary: + // Helper function for get() and set(). + // Caches attribute name values so we don't do the string ops every time. + // tags: + // private + + var apn = this._attrPairNames; + if(apn[name]){ return apn[name]; } + var uc = name.charAt(0).toUpperCase() + name.substr(1); + return (apn[name] = { + n: name+"Node", + s: "_set"+uc+"Attr", + g: "_get"+uc+"Attr" + }); + }, + + toString: function(){ + // summary: + // Returns a string that represents the widget + // description: + // When a widget is cast to a string, this method will be used to generate the + // output. Currently, it does not implement any sort of reversible + // serialization. + return '[Widget ' + this.declaredClass + ', ' + (this.id || 'NO ID') + ']'; // String + }, + + getDescendants: function(){ + // summary: + // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. + // This method should generally be avoided as it returns widgets declared in templates, which are + // supposed to be internal/hidden, but it's left here for back-compat reasons. + + return this.containerNode ? dojo.query('[widgetId]', this.containerNode).map(dijit.byNode) : []; // dijit._Widget[] + }, + + getChildren: function(){ + // summary: + // Returns all the widgets contained by this, i.e., all widgets underneath this.containerNode. + // Does not return nested widgets, nor widgets that are part of this widget's template. + return this.containerNode ? dijit.findWidgets(this.containerNode) : []; // dijit._Widget[] + }, + + // nodesWithKeyClick: [private] String[] + // List of nodes that correctly handle click events via native browser support, + // and don't need dijit's help + nodesWithKeyClick: ["input", "button"], + + connect: function( + /*Object|null*/ obj, + /*String|Function*/ event, + /*String|Function*/ method){ + // summary: + // Connects specified obj/event to specified method of this object + // and registers for disconnect() on widget destroy. + // description: + // Provide widget-specific analog to dojo.connect, except with the + // implicit use of this widget as the target object. + // This version of connect also provides a special "ondijitclick" + // event which triggers on a click or space or enter keyup + // returns: + // A handle that can be passed to `disconnect` in order to disconnect before + // the widget is destroyed. + // example: + // | var btn = new dijit.form.Button(); + // | // when foo.bar() is called, call the listener we're going to + // | // provide in the scope of btn + // | btn.connect(foo, "bar", function(){ + // | console.debug(this.toString()); + // | }); + // tags: + // protected + + var d = dojo, + dc = d._connect, + handles = []; + if(event == "ondijitclick"){ + // add key based click activation for unsupported nodes. + // do all processing onkey up to prevent spurious clicks + // for details see comments at top of this file where _lastKeyDownNode is defined + if(dojo.indexOf(this.nodesWithKeyClick, obj.nodeName.toLowerCase()) == -1){ // is NOT input or button + var m = d.hitch(this, method); + handles.push( + dc(obj, "onkeydown", this, function(e){ + //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode)); + if((e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) && + !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ + // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work + dijit._lastKeyDownNode = e.target; + e.preventDefault(); // stop event to prevent scrolling on space key in IE + } + }), + dc(obj, "onkeyup", this, function(e){ + //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", dijit._lastKeyDownNode, ", equality is ", (e.target === dijit._lastKeyDownNode)); + if( (e.keyCode == d.keys.ENTER || e.keyCode == d.keys.SPACE) && + e.target === dijit._lastKeyDownNode && + !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey){ + //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert + dijit._lastKeyDownNode = null; + return m(e); + } + }) + ); + } + event = "onclick"; + } + handles.push(dc(obj, event, this, method)); + + this._connects.push(handles); + return handles; // _Widget.Handle + }, + + disconnect: function(/* _Widget.Handle */ handles){ + // summary: + // Disconnects handle created by `connect`. + // Also removes handle from this widget's list of connects. + // tags: + // protected + for(var i=0; i<this._connects.length; i++){ + if(this._connects[i] == handles){ + dojo.forEach(handles, dojo.disconnect); + this._connects.splice(i, 1); + return; + } + } + }, + + subscribe: function( + /*String*/ topic, + /*String|Function*/ method){ + // summary: + // Subscribes to the specified topic and calls the specified method + // of this object and registers for unsubscribe() on widget destroy. + // description: + // Provide widget-specific analog to dojo.subscribe, except with the + // implicit use of this widget as the target object. + // example: + // | var btn = new dijit.form.Button(); + // | // when /my/topic is published, this button changes its label to + // | // be the parameter of the topic. + // | btn.subscribe("/my/topic", function(v){ + // | this.set("label", v); + // | }); + var d = dojo, + handle = d.subscribe(topic, this, method); + + // return handles for Any widget that may need them + this._subscribes.push(handle); + return handle; + }, + + unsubscribe: function(/*Object*/ handle){ + // summary: + // Unsubscribes handle created by this.subscribe. + // Also removes handle from this widget's list of subscriptions + for(var i=0; i<this._subscribes.length; i++){ + if(this._subscribes[i] == handle){ + dojo.unsubscribe(handle); + this._subscribes.splice(i, 1); + return; + } + } + }, + + isLeftToRight: function(){ + // summary: + // Return this widget's explicit or implicit orientation (true for LTR, false for RTL) + // tags: + // protected + return this.dir ? (this.dir == "ltr") : dojo._isBodyLtr(); //Boolean + }, + + isFocusable: function(){ + // summary: + // Return true if this widget can currently be focused + // and false if not + return this.focus && (dojo.style(this.domNode, "display") != "none"); + }, + + placeAt: function(/* String|DomNode|_Widget */reference, /* String?|Int? */position){ + // summary: + // Place this widget's domNode reference somewhere in the DOM based + // on standard dojo.place conventions, or passing a Widget reference that + // contains and addChild member. + // + // description: + // A convenience function provided in all _Widgets, providing a simple + // shorthand mechanism to put an existing (or newly created) Widget + // somewhere in the dom, and allow chaining. + // + // reference: + // The String id of a domNode, a domNode reference, or a reference to a Widget posessing + // an addChild method. + // + // position: + // If passed a string or domNode reference, the position argument + // accepts a string just as dojo.place does, one of: "first", "last", + // "before", or "after". + // + // If passed a _Widget reference, and that widget reference has an ".addChild" method, + // it will be called passing this widget instance into that method, supplying the optional + // position index passed. + // + // returns: + // dijit._Widget + // Provides a useful return of the newly created dijit._Widget instance so you + // can "chain" this function by instantiating, placing, then saving the return value + // to a variable. + // + // example: + // | // create a Button with no srcNodeRef, and place it in the body: + // | var button = new dijit.form.Button({ label:"click" }).placeAt(dojo.body()); + // | // now, 'button' is still the widget reference to the newly created button + // | dojo.connect(button, "onClick", function(e){ console.log('click'); }); + // + // example: + // | // create a button out of a node with id="src" and append it to id="wrapper": + // | var button = new dijit.form.Button({},"src").placeAt("wrapper"); + // + // example: + // | // place a new button as the first element of some div + // | var button = new dijit.form.Button({ label:"click" }).placeAt("wrapper","first"); + // + // example: + // | // create a contentpane and add it to a TabContainer + // | var tc = dijit.byId("myTabs"); + // | new dijit.layout.ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc) + + if(reference.declaredClass && reference.addChild){ + reference.addChild(this, position); + }else{ + dojo.place(this.domNode, reference, position); + } + return this; + }, + + _onShow: function(){ + // summary: + // Internal method called when this widget is made visible. + // See `onShow` for details. + this.onShow(); + }, + + onShow: function(){ + // summary: + // Called when this widget becomes the selected pane in a + // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, + // `dijit.layout.AccordionContainer`, etc. + // + // Also called to indicate display of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. + // tags: + // callback + }, + + onHide: function(){ + // summary: + // Called when another widget becomes the selected pane in a + // `dijit.layout.TabContainer`, `dijit.layout.StackContainer`, + // `dijit.layout.AccordionContainer`, etc. + // + // Also called to indicate hide of a `dijit.Dialog`, `dijit.TooltipDialog`, or `dijit.TitlePane`. + // tags: + // callback + }, + + onClose: function(){ + // summary: + // Called when this widget is being displayed as a popup (ex: a Calendar popped + // up from a DateTextBox), and it is hidden. + // This is called from the dijit.popup code, and should not be called directly. + // + // Also used as a parameter for children of `dijit.layout.StackContainer` or subclasses. + // Callback if a user tries to close the child. Child will be closed if this function returns true. + // tags: + // extension + + return true; // Boolean + } +}); + +})(); + +} + +if(!dojo._hasResource["dojo.string"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.string"] = true; +dojo.provide("dojo.string"); + +/*===== +dojo.string = { + // summary: String utilities for Dojo +}; +=====*/ + +dojo.string.rep = function(/*String*/str, /*Integer*/num){ + // summary: + // Efficiently replicate a string `n` times. + // str: + // the string to replicate + // num: + // number of times to replicate the string + + if(num <= 0 || !str){ return ""; } + + var buf = []; + for(;;){ + if(num & 1){ + buf.push(str); + } + if(!(num >>= 1)){ break; } + str += str; + } + return buf.join(""); // String +}; + +dojo.string.pad = function(/*String*/text, /*Integer*/size, /*String?*/ch, /*Boolean?*/end){ + // summary: + // Pad a string to guarantee that it is at least `size` length by + // filling with the character `ch` at either the start or end of the + // string. Pads at the start, by default. + // text: + // the string to pad + // size: + // length to provide padding + // ch: + // character to pad, defaults to '0' + // end: + // adds padding at the end if true, otherwise pads at start + // example: + // | // Fill the string to length 10 with "+" characters on the right. Yields "Dojo++++++". + // | dojo.string.pad("Dojo", 10, "+", true); + + if(!ch){ + ch = '0'; + } + var out = String(text), + pad = dojo.string.rep(ch, Math.ceil((size - out.length) / ch.length)); + return end ? out + pad : pad + out; // String +}; + +dojo.string.substitute = function( /*String*/ template, + /*Object|Array*/map, + /*Function?*/ transform, + /*Object?*/ thisObject){ + // summary: + // Performs parameterized substitutions on a string. Throws an + // exception if any parameter is unmatched. + // template: + // a string with expressions in the form `${key}` to be replaced or + // `${key:format}` which specifies a format function. keys are case-sensitive. + // map: + // hash to search for substitutions + // transform: + // a function to process all parameters before substitution takes + // place, e.g. mylib.encodeXML + // thisObject: + // where to look for optional format function; default to the global + // namespace + // example: + // Substitutes two expressions in a string from an Array or Object + // | // returns "File 'foo.html' is not found in directory '/temp'." + // | // by providing substitution data in an Array + // | dojo.string.substitute( + // | "File '${0}' is not found in directory '${1}'.", + // | ["foo.html","/temp"] + // | ); + // | + // | // also returns "File 'foo.html' is not found in directory '/temp'." + // | // but provides substitution data in an Object structure. Dotted + // | // notation may be used to traverse the structure. + // | dojo.string.substitute( + // | "File '${name}' is not found in directory '${info.dir}'.", + // | { name: "foo.html", info: { dir: "/temp" } } + // | ); + // example: + // Use a transform function to modify the values: + // | // returns "file 'foo.html' is not found in directory '/temp'." + // | dojo.string.substitute( + // | "${0} is not found in ${1}.", + // | ["foo.html","/temp"], + // | function(str){ + // | // try to figure out the type + // | var prefix = (str.charAt(0) == "/") ? "directory": "file"; + // | return prefix + " '" + str + "'"; + // | } + // | ); + // example: + // Use a formatter + // | // returns "thinger -- howdy" + // | dojo.string.substitute( + // | "${0:postfix}", ["thinger"], null, { + // | postfix: function(value, key){ + // | return value + " -- howdy"; + // | } + // | } + // | ); + + thisObject = thisObject || dojo.global; + transform = transform ? + dojo.hitch(thisObject, transform) : function(v){ return v; }; + + return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g, + function(match, key, format){ + var value = dojo.getObject(key, false, map); + if(format){ + value = dojo.getObject(format, false, thisObject).call(thisObject, value, key); + } + return transform(value, key).toString(); + }); // String +}; + +/*===== +dojo.string.trim = function(str){ + // summary: + // Trims whitespace from both sides of the string + // str: String + // String to be trimmed + // returns: String + // Returns the trimmed string + // description: + // This version of trim() was taken from [Steven Levithan's blog](http://blog.stevenlevithan.com/archives/faster-trim-javascript). + // The short yet performant version of this function is dojo.trim(), + // which is part of Dojo base. Uses String.prototype.trim instead, if available. + return ""; // String +} +=====*/ + +dojo.string.trim = String.prototype.trim ? + dojo.trim : // aliasing to the native function + function(str){ + str = str.replace(/^\s+/, ''); + for(var i = str.length - 1; i >= 0; i--){ + if(/\S/.test(str.charAt(i))){ + str = str.substring(0, i + 1); + break; + } + } + return str; + }; + +} + +if(!dojo._hasResource["dojo.cache"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dojo.cache"] = true; +dojo.provide("dojo.cache"); + +/*===== +dojo.cache = { + // summary: + // A way to cache string content that is fetchable via `dojo.moduleUrl`. +}; +=====*/ + +(function(){ + var cache = {}; + dojo.cache = function(/*String||Object*/module, /*String*/url, /*String||Object?*/value){ + // summary: + // A getter and setter for storing the string content associated with the + // module and url arguments. + // description: + // module and url are used to call `dojo.moduleUrl()` to generate a module URL. + // If value is specified, the cache value for the moduleUrl will be set to + // that value. Otherwise, dojo.cache will fetch the moduleUrl and store it + // in its internal cache and return that cached value for the URL. To clear + // a cache value pass null for value. Since XMLHttpRequest (XHR) is used to fetch the + // the URL contents, only modules on the same domain of the page can use this capability. + // The build system can inline the cache values though, to allow for xdomain hosting. + // module: String||Object + // If a String, the module name to use for the base part of the URL, similar to module argument + // to `dojo.moduleUrl`. If an Object, something that has a .toString() method that + // generates a valid path for the cache item. For example, a dojo._Url object. + // url: String + // The rest of the path to append to the path derived from the module argument. If + // module is an object, then this second argument should be the "value" argument instead. + // value: String||Object? + // If a String, the value to use in the cache for the module/url combination. + // If an Object, it can have two properties: value and sanitize. The value property + // should be the value to use in the cache, and sanitize can be set to true or false, + // to indicate if XML declarations should be removed from the value and if the HTML + // inside a body tag in the value should be extracted as the real value. The value argument + // or the value property on the value argument are usually only used by the build system + // as it inlines cache content. + // example: + // To ask dojo.cache to fetch content and store it in the cache (the dojo["cache"] style + // of call is used to avoid an issue with the build system erroneously trying to intern + // this example. To get the build system to intern your dojo.cache calls, use the + // "dojo.cache" style of call): + // | //If template.html contains "<h1>Hello</h1>" that will be + // | //the value for the text variable. + // | var text = dojo["cache"]("my.module", "template.html"); + // example: + // To ask dojo.cache to fetch content and store it in the cache, and sanitize the input + // (the dojo["cache"] style of call is used to avoid an issue with the build system + // erroneously trying to intern this example. To get the build system to intern your + // dojo.cache calls, use the "dojo.cache" style of call): + // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the + // | //text variable will contain just "<h1>Hello</h1>". + // | var text = dojo["cache"]("my.module", "template.html", {sanitize: true}); + // example: + // Same example as previous, but demostrates how an object can be passed in as + // the first argument, then the value argument can then be the second argument. + // | //If template.html contains "<html><body><h1>Hello</h1></body></html>", the + // | //text variable will contain just "<h1>Hello</h1>". + // | var text = dojo["cache"](new dojo._Url("my/module/template.html"), {sanitize: true}); + + //Module could be a string, or an object that has a toString() method + //that will return a useful path. If it is an object, then the "url" argument + //will actually be the value argument. + if(typeof module == "string"){ + var pathObj = dojo.moduleUrl(module, url); + }else{ + pathObj = module; + value = url; + } + var key = pathObj.toString(); + + var val = value; + if(value != undefined && !dojo.isString(value)){ + val = ("value" in value ? value.value : undefined); + } + + var sanitize = value && value.sanitize ? true : false; + + if(typeof val == "string"){ + //We have a string, set cache value + val = cache[key] = sanitize ? dojo.cache._sanitize(val) : val; + }else if(val === null){ + //Remove cached value + delete cache[key]; + }else{ + //Allow cache values to be empty strings. If key property does + //not exist, fetch it. + if(!(key in cache)){ + val = dojo._getText(key); + cache[key] = sanitize ? dojo.cache._sanitize(val) : val; + } + val = cache[key]; + } + return val; //String + }; + + dojo.cache._sanitize = function(/*String*/val){ + // summary: + // Strips <?xml ...?> declarations so that external SVG and XML + // documents can be added to a document without worry. Also, if the string + // is an HTML document, only the part inside the body tag is returned. + // description: + // Copied from dijit._Templated._sanitizeTemplateString. + if(val){ + val = val.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, ""); + var matches = val.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im); + if(matches){ + val = matches[1]; + } + }else{ + val = ""; + } + return val; //String + }; +})(); + +} + +if(!dojo._hasResource["dijit._Templated"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._Templated"] = true; +dojo.provide("dijit._Templated"); + + + + + + +dojo.declare("dijit._Templated", + null, + { + // summary: + // Mixin for widgets that are instantiated from a template + + // templateString: [protected] String + // A string that represents the widget template. Pre-empts the + // templatePath. In builds that have their strings "interned", the + // templatePath is converted to an inline templateString, thereby + // preventing a synchronous network call. + // + // Use in conjunction with dojo.cache() to load from a file. + templateString: null, + + // templatePath: [protected deprecated] String + // Path to template (HTML file) for this widget relative to dojo.baseUrl. + // Deprecated: use templateString with dojo.cache() instead. + templatePath: null, + + // widgetsInTemplate: [protected] Boolean + // Should we parse the template to find widgets that might be + // declared in markup inside it? False by default. + widgetsInTemplate: false, + + // skipNodeCache: [protected] Boolean + // If using a cached widget template node poses issues for a + // particular widget class, it can set this property to ensure + // that its template is always re-built from a string + _skipNodeCache: false, + + // _earlyTemplatedStartup: Boolean + // A fallback to preserve the 1.0 - 1.3 behavior of children in + // templates having their startup called before the parent widget + // fires postCreate. Defaults to 'false', causing child widgets to + // have their .startup() called immediately before a parent widget + // .startup(), but always after the parent .postCreate(). Set to + // 'true' to re-enable to previous, arguably broken, behavior. + _earlyTemplatedStartup: false, + + // _attachPoints: [private] String[] + // List of widget attribute names associated with dojoAttachPoint=... in the + // template, ex: ["containerNode", "labelNode"] +/*===== + _attachPoints: [], + =====*/ + + constructor: function(){ + this._attachPoints = []; + }, + + _stringRepl: function(tmpl){ + // summary: + // Does substitution of ${foo} type properties in template string + // tags: + // private + var className = this.declaredClass, _this = this; + // Cache contains a string because we need to do property replacement + // do the property replacement + return dojo.string.substitute(tmpl, this, function(value, key){ + if(key.charAt(0) == '!'){ value = dojo.getObject(key.substr(1), false, _this); } + if(typeof value == "undefined"){ throw new Error(className+" template:"+key); } // a debugging aide + if(value == null){ return ""; } + + // Substitution keys beginning with ! will skip the transform step, + // in case a user wishes to insert unescaped markup, e.g. ${!foo} + return key.charAt(0) == "!" ? value : + // Safer substitution, see heading "Attribute values" in + // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 + value.toString().replace(/"/g,"""); //TODO: add &? use encodeXML method? + }, this); + }, + + // method over-ride + buildRendering: function(){ + // summary: + // Construct the UI for this widget from a template, setting this.domNode. + // tags: + // protected + + // Lookup cached version of template, and download to cache if it + // isn't there already. Returns either a DomNode or a string, depending on + // whether or not the template contains ${foo} replacement parameters. + var cached = dijit._Templated.getCachedTemplate(this.templatePath, this.templateString, this._skipNodeCache); + + var node; + if(dojo.isString(cached)){ + node = dojo._toDom(this._stringRepl(cached)); + if(node.nodeType != 1){ + // Flag common problems such as templates with multiple top level nodes (nodeType == 11) + throw new Error("Invalid template: " + cached); + } + }else{ + // if it's a node, all we have to do is clone it + node = cached.cloneNode(true); + } + + this.domNode = node; + + // recurse through the node, looking for, and attaching to, our + // attachment points and events, which should be defined on the template node. + this._attachTemplateNodes(node); + + if(this.widgetsInTemplate){ + // Make sure dojoType is used for parsing widgets in template. + // The dojo.parser.query could be changed from multiversion support. + var parser = dojo.parser, qry, attr; + if(parser._query != "[dojoType]"){ + qry = parser._query; + attr = parser._attrName; + parser._query = "[dojoType]"; + parser._attrName = "dojoType"; + } + + // Store widgets that we need to start at a later point in time + var cw = (this._startupWidgets = dojo.parser.parse(node, { + noStart: !this._earlyTemplatedStartup, + inherited: {dir: this.dir, lang: this.lang} + })); + + // Restore the query. + if(qry){ + parser._query = qry; + parser._attrName = attr; + } + + this._supportingWidgets = dijit.findWidgets(node); + + this._attachTemplateNodes(cw, function(n,p){ + return n[p]; + }); + } + + this._fillContent(this.srcNodeRef); + }, + + _fillContent: function(/*DomNode*/ source){ + // summary: + // Relocate source contents to templated container node. + // this.containerNode must be able to receive children, or exceptions will be thrown. + // tags: + // protected + var dest = this.containerNode; + if(source && dest){ + while(source.hasChildNodes()){ + dest.appendChild(source.firstChild); + } + } + }, + + _attachTemplateNodes: function(rootNode, getAttrFunc){ + // summary: + // Iterate through the template and attach functions and nodes accordingly. + // description: + // Map widget properties and functions to the handlers specified in + // the dom node and it's descendants. This function iterates over all + // nodes and looks for these properties: + // * dojoAttachPoint + // * dojoAttachEvent + // * waiRole + // * waiState + // rootNode: DomNode|Array[Widgets] + // the node to search for properties. All children will be searched. + // getAttrFunc: Function? + // a function which will be used to obtain property for a given + // DomNode/Widget + // tags: + // private + + getAttrFunc = getAttrFunc || function(n,p){ return n.getAttribute(p); }; + + var nodes = dojo.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*")); + var x = dojo.isArray(rootNode) ? 0 : -1; + for(; x<nodes.length; x++){ + var baseNode = (x == -1) ? rootNode : nodes[x]; + if(this.widgetsInTemplate && getAttrFunc(baseNode, "dojoType")){ + continue; + } + // Process dojoAttachPoint + var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint"); + if(attachPoint){ + var point, points = attachPoint.split(/\s*,\s*/); + while((point = points.shift())){ + if(dojo.isArray(this[point])){ + this[point].push(baseNode); + }else{ + this[point]=baseNode; + } + this._attachPoints.push(point); + } + } + + // Process dojoAttachEvent + var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent"); + if(attachEvent){ + // NOTE: we want to support attributes that have the form + // "domEvent: nativeEvent; ..." + var event, events = attachEvent.split(/\s*,\s*/); + var trim = dojo.trim; + while((event = events.shift())){ + if(event){ + var thisFunc = null; + if(event.indexOf(":") != -1){ + // oh, if only JS had tuple assignment + var funcNameArr = event.split(":"); + event = trim(funcNameArr[0]); + thisFunc = trim(funcNameArr[1]); + }else{ + event = trim(event); + } + if(!thisFunc){ + thisFunc = event; + } + this.connect(baseNode, event, thisFunc); + } + } + } + + // waiRole, waiState + var role = getAttrFunc(baseNode, "waiRole"); + if(role){ + dijit.setWaiRole(baseNode, role); + } + var values = getAttrFunc(baseNode, "waiState"); + if(values){ + dojo.forEach(values.split(/\s*,\s*/), function(stateValue){ + if(stateValue.indexOf('-') != -1){ + var pair = stateValue.split('-'); + dijit.setWaiState(baseNode, pair[0], pair[1]); + } + }); + } + } + }, + + startup: function(){ + dojo.forEach(this._startupWidgets, function(w){ + if(w && !w._started && w.startup){ + w.startup(); + } + }); + this.inherited(arguments); + }, + + destroyRendering: function(){ + // Delete all attach points to prevent IE6 memory leaks. + dojo.forEach(this._attachPoints, function(point){ + delete this[point]; + }, this); + this._attachPoints = []; + + this.inherited(arguments); + } + } +); + +// key is either templatePath or templateString; object is either string or DOM tree +dijit._Templated._templateCache = {}; + +dijit._Templated.getCachedTemplate = function(templatePath, templateString, alwaysUseString){ + // summary: + // Static method to get a template based on the templatePath or + // templateString key + // templatePath: String||dojo.uri.Uri + // The URL to get the template from. + // templateString: String? + // a string to use in lieu of fetching the template from a URL. Takes precedence + // over templatePath + // returns: Mixed + // Either string (if there are ${} variables that need to be replaced) or just + // a DOM tree (if the node can be cloned directly) + + // is it already cached? + var tmplts = dijit._Templated._templateCache; + var key = templateString || templatePath; + var cached = tmplts[key]; + if(cached){ + try{ + // if the cached value is an innerHTML string (no ownerDocument) or a DOM tree created within the current document, then use the current cached value + if(!cached.ownerDocument || cached.ownerDocument == dojo.doc){ + // string or node of the same document + return cached; + } + }catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded + dojo.destroy(cached); + } + + // If necessary, load template string from template path + if(!templateString){ + templateString = dojo.cache(templatePath, {sanitize: true}); + } + templateString = dojo.string.trim(templateString); + + if(alwaysUseString || templateString.match(/\$\{([^\}]+)\}/g)){ + // there are variables in the template so all we can do is cache the string + return (tmplts[key] = templateString); //String + }else{ + // there are no variables in the template so we can cache the DOM tree + var node = dojo._toDom(templateString); + if(node.nodeType != 1){ + throw new Error("Invalid template: " + templateString); + } + return (tmplts[key] = node); //Node + } +}; + +if(dojo.isIE){ + dojo.addOnWindowUnload(function(){ + var cache = dijit._Templated._templateCache; + for(var key in cache){ + var value = cache[key]; + if(typeof value == "object"){ // value is either a string or a DOM node template + dojo.destroy(value); + } + delete cache[key]; + } + }); +} + +// These arguments can be specified for widgets which are used in templates. +// Since any widget can be specified as sub widgets in template, mix it +// into the base widget class. (This is a hack, but it's effective.) +dojo.extend(dijit._Widget,{ + dojoAttachEvent: "", + dojoAttachPoint: "", + waiRole: "", + waiState:"" +}); + +} + +if(!dojo._hasResource["dijit._Container"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._Container"] = true; +dojo.provide("dijit._Container"); + +dojo.declare("dijit._Container", + null, + { + // summary: + // Mixin for widgets that contain a set of widget children. + // description: + // Use this mixin for widgets that needs to know about and + // keep track of their widget children. Suitable for widgets like BorderContainer + // and TabContainer which contain (only) a set of child widgets. + // + // It's not suitable for widgets like ContentPane + // which contains mixed HTML (plain DOM nodes in addition to widgets), + // and where contained widgets are not necessarily directly below + // this.containerNode. In that case calls like addChild(node, position) + // wouldn't make sense. + + // isContainer: [protected] Boolean + // Indicates that this widget acts as a "parent" to the descendant widgets. + // When the parent is started it will call startup() on the child widgets. + // See also `isLayoutContainer`. + isContainer: true, + + buildRendering: function(){ + this.inherited(arguments); + if(!this.containerNode){ + // all widgets with descendants must set containerNode + this.containerNode = this.domNode; + } + }, + + addChild: function(/*dijit._Widget*/ widget, /*int?*/ insertIndex){ + // summary: + // Makes the given widget a child of this widget. + // description: + // Inserts specified child widget's dom node as a child of this widget's + // container node, and possibly does other processing (such as layout). + + var refNode = this.containerNode; + if(insertIndex && typeof insertIndex == "number"){ + var children = this.getChildren(); + if(children && children.length >= insertIndex){ + refNode = children[insertIndex-1].domNode; + insertIndex = "after"; + } + } + dojo.place(widget.domNode, refNode, insertIndex); + + // If I've been started but the child widget hasn't been started, + // start it now. Make sure to do this after widget has been + // inserted into the DOM tree, so it can see that it's being controlled by me, + // so it doesn't try to size itself. + if(this._started && !widget._started){ + widget.startup(); + } + }, + + removeChild: function(/*Widget or int*/ widget){ + // summary: + // Removes the passed widget instance from this widget but does + // not destroy it. You can also pass in an integer indicating + // the index within the container to remove + + if(typeof widget == "number" && widget > 0){ + widget = this.getChildren()[widget]; + } + + if(widget){ + var node = widget.domNode; + if(node && node.parentNode){ + node.parentNode.removeChild(node); // detach but don't destroy + } + } + }, + + hasChildren: function(){ + // summary: + // Returns true if widget has children, i.e. if this.containerNode contains something. + return this.getChildren().length > 0; // Boolean + }, + + destroyDescendants: function(/*Boolean*/ preserveDom){ + // summary: + // Destroys all the widgets inside this.containerNode, + // but not this widget itself + dojo.forEach(this.getChildren(), function(child){ child.destroyRecursive(preserveDom); }); + }, + + _getSiblingOfChild: function(/*dijit._Widget*/ child, /*int*/ dir){ + // summary: + // Get the next or previous widget sibling of child + // dir: + // if 1, get the next sibling + // if -1, get the previous sibling + // tags: + // private + var node = child.domNode, + which = (dir>0 ? "nextSibling" : "previousSibling"); + do{ + node = node[which]; + }while(node && (node.nodeType != 1 || !dijit.byNode(node))); + return node && dijit.byNode(node); // dijit._Widget + }, + + getIndexOfChild: function(/*dijit._Widget*/ child){ + // summary: + // Gets the index of the child in this container or -1 if not found + return dojo.indexOf(this.getChildren(), child); // int + }, + + startup: function(){ + // summary: + // Called after all the widgets have been instantiated and their + // dom nodes have been inserted somewhere under dojo.doc.body. + // + // Widgets should override this method to do any initialization + // dependent on other widgets existing, and then call + // this superclass method to finish things off. + // + // startup() in subclasses shouldn't do anything + // size related because the size of the widget hasn't been set yet. + + if(this._started){ return; } + + // Startup all children of this widget + dojo.forEach(this.getChildren(), function(child){ child.startup(); }); + + this.inherited(arguments); + } + } +); + +} + +if(!dojo._hasResource["dijit._Contained"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._Contained"] = true; +dojo.provide("dijit._Contained"); + +dojo.declare("dijit._Contained", + null, + { + // summary: + // Mixin for widgets that are children of a container widget + // + // example: + // | // make a basic custom widget that knows about it's parents + // | dojo.declare("my.customClass",[dijit._Widget,dijit._Contained],{}); + + getParent: function(){ + // summary: + // Returns the parent widget of this widget, assuming the parent + // specifies isContainer + var parent = dijit.getEnclosingWidget(this.domNode.parentNode); + return parent && parent.isContainer ? parent : null; + }, + + _getSibling: function(/*String*/ which){ + // summary: + // Returns next or previous sibling + // which: + // Either "next" or "previous" + // tags: + // private + var node = this.domNode; + do{ + node = node[which+"Sibling"]; + }while(node && node.nodeType != 1); + return node && dijit.byNode(node); // dijit._Widget + }, + + getPreviousSibling: function(){ + // summary: + // Returns null if this is the first child of the parent, + // otherwise returns the next element sibling to the "left". + + return this._getSibling("previous"); // dijit._Widget + }, + + getNextSibling: function(){ + // summary: + // Returns null if this is the last child of the parent, + // otherwise returns the next element sibling to the "right". + + return this._getSibling("next"); // dijit._Widget + }, + + getIndexInParent: function(){ + // summary: + // Returns the index of this widget within its container parent. + // It returns -1 if the parent does not exist, or if the parent + // is not a dijit._Container + + var p = this.getParent(); + if(!p || !p.getIndexOfChild){ + return -1; // int + } + return p.getIndexOfChild(this); // int + } + } + ); + + +} + +if(!dojo._hasResource["dijit.layout._LayoutWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.layout._LayoutWidget"] = true; +dojo.provide("dijit.layout._LayoutWidget"); + + + + + +dojo.declare("dijit.layout._LayoutWidget", + [dijit._Widget, dijit._Container, dijit._Contained], + { + // summary: + // Base class for a _Container widget which is responsible for laying out its children. + // Widgets which mixin this code must define layout() to manage placement and sizing of the children. + + // baseClass: [protected extension] String + // This class name is applied to the widget's domNode + // and also may be used to generate names for sub nodes, + // for example dijitTabContainer-content. + baseClass: "dijitLayoutContainer", + + // isLayoutContainer: [protected] Boolean + // Indicates that this widget is going to call resize() on its + // children widgets, setting their size, when they become visible. + isLayoutContainer: true, + + postCreate: function(){ + dojo.addClass(this.domNode, "dijitContainer"); + + this.inherited(arguments); + }, + + startup: function(){ + // summary: + // Called after all the widgets have been instantiated and their + // dom nodes have been inserted somewhere under dojo.doc.body. + // + // Widgets should override this method to do any initialization + // dependent on other widgets existing, and then call + // this superclass method to finish things off. + // + // startup() in subclasses shouldn't do anything + // size related because the size of the widget hasn't been set yet. + + if(this._started){ return; } + + // Need to call inherited first - so that child widgets get started + // up correctly + this.inherited(arguments); + + // If I am a not being controlled by a parent layout widget... + var parent = this.getParent && this.getParent() + if(!(parent && parent.isLayoutContainer)){ + // Do recursive sizing and layout of all my descendants + // (passing in no argument to resize means that it has to glean the size itself) + this.resize(); + + // Since my parent isn't a layout container, and my style *may be* width=height=100% + // or something similar (either set directly or via a CSS class), + // monitor when my size changes so that I can re-layout. + // For browsers where I can't directly monitor when my size changes, + // monitor when the viewport changes size, which *may* indicate a size change for me. + this.connect(dojo.isIE ? this.domNode : dojo.global, 'onresize', function(){ + // Using function(){} closure to ensure no arguments to resize. + this.resize(); + }); + } + }, + + resize: function(changeSize, resultSize){ + // summary: + // Call this to resize a widget, or after its size has changed. + // description: + // Change size mode: + // When changeSize is specified, changes the marginBox of this widget + // and forces it to relayout its contents accordingly. + // changeSize may specify height, width, or both. + // + // If resultSize is specified it indicates the size the widget will + // become after changeSize has been applied. + // + // Notification mode: + // When changeSize is null, indicates that the caller has already changed + // the size of the widget, or perhaps it changed because the browser + // window was resized. Tells widget to relayout its contents accordingly. + // + // If resultSize is also specified it indicates the size the widget has + // become. + // + // In either mode, this method also: + // 1. Sets this._borderBox and this._contentBox to the new size of + // the widget. Queries the current domNode size if necessary. + // 2. Calls layout() to resize contents (and maybe adjust child widgets). + // + // changeSize: Object? + // Sets the widget to this margin-box size and position. + // May include any/all of the following properties: + // | {w: int, h: int, l: int, t: int} + // + // resultSize: Object? + // The margin-box size of this widget after applying changeSize (if + // changeSize is specified). If caller knows this size and + // passes it in, we don't need to query the browser to get the size. + // | {w: int, h: int} + + var node = this.domNode; + + // set margin box size, unless it wasn't specified, in which case use current size + if(changeSize){ + dojo.marginBox(node, changeSize); + + // set offset of the node + if(changeSize.t){ node.style.top = changeSize.t + "px"; } + if(changeSize.l){ node.style.left = changeSize.l + "px"; } + } + + // If either height or width wasn't specified by the user, then query node for it. + // But note that setting the margin box and then immediately querying dimensions may return + // inaccurate results, so try not to depend on it. + var mb = resultSize || {}; + dojo.mixin(mb, changeSize || {}); // changeSize overrides resultSize + if( !("h" in mb) || !("w" in mb) ){ + mb = dojo.mixin(dojo.marginBox(node), mb); // just use dojo.marginBox() to fill in missing values + } + + // Compute and save the size of my border box and content box + // (w/out calling dojo.contentBox() since that may fail if size was recently set) + var cs = dojo.getComputedStyle(node); + var me = dojo._getMarginExtents(node, cs); + var be = dojo._getBorderExtents(node, cs); + var bb = (this._borderBox = { + w: mb.w - (me.w + be.w), + h: mb.h - (me.h + be.h) + }); + var pe = dojo._getPadExtents(node, cs); + this._contentBox = { + l: dojo._toPixelValue(node, cs.paddingLeft), + t: dojo._toPixelValue(node, cs.paddingTop), + w: bb.w - pe.w, + h: bb.h - pe.h + }; + + // Callback for widget to adjust size of its children + this.layout(); + }, + + layout: function(){ + // summary: + // Widgets override this method to size and position their contents/children. + // When this is called this._contentBox is guaranteed to be set (see resize()). + // + // This is called after startup(), and also when the widget's size has been + // changed. + // tags: + // protected extension + }, + + _setupChild: function(/*dijit._Widget*/child){ + // summary: + // Common setup for initial children and children which are added after startup + // tags: + // protected extension + + dojo.addClass(child.domNode, this.baseClass+"-child"); + if(child.baseClass){ + dojo.addClass(child.domNode, this.baseClass+"-"+child.baseClass); + } + }, + + addChild: function(/*dijit._Widget*/ child, /*Integer?*/ insertIndex){ + // Overrides _Container.addChild() to call _setupChild() + this.inherited(arguments); + if(this._started){ + this._setupChild(child); + } + }, + + removeChild: function(/*dijit._Widget*/ child){ + // Overrides _Container.removeChild() to remove class added by _setupChild() + dojo.removeClass(child.domNode, this.baseClass+"-child"); + if(child.baseClass){ + dojo.removeClass(child.domNode, this.baseClass+"-"+child.baseClass); + } + this.inherited(arguments); + } + } +); + +dijit.layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){ + // summary: + // Given the margin-box size of a node, return its content box size. + // Functions like dojo.contentBox() but is more reliable since it doesn't have + // to wait for the browser to compute sizes. + var cs = dojo.getComputedStyle(node); + var me = dojo._getMarginExtents(node, cs); + var pb = dojo._getPadBorderExtents(node, cs); + return { + l: dojo._toPixelValue(node, cs.paddingLeft), + t: dojo._toPixelValue(node, cs.paddingTop), + w: mb.w - (me.w + pb.w), + h: mb.h - (me.h + pb.h) + }; +}; + +(function(){ + var capitalize = function(word){ + return word.substring(0,1).toUpperCase() + word.substring(1); + }; + + var size = function(widget, dim){ + // size the child + widget.resize ? widget.resize(dim) : dojo.marginBox(widget.domNode, dim); + + // record child's size, but favor our own numbers when we have them. + // the browser lies sometimes + dojo.mixin(widget, dojo.marginBox(widget.domNode)); + dojo.mixin(widget, dim); + }; + + dijit.layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Object[]*/ children){ + // summary + // Layout a bunch of child dom nodes within a parent dom node + // container: + // parent node + // dim: + // {l, t, w, h} object specifying dimensions of container into which to place children + // children: + // an array like [ {domNode: foo, layoutAlign: "bottom" }, {domNode: bar, layoutAlign: "client"} ] + + // copy dim because we are going to modify it + dim = dojo.mixin({}, dim); + + dojo.addClass(container, "dijitLayoutContainer"); + + // Move "client" elements to the end of the array for layout. a11y dictates that the author + // needs to be able to put them in the document in tab-order, but this algorithm requires that + // client be last. + children = dojo.filter(children, function(item){ return item.layoutAlign != "client"; }) + .concat(dojo.filter(children, function(item){ return item.layoutAlign == "client"; })); + + // set positions/sizes + dojo.forEach(children, function(child){ + var elm = child.domNode, + pos = child.layoutAlign; + + // set elem to upper left corner of unused space; may move it later + var elmStyle = elm.style; + elmStyle.left = dim.l+"px"; + elmStyle.top = dim.t+"px"; + elmStyle.bottom = elmStyle.right = "auto"; + + dojo.addClass(elm, "dijitAlign" + capitalize(pos)); + + // set size && adjust record of remaining space. + // note that setting the width of a <div> may affect its height. + if(pos == "top" || pos == "bottom"){ + size(child, { w: dim.w }); + dim.h -= child.h; + if(pos == "top"){ + dim.t += child.h; + }else{ + elmStyle.top = dim.t + dim.h + "px"; + } + }else if(pos == "left" || pos == "right"){ + size(child, { h: dim.h }); + dim.w -= child.w; + if(pos == "left"){ + dim.l += child.w; + }else{ + elmStyle.left = dim.l + dim.w + "px"; + } + }else if(pos == "client"){ + size(child, dim); + } + }); + }; + +})(); + +} + +if(!dojo._hasResource["dijit._CssStateMixin"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit._CssStateMixin"] = true; +dojo.provide("dijit._CssStateMixin"); + + +dojo.declare("dijit._CssStateMixin", [], { + // summary: + // Mixin for widgets to set CSS classes on the widget DOM nodes depending on hover/mouse press/focus + // state changes, and also higher-level state changes such becoming disabled or selected. + // + // description: + // By mixing this class into your widget, and setting the this.baseClass attribute, it will automatically + // maintain CSS classes on the widget root node (this.domNode) depending on hover, + // active, focus, etc. state. Ex: with a baseClass of dijitButton, it will apply the classes + // dijitButtonHovered and dijitButtonActive, as the user moves the mouse over the widget and clicks it. + // + // It also sets CSS like dijitButtonDisabled based on widget semantic state. + // + // By setting the cssStateNodes attribute, a widget can also track events on subnodes (like buttons + // within the widget). + + // cssStateNodes: [protected] Object + // List of sub-nodes within the widget that need CSS classes applied on mouse hover/press and focus + //. + // Each entry in the hash is a an attachpoint names (like "upArrowButton") mapped to a CSS class names + // (like "dijitUpArrowButton"). Example: + // | { + // | "upArrowButton": "dijitUpArrowButton", + // | "downArrowButton": "dijitDownArrowButton" + // | } + // The above will set the CSS class dijitUpArrowButton to the this.upArrowButton DOMNode when it + // is hovered, etc. + cssStateNodes: {}, + + postCreate: function(){ + this.inherited(arguments); + + // Automatically monitor mouse events (essentially :hover and :active) on this.domNode + dojo.forEach(["onmouseenter", "onmouseleave", "onmousedown"], function(e){ + this.connect(this.domNode, e, "_cssMouseEvent"); + }, this); + + // Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node + this.connect(this, "set", function(name, value){ + if(arguments.length >= 2 && {disabled: true, readOnly: true, checked:true, selected:true}[name]){ + this._setStateClass(); + } + }); + + // The widget coming in/out of the focus change affects it's state + dojo.forEach(["_onFocus", "_onBlur"], function(ap){ + this.connect(this, ap, "_setStateClass"); + }, this); + + // Events on sub nodes within the widget + for(var ap in this.cssStateNodes){ + this._trackMouseState(this[ap], this.cssStateNodes[ap]); + } + // Set state initially; there's probably no hover/active/focus state but widget might be + // disabled/readonly so we want to set CSS classes for those conditions. + this._setStateClass(); + }, + + _cssMouseEvent: function(/*Event*/ event){ + // summary: + // Sets _hovering and _active properties depending on mouse state, + // then calls _setStateClass() to set appropriate CSS classes for this.domNode. + + if(!this.disabled){ + switch(event.type){ + case "mouseenter": + case "mouseover": // generated on non-IE browsers even though we connected to mouseenter + this._hovering = true; + this._active = this._mouseDown; + break; + + case "mouseleave": + case "mouseout": // generated on non-IE browsers even though we connected to mouseleave + this._hovering = false; + this._active = false; + break; + + case "mousedown" : + this._active = true; + this._mouseDown = true; + // Set a global event to handle mouseup, so it fires properly + // even if the cursor leaves this.domNode before the mouse up event. + // Alternately could set active=false on mouseout. + var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){ + this._active = false; + this._mouseDown = false; + this._setStateClass(); + this.disconnect(mouseUpConnector); + }); + break; + } + this._setStateClass(); + } + }, + + _setStateClass: function(){ + // summary: + // Update the visual state of the widget by setting the css classes on this.domNode + // (or this.stateNode if defined) by combining this.baseClass with + // various suffixes that represent the current widget state(s). + // + // description: + // In the case where a widget has multiple + // states, it sets the class based on all possible + // combinations. For example, an invalid form widget that is being hovered + // will be "dijitInput dijitInputInvalid dijitInputHover dijitInputInvalidHover". + // + // The widget may have one or more of the following states, determined + // by this.state, this.checked, this.valid, and this.selected: + // - Error - ValidationTextBox sets this.state to "Error" if the current input value is invalid + // - Checked - ex: a checkmark or a ToggleButton in a checked state, will have this.checked==true + // - Selected - ex: currently selected tab will have this.selected==true + // + // In addition, it may have one or more of the following states, + // based on this.disabled and flags set in _onMouse (this._active, this._hovering, this._focused): + // - Disabled - if the widget is disabled + // - Active - if the mouse (or space/enter key?) is being pressed down + // - Focused - if the widget has focus + // - Hover - if the mouse is over the widget + + // Compute new set of classes + var newStateClasses = this.baseClass.split(" "); + + function multiply(modifier){ + newStateClasses = newStateClasses.concat(dojo.map(newStateClasses, function(c){ return c+modifier; }), "dijit"+modifier); + } + + if(!this.isLeftToRight()){ + // For RTL mode we need to set an addition class like dijitTextBoxRtl. + multiply("Rtl"); + } + + if(this.checked){ + multiply("Checked"); + } + if(this.state){ + multiply(this.state); + } + if(this.selected){ + multiply("Selected"); + } + + if(this.disabled){ + multiply("Disabled"); + }else if(this.readOnly){ + multiply("ReadOnly"); + }else{ + if(this._active){ + multiply("Active"); + }else if(this._hovering){ + multiply("Hover"); + } + } + + if(this._focused){ + multiply("Focused"); + } + + // Remove old state classes and add new ones. + // For performance concerns we only write into domNode.className once. + var tn = this.stateNode || this.domNode, + classHash = {}; // set of all classes (state and otherwise) for node + + dojo.forEach(tn.className.split(" "), function(c){ classHash[c] = true; }); + + if("_stateClasses" in this){ + dojo.forEach(this._stateClasses, function(c){ delete classHash[c]; }); + } + + dojo.forEach(newStateClasses, function(c){ classHash[c] = true; }); + + var newClasses = []; + for(var c in classHash){ + newClasses.push(c); + } + tn.className = newClasses.join(" "); + + this._stateClasses = newStateClasses; + }, + + _trackMouseState: function(/*DomNode*/ node, /*String*/ clazz){ + // summary: + // Track mouse/focus events on specified node and set CSS class on that node to indicate + // current state. Usually not called directly, but via cssStateNodes attribute. + // description: + // Given class=foo, will set the following CSS class on the node + // - fooActive: if the user is currently pressing down the mouse button while over the node + // - fooHover: if the user is hovering the mouse over the node, but not pressing down a button + // - fooFocus: if the node is focused + // + // Note that it won't set any classes if the widget is disabled. + // node: DomNode + // Should be a sub-node of the widget, not the top node (this.domNode), since the top node + // is handled specially and automatically just by mixing in this class. + // clazz: String + // CSS class name (ex: dijitSliderUpArrow). + + // Current state of node (initially false) + // NB: setting specifically to false because dojo.toggleClass() needs true boolean as third arg + var hovering=false, active=false, focused=false; + + var self = this, + cn = dojo.hitch(this, "connect", node); + + function setClass(){ + var disabled = ("disabled" in self && self.disabled) || ("readonly" in self && self.readonly); + dojo.toggleClass(node, clazz+"Hover", hovering && !active && !disabled); + dojo.toggleClass(node, clazz+"Active", active && !disabled); + dojo.toggleClass(node, clazz+"Focused", focused && !disabled); + } + + // Mouse + cn("onmouseenter", function(){ + hovering = true; + setClass(); + }); + cn("onmouseleave", function(){ + hovering = false; + active = false; + setClass(); + }); + cn("onmousedown", function(){ + active = true; + setClass(); + }); + cn("onmouseup", function(){ + active = false; + setClass(); + }); + + // Focus + cn("onfocus", function(){ + focused = true; + setClass(); + }); + cn("onblur", function(){ + focused = false; + setClass(); + }); + + // Just in case widget is enabled/disabled while it has focus/hover/active state. + // Maybe this is overkill. + this.connect(this, "set", function(name, value){ + if(name == "disabled" || name == "readOnly"){ + setClass(); + } + }); + } +}); + +} + +if(!dojo._hasResource["dijit.form._FormWidget"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.form._FormWidget"] = true; +dojo.provide("dijit.form._FormWidget"); + + + + + + + +dojo.declare("dijit.form._FormWidget", [dijit._Widget, dijit._Templated, dijit._CssStateMixin], + { + // summary: + // Base class for widgets corresponding to native HTML elements such as <checkbox> or <button>, + // which can be children of a <form> node or a `dijit.form.Form` widget. + // + // description: + // Represents a single HTML element. + // All these widgets should have these attributes just like native HTML input elements. + // You can set them during widget construction or afterwards, via `dijit._Widget.attr`. + // + // They also share some common methods. + + // name: String + // Name used when submitting form; same as "name" attribute or plain HTML elements + name: "", + + // alt: String + // Corresponds to the native HTML <input> element's attribute. + alt: "", + + // value: String + // Corresponds to the native HTML <input> element's attribute. + value: "", + + // type: String + // Corresponds to the native HTML <input> element's attribute. + type: "text", + + // tabIndex: Integer + // Order fields are traversed when user hits the tab key + tabIndex: "0", + + // disabled: Boolean + // Should this widget respond to user input? + // In markup, this is specified as "disabled='disabled'", or just "disabled". + disabled: false, + + // intermediateChanges: Boolean + // Fires onChange for each value change or only on demand + intermediateChanges: false, + + // scrollOnFocus: Boolean + // On focus, should this widget scroll into view? + scrollOnFocus: true, + + // These mixins assume that the focus node is an INPUT, as many but not all _FormWidgets are. + attributeMap: dojo.delegate(dijit._Widget.prototype.attributeMap, { + value: "focusNode", + id: "focusNode", + tabIndex: "focusNode", + alt: "focusNode", + title: "focusNode" + }), + + postMixInProperties: function(){ + // Setup name=foo string to be referenced from the template (but only if a name has been specified) + // Unfortunately we can't use attributeMap to set the name due to IE limitations, see #8660 + // Regarding escaping, see heading "Attribute values" in + // http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2 + this.nameAttrSetting = this.name ? ('name="' + this.name.replace(/'/g, """) + '"') : ''; + this.inherited(arguments); + }, + + postCreate: function(){ + this.inherited(arguments); + this.connect(this.domNode, "onmousedown", "_onMouseDown"); + }, + + _setDisabledAttr: function(/*Boolean*/ value){ + this.disabled = value; + dojo.attr(this.focusNode, 'disabled', value); + if(this.valueNode){ + dojo.attr(this.valueNode, 'disabled', value); + } + dijit.setWaiState(this.focusNode, "disabled", value); + + if(value){ + // reset these, because after the domNode is disabled, we can no longer receive + // mouse related events, see #4200 + this._hovering = false; + this._active = false; + + // clear tab stop(s) on this widget's focusable node(s) (ComboBox has two focusable nodes) + var attachPointNames = "tabIndex" in this.attributeMap ? this.attributeMap.tabIndex : "focusNode"; + dojo.forEach(dojo.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){ + var node = this[attachPointName]; + // complex code because tabIndex=-1 on a <div> doesn't work on FF + if(dojo.isWebKit || dijit.hasDefaultTabStop(node)){ // see #11064 about webkit bug + node.setAttribute('tabIndex', "-1"); + }else{ + node.removeAttribute('tabIndex'); + } + }, this); + }else{ + this.focusNode.setAttribute('tabIndex', this.tabIndex); + } + }, + + setDisabled: function(/*Boolean*/ disabled){ + // summary: + // Deprecated. Use set('disabled', ...) instead. + dojo.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0"); + this.set('disabled', disabled); + }, + + _onFocus: function(e){ + if(this.scrollOnFocus){ + dojo.window.scrollIntoView(this.domNode); + } + this.inherited(arguments); + }, + + isFocusable: function(){ + // summary: + // Tells if this widget is focusable or not. Used internally by dijit. + // tags: + // protected + return !this.disabled && !this.readOnly && this.focusNode && (dojo.style(this.domNode, "display") != "none"); + }, + + focus: function(){ + // summary: + // Put focus on this widget + dijit.focus(this.focusNode); + }, + + compare: function(/*anything*/val1, /*anything*/val2){ + // summary: + // Compare 2 values (as returned by attr('value') for this widget). + // tags: + // protected + if(typeof val1 == "number" && typeof val2 == "number"){ + return (isNaN(val1) && isNaN(val2)) ? 0 : val1 - val2; + }else if(val1 > val2){ + return 1; + }else if(val1 < val2){ + return -1; + }else{ + return 0; + } + }, + + onChange: function(newValue){ + // summary: + // Callback when this widget's value is changed. + // tags: + // callback + }, + + // _onChangeActive: [private] Boolean + // Indicates that changes to the value should call onChange() callback. + // This is false during widget initialization, to avoid calling onChange() + // when the initial value is set. + _onChangeActive: false, + + _handleOnChange: function(/*anything*/ newValue, /* Boolean? */ priorityChange){ + // summary: + // Called when the value of the widget is set. Calls onChange() if appropriate + // newValue: + // the new value + // priorityChange: + // For a slider, for example, dragging the slider is priorityChange==false, + // but on mouse up, it's priorityChange==true. If intermediateChanges==true, + // onChange is only called form priorityChange=true events. + // tags: + // private + this._lastValue = newValue; + if(this._lastValueReported == undefined && (priorityChange === null || !this._onChangeActive)){ + // this block executes not for a change, but during initialization, + // and is used to store away the original value (or for ToggleButton, the original checked state) + this._resetValue = this._lastValueReported = newValue; + } + if((this.intermediateChanges || priorityChange || priorityChange === undefined) && + ((typeof newValue != typeof this._lastValueReported) || + this.compare(newValue, this._lastValueReported) != 0)){ + this._lastValueReported = newValue; + if(this._onChangeActive){ + if(this._onChangeHandle){ + clearTimeout(this._onChangeHandle); + } + // setTimout allows hidden value processing to run and + // also the onChange handler can safely adjust focus, etc + this._onChangeHandle = setTimeout(dojo.hitch(this, + function(){ + this._onChangeHandle = null; + this.onChange(newValue); + }), 0); // try to collapse multiple onChange's fired faster than can be processed + } + } + }, + + create: function(){ + // Overrides _Widget.create() + this.inherited(arguments); + this._onChangeActive = true; + }, + + destroy: function(){ + if(this._onChangeHandle){ // destroy called before last onChange has fired + clearTimeout(this._onChangeHandle); + this.onChange(this._lastValueReported); + } + this.inherited(arguments); + }, + + setValue: function(/*String*/ value){ + // summary: + // Deprecated. Use set('value', ...) instead. + dojo.deprecated("dijit.form._FormWidget:setValue("+value+") is deprecated. Use set('value',"+value+") instead.", "", "2.0"); + this.set('value', value); + }, + + getValue: function(){ + // summary: + // Deprecated. Use get('value') instead. + dojo.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0"); + return this.get('value'); + }, + + _onMouseDown: function(e){ + // If user clicks on the button, even if the mouse is released outside of it, + // this button should get focus (to mimics native browser buttons). + // This is also needed on chrome because otherwise buttons won't get focus at all, + // which leads to bizarre focus restore on Dialog close etc. + if(!e.ctrlKey && this.isFocusable()){ // !e.ctrlKey to ignore right-click on mac + // Set a global event to handle mouseup, so it fires properly + // even if the cursor leaves this.domNode before the mouse up event. + var mouseUpConnector = this.connect(dojo.body(), "onmouseup", function(){ + if (this.isFocusable()) { + this.focus(); + } + this.disconnect(mouseUpConnector); + }); + } + } +}); + +dojo.declare("dijit.form._FormValueWidget", dijit.form._FormWidget, +{ + // summary: + // Base class for widgets corresponding to native HTML elements such as <input> or <select> that have user changeable values. + // description: + // Each _FormValueWidget represents a single input value, and has a (possibly hidden) <input> element, + // to which it serializes it's input value, so that form submission (either normal submission or via FormBind?) + // works as expected. + + // Don't attempt to mixin the 'type', 'name' attributes here programatically -- they must be declared + // directly in the template as read by the parser in order to function. IE is known to specifically + // require the 'name' attribute at element creation time. See #8484, #8660. + // TODO: unclear what that {value: ""} is for; FormWidget.attributeMap copies value to focusNode, + // so maybe {value: ""} is so the value *doesn't* get copied to focusNode? + // Seems like we really want value removed from attributeMap altogether + // (although there's no easy way to do that now) + + // readOnly: Boolean + // Should this widget respond to user input? + // In markup, this is specified as "readOnly". + // Similar to disabled except readOnly form values are submitted. + readOnly: false, + + attributeMap: dojo.delegate(dijit.form._FormWidget.prototype.attributeMap, { + value: "", + readOnly: "focusNode" + }), + + _setReadOnlyAttr: function(/*Boolean*/ value){ + this.readOnly = value; + dojo.attr(this.focusNode, 'readOnly', value); + dijit.setWaiState(this.focusNode, "readonly", value); + }, + + postCreate: function(){ + this.inherited(arguments); + + if(dojo.isIE){ // IE won't stop the event with keypress + this.connect(this.focusNode || this.domNode, "onkeydown", this._onKeyDown); + } + // Update our reset value if it hasn't yet been set (because this.set() + // is only called when there *is* a value) + if(this._resetValue === undefined){ + this._resetValue = this.value; + } + }, + + _setValueAttr: function(/*anything*/ newValue, /*Boolean, optional*/ priorityChange){ + // summary: + // Hook so attr('value', value) works. + // description: + // Sets the value of the widget. + // If the value has changed, then fire onChange event, unless priorityChange + // is specified as null (or false?) + this.value = newValue; + this._handleOnChange(newValue, priorityChange); + }, + + _getValueAttr: function(){ + // summary: + // Hook so attr('value') works. + return this._lastValue; + }, + + undo: function(){ + // summary: + // Restore the value to the last value passed to onChange + this._setValueAttr(this._lastValueReported, false); + }, + + reset: function(){ + // summary: + // Reset the widget's value to what it was at initialization time + this._hasBeenBlurred = false; + this._setValueAttr(this._resetValue, true); + }, + + _onKeyDown: function(e){ + if(e.keyCode == dojo.keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){ + var te; + if(dojo.isIE){ + e.preventDefault(); // default behavior needs to be stopped here since keypress is too late + te = document.createEventObject(); + te.keyCode = dojo.keys.ESCAPE; + te.shiftKey = e.shiftKey; + e.srcElement.fireEvent('onkeypress', te); + } + } + }, + + _layoutHackIE7: function(){ + // summary: + // Work around table sizing bugs on IE7 by forcing redraw + + if(dojo.isIE == 7){ // fix IE7 layout bug when the widget is scrolled out of sight + var domNode = this.domNode; + var parent = domNode.parentNode; + var pingNode = domNode.firstChild || domNode; // target node most unlikely to have a custom filter + var origFilter = pingNode.style.filter; // save custom filter, most likely nothing + var _this = this; + while(parent && parent.clientHeight == 0){ // search for parents that haven't rendered yet + (function ping(){ + var disconnectHandle = _this.connect(parent, "onscroll", + function(e){ + _this.disconnect(disconnectHandle); // only call once + pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique + setTimeout(function(){ pingNode.style.filter = origFilter }, 0); // restore custom filter, if any + } + ); + })(); + parent = parent.parentNode; + } + } + } +}); + +} + +if(!dojo._hasResource["dijit.dijit"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. +dojo._hasResource["dijit.dijit"] = true; +dojo.provide("dijit.dijit"); + +/*===== +dijit.dijit = { + // summary: + // A roll-up for common dijit methods + // description: + // A rollup file for the build system including the core and common + // dijit files. + // + // example: + // | <script type="text/javascript" src="js/dojo/dijit/dijit.js"></script> + // +}; +=====*/ + +// All the stuff in _base (these are the function that are guaranteed available without an explicit dojo.require) + + +// And some other stuff that we tend to pull in all the time anyway + + + + + + + +} + |