/* 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 }