tt-rss/lib/dojo/tt-rss-layer.js.uncompressed.js
2013-03-18 10:26:26 +04:00

33867 lines
1.1 MiB

require({cache:{
'dijit/form/TextBox':function(){
require({cache:{
'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
define("dijit/form/TextBox", [
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch
"dojo/sniff", // has("ie") has("mozilla")
"./_FormValueWidget",
"./_TextBoxMixin",
"dojo/text!./templates/TextBox.html",
"../main" // to export dijit._setSelectionRange, remove in 2.0
], function(declare, domConstruct, domStyle, kernel, lang, has,
_FormValueWidget, _TextBoxMixin, template, dijit){
// module:
// dijit/form/TextBox
var TextBox = declare("dijit.form.TextBox", [_FormValueWidget, _TextBoxMixin], {
// summary:
// A base class for textbox form inputs
templateString: template,
_singleNodeTemplate: '<input class="dijit dijitReset dijitLeft dijitInputField" data-dojo-attach-point="textbox,focusNode" autocomplete="off" type="${type}" ${!nameAttrSetting} />',
_buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
baseClass: "dijitTextBox",
postMixInProperties: function(){
var type = this.type.toLowerCase();
if(this.templateString && this.templateString.toLowerCase() == "input" || ((type == "hidden" || type == "file") && this.templateString == this.constructor.prototype.templateString)){
this.templateString = this._singleNodeTemplate;
}
this.inherited(arguments);
},
postCreate: function(){
this.inherited(arguments);
if(has("ie") < 9){
// IE INPUT tag fontFamily has to be set directly using STYLE
// the defer gives IE a chance to render the TextBox and to deal with font inheritance
this.defer(function(){
try{
var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
if(s){
var ff = s.fontFamily;
if(ff){
var inputs = this.domNode.getElementsByTagName("INPUT");
if(inputs){
for(var i=0; i < inputs.length; i++){
inputs[i].style.fontFamily = ff;
}
}
}
}
}catch(e){/*when used in a Dialog, and this is called before the dialog is
shown, s.fontFamily would trigger "Invalid Argument" error.*/}
});
}
},
_onInput: function(e){
this.inherited(arguments);
if(this.intermediateChanges){ // _TextBoxMixin uses onInput
// allow the key to post to the widget input box
this.defer(function(){ this._handleOnChange(this.get('value'), false); });
}
},
_setPlaceHolderAttr: function(v){
this._set("placeHolder", v);
if(!this._phspan){
this._attachPoints.push('_phspan');
// dijitInputField class gives placeHolder same padding as the input field
// parent node already has dijitInputField class but it doesn't affect this <span>
// since it's position: absolute.
this._phspan = domConstruct.create('span',{ onmousedown:function(e){ e.preventDefault(); }, className:'dijitPlaceHolder dijitInputField'},this.textbox,'after');
}
this._phspan.innerHTML="";
this._phspan.appendChild(this._phspan.ownerDocument.createTextNode(v));
this._updatePlaceHolder();
},
_updatePlaceHolder: function(){
if(this._phspan){
this._phspan.style.display=(this.placeHolder&&!this.focused&&!this.textbox.value)?"":"none";
}
},
_setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
this.inherited(arguments);
this._updatePlaceHolder();
},
getDisplayedValue: function(){
// summary:
// Deprecated. Use get('displayedValue') instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::getDisplayedValue() is deprecated. Use get('displayedValue') instead.", "", "2.0");
return this.get('displayedValue');
},
setDisplayedValue: function(/*String*/ value){
// summary:
// Deprecated. Use set('displayedValue', ...) instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::setDisplayedValue() is deprecated. Use set('displayedValue', ...) instead.", "", "2.0");
this.set('displayedValue', value);
},
_onBlur: function(e){
if(this.disabled){ return; }
this.inherited(arguments);
this._updatePlaceHolder();
if(has("mozilla")){
if(this.selectOnClick){
// clear selection so that the next mouse click doesn't reselect
this.textbox.selectionStart = this.textbox.selectionEnd = undefined;
}
}
},
_onFocus: function(/*String*/ by){
if(this.disabled || this.readOnly){ return; }
this.inherited(arguments);
this._updatePlaceHolder();
}
});
if(has("ie")){
TextBox.prototype._isTextSelected = function(){
var range = this.ownerDocument.selection.createRange();
var parent = range.parentElement();
return parent == this.textbox && range.text.length > 0;
};
// Overrides definition of _setSelectionRange from _TextBoxMixin (TODO: move to _TextBoxMixin.js?)
dijit._setSelectionRange = _TextBoxMixin._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
if(element.createTextRange){
var r = element.createTextRange();
r.collapse(true);
r.moveStart("character", -99999); // move to 0
r.moveStart("character", start); // delta from 0 is the correct position
r.moveEnd("character", stop-start);
r.select();
}
}
}
return TextBox;
});
},
'dijit/_base/scroll':function(){
define("dijit/_base/scroll", [
"dojo/window", // windowUtils.scrollIntoView
"../main" // export symbol to dijit
], function(windowUtils, dijit){
// module:
// dijit/_base/scroll
/*=====
return {
// summary:
// Back compatibility module, new code should use windowUtils directly instead of using this module.
};
=====*/
dijit.scrollIntoView = function(/*DomNode*/ node, /*Object?*/ pos){
// summary:
// Scroll the passed node into view, if it is not already.
// Deprecated, use `windowUtils.scrollIntoView` instead.
windowUtils.scrollIntoView(node, pos);
};
});
},
'dijit/_TemplatedMixin':function(){
define("dijit/_TemplatedMixin", [
"dojo/_base/lang", // lang.getObject
"dojo/touch",
"./_WidgetBase",
"dojo/string", // string.substitute string.trim
"dojo/cache", // dojo.cache
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.destroy, domConstruct.toDom
"dojo/sniff", // has("ie")
"dojo/_base/unload" // unload.addOnWindowUnload
], function(lang, touch, _WidgetBase, string, cache, array, declare, domConstruct, has, unload) {
// module:
// dijit/_TemplatedMixin
var _TemplatedMixin = declare("dijit._TemplatedMixin", null, {
// summary:
// Mixin for widgets that are instantiated from a template
// templateString: [protected] String
// A string that represents the widget template.
// 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 require([... "dojo/text!..."], ...) instead
templatePath: null,
// skipNodeCache: [protected] Boolean
// If using a cached widget template nodes 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 data-dojo-attach-point=... in the
// template, ex: ["containerNode", "labelNode"]
_attachPoints: [],
// _attachEvents: [private] Handle[]
// List of connections associated with data-dojo-attach-event=... in the
// template
_attachEvents: [],
=====*/
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
this._attachPoints = [];
this._attachEvents = [];
},
_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 string.substitute(tmpl, this, function(value, key){
if(key.charAt(0) == '!'){ value = lang.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,"&quot;"); //TODO: add &amp? use encodeXML method?
}, this);
},
buildRendering: function(){
// summary:
// Construct the UI for this widget from a template, setting this.domNode.
// tags:
// protected
if(!this.templateString){
this.templateString = cache(this.templatePath, {sanitize: true});
}
// 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 = _TemplatedMixin.getCachedTemplate(this.templateString, this._skipNodeCache, this.ownerDocument);
var node;
if(lang.isString(cached)){
node = domConstruct.toDom(this._stringRepl(cached), this.ownerDocument);
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;
// Call down to _Widget.buildRendering() to get base classes assigned
// TODO: change the baseClass assignment to _setBaseClassAttr
this.inherited(arguments);
// 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, function(n,p){ return n.getAttribute(p); });
this._beforeFillContent(); // hook for _WidgetsInTemplateMixin
this._fillContent(this.srcNodeRef);
},
_beforeFillContent: function(){
},
_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.
// Alternately, if rootNode is an array of widgets, then will process data-dojo-attach-point
// etc. for those widgets.
// 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/data-dojo-attach-point
// - dojoAttachEvent/data-dojo-attach-event
// rootNode: DomNode|Widget[]
// 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
var nodes = lang.isArray(rootNode) ? rootNode : (rootNode.all || rootNode.getElementsByTagName("*"));
var x = lang.isArray(rootNode) ? 0 : -1;
for(; x < 0 || nodes[x]; x++){ // don't access nodes.length on IE, see #14346
var baseNode = (x == -1) ? rootNode : nodes[x];
if(this.widgetsInTemplate && (getAttrFunc(baseNode, "dojoType") || getAttrFunc(baseNode, "data-dojo-type"))){
continue;
}
// Process data-dojo-attach-point
var attachPoint = getAttrFunc(baseNode, "dojoAttachPoint") || getAttrFunc(baseNode, "data-dojo-attach-point");
if(attachPoint){
var point, points = attachPoint.split(/\s*,\s*/);
while((point = points.shift())){
if(lang.isArray(this[point])){
this[point].push(baseNode);
}else{
this[point]=baseNode;
}
this._attachPoints.push(point);
}
}
// Process data-dojo-attach-event
var attachEvent = getAttrFunc(baseNode, "dojoAttachEvent") || getAttrFunc(baseNode, "data-dojo-attach-event");
if(attachEvent){
// NOTE: we want to support attributes that have the form
// "domEvent: nativeEvent; ..."
var event, events = attachEvent.split(/\s*,\s*/);
var trim = lang.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;
}
// Map "press", "move" and "release" to keys.touch, keys.move, keys.release
this._attachEvents.push(this.connect(baseNode, touch[event] || event, thisFunc));
}
}
}
}
},
destroyRendering: function(){
// Delete all attach points to prevent IE6 memory leaks.
array.forEach(this._attachPoints, function(point){
delete this[point];
}, this);
this._attachPoints = [];
// And same for event handlers
array.forEach(this._attachEvents, this.disconnect, this);
this._attachEvents = [];
this.inherited(arguments);
}
});
// key is templateString; object is either string or DOM tree
_TemplatedMixin._templateCache = {};
_TemplatedMixin.getCachedTemplate = function(templateString, alwaysUseString, doc){
// summary:
// Static method to get a template based on the templatePath or
// templateString key
// templateString: String
// The template
// alwaysUseString: Boolean
// Don't cache the DOM tree for this template, even if it doesn't have any variables
// doc: Document?
// The target document. Defaults to document global if unspecified.
// 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 = _TemplatedMixin._templateCache;
var key = templateString;
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 == (doc || document)){
// string or node of the same document
return cached;
}
}catch(e){ /* squelch */ } // IE can throw an exception if cached.ownerDocument was reloaded
domConstruct.destroy(cached);
}
templateString = 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 = domConstruct.toDom(templateString, doc);
if(node.nodeType != 1){
throw new Error("Invalid template: " + templateString);
}
return (tmplts[key] = node); //Node
}
};
if(has("ie")){
unload.addOnWindowUnload(function(){
var cache = _TemplatedMixin._templateCache;
for(var key in cache){
var value = cache[key];
if(typeof value == "object"){ // value is either a string or a DOM node template
domConstruct.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.).
// Remove for 2.0. Also, hide from API doc parser.
lang.extend(_WidgetBase, /*===== {} || =====*/ {
dojoAttachEvent: "",
dojoAttachPoint: ""
});
return _TemplatedMixin;
});
},
'dijit/_CssStateMixin':function(){
define("dijit/_CssStateMixin", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant()
"dojo/dom-class", // domClass.toggle
"dojo/has",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/ready",
"dojo/_base/window", // win.body
"./registry"
], function(array, declare, dom, domClass, has, lang, on, ready, win, registry){
// module:
// dijit/_CssStateMixin
var CssStateMixin = 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: {},
// hovering: [readonly] Boolean
// True if cursor is over this widget
hovering: false,
// active: [readonly] Boolean
// True if mouse was pressed while over this widget, and hasn't been released yet
active: false,
_applyAttributes: function(){
// This code would typically be in postCreate(), but putting in _applyAttributes() for
// performance: so the class changes happen before DOM is inserted into the document.
// Change back to postCreate() in 2.0. See #11635.
this.inherited(arguments);
// Monitoring changes to disabled, readonly, etc. state, and update CSS class of root node
array.forEach(["disabled", "readOnly", "checked", "selected", "focused", "state", "hovering", "active", "_opened"], function(attr){
this.watch(attr, lang.hitch(this, "_setStateClass"));
}, this);
// Track hover and active mouse events on widget root node, plus possibly on subnodes
for(var ap in this.cssStateNodes){
this._trackMouseState(this[ap], this.cssStateNodes[ap]);
}
this._trackMouseState(this.domNode, this.baseClass);
// Set state initially; there's probably no hover/active/focus state but widget might be
// disabled/readonly/checked/selected so we want to set CSS classes for those conditions.
this._setStateClass();
},
_cssMouseEvent: function(/*Event*/ event){
// summary:
// Handler for CSS event on this.domNode. Sets hovering and active properties depending on mouse state,
// which triggers _setStateClass() to set appropriate CSS classes for this.domNode.
if(!this.disabled){
switch(event.type){
case "mouseover":
this._set("hovering", true);
this._set("active", this._mouseDown);
break;
case "mouseout":
this._set("hovering", false);
this._set("active", false);
break;
case "mousedown":
case "touchstart":
this._set("active", true);
break;
case "mouseup":
case "touchend":
this._set("active", false);
break;
}
}
},
_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
// - Incomplete - ValidationTextBox sets this.state to "Incomplete" if the current input value is not finished yet
// - 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) and from focus manager (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(array.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");
}
var checkedState = this.checked == "mixed" ? "Mixed" : (this.checked ? "Checked" : "");
if(this.checked){
multiply(checkedState);
}
if(this.state){
multiply(this.state);
}
if(this.selected){
multiply("Selected");
}
if(this._opened){
multiply("Opened");
}
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
array.forEach(tn.className.split(" "), function(c){ classHash[c] = true; });
if("_stateClasses" in this){
array.forEach(this._stateClasses, function(c){ delete classHash[c]; });
}
array.forEach(newStateClasses, function(c){ classHash[c] = true; });
var newClasses = [];
for(var c in classHash){
newClasses.push(c);
}
tn.className = newClasses.join(" ");
this._stateClasses = newStateClasses;
},
_subnodeCssMouseEvent: function(node, clazz, evt){
// summary:
// Handler for hover/active mouse event on widget's subnode
if(this.disabled || this.readOnly){
return;
}
function hover(isHovering){
domClass.toggle(node, clazz+"Hover", isHovering);
}
function active(isActive){
domClass.toggle(node, clazz+"Active", isActive);
}
function focused(isFocused){
domClass.toggle(node, clazz+"Focused", isFocused);
}
switch(evt.type){
case "mouseover":
hover(true);
break;
case "mouseout":
hover(false);
active(false);
break;
case "mousedown":
case "touchstart":
active(true);
break;
case "mouseup":
case "touchend":
active(false);
break;
case "focus":
case "focusin":
focused(true);
break;
case "blur":
case "focusout":
focused(false);
break;
}
},
_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)
// Flag for listener code below to call this._cssMouseEvent() or this._subnodeCssMouseEvent()
// when node is hovered/active
node._cssState = clazz;
}
});
ready(function(){
// Document level listener to catch hover etc. events on widget root nodes and subnodes.
// Note that when the mouse is moved quickly, a single onmouseenter event could signal that multiple widgets
// have been hovered or unhovered (try test_Accordion.html)
function handler(evt){
// Poor man's event propagation. Don't propagate event to ancestors of evt.relatedTarget,
// to avoid processing mouseout events moving from a widget's domNode to a descendant node;
// such events shouldn't be interpreted as a mouseleave on the widget.
if(!dom.isDescendant(evt.relatedTarget, evt.target)){
for(var node = evt.target; node && node != evt.relatedTarget; node = node.parentNode){
// Process any nodes with _cssState property. They are generally widget root nodes,
// but could also be sub-nodes within a widget
if(node._cssState){
var widget = registry.getEnclosingWidget(node);
if(widget){
if(node == widget.domNode){
// event on the widget's root node
widget._cssMouseEvent(evt);
}else{
// event on widget's sub-node
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
}
}
}
}
}
function ieHandler(evt){
evt.target = evt.srcElement;
handler(evt);
}
// Use addEventListener() (and attachEvent() on IE) to catch the relevant events even if other handlers
// (on individual nodes) call evt.stopPropagation() or event.stopEvent().
// Currently typematic.js is doing that, not sure why.
// Don't monitor mouseover/mouseout on mobile because iOS generates "phantom" mouseover/mouseout events when
// drag-scrolling, at the point in the viewport where the drag originated. Test the Tree in api viewer.
var body = win.body(),
types = (has("touch") ? [] : ["mouseover", "mouseout"]).concat(["mousedown", "touchstart", "mouseup", "touchend"]);
array.forEach(types, function(type){
if(body.addEventListener){
body.addEventListener(type, handler, true); // W3C
}else{
body.attachEvent("on"+type, ieHandler); // IE
}
});
// Track focus events on widget sub-nodes that have been registered via _trackMouseState().
// However, don't track focus events on the widget root nodes, because focus is tracked via the
// focus manager (and it's not really tracking focus, but rather tracking that focus is on one of the widget's
// nodes or a subwidget's node or a popup node, etc.)
// Remove for 2.0 (if focus CSS needed, just use :focus pseudo-selector).
on(body, "focusin, focusout", function(evt){
var node = evt.target;
if(node._cssState && !node.getAttribute("widgetId")){
var widget = registry.getEnclosingWidget(node);
widget._subnodeCssMouseEvent(node, node._cssState, evt);
}
});
});
return CssStateMixin;
});
},
'dijit/layout/ScrollingTabController':function(){
require({cache:{
'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>"}});
define("dijit/layout/ScrollingTabController", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.contains
"dojo/dom-geometry", // domGeometry.contentBox
"dojo/dom-style", // domStyle.style
"dojo/_base/fx", // Animation
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/query", // query
"dojo/sniff", // has("ie"), has("webkit"), has("quirks")
"../registry", // registry.byId()
"dojo/text!./templates/ScrollingTabController.html",
"dojo/text!./templates/_ScrollingTabControllerButton.html",
"./TabController",
"./utils", // marginBox2contextBox, layoutChildren
"../_WidgetsInTemplateMixin",
"../Menu",
"../MenuItem",
"../form/Button",
"../_HasDropDown",
"dojo/NodeList-dom" // NodeList.style
], function(array, declare, domClass, domGeometry, domStyle, fx, lang, on, query, has,
registry, tabControllerTemplate, buttonTemplate, TabController, layoutUtils, _WidgetsInTemplateMixin,
Menu, MenuItem, Button, _HasDropDown){
// module:
// dijit/layout/ScrollingTabController
var ScrollingTabController = declare("dijit.layout.ScrollingTabController", [TabController, _WidgetsInTemplateMixin], {
// summary:
// Set of tabs with left/right arrow keys and a menu to switch between tabs not
// all fitting on a single row.
// Works only for horizontal tabs (either above or below the content, not to the left
// or right).
// tags:
// private
baseClass: "dijitTabController dijitScrollingTabController",
templateString: tabControllerTemplate,
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// tabStripClass: [const] String
// The css class to apply to the tab strip, if it is visible.
tabStripClass: "",
widgetsInTemplate: true,
// _minScroll: Number
// The distance in pixels from the edge of the tab strip which,
// if a scroll animation is less than, forces the scroll to
// go all the way to the left/right.
_minScroll: 5,
// Override default behavior mapping class to DOMNode
_setClassAttr: { node: "containerNode", type: "class" },
buildRendering: function(){
this.inherited(arguments);
var n = this.domNode;
this.scrollNode = this.tablistWrapper;
this._initButtons();
if(!this.tabStripClass){
this.tabStripClass = "dijitTabContainer" +
this.tabPosition.charAt(0).toUpperCase() +
this.tabPosition.substr(1).replace(/-.*/, "") +
"None";
domClass.add(n, "tabStrip-disabled")
}
domClass.add(this.tablistWrapper, this.tabStripClass);
},
onStartup: function(){
this.inherited(arguments);
// TabController is hidden until it finishes drawing, to give
// a less visually jumpy instantiation. When it's finished, set visibility to ""
// to that the tabs are hidden/shown depending on the container's visibility setting.
domStyle.set(this.domNode, "visibility", "");
this._postStartup = true;
// changes to the tab button label or iconClass will have changed the width of the
// buttons, so do a resize
this.own(on(this.containerNode, "attrmodified-label, attrmodified-iconclass", lang.hitch(this, function(evt){
if(this._dim){
this.resize(this._dim);
}
})));
},
onAddChild: function(page, insertIndex){
this.inherited(arguments);
// Increment the width of the wrapper when a tab is added
// This makes sure that the buttons never wrap.
// The value 200 is chosen as it should be bigger than most
// Tab button widths.
domStyle.set(this.containerNode, "width",
(domStyle.get(this.containerNode, "width") + 200) + "px");
},
onRemoveChild: function(page, insertIndex){
// null out _selectedTab because we are about to delete that dom node
var button = this.pane2button[page.id];
if(this._selectedTab === button.domNode){
this._selectedTab = null;
}
this.inherited(arguments);
},
_initButtons: function(){
// summary:
// Creates the buttons used to scroll to view tabs that
// may not be visible if the TabContainer is too narrow.
// Make a list of the buttons to display when the tab labels become
// wider than the TabContainer, and hide the other buttons.
// Also gets the total width of the displayed buttons.
this._btnWidth = 0;
this._buttons = query("> .tabStripButton", this.domNode).filter(function(btn){
if((this.useMenu && btn == this._menuBtn.domNode) ||
(this.useSlider && (btn == this._rightBtn.domNode || btn == this._leftBtn.domNode))){
this._btnWidth += domGeometry.getMarginSize(btn).w;
return true;
}else{
domStyle.set(btn, "display", "none");
return false;
}
}, this);
},
_getTabsWidth: function(){
var children = this.getChildren();
if(children.length){
var leftTab = children[this.isLeftToRight() ? 0 : children.length - 1].domNode,
rightTab = children[this.isLeftToRight() ? children.length - 1 : 0].domNode;
return rightTab.offsetLeft + rightTab.offsetWidth - leftTab.offsetLeft;
}else{
return 0;
}
},
_enableBtn: function(width){
// summary:
// Determines if the tabs are wider than the width of the TabContainer, and
// thus that we need to display left/right/menu navigation buttons.
var tabsWidth = this._getTabsWidth();
width = width || domStyle.get(this.scrollNode, "width");
return tabsWidth > 0 && width < tabsWidth;
},
resize: function(dim){
// summary:
// Hides or displays the buttons used to scroll the tab list and launch the menu
// that selects tabs.
// Save the dimensions to be used when a child is renamed.
this._dim = dim;
// Set my height to be my natural height (tall enough for one row of tab labels),
// and my content-box width based on margin-box width specified in dim parameter.
// But first reset scrollNode.height in case it was set by layoutChildren() call
// in a previous run of this method.
this.scrollNode.style.height = "auto";
var cb = this._contentBox = layoutUtils.marginBox2contentBox(this.domNode, {h: 0, w: dim.w});
cb.h = this.scrollNode.offsetHeight;
domGeometry.setContentSize(this.domNode, cb);
// Show/hide the left/right/menu navigation buttons depending on whether or not they
// are needed.
var enable = this._enableBtn(this._contentBox.w);
this._buttons.style("display", enable ? "" : "none");
// Position and size the navigation buttons and the tablist
this._leftBtn.layoutAlign = "left";
this._rightBtn.layoutAlign = "right";
this._menuBtn.layoutAlign = this.isLeftToRight() ? "right" : "left";
layoutUtils.layoutChildren(this.domNode, this._contentBox,
[this._menuBtn, this._leftBtn, this._rightBtn, {domNode: this.scrollNode, layoutAlign: "client"}]);
// set proper scroll so that selected tab is visible
if(this._selectedTab){
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
this.scrollNode.scrollLeft = this._convertToScrollLeft(this._getScrollForSelectedTab());
}
// Enable/disabled left right buttons depending on whether or not user can scroll to left or right
this._setButtonClass(this._getScroll());
this._postResize = true;
// Return my size so layoutChildren() can use it.
// Also avoids IE9 layout glitch on browser resize when scroll buttons present
return {h: this._contentBox.h, w: dim.w};
},
_getScroll: function(){
// summary:
// Returns the current scroll of the tabs where 0 means
// "scrolled all the way to the left" and some positive number, based on #
// of pixels of possible scroll (ex: 1000) means "scrolled all the way to the right"
return (this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")) ? this.scrollNode.scrollLeft :
domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width")
+ (has("ie") >= 8 ? -1 : 1) * this.scrollNode.scrollLeft;
},
_convertToScrollLeft: function(val){
// summary:
// Given a scroll value where 0 means "scrolled all the way to the left"
// and some positive number, based on # of pixels of possible scroll (ex: 1000)
// means "scrolled all the way to the right", return value to set this.scrollNode.scrollLeft
// to achieve that scroll.
//
// This method is to adjust for RTL funniness in various browsers and versions.
if(this.isLeftToRight() || has("ie") < 8 || (has("ie") && has("quirks")) || has("webkit")){
return val;
}else{
var maxScroll = domStyle.get(this.containerNode, "width") - domStyle.get(this.scrollNode, "width");
return (has("ie") >= 8 ? -1 : 1) * (val - maxScroll);
}
},
onSelectChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Smoothly scrolls to a tab when it is selected.
var tab = this.pane2button[page.id];
if(!tab || !page){return;}
var node = tab.domNode;
// Save the selection
if(node != this._selectedTab){
this._selectedTab = node;
// Scroll to the selected tab, except on startup, when scrolling is handled in resize()
if(this._postResize){
var sl = this._getScroll();
if(sl > node.offsetLeft ||
sl + domStyle.get(this.scrollNode, "width") <
node.offsetLeft + domStyle.get(node, "width")){
this.createSmoothScroll().play();
}
}
}
this.inherited(arguments);
},
_getScrollBounds: function(){
// summary:
// Returns the minimum and maximum scroll setting to show the leftmost and rightmost
// tabs (respectively)
var children = this.getChildren(),
scrollNodeWidth = domStyle.get(this.scrollNode, "width"), // about 500px
containerWidth = domStyle.get(this.containerNode, "width"), // 50,000px
maxPossibleScroll = containerWidth - scrollNodeWidth, // scrolling until right edge of containerNode visible
tabsWidth = this._getTabsWidth();
if(children.length && tabsWidth > scrollNodeWidth){
// Scrolling should happen
return {
min: this.isLeftToRight() ? 0 : children[children.length-1].domNode.offsetLeft,
max: this.isLeftToRight() ?
(children[children.length-1].domNode.offsetLeft + children[children.length-1].domNode.offsetWidth) - scrollNodeWidth :
maxPossibleScroll
};
}else{
// No scrolling needed, all tabs visible, we stay either scrolled to far left or far right (depending on dir)
var onlyScrollPosition = this.isLeftToRight() ? 0 : maxPossibleScroll;
return {
min: onlyScrollPosition,
max: onlyScrollPosition
};
}
},
_getScrollForSelectedTab: function(){
// summary:
// Returns the scroll value setting so that the selected tab
// will appear in the center
var w = this.scrollNode,
n = this._selectedTab,
scrollNodeWidth = domStyle.get(this.scrollNode, "width"),
scrollBounds = this._getScrollBounds();
// TODO: scroll minimal amount (to either right or left) so that
// selected tab is fully visible, and just return if it's already visible?
var pos = (n.offsetLeft + domStyle.get(n, "width")/2) - scrollNodeWidth/2;
pos = Math.min(Math.max(pos, scrollBounds.min), scrollBounds.max);
// TODO:
// If scrolling close to the left side or right side, scroll
// all the way to the left or right. See this._minScroll.
// (But need to make sure that doesn't scroll the tab out of view...)
return pos;
},
createSmoothScroll: function(x){
// summary:
// Creates a dojo._Animation object that smoothly scrolls the tab list
// either to a fixed horizontal pixel value, or to the selected tab.
// description:
// If an number argument is passed to the function, that horizontal
// pixel position is scrolled to. Otherwise the currently selected
// tab is scrolled to.
// x: Integer?
// An optional pixel value to scroll to, indicating distance from left.
// Calculate position to scroll to
if(arguments.length > 0){
// position specified by caller, just make sure it's within bounds
var scrollBounds = this._getScrollBounds();
x = Math.min(Math.max(x, scrollBounds.min), scrollBounds.max);
}else{
// scroll to center the current tab
x = this._getScrollForSelectedTab();
}
if(this._anim && this._anim.status() == "playing"){
this._anim.stop();
}
var self = this,
w = this.scrollNode,
anim = new fx.Animation({
beforeBegin: function(){
if(this.curve){ delete this.curve; }
var oldS = w.scrollLeft,
newS = self._convertToScrollLeft(x);
anim.curve = new fx._Line(oldS, newS);
},
onAnimate: function(val){
w.scrollLeft = val;
}
});
this._anim = anim;
// Disable/enable left/right buttons according to new scroll position
this._setButtonClass(x);
return anim; // dojo/_base/fx/Animation
},
_getBtnNode: function(/*Event*/ e){
// summary:
// Gets a button DOM node from a mouse click event.
// e:
// The mouse click event.
var n = e.target;
while(n && !domClass.contains(n, "tabStripButton")){
n = n.parentNode;
}
return n;
},
doSlideRight: function(/*Event*/ e){
// summary:
// Scrolls the menu to the right.
// e:
// The mouse click event.
this.doSlide(1, this._getBtnNode(e));
},
doSlideLeft: function(/*Event*/ e){
// summary:
// Scrolls the menu to the left.
// e:
// The mouse click event.
this.doSlide(-1,this._getBtnNode(e));
},
doSlide: function(/*Number*/ direction, /*DomNode*/ node){
// summary:
// Scrolls the tab list to the left or right by 75% of the widget width.
// direction:
// If the direction is 1, the widget scrolls to the right, if it is -1,
// it scrolls to the left.
if(node && domClass.contains(node, "dijitTabDisabled")){return;}
var sWidth = domStyle.get(this.scrollNode, "width");
var d = (sWidth * 0.75) * direction;
var to = this._getScroll() + d;
this._setButtonClass(to);
this.createSmoothScroll(to).play();
},
_setButtonClass: function(/*Number*/ scroll){
// summary:
// Disables the left scroll button if the tabs are scrolled all the way to the left,
// or the right scroll button in the opposite case.
// scroll: Integer
// amount of horizontal scroll
var scrollBounds = this._getScrollBounds();
this._leftBtn.set("disabled", scroll <= scrollBounds.min);
this._rightBtn.set("disabled", scroll >= scrollBounds.max);
}
});
var ScrollingTabControllerButtonMixin = declare("dijit.layout._ScrollingTabControllerButtonMixin", null, {
baseClass: "dijitTab tabStripButton",
templateString: buttonTemplate,
// Override inherited tabIndex: 0 from dijit/form/Button, because user shouldn't be
// able to tab to the left/right/menu buttons
tabIndex: "",
// Similarly, override FormWidget.isFocusable() because clicking a button shouldn't focus it
// either (this override avoids focus() call in FormWidget.js)
isFocusable: function(){ return false; }
});
// Class used in template
declare("dijit.layout._ScrollingTabControllerButton",
[Button, ScrollingTabControllerButtonMixin]);
// Class used in template
declare(
"dijit.layout._ScrollingTabControllerMenuButton",
[Button, _HasDropDown, ScrollingTabControllerButtonMixin],
{
// id of the TabContainer itself
containerId: "",
// -1 so user can't tab into the button, but so that button can still be focused programatically.
// Because need to move focus to the button (or somewhere) before the menu is hidden or IE6 will crash.
tabIndex: "-1",
isLoaded: function(){
// recreate menu every time, in case the TabContainer's list of children (or their icons/labels) have changed
return false;
},
loadDropDown: function(callback){
this.dropDown = new Menu({
id: this.containerId + "_menu",
ownerDocument: this.ownerDocument,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
var container = registry.byId(this.containerId);
array.forEach(container.getChildren(), function(page){
var menuItem = new MenuItem({
id: page.id + "_stcMi",
label: page.title,
iconClass: page.iconClass,
disabled: page.disabled,
ownerDocument: this.ownerDocument,
dir: page.dir,
lang: page.lang,
textDir: page.textDir,
onClick: function(){
container.selectChild(page);
}
});
this.dropDown.addChild(menuItem);
}, this);
callback();
},
closeDropDown: function(/*Boolean*/ focus){
this.inherited(arguments);
if(this.dropDown){
this.dropDown.destroyRecursive();
delete this.dropDown;
}
}
});
return ScrollingTabController;
});
},
'dijit/DialogUnderlay':function(){
define("dijit/DialogUnderlay", [
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/window", // winUtils.getBox
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe"
], function(declare, domAttr, winUtils, _Widget, _TemplatedMixin, BackgroundIframe){
// module:
// dijit/DialogUnderlay
return declare("dijit.DialogUnderlay", [_Widget, _TemplatedMixin], {
// summary:
// The component that blocks the screen behind a `dijit.Dialog`
//
// description:
// A component used to block input behind a `dijit.Dialog`. Only a single
// instance of this widget is created by `dijit.Dialog`, and saved as
// a reference to be shared between all Dialogs as `dijit._underlay`
//
// The underlay itself can be styled based on and id:
// | #myDialog_underlay { background-color:red; }
//
// In the case of `dijit.Dialog`, this id is based on the id of the Dialog,
// suffixed with _underlay.
// Template has two divs; outer div is used for fade-in/fade-out, and also to hold background iframe.
// Inner div has opacity specified in CSS file.
templateString: "<div class='dijitDialogUnderlayWrapper'><div class='dijitDialogUnderlay' data-dojo-attach-point='node'></div></div>",
// Parameters on creation or updatable later
// dialogId: String
// Id of the dialog.... DialogUnderlay's id is based on this id
dialogId: "",
// class: String
// This class name is used on the DialogUnderlay node, in addition to dijitDialogUnderlay
"class": "",
_setDialogIdAttr: function(id){
domAttr.set(this.node, "id", id + "_underlay");
this._set("dialogId", id);
},
_setClassAttr: function(clazz){
this.node.className = "dijitDialogUnderlay " + clazz;
this._set("class", clazz);
},
postCreate: function(){
// summary:
// Append the underlay to the body
this.ownerDocumentBody.appendChild(this.domNode);
},
layout: function(){
// summary:
// Sets the background to the size of the viewport
//
// description:
// Sets the background to the size of the viewport (rather than the size
// of the document) since we need to cover the whole browser window, even
// if the document is only a few lines long.
// tags:
// private
var is = this.node.style,
os = this.domNode.style;
// hide the background temporarily, so that the background itself isn't
// causing scrollbars to appear (might happen when user shrinks browser
// window and then we are called to resize)
os.display = "none";
// then resize and show
var viewport = winUtils.getBox(this.ownerDocument);
os.top = viewport.t + "px";
os.left = viewport.l + "px";
is.width = viewport.w + "px";
is.height = viewport.h + "px";
os.display = "block";
},
show: function(){
// summary:
// Show the dialog underlay
this.domNode.style.display = "block";
this.layout();
this.bgIframe = new BackgroundIframe(this.domNode);
},
hide: function(){
// summary:
// Hides the dialog underlay
this.bgIframe.destroy();
delete this.bgIframe;
this.domNode.style.display = "none";
}
});
});
},
'dijit/place':function(){
define("dijit/place", [
"dojo/_base/array", // array.forEach array.map array.some
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/window", // win.body
"dojo/window", // winUtils.getBox
"./main" // dijit (defining dijit.place to match API doc)
], function(array, domGeometry, domStyle, kernel, win, winUtils, dijit){
// module:
// dijit/place
function _place(/*DomNode*/ node, choices, layoutNode, aroundNodeCoords){
// 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, size)
// 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.
// It also passes in the available size for the popup, which is useful for tooltips to
// tell them that their width is limited to a certain amount. layoutNode() may return a value expressing
// how much the popup had to be modified to fit into the available space. This is used to determine
// what the best placement is.
// aroundNodeCoords: Object
// Size of aroundNode, ex: {w: 200, h: 50}
// get {x: 10, y: 10, w: 100, h:100} type obj representing position of
// viewport over document
var view = winUtils.getBox(node.ownerDocument);
// This won't work if the node is inside a <div style="position: relative">,
// so reattach it to win.doc.body. (Otherwise, the positioning will be wrong
// and also it might get cutoff)
if(!node.parentNode || String(node.parentNode.tagName).toLowerCase() != "body"){
win.body(node.ownerDocument).appendChild(node);
}
var best = null;
array.some(choices, function(choice){
var corner = choice.corner;
var pos = choice.pos;
var overflow = 0;
// calculate amount of space available given specified position of node
var spaceAvailable = {
w: {
'L': view.l + view.w - pos.x,
'R': pos.x - view.l,
'M': view.w
}[corner.charAt(1)],
h: {
'T': view.t + view.h - pos.y,
'B': pos.y - view.t,
'M': view.h
}[corner.charAt(0)]
};
// Clear left/right position settings set earlier so they don't interfere with calculations,
// specifically when layoutNode() (a.k.a. Tooltip.orient()) measures natural width of Tooltip
var s = node.style;
s.left = s.right = "auto";
// 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 tooltip's size changes based on position, due to triangle)
if(layoutNode){
var res = layoutNode(node, choice.aroundCorner, corner, spaceAvailable, aroundNodeCoords);
overflow = typeof res == "undefined" ? 0 : res;
}
// get node's size
var style = node.style;
var oldDisplay = style.display;
var oldVis = style.visibility;
if(style.display == "none"){
style.visibility = "hidden";
style.display = "";
}
var bb = domGeometry.position(node);
style.display = oldDisplay;
style.visibility = oldVis;
// coordinates and size of node with specified corner placed at pos,
// and clipped by viewport
var
startXpos = {
'L': pos.x,
'R': pos.x - bb.w,
'M': Math.max(view.l, Math.min(view.l + view.w, pos.x + (bb.w >> 1)) - bb.w) // M orientation is more flexible
}[corner.charAt(1)],
startYpos = {
'T': pos.y,
'B': pos.y - bb.h,
'M': Math.max(view.t, Math.min(view.t + view.h, pos.y + (bb.h >> 1)) - bb.h)
}[corner.charAt(0)],
startX = Math.max(view.l, startXpos),
startY = Math.max(view.t, startYpos),
endX = Math.min(view.l + view.w, startXpos + bb.w),
endY = Math.min(view.t + view.h, startYpos + bb.h),
width = endX - startX,
height = endY - startY;
overflow += (bb.w - width) + (bb.h - height);
if(best == null || overflow < best.overflow){
best = {
corner: corner,
aroundCorner: choice.aroundCorner,
x: startX,
y: startY,
w: width,
h: height,
overflow: overflow,
spaceAvailable: spaceAvailable
};
}
return !overflow;
});
// In case the best position is not the last one we checked, need to call
// layoutNode() again.
if(best.overflow && layoutNode){
layoutNode(node, best.aroundCorner, best.corner, best.spaceAvailable, aroundNodeCoords);
}
// And then position the node. Do this last, after the layoutNode() above
// has sized the node, due to browser quirks when the viewport is scrolled
// (specifically that a Tooltip will shrink to fit as though the window was
// scrolled to the left).
//
// In RTL mode, set style.right rather than style.left so in the common case,
// window resizes move the popup along with the aroundNode.
var l = domGeometry.isBodyLtr(node.ownerDocument),
s = node.style;
s.top = best.y + "px";
s[l ? "left" : "right"] = (l ? best.x : view.w - best.x - best.w) + "px";
s[l ? "right" : "left"] = "auto"; // needed for FF or else tooltip goes to far left
return best;
}
var place = {
// summary:
// Code to place a DOMNode relative to another DOMNode.
// Load using require(["dijit/place"], function(place){ ... }).
at: function(node, pos, corners, 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.
// node: DOMNode
// The node to position
// pos: dijit/place.__Position
// Object like {x: 10, y: 20}
// corners: String[]
// 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: dijit/place.__Position?
// optional param to 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).
// | place(node, {x: 10, y: 20}, ["TR", "BL"])
var choices = array.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 _place(node, choices);
},
around: function(
/*DomNode*/ node,
/*DomNode|dijit/place.__Rectangle*/ anchor,
/*String[]*/ positions,
/*Boolean*/ leftToRight,
/*Function?*/ layoutNode){
// summary:
// Position node adjacent or kitty-corner to anchor
// 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.
// anchor:
// Either a DOMNode or a rectangle (object with x, y, width, height).
// positions:
// Ordered list of positions to try matching up.
//
// - before: places drop down to the left of the anchor node/widget, or to the right in the case
// of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
// with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
// - after: places drop down to the right of the anchor node/widget, or to the left in the case
// of RTL scripts like Hebrew and Arabic; aligns either the top of the drop down
// with the top of the anchor, or the bottom of the drop down with bottom of the anchor.
// - before-centered: centers drop down to the left of the anchor node/widget, or to the right
// in the case of RTL scripts like Hebrew and Arabic
// - after-centered: centers drop down to the right of the anchor node/widget, or to the left
// in the case of RTL scripts like Hebrew and Arabic
// - above-centered: drop down is centered above anchor node
// - above: drop down goes above anchor node, left sides aligned
// - above-alt: drop down goes above anchor node, right sides aligned
// - below-centered: drop down is centered above anchor node
// - below: drop down goes below anchor node
// - below-alt: drop down goes below anchor node, right sides aligned
// 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.
// leftToRight:
// True if widget is LTR, false if widget is RTL. Affects the behavior of "above" and "below"
// positions slightly.
// example:
// | placeAroundNode(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)
//
// if around is a DOMNode (or DOMNode id), convert to coordinates
var aroundNodePos = (typeof anchor == "string" || "offsetWidth" in anchor)
? domGeometry.position(anchor, true)
: anchor;
// Compute position and size of visible part of anchor (it may be partially hidden by ancestor nodes w/scrollbars)
if(anchor.parentNode){
// ignore nodes between position:relative and position:absolute
var sawPosAbsolute = domStyle.getComputedStyle(anchor).position == "absolute";
var parent = anchor.parentNode;
while(parent && parent.nodeType == 1 && parent.nodeName != "BODY"){ //ignoring the body will help performance
var parentPos = domGeometry.position(parent, true),
pcs = domStyle.getComputedStyle(parent);
if(/relative|absolute/.test(pcs.position)){
sawPosAbsolute = false;
}
if(!sawPosAbsolute && /hidden|auto|scroll/.test(pcs.overflow)){
var bottomYCoord = Math.min(aroundNodePos.y + aroundNodePos.h, parentPos.y + parentPos.h);
var rightXCoord = Math.min(aroundNodePos.x + aroundNodePos.w, parentPos.x + parentPos.w);
aroundNodePos.x = Math.max(aroundNodePos.x, parentPos.x);
aroundNodePos.y = Math.max(aroundNodePos.y, parentPos.y);
aroundNodePos.h = bottomYCoord - aroundNodePos.y;
aroundNodePos.w = rightXCoord - aroundNodePos.x;
}
if(pcs.position == "absolute"){
sawPosAbsolute = true;
}
parent = parent.parentNode;
}
}
var x = aroundNodePos.x,
y = aroundNodePos.y,
width = "w" in aroundNodePos ? aroundNodePos.w : (aroundNodePos.w = aroundNodePos.width),
height = "h" in aroundNodePos ? aroundNodePos.h : (kernel.deprecated("place.around: dijit/place.__Rectangle: { x:"+x+", y:"+y+", height:"+aroundNodePos.height+", width:"+width+" } has been deprecated. Please use { x:"+x+", y:"+y+", h:"+aroundNodePos.height+", w:"+width+" }", "", "2.0"), aroundNodePos.h = aroundNodePos.height);
// Convert positions arguments into choices argument for _place()
var choices = [];
function push(aroundCorner, corner){
choices.push({
aroundCorner: aroundCorner,
corner: corner,
pos: {
x: {
'L': x,
'R': x + width,
'M': x + (width >> 1)
}[aroundCorner.charAt(1)],
y: {
'T': y,
'B': y + height,
'M': y + (height >> 1)
}[aroundCorner.charAt(0)]
}
})
}
array.forEach(positions, function(pos){
var ltr = leftToRight;
switch(pos){
case "above-centered":
push("TM", "BM");
break;
case "below-centered":
push("BM", "TM");
break;
case "after-centered":
ltr = !ltr;
// fall through
case "before-centered":
push(ltr ? "ML" : "MR", ltr ? "MR" : "ML");
break;
case "after":
ltr = !ltr;
// fall through
case "before":
push(ltr ? "TL" : "TR", ltr ? "TR" : "TL");
push(ltr ? "BL" : "BR", ltr ? "BR" : "BL");
break;
case "below-alt":
ltr = !ltr;
// fall through
case "below":
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
push(ltr ? "BL" : "BR", ltr ? "TL" : "TR");
push(ltr ? "BR" : "BL", ltr ? "TR" : "TL");
break;
case "above-alt":
ltr = !ltr;
// fall through
case "above":
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
push(ltr ? "TL" : "TR", ltr ? "BL" : "BR");
push(ltr ? "TR" : "TL", ltr ? "BR" : "BL");
break;
default:
// To assist dijit/_base/place, accept arguments of type {aroundCorner: "BL", corner: "TL"}.
// Not meant to be used directly.
push(pos.aroundCorner, pos.corner);
}
});
var position = _place(node, choices, layoutNode, {w: width, h: height});
position.aroundNodePos = aroundNodePos;
return position;
}
};
/*=====
place.__Position = {
// x: Integer
// horizontal coordinate in pixels, relative to document body
// y: Integer
// vertical coordinate in pixels, relative to document body
};
place.__Rectangle = {
// x: Integer
// horizontal offset in pixels, relative to document body
// y: Integer
// vertical offset in pixels, relative to document body
// w: Integer
// width in pixels. Can also be specified as "width" for backwards-compatibility.
// h: Integer
// height in pixels. Can also be specified as "height" for backwards-compatibility.
};
=====*/
return dijit.place = place; // setting dijit.place for back-compat, remove for 2.0
});
},
'dijit/_HasDropDown':function(){
define("dijit/_HasDropDown", [
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/_base/event", // event.stop
"dojo/dom", // dom.isDescendant
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.contains domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/has", // has("touch")
"dojo/keys", // keys.DOWN_ARROW keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.hitch lang.isFunction
"dojo/on",
"dojo/window", // winUtils.getBox
"./registry", // registry.byNode()
"./focus",
"./popup",
"./_FocusMixin"
], function(declare, Deferred, event,dom, domAttr, domClass, domGeometry, domStyle, has, keys, lang, on,
winUtils, registry, focus, popup, _FocusMixin){
// module:
// dijit/_HasDropDown
return declare("dijit._HasDropDown", _FocusMixin, {
// summary:
// Mixin for widgets that need drop down ability.
// _buttonNode: [protected] DomNode
// The button/icon/node to click to display the drop down.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then either focusNode or domNode (if focusNode is also missing) will be used.
_buttonNode: null,
// _arrowWrapperNode: [protected] DomNode
// Will set CSS class dijitUpArrow, dijitDownArrow, dijitRightArrow etc. on this node depending
// on where the drop down is set to be positioned.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then _buttonNode will be used.
_arrowWrapperNode: null,
// _popupStateNode: [protected] DomNode
// The node to set the popupActive class on.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then focusNode or _buttonNode (if focusNode is missing) will be used.
_popupStateNode: null,
// _aroundNode: [protected] DomNode
// The node to display the popup around.
// Can be set via a data-dojo-attach-point assignment.
// If missing, then domNode will be used.
_aroundNode: null,
// dropDown: [protected] Widget
// The widget to display as a popup. This widget *must* be
// defined before the startup function is called.
dropDown: null,
// autoWidth: [protected] Boolean
// Set to true to make the drop down at least as wide as this
// widget. Set to false if the drop down should just be its
// default width
autoWidth: true,
// forceWidth: [protected] Boolean
// Set to true to make the drop down exactly as wide as this
// widget. Overrides autoWidth.
forceWidth: false,
// maxHeight: [protected] Integer
// The max height for our dropdown.
// Any dropdown taller than this will have scrollbars.
// Set to 0 for no max height, or -1 to limit height to available space in viewport
maxHeight: 0,
// dropDownPosition: [const] 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.
//
dropDownPosition: ["below","above"],
// _stopClickEvents: Boolean
// When set to false, the click events will not be stopped, in
// case you want to use them in your subclass
_stopClickEvents: true,
_onDropDownMouseDown: function(/*Event*/ e){
// summary:
// Callback when the user mousedown's on the arrow icon
if(this.disabled || this.readOnly){ return; }
// Prevent default to stop things like text selection, but don't stop propagation, so that:
// 1. TimeTextBox etc. can focus the <input> on mousedown
// 2. dropDownButtonActive class applied by _CssStateMixin (on button depress)
// 3. user defined onMouseDown handler fires
e.preventDefault();
this._docHandler = this.connect(this.ownerDocument, "mouseup", "_onDropDownMouseUp");
this.toggleDropDown();
},
_onDropDownMouseUp: function(/*Event?*/ e){
// summary:
// Callback when the user lifts their mouse after mouse down on the arrow icon.
// If the drop down is a simple menu and the mouse is over the menu, we execute it, otherwise, we focus our
// drop down widget. If the event is missing, then we are not
// a mouseup event.
//
// This is useful for the common mouse movement pattern
// with native browser `<select>` nodes:
//
// 1. mouse down on the select node (probably on the arrow)
// 2. move mouse to a menu item while holding down the mouse button
// 3. mouse up. this selects the menu item as though the user had clicked it.
if(e && this._docHandler){
this.disconnect(this._docHandler);
}
var dropDown = this.dropDown, overMenu = false;
if(e && this._opened){
// This code deals with the corner-case when the drop down covers the original widget,
// because it's so large. In that case mouse-up shouldn't select a value from the menu.
// Find out if our target is somewhere in our dropdown widget,
// but not over our _buttonNode (the clickable node)
var c = domGeometry.position(this._buttonNode, true);
if(!(e.pageX >= c.x && e.pageX <= c.x + c.w) ||
!(e.pageY >= c.y && e.pageY <= c.y + c.h)){
var t = e.target;
while(t && !overMenu){
if(domClass.contains(t, "dijitPopup")){
overMenu = true;
}else{
t = t.parentNode;
}
}
if(overMenu){
t = e.target;
if(dropDown.onItemClick){
var menuItem;
while(t && !(menuItem = registry.byNode(t))){
t = t.parentNode;
}
if(menuItem && menuItem.onClick && menuItem.getParent){
menuItem.getParent().onItemClick(menuItem, e);
}
}
return;
}
}
}
if(this._opened){
if(dropDown.focus && dropDown.autoFocus !== false){
// Focus the dropdown widget - do it on a delay so that we
// don't steal back focus from the dropdown.
this._focusDropDownTimer = this.defer(function(){
dropDown.focus();
delete this._focusDropDownTimer;
});
}
}else{
// The drop down arrow icon probably can't receive focus, but widget itself should get focus.
// defer() needed to make it work on IE (test DateTextBox)
this.defer("focus");
}
if(has("touch")){
this._justGotMouseUp = true;
this.defer(function(){
this._justGotMouseUp = false;
});
}
},
_onDropDownClick: function(/*Event*/ e){
if(has("touch") && !this._justGotMouseUp){
// If there was no preceding mousedown/mouseup (like on android), then simulate them to
// toggle the drop down.
//
// The if(has("touch") is necessary since IE and desktop safari get spurious onclick events
// when there are nested tables (specifically, clicking on a table that holds a dijit/form/Select,
// but not on the Select itself, causes an onclick event on the Select)
this._onDropDownMouseDown(e);
this._onDropDownMouseUp(e);
}
// The drop down was already opened on mousedown/keydown; just need to call stopEvent().
if(this._stopClickEvents){
event.stop(e);
}
},
buildRendering: function(){
this.inherited(arguments);
this._buttonNode = this._buttonNode || this.focusNode || this.domNode;
this._popupStateNode = this._popupStateNode || this.focusNode || this._buttonNode;
// Add a class to the "dijitDownArrowButton" type class to _buttonNode so theme can set direction of arrow
// based on where drop down will normally appear
var defaultPos = {
"after" : this.isLeftToRight() ? "Right" : "Left",
"before" : this.isLeftToRight() ? "Left" : "Right",
"above" : "Up",
"below" : "Down",
"left" : "Left",
"right" : "Right"
}[this.dropDownPosition[0]] || this.dropDownPosition[0] || "Down";
domClass.add(this._arrowWrapperNode || this._buttonNode, "dijit" + defaultPos + "ArrowButton");
},
postCreate: function(){
// summary:
// set up nodes and connect our mouse and keyboard events
this.inherited(arguments);
var keyboardEventNode = this.focusNode || this.domNode;
this.own(
on(this._buttonNode, "mousedown", lang.hitch(this, "_onDropDownMouseDown")),
on(this._buttonNode, "click", lang.hitch(this, "_onDropDownClick")),
on(keyboardEventNode, "keydown", lang.hitch(this, "_onKey")),
on(keyboardEventNode, "keyup", lang.hitch(this, "_onKeyUp"))
);
},
destroy: function(){
if(this.dropDown){
// Destroy the drop down, unless it's already been destroyed. This can happen because
// the drop down is a direct child of <body> even though it's logically my child.
if(!this.dropDown._destroyed){
this.dropDown.destroyRecursive();
}
delete this.dropDown;
}
this.inherited(arguments);
},
_onKey: function(/*Event*/ e){
// summary:
// Callback when the user presses a key while focused on the button node
if(this.disabled || this.readOnly){ return; }
var d = this.dropDown, target = e.target;
if(d && this._opened && d.handleKey){
if(d.handleKey(e) === false){
/* false return code means that the drop down handled the key */
event.stop(e);
return;
}
}
if(d && this._opened && e.keyCode == keys.ESCAPE){
this.closeDropDown();
event.stop(e);
}else if(!this._opened &&
(e.keyCode == keys.DOWN_ARROW ||
( (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) &&
//ignore enter and space if the event is for a text input
((target.tagName || "").toLowerCase() !== 'input' ||
(target.type && target.type.toLowerCase() !== 'text'))))){
// Toggle the drop down, but wait until keyup so that the drop down doesn't
// get a stray keyup event, or in the case of key-repeat (because user held
// down key for too long), stray keydown events
this._toggleOnKeyUp = true;
event.stop(e);
}
},
_onKeyUp: function(){
if(this._toggleOnKeyUp){
delete this._toggleOnKeyUp;
this.toggleDropDown();
var d = this.dropDown; // drop down may not exist until toggleDropDown() call
if(d && d.focus){
this.defer(lang.hitch(d, "focus"), 1);
}
}
},
_onBlur: function(){
// summary:
// Called magically when focus has shifted away from this widget and it's dropdown
// Don't focus on button if the user has explicitly focused on something else (happens
// when user clicks another control causing the current popup to close)..
// But if focus is inside of the drop down then reset focus to me, because IE doesn't like
// it when you display:none a node with focus.
var focusMe = focus.curNode && this.dropDown && dom.isDescendant(focus.curNode, this.dropDown.domNode);
this.closeDropDown(focusMe);
this.inherited(arguments);
},
isLoaded: function(){
// summary:
// Returns true if the dropdown exists and it's data is loaded. This can
// be overridden in order to force a call to loadDropDown().
// tags:
// protected
return true;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and then calls
// the given callback.
// tags:
// protected
// TODO: for 2.0, change API to return a Deferred, instead of calling loadCallback?
loadCallback();
},
loadAndOpenDropDown: function(){
// summary:
// Creates the drop down if it doesn't exist, loads the data
// if there's an href and it hasn't been loaded yet, and
// then opens the drop down. This is basically a callback when the
// user presses the down arrow button to open the drop down.
// returns: Deferred
// Deferred for the drop down widget that
// fires when drop down is created and loaded
// tags:
// protected
var d = new Deferred(),
afterLoad = lang.hitch(this, function(){
this.openDropDown();
d.resolve(this.dropDown);
});
if(!this.isLoaded()){
this.loadDropDown(afterLoad);
}else{
afterLoad();
}
return d;
},
toggleDropDown: function(){
// summary:
// Callback when the user presses the down arrow button or presses
// the down arrow key to open/close the drop down.
// Toggle the drop-down widget; if it is up, close it, if not, open it
// tags:
// protected
if(this.disabled || this.readOnly){ return; }
if(!this._opened){
this.loadAndOpenDropDown();
}else{
this.closeDropDown();
}
},
openDropDown: function(){
// summary:
// Opens the dropdown for this widget. To be called only when this.dropDown
// has been created and is ready to display (ie, it's data is loaded).
// returns:
// return value of dijit/popup.open()
// tags:
// protected
var dropDown = this.dropDown,
ddNode = dropDown.domNode,
aroundNode = this._aroundNode || this.domNode,
self = this;
// Prepare our popup's height and honor maxHeight if it exists.
// TODO: isn't maxHeight dependent on the return value from dijit/popup.open(),
// ie, dependent on how much space is available (BK)
if(!this._preparedNode){
this._preparedNode = true;
// Check if we have explicitly set width and height on the dropdown widget dom node
if(ddNode.style.width){
this._explicitDDWidth = true;
}
if(ddNode.style.height){
this._explicitDDHeight = true;
}
}
// Code for resizing dropdown (height limitation, or increasing width to match my width)
if(this.maxHeight || this.forceWidth || this.autoWidth){
var myStyle = {
display: "",
visibility: "hidden"
};
if(!this._explicitDDWidth){
myStyle.width = "";
}
if(!this._explicitDDHeight){
myStyle.height = "";
}
domStyle.set(ddNode, myStyle);
// Figure out maximum height allowed (if there is a height restriction)
var maxHeight = this.maxHeight;
if(maxHeight == -1){
// limit height to space available in viewport either above or below my domNode
// (whichever side has more room)
var viewport = winUtils.getBox(this.ownerDocument),
position = domGeometry.position(aroundNode, false);
maxHeight = Math.floor(Math.max(position.y, viewport.h - (position.y + position.h)));
}
// Attach dropDown to DOM and make make visibility:hidden rather than display:none
// so we call startup() and also get the size
popup.moveOffScreen(dropDown);
if(dropDown.startup && !dropDown._started){
dropDown.startup(); // this has to be done after being added to the DOM
}
// Get size of drop down, and determine if vertical scroll bar needed. If no scroll bar needed,
// use overflow:visible rather than overflow:hidden so off-by-one errors don't hide drop down border.
var mb = domGeometry.getMarginSize(ddNode);
var overHeight = (maxHeight && mb.h > maxHeight);
domStyle.set(ddNode, {
overflowX: "visible",
overflowY: overHeight ? "auto" : "visible"
});
if(overHeight){
mb.h = maxHeight;
if("w" in mb){
mb.w += 16; // room for vertical scrollbar
}
}else{
delete mb.h;
}
// Adjust dropdown width to match or be larger than my width
if(this.forceWidth){
mb.w = aroundNode.offsetWidth;
}else if(this.autoWidth){
mb.w = Math.max(mb.w, aroundNode.offsetWidth);
}else{
delete mb.w;
}
// And finally, resize the dropdown to calculated height and width
if(lang.isFunction(dropDown.resize)){
dropDown.resize(mb);
}else{
domGeometry.setMarginBox(ddNode, mb);
}
}
var retVal = popup.open({
parent: this,
popup: dropDown,
around: aroundNode,
orient: this.dropDownPosition,
onExecute: function(){
self.closeDropDown(true);
},
onCancel: function(){
self.closeDropDown(true);
},
onClose: function(){
domAttr.set(self._popupStateNode, "popupActive", false);
domClass.remove(self._popupStateNode, "dijitHasDropDownOpen");
self._set("_opened", false); // use set() because _CssStateMixin is watching
}
});
domAttr.set(this._popupStateNode, "popupActive", "true");
domClass.add(this._popupStateNode, "dijitHasDropDownOpen");
this._set("_opened", true); // use set() because _CssStateMixin is watching
this.domNode.setAttribute("aria-expanded", "true");
return retVal;
},
closeDropDown: function(/*Boolean*/ focus){
// summary:
// Closes the drop down on this widget
// focus:
// If true, refocuses the button widget
// tags:
// protected
if(this._focusDropDownTimer){
this._focusDropDownTimer.remove();
delete this._focusDropDownTimer;
}
if(this._opened){
this.domNode.setAttribute("aria-expanded", "false");
if(focus){ this.focus(); }
popup.close(this.dropDown);
this._opened = false;
}
}
});
});
},
'dijit/tree/TreeStoreModel':function(){
define("dijit/tree/TreeStoreModel", [
"dojo/_base/array", // array.filter array.forEach array.indexOf array.some
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/_base/lang" // lang.hitch
], function(array, aspect, declare, lang){
// module:
// dijit/tree/TreeStoreModel
return declare("dijit.tree.TreeStoreModel", null, {
// summary:
// Implements dijit/Tree/model connecting to a dojo.data store with a single
// root item. Any methods passed into the constructor will override
// the ones defined here.
// store: dojo/data/api/Read
// Underlying store
store: null,
// childrenAttrs: String[]
// One or more attribute names (attributes in the dojo.data item) that specify that item's children
childrenAttrs: ["children"],
// newItemIdAttr: String
// Name of attribute in the Object passed to newItem() that specifies the id.
//
// If newItemIdAttr is set then it's used when newItem() is called to see if an
// item with the same id already exists, and if so just links to the old item
// (so that the old item ends up with two parents).
//
// Setting this to null or "" will make every drop create a new item.
newItemIdAttr: "id",
// labelAttr: String
// If specified, get label for tree node from this attribute, rather
// than by calling store.getLabel()
labelAttr: "",
// root: [readonly] dojo/data/Item
// Pointer to the root item (read only, not a parameter)
root: null,
// query: anything
// Specifies datastore query to return the root item for the tree.
// Must only return a single item. Alternately can just pass in pointer
// to root item.
// example:
// | {id:'ROOT'}
query: null,
// deferItemLoadingUntilExpand: Boolean
// Setting this to true will cause the TreeStoreModel to defer calling loadItem on nodes
// until they are expanded. This allows for lazying loading where only one
// loadItem (and generally one network call, consequently) per expansion
// (rather than one for each child).
// This relies on partial loading of the children items; each children item of a
// fully loaded item should contain the label and info about having children.
deferItemLoadingUntilExpand: false,
constructor: function(/* Object */ args){
// summary:
// Passed the arguments listed above (store, etc)
// tags:
// private
lang.mixin(this, args);
this.connects = [];
var store = this.store;
if(!store.getFeatures()['dojo.data.api.Identity']){
throw new Error("dijit.tree.TreeStoreModel: store must support dojo.data.Identity");
}
// if the store supports Notification, subscribe to the notification events
if(store.getFeatures()['dojo.data.api.Notification']){
this.connects = this.connects.concat([
aspect.after(store, "onNew", lang.hitch(this, "onNewItem"), true),
aspect.after(store, "onDelete", lang.hitch(this, "onDeleteItem"), true),
aspect.after(store, "onSet", lang.hitch(this, "onSetItem"), true)
]);
}
},
destroy: function(){
var h;
while(h = this.connects.pop()){ h.remove(); }
// TODO: should cancel any in-progress processing of getRoot(), getChildren()
},
// =======================================================================
// Methods for traversing hierarchy
getRoot: function(onItem, onError){
// summary:
// Calls onItem with the root item for the tree, possibly a fabricated item.
// Calls onError on error.
if(this.root){
onItem(this.root);
}else{
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(items){
if(items.length != 1){
throw new Error("dijit.tree.TreeStoreModel: root query returned " + items.length +
" items, but must return exactly one");
}
this.root = items[0];
onItem(this.root);
}),
onError: onError
});
}
},
mayHaveChildren: function(/*dojo/data/Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
return array.some(this.childrenAttrs, function(attr){
return this.store.hasAttribute(item, attr);
}, this);
},
getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ onComplete, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
var store = this.store;
if(!store.isItemLoaded(parentItem)){
// The parent is not loaded yet, we must be in deferItemLoadingUntilExpand
// mode, so we will load it and just return the children (without loading each
// child item)
var getChildren = lang.hitch(this, arguments.callee);
store.loadItem({
item: parentItem,
onItem: function(parentItem){
getChildren(parentItem, onComplete, onError);
},
onError: onError
});
return;
}
// get children of specified item
var childItems = [];
for(var i=0; i<this.childrenAttrs.length; i++){
var vals = store.getValues(parentItem, this.childrenAttrs[i]);
childItems = childItems.concat(vals);
}
// count how many items need to be loaded
var _waitCount = 0;
if(!this.deferItemLoadingUntilExpand){
array.forEach(childItems, function(item){ if(!store.isItemLoaded(item)){ _waitCount++; } });
}
if(_waitCount == 0){
// all items are already loaded (or we aren't loading them). proceed...
onComplete(childItems);
}else{
// still waiting for some or all of the items to load
array.forEach(childItems, function(item, idx){
if(!store.isItemLoaded(item)){
store.loadItem({
item: item,
onItem: function(item){
childItems[idx] = item;
if(--_waitCount == 0){
// all nodes have been loaded, send them to the tree
onComplete(childItems);
}
},
onError: onError
});
}
});
}
},
// =======================================================================
// Inspecting items
isItem: function(/* anything */ something){
return this.store.isItem(something); // Boolean
},
fetchItemByIdentity: function(/* object */ keywordArgs){
this.store.fetchItemByIdentity(keywordArgs);
},
getIdentity: function(/* item */ item){
return this.store.getIdentity(item); // Object
},
getLabel: function(/*dojo/data/Item*/ item){
// summary:
// Get the label for an item
if(this.labelAttr){
return this.store.getValue(item,this.labelAttr); // String
}else{
return this.store.getLabel(item); // String
}
},
// =======================================================================
// Write interface
newItem: function(/* dijit/tree/dndSource.__Item */ args, /*dojo/data/api/Item*/ parent, /*int?*/ insertIndex){
// summary:
// Creates a new item. See `dojo/data/api/Write` for details on args.
// Used in drag & drop when item from external source dropped onto tree.
// description:
// Developers will need to override this method if new items get added
// to parents with multiple children attributes, in order to define which
// children attribute points to the new item.
var pInfo = {parent: parent, attribute: this.childrenAttrs[0]}, LnewItem;
if(this.newItemIdAttr && args[this.newItemIdAttr]){
// Maybe there's already a corresponding item in the store; if so, reuse it.
this.fetchItemByIdentity({identity: args[this.newItemIdAttr], scope: this, onItem: function(item){
if(item){
// There's already a matching item in store, use it
this.pasteItem(item, null, parent, true, insertIndex);
}else{
// Create new item in the tree, based on the drag source.
LnewItem=this.store.newItem(args, pInfo);
if(LnewItem && (insertIndex!=undefined)){
// Move new item to desired position
this.pasteItem(LnewItem, parent, parent, false, insertIndex);
}
}
}});
}else{
// [as far as we know] there is no id so we must assume this is a new item
LnewItem=this.store.newItem(args, pInfo);
if(LnewItem && (insertIndex!=undefined)){
// Move new item to desired position
this.pasteItem(LnewItem, parent, parent, false, insertIndex);
}
}
},
pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
// summary:
// Move or copy an item from one parent item to another.
// Used in drag & drop
var store = this.store,
parentAttr = this.childrenAttrs[0]; // name of "children" attr in parent item
// remove child from source item, and record the attribute that child occurred in
if(oldParentItem){
array.forEach(this.childrenAttrs, function(attr){
if(store.containsValue(oldParentItem, attr, childItem)){
if(!bCopy){
var values = array.filter(store.getValues(oldParentItem, attr), function(x){
return x != childItem;
});
store.setValues(oldParentItem, attr, values);
}
parentAttr = attr;
}
});
}
// modify target item's children attribute to include this item
if(newParentItem){
if(typeof insertIndex == "number"){
// call slice() to avoid modifying the original array, confusing the data store
var childItems = store.getValues(newParentItem, parentAttr).slice();
childItems.splice(insertIndex, 0, childItem);
store.setValues(newParentItem, parentAttr, childItems);
}else{
store.setValues(newParentItem, parentAttr,
store.getValues(newParentItem, parentAttr).concat(childItem));
}
}
},
// =======================================================================
// Callbacks
onChange: function(/*dojo/data/Item*/ /*===== item =====*/){
// summary:
// Callback whenever an item has changed, so that Tree
// can update the label, icon, etc. Note that changes
// to an item's children or parent(s) will trigger an
// onChildrenChange() so you can ignore those changes here.
// tags:
// callback
},
onChildrenChange: function(/*===== parent, newChildrenList =====*/){
// summary:
// Callback to do notifications about new, updated, or deleted items.
// parent: dojo/data/Item
// newChildrenList: dojo/data/Item[]
// tags:
// callback
},
onDelete: function(/*dojo/data/Item*/ /*===== item =====*/){
// summary:
// Callback when an item has been deleted.
// description:
// Note that there will also be an onChildrenChange() callback for the parent
// of this item.
// tags:
// callback
},
// =======================================================================
// Events from data store
onNewItem: function(/* dojo/data/Item */ item, /* Object */ parentInfo){
// summary:
// Handler for when new items appear in the store, either from a drop operation
// or some other way. Updates the tree view (if necessary).
// description:
// If the new item is a child of an existing item,
// calls onChildrenChange() with the new list of children
// for that existing item.
//
// tags:
// extension
// We only care about the new item if it has a parent that corresponds to a TreeNode
// we are currently displaying
if(!parentInfo){
return;
}
// Call onChildrenChange() on parent (ie, existing) item with new list of children
// In the common case, the new list of children is simply parentInfo.newValue or
// [ parentInfo.newValue ], although if items in the store has multiple
// child attributes (see `childrenAttr`), then it's a superset of parentInfo.newValue,
// so call getChildren() to be sure to get right answer.
this.getChildren(parentInfo.item, lang.hitch(this, function(children){
this.onChildrenChange(parentInfo.item, children);
}));
},
onDeleteItem: function(/*Object*/ item){
// summary:
// Handler for delete notifications from underlying store
this.onDelete(item);
},
onSetItem: function(item, attribute /*===== , oldValue, newValue =====*/){
// summary:
// Updates the tree view according to changes in the data store.
// description:
// Handles updates to an item's children by calling onChildrenChange(), and
// other updates to an item by calling onChange().
//
// See `onNewItem` for more details on handling updates to an item's children.
// item: Item
// attribute: attribute-name-string
// oldValue: Object|Array
// newValue: Object|Array
// tags:
// extension
if(array.indexOf(this.childrenAttrs, attribute) != -1){
// item's children list changed
this.getChildren(item, lang.hitch(this, function(children){
// See comments in onNewItem() about calling getChildren()
this.onChildrenChange(item, children);
}));
}else{
// item's label/icon/etc. changed.
this.onChange(item);
}
}
});
});
},
'dijit/_MenuBase':function(){
define("dijit/_MenuBase", [
"dojo/_base/array", // array.indexOf
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant domClass.replace
"dojo/dom-attr",
"dojo/dom-class", // domClass.replace
"dojo/_base/lang", // lang.hitch
"dojo/mouse", // mouse.enter, mouse.leave
"dojo/on",
"dojo/window",
"./a11yclick",
"./popup",
"./registry",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(array, declare, dom, domAttr, domClass, lang, mouse, on, winUtils,
a11yclick, pm, registry, _Widget, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/_MenuBase
return declare("dijit._MenuBase",
[_Widget, _TemplatedMixin, _KeyNavContainer],
{
// summary:
// Base class for Menu and MenuBar
// parentMenu: [readonly] Widget
// pointer to menu that displayed me
parentMenu: null,
// popupDelay: Integer
// number of milliseconds before hovering (without clicking) causes the popup to automatically open.
popupDelay: 500,
// autoFocus: Boolean
// A toggle to control whether or not a Menu gets focused when opened as a drop down from a MenuBar
// or DropDownButton/ComboButton. Note though that it always get focused when opened via the keyboard.
autoFocus: false,
childSelector: function(/*DOMNode*/ node){
// summary:
// Selector (passed to on.selector()) used to identify MenuItem child widgets, but exclude inert children
// like MenuSeparator. If subclass overrides to a string (ex: "> *"), the subclass must require dojo/query.
// tags:
// protected
var widget = registry.byNode(node);
return node.parentNode == this.containerNode && widget && widget.focus;
},
postCreate: function(){
var self = this,
matches = typeof this.childSelector == "string" ? this.childSelector : lang.hitch(this, "childSelector");
this.own(
on(this.containerNode, on.selector(matches, mouse.enter), function(){
self.onItemHover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, mouse.leave), function(){
self.onItemUnhover(registry.byNode(this));
}),
on(this.containerNode, on.selector(matches, a11yclick), function(evt){
self.onItemClick(registry.byNode(this), evt);
evt.stopPropagation();
evt.preventDefault();
})
);
this.inherited(arguments);
},
onExecute: function(){
// summary:
// Attach point for notification about when a menu item has been executed.
// This is an internal mechanism used for Menus to signal to their parent to
// close them, because they are about to execute the onClick handler. In
// general developers should not attach to or override this method.
// tags:
// protected
},
onCancel: function(/*Boolean*/ /*===== closeAll =====*/){
// summary:
// Attach point for notification about when the user cancels the current menu
// This is an internal mechanism used for Menus to signal to their parent to
// close them. In general developers should not attach to or override this method.
// tags:
// protected
},
_moveToPopup: function(/*Event*/ evt){
// summary:
// This handles the right arrow key (left arrow key on RTL systems),
// which will either open a submenu, or move to the next item in the
// ancestor MenuBar
// tags:
// private
if(this.focusedChild && this.focusedChild.popup && !this.focusedChild.disabled){
this.onItemClick(this.focusedChild, evt);
}else{
var topMenu = this._getTopMenu();
if(topMenu && topMenu._isMenuBar){
topMenu.focusNext();
}
}
},
_onPopupHover: function(/*Event*/ /*===== evt =====*/){
// summary:
// This handler is called when the mouse moves over the popup.
// tags:
// private
// if the mouse hovers over a menu popup that is in pending-close state,
// then stop the close operation.
// This can't be done in onItemHover since some popup targets don't have MenuItems (e.g. ColorPicker)
if(this.currentPopup && this.currentPopup._pendingClose_timer){
var parentMenu = this.currentPopup.parentMenu;
// highlight the parent menu item pointing to this popup
if(parentMenu.focusedChild){
parentMenu.focusedChild._setSelected(false);
}
parentMenu.focusedChild = this.currentPopup.from_item;
parentMenu.focusedChild._setSelected(true);
// cancel the pending close
this._stopPendingCloseTimer(this.currentPopup);
}
},
onItemHover: function(/*MenuItem*/ item){
// summary:
// Called when cursor is over a MenuItem.
// tags:
// protected
// Don't do anything unless user has "activated" the menu by:
// 1) clicking it
// 2) opening it from a parent menu (which automatically focuses it)
if(this.isActive){
this.focusChild(item);
if(this.focusedChild.popup && !this.focusedChild.disabled && !this.hover_timer){
this.hover_timer = this.defer("_openPopup", this.popupDelay);
}
}
// if the user is mixing mouse and keyboard navigation,
// then the menu may not be active but a menu item has focus,
// but it's not the item that the mouse just hovered over.
// To avoid both keyboard and mouse selections, use the latest.
if(this.focusedChild){
this.focusChild(item);
}
this._hoveredChild = item;
item._set("hovering", true);
},
_onChildBlur: function(item){
// summary:
// Called when a child MenuItem becomes inactive because focus
// has been removed from the MenuItem *and* it's descendant menus.
// tags:
// private
this._stopPopupTimer();
item._setSelected(false);
// Close all popups that are open and descendants of this menu
var itemPopup = item.popup;
if(itemPopup){
this._stopPendingCloseTimer(itemPopup);
itemPopup._pendingClose_timer = this.defer(function(){
itemPopup._pendingClose_timer = null;
if(itemPopup.parentMenu){
itemPopup.parentMenu.currentPopup = null;
}
pm.close(itemPopup); // this calls onClose
}, this.popupDelay);
}
},
onItemUnhover: function(/*MenuItem*/ item){
// summary:
// Callback fires when mouse exits a MenuItem
// tags:
// protected
if(this.isActive){
this._stopPopupTimer();
}
if(this._hoveredChild == item){ this._hoveredChild = null; }
item._set("hovering", false);
},
_stopPopupTimer: function(){
// summary:
// Cancels the popup timer because the user has stop hovering
// on the MenuItem, etc.
// tags:
// private
if(this.hover_timer){
this.hover_timer = this.hover_timer.remove();
}
},
_stopPendingCloseTimer: function(/*dijit/_WidgetBase*/ popup){
// summary:
// Cancels the pending-close timer because the close has been preempted
// tags:
// private
if(popup._pendingClose_timer){
popup._pendingClose_timer = popup._pendingClose_timer.remove();
}
},
_stopFocusTimer: function(){
// summary:
// Cancels the pending-focus timer because the menu was closed before focus occured
// tags:
// private
if(this._focus_timer){
this._focus_timer = this._focus_timer.remove();
}
},
_getTopMenu: function(){
// summary:
// Returns the top menu in this chain of Menus
// tags:
// private
for(var top=this; top.parentMenu; top=top.parentMenu);
return top;
},
onItemClick: function(/*dijit/_WidgetBase*/ item, /*Event*/ evt){
// summary:
// Handle clicks on an item.
// tags:
// private
// this can't be done in _onFocus since the _onFocus events occurs asynchronously
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu
this._markActive();
}
this.focusChild(item);
if(item.disabled){ return false; }
if(item.popup){
this._openPopup(evt.type == "keypress");
}else{
// before calling user defined handler, close hierarchy of menus
// and restore focus to place it was when menu was opened
this.onExecute();
// user defined handler for click
item._onClick ? item._onClick(evt) : item.onClick(evt);
}
},
_openPopup: function(/*Boolean*/ focus){
// summary:
// Open the popup to the side of/underneath the current menu item, and optionally focus first item
// tags:
// protected
this._stopPopupTimer();
var from_item = this.focusedChild;
if(!from_item){ return; } // the focused child lost focus since the timer was started
var popup = from_item.popup;
if(!popup.isShowingNow){
if(this.currentPopup){
this._stopPendingCloseTimer(this.currentPopup);
pm.close(this.currentPopup);
}
popup.parentMenu = this;
popup.from_item = from_item; // helps finding the parent item that should be focused for this popup
var self = this;
pm.open({
parent: this,
popup: popup,
around: from_item.domNode,
orient: this._orient || ["after", "before"],
onCancel: function(){ // called when the child menu is canceled
// set isActive=false (_closeChild vs _cleanUp) so that subsequent hovering will NOT open child menus
// which seems aligned with the UX of most applications (e.g. notepad, wordpad, paint shop pro)
self.focusChild(from_item); // put focus back on my node
self._cleanUp(); // close the submenu (be sure this is done _after_ focus is moved)
from_item._setSelected(true); // oops, _cleanUp() deselected the item
self.focusedChild = from_item; // and unset focusedChild
},
onExecute: lang.hitch(this, "_cleanUp")
});
this.currentPopup = popup;
// detect mouseovers to handle lazy mouse movements that temporarily focus other menu items
popup.connect(popup.domNode, "onmouseenter", lang.hitch(self, "_onPopupHover")); // cleaned up when the popped-up widget is destroyed on close
}
if(focus && popup.focus){
// If user is opening the popup via keyboard (right arrow, or down arrow for MenuBar), then focus the popup.
// If the cursor happens to collide with the popup, it will generate an onmouseover event
// even though the mouse wasn't moved. Use defer() to call popup.focus so that
// our focus() call overrides the onmouseover event, rather than vice-versa. (#8742)
popup._focus_timer = this.defer(lang.hitch(popup, function(){
this._focus_timer = null;
this.focus();
}));
}
},
_markActive: function(){
// summary:
// Mark this menu's state as active.
// Called when this Menu gets focus from:
//
// 1. clicking it (mouse or via space/arrow key)
// 2. being opened by a parent menu.
//
// This is not called just from mouse hover.
// Focusing a menu via TAB does NOT automatically set isActive
// since TAB is a navigation operation and not a selection one.
// For Windows apps, pressing the ALT key focuses the menubar
// menus (similar to TAB navigation) but the menu is not active
// (ie no dropdown) until an item is clicked.
this.isActive = true;
domClass.replace(this.domNode, "dijitMenuActive", "dijitMenuPassive");
},
onOpen: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback when this menu is opened.
// This is called by the popup manager as notification that the menu
// was opened.
// tags:
// private
this.isShowingNow = true;
this._markActive();
},
_markInactive: function(){
// summary:
// Mark this menu's state as inactive.
this.isActive = false; // don't do this in _onBlur since the state is pending-close until we get here
domClass.replace(this.domNode, "dijitMenuPassive", "dijitMenuActive");
},
onClose: function(){
// summary:
// Callback when this menu is closed.
// This is called by the popup manager as notification that the menu
// was closed.
// tags:
// private
this._stopFocusTimer();
this._markInactive();
this.isShowingNow = false;
this.parentMenu = null;
},
_closeChild: function(){
// summary:
// Called when submenu is clicked or focus is lost. Close hierarchy of menus.
// tags:
// private
this._stopPopupTimer();
if(this.currentPopup){
// If focus is on a descendant MenuItem then move focus to me,
// because IE doesn't like it when you display:none a node with focus,
// and also so keyboard users don't lose control.
// Likely, immediately after a user defined onClick handler will move focus somewhere
// else, like a Dialog.
if(array.indexOf(this._focusManager.activeStack, this.id) >= 0){
domAttr.set(this.focusedChild.focusNode, "tabIndex", this.tabIndex);
this.focusedChild.focusNode.focus();
}
// Close all popups that are open and descendants of this menu
pm.close(this.currentPopup);
this.currentPopup = null;
}
if(this.focusedChild){ // unhighlight the focused item
this.focusedChild._setSelected(false);
this.onItemUnhover(this.focusedChild);
this.focusedChild = null;
}
},
_onItemFocus: function(/*MenuItem*/ item){
// summary:
// Called when child of this Menu gets focus from:
//
// 1. clicking it
// 2. tabbing into it
// 3. being opened by a parent menu.
//
// This is not called just from mouse hover.
if(this._hoveredChild && this._hoveredChild != item){
this.onItemUnhover(this._hoveredChild); // any previous mouse movement is trumped by focus selection
}
},
_onBlur: function(){
// summary:
// Called when focus is moved away from this Menu and it's submenus.
// tags:
// protected
this._cleanUp();
this.inherited(arguments);
},
_cleanUp: function(){
// summary:
// Called when the user is done with this menu. Closes hierarchy of menus.
// tags:
// private
this._closeChild(); // don't call this.onClose since that's incorrect for MenuBar's that never close
if(typeof this.isShowingNow == 'undefined'){ // non-popup menu doesn't call onClose
this._markInactive();
}
}
});
});
},
'dijit/focus':function(){
define("dijit/focus", [
"dojo/aspect",
"dojo/_base/declare", // declare
"dojo/dom", // domAttr.get dom.isDescendant
"dojo/dom-attr", // domAttr.get dom.isDescendant
"dojo/dom-construct", // connect to domConstruct.empty, domConstruct.destroy
"dojo/Evented",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/ready",
"dojo/sniff", // has("ie")
"dojo/Stateful",
"dojo/_base/unload", // unload.addOnWindowUnload
"dojo/_base/window", // win.body
"dojo/window", // winUtils.get
"./a11y", // a11y.isTabNavigable
"./registry", // registry.byId
"./main" // to set dijit.focus
], function(aspect, declare, dom, domAttr, domConstruct, Evented, lang, on, ready, has, Stateful, unload, win, winUtils,
a11y, registry, dijit){
// module:
// dijit/focus
var FocusManager = declare([Stateful, Evented], {
// summary:
// Tracks the currently focused node, and which widgets are currently "active".
// Access via require(["dijit/focus"], function(focus){ ... }).
//
// A widget is considered active if it or a descendant widget has focus,
// or if a non-focusable node of this widget or a descendant was recently clicked.
//
// Call focus.watch("curNode", callback) to track the current focused DOMNode,
// or focus.watch("activeStack", callback) to track the currently focused stack of widgets.
//
// Call focus.on("widget-blur", func) or focus.on("widget-focus", ...) to monitor when
// when widgets become active/inactive
//
// Finally, focus(node) will focus a node, suppressing errors if the node doesn't exist.
// curNode: DomNode
// Currently focused item on screen
curNode: null,
// activeStack: dijit/_WidgetBase[]
// List of currently active widgets (focused widget and it's ancestors)
activeStack: [],
constructor: function(){
// Don't leave curNode/prevNode pointing to bogus elements
var check = lang.hitch(this, function(node){
if(dom.isDescendant(this.curNode, node)){
this.set("curNode", null);
}
if(dom.isDescendant(this.prevNode, node)){
this.set("prevNode", null);
}
});
aspect.before(domConstruct, "empty", check);
aspect.before(domConstruct, "destroy", check);
},
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 with remove() method to deregister.
return this.registerWin(iframe.contentWindow, iframe);
},
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 with remove() method to deregister.
// TODO: make this function private in 2.0; Editor/users should call registerIframe(),
var _this = this;
var mousedownListener = function(evt){
_this._justMouseDowned = true;
setTimeout(function(){ _this._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(has("ie") && evt && evt.srcElement && evt.srcElement.parentNode == null){
return;
}
_this._onTouchNode(effectiveNode || evt.target || evt.srcElement, "mouse");
};
// Listen for blur and focus events on targetWindow's document.
// Using attachEvent()/addEventListener() rather than on() to try to catch mouseDown events even
// if other code calls evt.stopPropagation(). But rethink for 2.0 since that doesn't work for attachEvent(),
// which watches events at the bubbling phase rather than capturing phase, like addEventListener(..., false).
// 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 = has("ie") ? targetWindow.document.documentElement : targetWindow.document;
if(doc){
if(has("ie")){
targetWindow.document.body.attachEvent('onmousedown', mousedownListener);
var focusinListener = function(evt){
// IE reports that nodes like <body> have gotten focus, even though they have tabIndex=-1,
// ignore those events
var tag = evt.srcElement.tagName.toLowerCase();
if(tag == "#document" || tag == "body"){ return; }
// Previous code called _onTouchNode() for any activate event on a non-focusable node. Can
// probably just ignore such an event as it will be handled by onmousedown handler above, but
// leaving the code for now.
if(a11y.isTabNavigable(evt.srcElement)){
_this._onFocusNode(effectiveNode || evt.srcElement);
}else{
_this._onTouchNode(effectiveNode || evt.srcElement);
}
};
doc.attachEvent('onfocusin', focusinListener);
var focusoutListener = function(evt){
_this._onBlurNode(effectiveNode || evt.srcElement);
};
doc.attachEvent('onfocusout', focusoutListener);
return {
remove: function(){
targetWindow.document.detachEvent('onmousedown', mousedownListener);
doc.detachEvent('onfocusin', focusinListener);
doc.detachEvent('onfocusout', focusoutListener);
doc = null; // prevent memory leak (apparent circular reference via closure)
}
};
}else{
doc.body.addEventListener('mousedown', mousedownListener, true);
doc.body.addEventListener('touchstart', mousedownListener, true);
var focusListener = function(evt){
_this._onFocusNode(effectiveNode || evt.target);
};
doc.addEventListener('focus', focusListener, true);
var blurListener = function(evt){
_this._onBlurNode(effectiveNode || evt.target);
};
doc.addEventListener('blur', blurListener, true);
return {
remove: function(){
doc.body.removeEventListener('mousedown', mousedownListener, true);
doc.body.removeEventListener('touchstart', mousedownListener, true);
doc.removeEventListener('focus', focusListener, true);
doc.removeEventListener('blur', blurListener, true);
doc = null; // prevent memory leak (apparent circular reference via closure)
}
};
}
}
},
_onBlurNode: function(/*DomNode*/ node){
// summary:
// Called when focus leaves a node.
// Usually ignored, _unless_ it *isn't* followed by touching another node,
// which indicates that we tabbed off the last field on the page,
// in which case every widget is marked inactive
// If the blur event isn't followed by a focus event, it means the user clicked on something unfocusable,
// so clear focus.
if(this._clearFocusTimer){
clearTimeout(this._clearFocusTimer);
}
this._clearFocusTimer = setTimeout(lang.hitch(this, function(){
this.set("prevNode", this.curNode);
this.set("curNode", null);
}), 0);
if(this._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 or touch event then mark all widgets as inactive.
if(this._clearActiveWidgetsTimer){
clearTimeout(this._clearActiveWidgetsTimer);
}
this._clearActiveWidgetsTimer = setTimeout(lang.hitch(this, function(){
delete this._clearActiveWidgetsTimer;
this._setStack([]);
}), 0);
},
_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(this._clearActiveWidgetsTimer){
clearTimeout(this._clearActiveWidgetsTimer);
delete this._clearActiveWidgetsTimer;
}
// compute stack of active widgets (ex: ComboButton --> Menu --> MenuItem)
var newStack=[];
try{
while(node){
var popupParent = domAttr.get(node, "dijitPopupParent");
if(popupParent){
node=registry.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 === win.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=winUtils.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 && registry.byId(id);
if(widget && !(by == "mouse" && widget.get("disabled"))){
newStack.unshift(id);
}
node=node.parentNode;
}
}
}catch(e){ /* squelch */ }
this._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;
}
// There was probably a blur event right before this event, but since we have a new focus, don't
// do anything with the blur
if(this._clearFocusTimer){
clearTimeout(this._clearFocusTimer);
delete this._clearFocusTimer;
}
this._onTouchNode(node);
if(node == this.curNode){ return; }
this.set("prevNode", this.curNode);
this.set("curNode", 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 = this.activeStack;
this.set("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, set focused=false
for(var i=oldStack.length-1; i>=nCommon; i--){
widget = registry.byId(oldStack[i]);
if(widget){
widget._hasBeenBlurred = true; // TODO: used by form widgets, should be moved there
widget.set("focused", false);
if(widget._focusManager == this){
widget._onBlur(by);
}
this.emit("widget-blur", widget, by);
}
}
// for all element that have come into focus, set focused=true
for(i=nCommon; i<newStack.length; i++){
widget = registry.byId(newStack[i]);
if(widget){
widget.set("focused", true);
if(widget._focusManager == this){
widget._onFocus(by);
}
this.emit("widget-focus", widget, by);
}
}
},
focus: function(node){
// summary:
// Focus the specified node, suppressing errors if they occur
if(node){
try{ node.focus(); }catch(e){/*quiet*/}
}
}
});
var singleton = new FocusManager();
// register top window and all the iframes it contains
ready(function(){
var handle = singleton.registerWin(winUtils.get(win.doc));
if(has("ie")){
unload.addOnWindowUnload(function(){
if(handle){ // because this gets called twice when doh.robot is running
handle.remove();
handle = null;
}
});
}
});
// Setup dijit.focus as a pointer to the singleton but also (for backwards compatibility)
// as a function to set focus. Remove for 2.0.
dijit.focus = function(node){
singleton.focus(node); // indirection here allows dijit/_base/focus.js to override behavior
};
for(var attr in singleton){
if(!/^_/.test(attr)){
dijit.focus[attr] = typeof singleton[attr] == "function" ? lang.hitch(singleton, attr) : singleton[attr];
}
}
singleton.watch(function(attr, oldVal, newVal){
dijit.focus[attr] = newVal;
});
return singleton;
});
},
'dojo/i18n':function(){
define("dojo/i18n", ["./_base/kernel", "require", "./has", "./_base/array", "./_base/config", "./_base/lang", "./_base/xhr", "./json", "module"],
function(dojo, require, has, array, config, lang, xhr, json, module){
// module:
// dojo/i18n
has.add("dojo-preload-i18n-Api",
// if true, define the preload localizations machinery
1
);
1 || has.add("dojo-v1x-i18n-Api",
// if true, define the v1.x i18n functions
1
);
var
thisModule = dojo.i18n =
{
// summary:
// This module implements the dojo/i18n! plugin and the v1.6- i18n API
// description:
// We choose to include our own plugin to leverage functionality already contained in dojo
// and thereby reduce the size of the plugin compared to various loader implementations. Also, this
// allows foreign AMD loaders to be used without their plugins.
},
nlsRe =
// regexp for reconstructing the master bundle name from parts of the regexp match
// nlsRe.exec("foo/bar/baz/nls/en-ca/foo") gives:
// ["foo/bar/baz/nls/en-ca/foo", "foo/bar/baz/nls/", "/", "/", "en-ca", "foo"]
// nlsRe.exec("foo/bar/baz/nls/foo") gives:
// ["foo/bar/baz/nls/foo", "foo/bar/baz/nls/", "/", "/", "foo", ""]
// so, if match[5] is blank, it means this is the top bundle definition.
// courtesy of http://requirejs.org
/(^.*(^|\/)nls)(\/|$)([^\/]*)\/?([^\/]*)/,
getAvailableLocales = function(
root,
locale,
bundlePath,
bundleName
){
// summary:
// return a vector of module ids containing all available locales with respect to the target locale
// For example, assuming:
//
// - the root bundle indicates specific bundles for "fr" and "fr-ca",
// - bundlePath is "myPackage/nls"
// - bundleName is "myBundle"
//
// Then a locale argument of "fr-ca" would return
//
// ["myPackage/nls/myBundle", "myPackage/nls/fr/myBundle", "myPackage/nls/fr-ca/myBundle"]
//
// Notice that bundles are returned least-specific to most-specific, starting with the root.
//
// If root===false indicates we're working with a pre-AMD i18n bundle that doesn't tell about the available locales;
// therefore, assume everything is available and get 404 errors that indicate a particular localization is not available
for(var result = [bundlePath + bundleName], localeParts = locale.split("-"), current = "", i = 0; i<localeParts.length; i++){
current += (current ? "-" : "") + localeParts[i];
if(!root || root[current]){
result.push(bundlePath + current + "/" + bundleName);
}
}
return result;
},
cache = {},
getBundleName = function(moduleName, bundleName, locale){
locale = locale ? locale.toLowerCase() : dojo.locale;
moduleName = moduleName.replace(/\./g, "/");
bundleName = bundleName.replace(/\./g, "/");
return (/root/i.test(locale)) ?
(moduleName + "/nls/" + bundleName) :
(moduleName + "/nls/" + locale + "/" + bundleName);
},
getL10nName = dojo.getL10nName = function(moduleName, bundleName, locale){
return moduleName = module.id + "!" + getBundleName(moduleName, bundleName, locale);
},
doLoad = function(require, bundlePathAndName, bundlePath, bundleName, locale, load){
// summary:
// get the root bundle which instructs which other bundles are required to construct the localized bundle
require([bundlePathAndName], function(root){
var current = lang.clone(root.root),
availableLocales = getAvailableLocales(!root._v1x && root, locale, bundlePath, bundleName);
require(availableLocales, function(){
for (var i = 1; i<availableLocales.length; i++){
current = lang.mixin(lang.clone(current), arguments[i]);
}
// target may not have been resolve (e.g., maybe only "fr" exists when "fr-ca" was requested)
var target = bundlePathAndName + "/" + locale;
cache[target] = current;
load();
});
});
},
normalize = function(id, toAbsMid){
// summary:
// id may be relative.
// preload has form `*preload*<path>/nls/<module>*<flattened locales>` and
// therefore never looks like a relative
return /^\./.test(id) ? toAbsMid(id) : id;
},
getLocalesToLoad = function(targetLocale){
var list = config.extraLocale || [];
list = lang.isArray(list) ? list : [list];
list.push(targetLocale);
return list;
},
load = function(id, require, load){
// summary:
// id is in one of the following formats
//
// 1. <path>/nls/<bundle>
// => load the bundle, localized to config.locale; load all bundles localized to
// config.extraLocale (if any); return the loaded bundle localized to config.locale.
//
// 2. <path>/nls/<locale>/<bundle>
// => load then return the bundle localized to <locale>
//
// 3. *preload*<path>/nls/<module>*<JSON array of available locales>
// => for config.locale and all config.extraLocale, load all bundles found
// in the best-matching bundle rollup. A value of 1 is returned, which
// is meaningless other than to say the plugin is executing the requested
// preloads
//
// In cases 1 and 2, <path> is always normalized to an absolute module id upon entry; see
// normalize. In case 3, it <path> is assumed to be absolute; this is arranged by the builder.
//
// To load a bundle means to insert the bundle into the plugin's cache and publish the bundle
// value to the loader. Given <path>, <bundle>, and a particular <locale>, the cache key
//
// <path>/nls/<bundle>/<locale>
//
// will hold the value. Similarly, then plugin will publish this value to the loader by
//
// define("<path>/nls/<bundle>/<locale>", <bundle-value>);
//
// Given this algorithm, other machinery can provide fast load paths be preplacing
// values in the plugin's cache, which is public. When a load is demanded the
// cache is inspected before starting any loading. Explicitly placing values in the plugin
// cache is an advanced/experimental feature that should not be needed; use at your own risk.
//
// For the normal AMD algorithm, the root bundle is loaded first, which instructs the
// plugin what additional localized bundles are required for a particular locale. These
// additional locales are loaded and a mix of the root and each progressively-specific
// locale is returned. For example:
//
// 1. The client demands "dojo/i18n!some/path/nls/someBundle
//
// 2. The loader demands load(some/path/nls/someBundle)
//
// 3. This plugin require's "some/path/nls/someBundle", which is the root bundle.
//
// 4. Assuming config.locale is "ab-cd-ef" and the root bundle indicates that localizations
// are available for "ab" and "ab-cd-ef" (note the missing "ab-cd", then the plugin
// requires "some/path/nls/ab/someBundle" and "some/path/nls/ab-cd-ef/someBundle"
//
// 5. Upon receiving all required bundles, the plugin constructs the value of the bundle
// ab-cd-ef as...
//
// mixin(mixin(mixin({}, require("some/path/nls/someBundle"),
// require("some/path/nls/ab/someBundle")),
// require("some/path/nls/ab-cd-ef/someBundle"));
//
// This value is inserted into the cache and published to the loader at the
// key/module-id some/path/nls/someBundle/ab-cd-ef.
//
// The special preload signature (case 3) instructs the plugin to stop servicing all normal requests
// (further preload requests will be serviced) until all ongoing preloading has completed.
//
// The preload signature instructs the plugin that a special rollup module is available that contains
// one or more flattened, localized bundles. The JSON array of available locales indicates which locales
// are available. Here is an example:
//
// *preload*some/path/nls/someModule*["root", "ab", "ab-cd-ef"]
//
// This indicates the following rollup modules are available:
//
// some/path/nls/someModule_ROOT
// some/path/nls/someModule_ab
// some/path/nls/someModule_ab-cd-ef
//
// Each of these modules is a normal AMD module that contains one or more flattened bundles in a hash.
// For example, assume someModule contained the bundles some/bundle/path/someBundle and
// some/bundle/path/someOtherBundle, then some/path/nls/someModule_ab would be expressed as follows:
//
// define({
// some/bundle/path/someBundle:<value of someBundle, flattened with respect to locale ab>,
// some/bundle/path/someOtherBundle:<value of someOtherBundle, flattened with respect to locale ab>,
// });
//
// E.g., given this design, preloading for locale=="ab" can execute the following algorithm:
//
// require(["some/path/nls/someModule_ab"], function(rollup){
// for(var p in rollup){
// var id = p + "/ab",
// cache[id] = rollup[p];
// define(id, rollup[p]);
// }
// });
//
// Similarly, if "ab-cd" is requested, the algorithm can determine that "ab" is the best available and
// load accordingly.
//
// The builder will write such rollups for every layer if a non-empty localeList profile property is
// provided. Further, the builder will include the following cache entry in the cache associated with
// any layer.
//
// "*now":function(r){r(['dojo/i18n!*preload*<path>/nls/<module>*<JSON array of available locales>']);}
//
// The *now special cache module instructs the loader to apply the provided function to context-require
// with respect to the particular layer being defined. This causes the plugin to hold all normal service
// requests until all preloading is complete.
//
// Notice that this algorithm is rarely better than the standard AMD load algorithm. Consider the normal case
// where the target locale has a single segment and a layer depends on a single bundle:
//
// Without Preloads:
//
// 1. Layer loads root bundle.
// 2. bundle is demanded; plugin loads single localized bundle.
//
// With Preloads:
//
// 1. Layer causes preloading of target bundle.
// 2. bundle is demanded; service is delayed until preloading complete; bundle is returned.
//
// In each case a single transaction is required to load the target bundle. In cases where multiple bundles
// are required and/or the locale has multiple segments, preloads still requires a single transaction whereas
// the normal path requires an additional transaction for each additional bundle/locale-segment. However all
// of these additional transactions can be done concurrently. Owing to this analysis, the entire preloading
// algorithm can be discard during a build by setting the has feature dojo-preload-i18n-Api to false.
if(has("dojo-preload-i18n-Api")){
var split = id.split("*"),
preloadDemand = split[1] == "preload";
if(preloadDemand){
if(!cache[id]){
// use cache[id] to prevent multiple preloads of the same preload; this shouldn't happen, but
// who knows what over-aggressive human optimizers may attempt
cache[id] = 1;
preloadL10n(split[2], json.parse(split[3]), 1, require);
}
// don't stall the loader!
load(1);
}
if(preloadDemand || waitForPreloads(id, require, load)){
return;
}
}
var match = nlsRe.exec(id),
bundlePath = match[1] + "/",
bundleName = match[5] || match[4],
bundlePathAndName = bundlePath + bundleName,
localeSpecified = (match[5] && match[4]),
targetLocale = localeSpecified || dojo.locale,
loadTarget = bundlePathAndName + "/" + targetLocale,
loadList = localeSpecified ? [targetLocale] : getLocalesToLoad(targetLocale),
remaining = loadList.length,
finish = function(){
if(!--remaining){
load(lang.delegate(cache[loadTarget]));
}
};
array.forEach(loadList, function(locale){
var target = bundlePathAndName + "/" + locale;
if(has("dojo-preload-i18n-Api")){
checkForLegacyModules(target);
}
if(!cache[target]){
doLoad(require, bundlePathAndName, bundlePath, bundleName, locale, finish);
}else{
finish();
}
});
};
if(has("dojo-unit-tests")){
var unitTests = thisModule.unitTests = [];
}
if(has("dojo-preload-i18n-Api") || 1 ){
var normalizeLocale = thisModule.normalizeLocale = function(locale){
var result = locale ? locale.toLowerCase() : dojo.locale;
return result == "root" ? "ROOT" : result;
},
isXd = function(mid, contextRequire){
return ( 1 && 1 ) ?
contextRequire.isXdUrl(require.toUrl(mid + ".js")) :
true;
},
preloading = 0,
preloadWaitQueue = [],
preloadL10n = thisModule._preloadLocalizations = function(/*String*/bundlePrefix, /*Array*/localesGenerated, /*boolean?*/ guaranteedAmdFormat, /*function?*/ contextRequire){
// summary:
// Load available flattened resource bundles associated with a particular module for dojo/locale and all dojo/config.extraLocale (if any)
// description:
// Only called by built layer files. The entire locale hierarchy is loaded. For example,
// if locale=="ab-cd", then ROOT, "ab", and "ab-cd" are loaded. This is different than v1.6-
// in that the v1.6- would only load ab-cd...which was *always* flattened.
//
// If guaranteedAmdFormat is true, then the module can be loaded with require thereby circumventing the detection algorithm
// and the extra possible extra transaction.
// If this function is called from legacy code, then guaranteedAmdFormat and contextRequire will be undefined. Since the function
// needs a require in order to resolve module ids, fall back to the context-require associated with this dojo/i18n module, which
// itself may have been mapped.
contextRequire = contextRequire || require;
function doRequire(mid, callback){
if(isXd(mid, contextRequire) || guaranteedAmdFormat){
contextRequire([mid], callback);
}else{
syncRequire([mid], callback, contextRequire);
}
}
function forEachLocale(locale, func){
// given locale= "ab-cd-ef", calls func on "ab-cd-ef", "ab-cd", "ab", "ROOT"; stops calling the first time func returns truthy
var parts = locale.split("-");
while(parts.length){
if(func(parts.join("-"))){
return;
}
parts.pop();
}
func("ROOT");
}
function preload(locale){
locale = normalizeLocale(locale);
forEachLocale(locale, function(loc){
if(array.indexOf(localesGenerated, loc)>=0){
var mid = bundlePrefix.replace(/\./g, "/")+"_"+loc;
preloading++;
doRequire(mid, function(rollup){
for(var p in rollup){
cache[require.toAbsMid(p) + "/" + loc] = rollup[p];
}
--preloading;
while(!preloading && preloadWaitQueue.length){
load.apply(null, preloadWaitQueue.shift());
}
});
return true;
}
return false;
});
}
preload();
array.forEach(dojo.config.extraLocale, preload);
},
waitForPreloads = function(id, require, load){
if(preloading){
preloadWaitQueue.push([id, require, load]);
}
return preloading;
},
checkForLegacyModules = function()
{};
}
if( 1 ){
// this code path assumes the dojo loader and won't work with a standard AMD loader
var amdValue = {},
evalBundle =
// use the function ctor to keep the minifiers away (also come close to global scope, but this is secondary)
new Function(
"__bundle", // the bundle to evalutate
"__checkForLegacyModules", // a function that checks if __bundle defined __mid in the global space
"__mid", // the mid that __bundle is intended to define
"__amdValue",
// returns one of:
// 1 => the bundle was an AMD bundle
// a legacy bundle object that is the value of __mid
// instance of Error => could not figure out how to evaluate bundle
// used to detect when __bundle calls define
"var define = function(mid, factory){define.called = 1; __amdValue.result = factory || mid;},"
+ " require = function(){define.called = 1;};"
+ "try{"
+ "define.called = 0;"
+ "eval(__bundle);"
+ "if(define.called==1)"
// bundle called define; therefore signal it's an AMD bundle
+ "return __amdValue;"
+ "if((__checkForLegacyModules = __checkForLegacyModules(__mid)))"
// bundle was probably a v1.6- built NLS flattened NLS bundle that defined __mid in the global space
+ "return __checkForLegacyModules;"
+ "}catch(e){}"
// evaulating the bundle was *neither* an AMD *nor* a legacy flattened bundle
// either way, re-eval *after* surrounding with parentheses
+ "try{"
+ "return eval('('+__bundle+')');"
+ "}catch(e){"
+ "return e;"
+ "}"
),
syncRequire = function(deps, callback, require){
var results = [];
array.forEach(deps, function(mid){
var url = require.toUrl(mid + ".js");
function load(text){
var result = evalBundle(text, checkForLegacyModules, mid, amdValue);
if(result===amdValue){
// the bundle was an AMD module; re-inject it through the normal AMD path
// we gotta do this since it could be an anonymous module and simply evaluating
// the text here won't provide the loader with the context to know what
// module is being defined()'d. With browser caching, this should be free; further
// this entire code path can be circumvented by using the AMD format to begin with
results.push(cache[url] = amdValue.result);
}else{
if(result instanceof Error){
console.error("failed to evaluate i18n bundle; url=" + url, result);
result = {};
}
// nls/<locale>/<bundle-name> indicates not the root.
results.push(cache[url] = (/nls\/[^\/]+\/[^\/]+$/.test(url) ? result : {root:result, _v1x:1}));
}
}
if(cache[url]){
results.push(cache[url]);
}else{
var bundle = require.syncLoadNls(mid);
// don't need to check for legacy since syncLoadNls returns a module if the module
// (1) was already loaded, or (2) was in the cache. In case 1, if syncRequire is called
// from getLocalization --> load, then load will have called checkForLegacyModules() before
// calling syncRequire; if syncRequire is called from preloadLocalizations, then we
// don't care about checkForLegacyModules() because that will be done when a particular
// bundle is actually demanded. In case 2, checkForLegacyModules() is never relevant
// because cached modules are always v1.7+ built modules.
if(bundle){
results.push(bundle);
}else{
if(!xhr){
try{
require.getText(url, true, load);
}catch(e){
results.push(cache[url] = {});
}
}else{
xhr.get({
url:url,
sync:true,
load:load,
error:function(){
results.push(cache[url] = {});
}
});
}
}
}
});
callback && callback.apply(null, results);
};
checkForLegacyModules = function(target){
// legacy code may have already loaded [e.g] the raw bundle x/y/z at x.y.z; when true, push into the cache
for(var result, names = target.split("/"), object = dojo.global[names[0]], i = 1; object && i<names.length-1; object = object[names[i++]]){}
if(object){
result = object[names[i]];
if(!result){
// fallback for incorrect bundle build of 1.6
result = object[names[i].replace(/-/g,"_")];
}
if(result){
cache[target] = result;
}
}
return result;
};
thisModule.getLocalization = function(moduleName, bundleName, locale){
var result,
l10nName = getBundleName(moduleName, bundleName, locale);
load(
l10nName,
// isXd() and syncRequire() need a context-require in order to resolve the mid with respect to a reference module.
// Since this legacy function does not have the concept of a reference module, resolve with respect to this
// dojo/i18n module, which, itself may have been mapped.
(!isXd(l10nName, require) ? function(deps, callback){ syncRequire(deps, callback, require); } : require),
function(result_){ result = result_; }
);
return result;
};
if(has("dojo-unit-tests")){
unitTests.push(function(doh){
doh.register("tests.i18n.unit", function(t){
var check;
check = evalBundle("{prop:1}", checkForLegacyModules, "nonsense", amdValue);
t.is({prop:1}, check); t.is(undefined, check[1]);
check = evalBundle("({prop:1})", checkForLegacyModules, "nonsense", amdValue);
t.is({prop:1}, check); t.is(undefined, check[1]);
check = evalBundle("{'prop-x':1}", checkForLegacyModules, "nonsense", amdValue);
t.is({'prop-x':1}, check); t.is(undefined, check[1]);
check = evalBundle("({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
t.is({'prop-x':1}, check); t.is(undefined, check[1]);
check = evalBundle("define({'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
check = evalBundle("define('some/module', {'prop-x':1})", checkForLegacyModules, "nonsense", amdValue);
t.is(amdValue, check); t.is({'prop-x':1}, amdValue.result);
check = evalBundle("this is total nonsense and should throw an error", checkForLegacyModules, "nonsense", amdValue);
t.is(check instanceof Error, true);
});
});
}
}
return lang.mixin(thisModule, {
dynamic:true,
normalize:normalize,
load:load,
cache:cache
});
});
},
'dijit/hccss':function(){
define("dijit/hccss", ["dojo/dom-class", "dojo/hccss", "dojo/ready", "dojo/_base/window"], function(domClass, has, ready, win){
// module:
// dijit/hccss
/*=====
return function(){
// summary:
// Test if computer is in high contrast mode, and sets `dijit_a11y` flag on `<body>` if it is.
// Deprecated, use ``dojo/hccss`` instead.
};
=====*/
// Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
// change this module to depend on dojo/domReady!
ready(90, function(){
if(has("highcontrast")){
domClass.add(win.body(), "dijit_a11y");
}
});
return has;
});
},
'dijit/tree/ForestStoreModel':function(){
define("dijit/tree/ForestStoreModel", [
"dojo/_base/array", // array.indexOf array.some
"dojo/_base/declare", // declare
"dojo/_base/kernel", // global
"dojo/_base/lang", // lang.hitch
"./TreeStoreModel"
], function(array, declare, kernel, lang, TreeStoreModel){
// module:
// dijit/tree/ForestStoreModel
return declare("dijit.tree.ForestStoreModel", TreeStoreModel, {
// summary:
// Interface between a dijit.Tree and a dojo.data store that doesn't have a root item,
// a.k.a. a store that has multiple "top level" items.
//
// description:
// Use this class to wrap a dojo.data store, making all the items matching the specified query
// appear as children of a fabricated "root item". If no query is specified then all the
// items returned by fetch() on the underlying store become children of the root item.
// This class allows dijit.Tree to assume a single root item, even if the store doesn't have one.
//
// When using this class the developer must override a number of methods according to their app and
// data, including:
//
// - onNewRootItem
// - onAddToRoot
// - onLeaveRoot
// - onNewItem
// - onSetItem
// Parameters to constructor
// rootId: String
// ID of fabricated root item
rootId: "$root$",
// rootLabel: String
// Label of fabricated root item
rootLabel: "ROOT",
// query: String
// Specifies the set of children of the root item.
// example:
// | {type:'continent'}
query: null,
// End of parameters to constructor
constructor: function(params){
// summary:
// Sets up variables, etc.
// tags:
// private
// Make dummy root item
this.root = {
store: this,
root: true,
id: params.rootId,
label: params.rootLabel,
children: params.rootChildren // optional param
};
},
// =======================================================================
// Methods for traversing hierarchy
mayHaveChildren: function(/*dojo/data/Item*/ item){
// summary:
// Tells if an item has or may have children. Implementing logic here
// avoids showing +/- expando icon for nodes that we know don't have children.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
// tags:
// extension
return item === this.root || this.inherited(arguments);
},
getChildren: function(/*dojo/data/Item*/ parentItem, /*function(items)*/ callback, /*function*/ onError){
// summary:
// Calls onComplete() with array of child items of given parent item, all loaded.
if(parentItem === this.root){
if(this.root.children){
// already loaded, just return
callback(this.root.children);
}else{
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(items){
this.root.children = items;
callback(items);
}),
onError: onError
});
}
}else{
this.inherited(arguments);
}
},
// =======================================================================
// Inspecting items
isItem: function(/* anything */ something){
return (something === this.root) ? true : this.inherited(arguments);
},
fetchItemByIdentity: function(/* object */ keywordArgs){
if(keywordArgs.identity == this.root.id){
var scope = keywordArgs.scope || kernel.global;
if(keywordArgs.onItem){
keywordArgs.onItem.call(scope, this.root);
}
}else{
this.inherited(arguments);
}
},
getIdentity: function(/* item */ item){
return (item === this.root) ? this.root.id : this.inherited(arguments);
},
getLabel: function(/* item */ item){
return (item === this.root) ? this.root.label : this.inherited(arguments);
},
// =======================================================================
// Write interface
newItem: function(/* dijit/tree/dndSource.__Item */ args, /*Item*/ parent, /*int?*/ insertIndex){
// summary:
// Creates a new item. See dojo/data/api/Write for details on args.
// Used in drag & drop when item from external source dropped onto tree.
if(parent === this.root){
this.onNewRootItem(args);
return this.store.newItem(args);
}else{
return this.inherited(arguments);
}
},
onNewRootItem: function(/* dijit/tree/dndSource.__Item */ /*===== args =====*/){
// summary:
// User can override this method to modify a new element that's being
// added to the root of the tree, for example to add a flag like root=true
},
pasteItem: function(/*Item*/ childItem, /*Item*/ oldParentItem, /*Item*/ newParentItem, /*Boolean*/ bCopy, /*int?*/ insertIndex){
// summary:
// Move or copy an item from one parent item to another.
// Used in drag & drop
if(oldParentItem === this.root){
if(!bCopy){
// It's onLeaveRoot()'s responsibility to modify the item so it no longer matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is no longer a child of the root node
this.onLeaveRoot(childItem);
}
}
this.inherited(arguments, [childItem,
oldParentItem === this.root ? null : oldParentItem,
newParentItem === this.root ? null : newParentItem,
bCopy,
insertIndex
]);
if(newParentItem === this.root){
// It's onAddToRoot()'s responsibility to modify the item so it matches
// this.query... thus triggering an onChildrenChange() event to notify the Tree
// that this element is now a child of the root node
this.onAddToRoot(childItem);
}
},
// =======================================================================
// Handling for top level children
onAddToRoot: function(/* item */ item){
// summary:
// Called when item added to root of tree; user must override this method
// to modify the item so that it matches the query for top level items
// example:
// | store.setValue(item, "root", true);
// tags:
// extension
console.log(this, ": item ", item, " added to root");
},
onLeaveRoot: function(/* item */ item){
// summary:
// Called when item removed from root of tree; user must override this method
// to modify the item so it doesn't match the query for top level items
// example:
// | store.unsetAttribute(item, "root");
// tags:
// extension
console.log(this, ": item ", item, " removed from root");
},
// =======================================================================
// Events from data store
_requeryTop: function(){
// reruns the query for the children of the root node,
// sending out an onSet notification if those children have changed
var oldChildren = this.root.children || [];
this.store.fetch({
query: this.query,
onComplete: lang.hitch(this, function(newChildren){
this.root.children = newChildren;
// If the list of children or the order of children has changed...
if(oldChildren.length != newChildren.length ||
array.some(oldChildren, function(item, idx){ return newChildren[idx] != item;})){
this.onChildrenChange(this.root, newChildren);
}
})
});
},
onNewItem: function(/* dojo/data/api/Item */ item, /* Object */ parentInfo){
// summary:
// Handler for when new items appear in the store. Developers should override this
// method to be more efficient based on their app/data.
// description:
// Note that the default implementation requeries the top level items every time
// a new item is created, since any new item could be a top level item (even in
// addition to being a child of another item, since items can have multiple parents).
//
// If developers can detect which items are possible top level items (based on the item and the
// parentInfo parameters), they should override this method to only call _requeryTop() for top
// level items. Often all top level items have parentInfo==null, but
// that will depend on which store you use and what your data is like.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
},
onDeleteItem: function(/*Object*/ item){
// summary:
// Handler for delete notifications from underlying store
// check if this was a child of root, and if so send notification that root's children
// have changed
if(array.indexOf(this.root.children, item) != -1){
this._requeryTop();
}
this.inherited(arguments);
},
onSetItem: function(/* item */ item,
/* attribute-name-string */ attribute,
/* Object|Array */ oldValue,
/* Object|Array */ newValue){
// summary:
// Updates the tree view according to changes to an item in the data store.
// Developers should override this method to be more efficient based on their app/data.
// description:
// Handles updates to an item's children by calling onChildrenChange(), and
// other updates to an item by calling onChange().
//
// Also, any change to any item re-executes the query for the tree's top-level items,
// since this modified item may have started/stopped matching the query for top level items.
//
// If possible, developers should override this function to only call _requeryTop() when
// the change to the item has caused it to stop/start being a top level item in the tree.
// tags:
// extension
this._requeryTop();
this.inherited(arguments);
}
});
});
},
'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n",
'dijit/form/_ComboBoxMenuMixin':function(){
define("dijit/form/_ComboBoxMenuMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/i18n", // i18n.getLocalization
"dojo/i18n!./nls/ComboBox"
], function(array, declare, domAttr, i18n){
// module:
// dijit/form/_ComboBoxMenuMixin
return declare( "dijit.form._ComboBoxMenuMixin", null, {
// summary:
// Focus-less menu for internal use in `dijit/form/ComboBox`
// tags:
// private
// _messages: Object
// Holds "next" and "previous" text for paging buttons on drop down
_messages: null,
postMixInProperties: function(){
this.inherited(arguments);
this._messages = i18n.getLocalization("dijit.form", "ComboBox", this.lang);
},
buildRendering: function(){
this.inherited(arguments);
// fill in template with i18n messages
this.previousButton.innerHTML = this._messages["previousMessage"];
this.nextButton.innerHTML = this._messages["nextMessage"];
},
_setValueAttr: function(/*Object*/ value){
this.value = value;
this.onChange(value);
},
onClick: function(/*DomNode*/ node){
if(node == this.previousButton){
this._setSelectedAttr(null);
this.onPage(-1);
}else if(node == this.nextButton){
this._setSelectedAttr(null);
this.onPage(1);
}else{
this.onChange(node);
}
},
// stubs
onChange: function(/*Number*/ /*===== direction =====*/){
// summary:
// Notifies ComboBox/FilteringSelect that user selected an option.
// tags:
// callback
},
onPage: function(/*Number*/ /*===== direction =====*/){
// summary:
// Notifies ComboBox/FilteringSelect that user clicked to advance to next/previous page.
// tags:
// callback
},
onClose: function(){
// summary:
// Callback from dijit.popup code to this widget, notifying it that it closed
// tags:
// private
this._setSelectedAttr(null);
},
_createOption: function(/*Object*/ item, labelFunc){
// summary:
// Creates an option to appear on the popup menu subclassed by
// `dijit/form/FilteringSelect`.
var menuitem = this._createMenuItem();
var labelObject = labelFunc(item);
if(labelObject.html){
menuitem.innerHTML = labelObject.label;
}else{
menuitem.appendChild(
menuitem.ownerDocument.createTextNode(labelObject.label)
);
}
// #3250: in blank options, assign a normal height
if(menuitem.innerHTML == ""){
menuitem.innerHTML = "&#160;"; // &nbsp;
}
// update menuitem.dir if BidiSupport was required
this.applyTextDir(menuitem, (menuitem.innerText || menuitem.textContent || ""));
return menuitem;
},
createOptions: function(results, options, labelFunc){
// summary:
// Fills in the items in the drop down list
// results:
// Array of items
// options:
// The options to the query function of the store
//
// labelFunc:
// Function to produce a label in the drop down list from a dojo.data item
this.items = results;
// display "Previous . . ." button
this.previousButton.style.display = (options.start == 0) ? "none" : "";
domAttr.set(this.previousButton, "id", this.id + "_prev");
// create options using _createOption function defined by parent
// ComboBox (or FilteringSelect) class
// #2309:
// iterate over cache nondestructively
array.forEach(results, function(item, i){
var menuitem = this._createOption(item, labelFunc);
menuitem.setAttribute("item", i); // index to this.items; use indirection to avoid mem leak
domAttr.set(menuitem, "id", this.id + i);
this.nextButton.parentNode.insertBefore(menuitem, this.nextButton);
}, this);
// display "Next . . ." button
var displayMore = false;
// Try to determine if we should show 'more'...
if(results.total && !results.total.then && results.total != -1){
if((options.start + options.count) < results.total){
displayMore = true;
}else if((options.start + options.count) > results.total && options.count == results.length){
// Weird return from a data store, where a start + count > maxOptions
// implies maxOptions isn't really valid and we have to go into faking it.
// And more or less assume more if count == results.length
displayMore = true;
}
}else if(options.count == results.length){
//Don't know the size, so we do the best we can based off count alone.
//So, if we have an exact match to count, assume more.
displayMore = true;
}
this.nextButton.style.display = displayMore ? "" : "none";
domAttr.set(this.nextButton,"id", this.id + "_next");
},
clearResultList: function(){
// summary:
// Clears the entries in the drop down list, but of course keeps the previous and next buttons.
var container = this.containerNode;
while(container.childNodes.length > 2){
container.removeChild(container.childNodes[container.childNodes.length-2]);
}
this._setSelectedAttr(null);
},
highlightFirstOption: function(){
// summary:
// Highlight the first real item in the list (not Previous Choices).
this.selectFirstNode();
},
highlightLastOption: function(){
// summary:
// Highlight the last real item in the list (not More Choices).
this.selectLastNode();
},
selectFirstNode: function(){
this.inherited(arguments);
if(this.getHighlightedOption() == this.previousButton){
this.selectNextNode();
}
},
selectLastNode: function(){
this.inherited(arguments);
if(this.getHighlightedOption() == this.nextButton){
this.selectPreviousNode();
}
},
getHighlightedOption: function(){
return this.selected;
}
});
});
},
'dijit/form/_SearchMixin':function(){
define("dijit/form/_SearchMixin", [
"dojo/data/util/filter", // patternToRegExp
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.clone lang.hitch
"dojo/query", // query
"dojo/sniff", // has("ie")
"dojo/string", // string.substitute
"dojo/when",
"../registry" // registry.byId
], function(filter, declare, event, keys, lang, query, has, string, when, registry){
// module:
// dijit/form/_SearchMixin
return declare("dijit.form._SearchMixin", null, {
// summary:
// A mixin that implements the base functionality to search a store based upon user-entered text such as
// with `dijit/form/ComboBox` or `dijit/form/FilteringSelect`
// tags:
// protected
// pageSize: Integer
// Argument to data provider.
// Specifies maximum number of search results to return per query
pageSize: Infinity,
// store: [const] dojo/store/api/Store
// Reference to data provider object used by this ComboBox.
// The store must accept an object hash of properties for its query. See `query` and `queryExpr` for details.
store: null,
// fetchProperties: Object
// Mixin to the store's fetch.
// For example, to set the sort order of the ComboBox menu, pass:
// | { sort: [{attribute:"name",descending: true}] }
// To override the default queryOptions so that deep=false, do:
// | { queryOptions: {ignoreCase: true, deep: false} }
fetchProperties:{},
// query: Object
// A query that can be passed to `store` to initially filter the items.
// ComboBox overwrites any reference to the `searchAttr` and sets it to the `queryExpr` with the user's input substituted.
query: {},
// searchDelay: Integer
// Delay in milliseconds between when user types something and we start
// searching based on that value
searchDelay: 200,
// searchAttr: String
// Search for items in the data store where this attribute (in the item)
// matches what the user typed
searchAttr: "name",
// queryExpr: String
// This specifies what query is sent to the data store,
// based on what the user has typed. Changing this expression will modify
// whether the results are only exact matches, a "starting with" match,
// etc.
// dojo.data query expression pattern.
// `${0}` will be substituted for the user text.
// `*` is used for wildcards.
// `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
queryExpr: "${0}*",
// ignoreCase: Boolean
// Set true if the query should ignore case when matching possible items
ignoreCase: true,
_abortQuery: function(){
// stop in-progress query
if(this.searchTimer){
this.searchTimer = this.searchTimer.remove();
}
if(this._queryDeferHandle){
this._queryDeferHandle = this._queryDeferHandle.remove();
}
if(this._fetchHandle){
if(this._fetchHandle.abort){
this._cancelingQuery = true;
this._fetchHandle.abort();
this._cancelingQuery = false;
}
if(this._fetchHandle.cancel){
this._cancelingQuery = true;
this._fetchHandle.cancel();
this._cancelingQuery = false;
}
this._fetchHandle = null;
}
},
_processInput: function(/*Event*/ evt){
// summary:
// Handles input (keyboard/paste) events
if(this.disabled || this.readOnly){ return; }
var key = evt.charOrCode;
// except for cutting/pasting case - ctrl + x/v
if(evt.altKey || ((evt.ctrlKey || evt.metaKey) && (key != 'x' && key != 'v')) || key == keys.SHIFT){
return; // throw out weird key combinations and spurious events
}
var doSearch = false;
this._prev_key_backspace = false;
switch(key){
case keys.DELETE:
case keys.BACKSPACE:
this._prev_key_backspace = true;
this._maskValidSubsetError = true;
doSearch = true;
break;
default:
// Non char keys (F1-F12 etc..) shouldn't start a search..
// Ascii characters and IME input (Chinese, Japanese etc.) should.
//IME input produces keycode == 229.
doSearch = typeof key == 'string' || key == 229;
}
if(doSearch){
// need to wait a tad before start search so that the event
// bubbles through DOM and we have value visible
if(!this.store){
this.onSearch();
}else{
this.searchTimer = this.defer("_startSearchFromInput", 1);
}
}
},
onSearch: function(/*===== results, query, options =====*/){
// summary:
// Callback when a search completes.
//
// results: Object
// An array of items from the originating _SearchMixin's store.
//
// query: Object
// A copy of the originating _SearchMixin's query property.
//
// options: Object
// The additional parameters sent to the originating _SearchMixin's store, including: start, count, queryOptions.
//
// tags:
// callback
},
_startSearchFromInput: function(){
this._startSearch(this.focusNode.value.replace(/([\\\*\?])/g, "\\$1"));
},
_startSearch: function(/*String*/ text){
// summary:
// Starts a search for elements matching text (text=="" means to return all items),
// and calls onSearch(...) when the search completes, to display the results.
this._abortQuery();
var
_this = this,
// Setup parameters to be passed to store.query().
// Create a new query to prevent accidentally querying for a hidden
// value from FilteringSelect's keyField
query = lang.clone(this.query), // #5970
options = {
start: 0,
count: this.pageSize,
queryOptions: { // remove for 2.0
ignoreCase: this.ignoreCase,
deep: true
}
},
qs = string.substitute(this.queryExpr, [text]),
q,
startQuery = function(){
var resPromise = _this._fetchHandle = _this.store.query(query, options);
if(_this.disabled || _this.readOnly || (q !== _this._lastQuery)){
return;
} // avoid getting unwanted notify
when(resPromise, function(res){
_this._fetchHandle = null;
if(!_this.disabled && !_this.readOnly && (q === _this._lastQuery)){ // avoid getting unwanted notify
when(resPromise.total, function(total){
res.total = total;
var pageSize = _this.pageSize;
if(isNaN(pageSize) || pageSize > res.total){ pageSize = res.total; }
// Setup method to fetching the next page of results
res.nextPage = function(direction){
// tell callback the direction of the paging so the screen
// reader knows which menu option to shout
options.direction = direction = direction !== false;
options.count = pageSize;
if(direction){
options.start += res.length;
if(options.start >= res.total){
options.count = 0;
}
}else{
options.start -= pageSize;
if(options.start < 0){
options.count = Math.max(pageSize + options.start, 0);
options.start = 0;
}
}
if(options.count <= 0){
res.length = 0;
_this.onSearch(res, query, options);
}else{
startQuery();
}
};
_this.onSearch(res, query, options);
});
}
}, function(err){
_this._fetchHandle = null;
if(!_this._cancelingQuery){ // don't treat canceled query as an error
console.error(_this.declaredClass + ' ' + err.toString());
}
});
};
lang.mixin(options, this.fetchProperties);
// Generate query
if(this.store._oldAPI){
// remove this branch for 2.0
q = qs;
}else{
// Query on searchAttr is a regex for benefit of dojo/store/Memory,
// but with a toString() method to help dojo/store/JsonRest.
// Search string like "Co*" converted to regex like /^Co.*$/i.
q = filter.patternToRegExp(qs, this.ignoreCase);
q.toString = function(){ return qs; };
}
// set _lastQuery, *then* start the timeout
// otherwise, if the user types and the last query returns before the timeout,
// _lastQuery won't be set and their input gets rewritten
this._lastQuery = query[this.searchAttr] = q;
this._queryDeferHandle = this.defer(startQuery, this.searchDelay);
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(){
this.query={};
this.fetchProperties={};
},
postMixInProperties: function(){
if(!this.store){
var list = this.list;
if(list){
this.store = registry.byId(list);
}
}
this.inherited(arguments);
}
});
});
},
'dojo/parser':function(){
define(
"dojo/parser", ["require", "./_base/kernel", "./_base/lang", "./_base/array", "./_base/config", "./_base/html", "./_base/window",
"./_base/url", "./_base/json", "./aspect", "./date/stamp", "./Deferred", "./has", "./query", "./on", "./ready"],
function(require, dojo, dlang, darray, config, dhtml, dwindow, _Url, djson, aspect, dates, Deferred, has, query, don, ready){
// module:
// dojo/parser
new Date("X"); // workaround for #11279, new Date("") == NaN
// 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).
var extendCnt = 0;
aspect.after(dlang, "extend", function(){
extendCnt++;
}, true);
function getNameMap(ctor){
// summary:
// Returns map from lowercase name to attribute name in class, ex: {onclick: "onClick"}
var map = ctor._nameCaseMap, proto = ctor.prototype;
// Create the map if it's undefined.
// Refresh the map if a superclass was possibly extended with new methods since the map was created.
if(!map || map._extendCnt < extendCnt){
map = ctor._nameCaseMap = {};
for(var name in proto){
if(name.charAt(0) === "_"){ continue; } // skip internal properties
map[name.toLowerCase()] = name;
}
map._extendCnt = extendCnt;
}
return map;
}
// Map from widget name or list of widget names(ex: "dijit/form/Button,acme/MyMixin") to a constructor.
var _ctorMap = {};
function getCtor(/*String[]*/ types){
// summary:
// Retrieves a constructor. If the types array contains more than one class/MID then the
// subsequent classes will be mixed into the first class and a unique constructor will be
// returned for that array.
var ts = types.join();
if(!_ctorMap[ts]){
var mixins = [];
for(var i = 0, l = types.length; i < l; i++){
var t = types[i];
// TODO: Consider swapping getObject and require in the future
mixins[mixins.length] = (_ctorMap[t] = _ctorMap[t] || (dlang.getObject(t) || (~t.indexOf('/') && require(t))));
}
var ctor = mixins.shift();
_ctorMap[ts] = mixins.length ? (ctor.createSubclass ? ctor.createSubclass(mixins) : ctor.extend.apply(ctor, mixins)) : ctor;
}
return _ctorMap[ts];
}
var parser = {
// summary:
// The Dom/Widget parsing package
_clearCache: function(){
// summary:
// Clear cached data. Used mainly for benchmarking.
extendCnt++;
_ctorMap = {};
},
_functionFromScript: function(script, attrData){
// summary:
// Convert a `<script type="dojo/method" args="a, b, c"> ... </script>`
// into a function
// script: DOMNode
// The `<script>` DOMNode
// attrData: String
// For HTML5 compliance, searches for attrData + "args" (typically
// "data-dojo-args") instead of "args"
var preamble = "",
suffix = "",
argsStr = (script.getAttribute(attrData + "args") || script.getAttribute("args")),
withStr = script.getAttribute("with");
// Convert any arguments supplied in script tag into an array to be passed to the
var fnArgs = (argsStr || "").split(/\s*,\s*/);
if(withStr && withStr.length){
darray.forEach(withStr.split(/\s*,\s*/), function(part){
preamble += "with("+part+"){";
suffix += "}";
});
}
return new Function(fnArgs, preamble + script.innerHTML + suffix);
},
instantiate: function(nodes, mixin, options){
// 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 DOM nodes
// 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.
// options: Object?
// An object used to hold kwArgs for instantiation.
// See parse.options argument for details.
mixin = mixin || {};
options = options || {};
var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
attrData = "data-" + (options.scope || dojo._scopeName) + "-",// typically "data-dojo-"
dataDojoType = attrData + "type", // typically "data-dojo-type"
dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
var list = [];
darray.forEach(nodes, function(node){
var type = dojoType in mixin ? mixin[dojoType] : node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
if(type){
var mixinsValue = node.getAttribute(dataDojoMixins),
types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
list.push({
node: node,
types: types
});
}
});
// Instantiate the nodes and return the objects
return this._instantiate(list, mixin, options);
},
_instantiate: function(nodes, mixin, options){
// summary:
// Takes array of objects representing 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 objects like
// | {
// | ctor: Function (may be null)
// | types: ["dijit/form/Button", "acme/MyMixin"] (used if ctor not specified)
// | 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.
// options: Object
// An options object used to hold kwArgs for instantiation.
// See parse.options argument for details.
// Call widget constructors
var thelist = darray.map(nodes, function(obj){
var ctor = obj.ctor || getCtor(obj.types);
// If we still haven't resolved a ctor, it is fatal now
if(!ctor){
throw new Error("Unable to resolve constructor for: '" + obj.types.join() + "'");
}
return this.construct(ctor, obj.node, mixin, options, obj.scripts, obj.inherited);
}, this);
// 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 && !options.noStart){
darray.forEach(thelist, function(instance){
if(typeof instance.startup === "function" && !instance._started){
instance.startup();
}
});
}
return thelist;
},
construct: function(ctor, node, mixin, options, scripts, inherited){
// summary:
// Calls new ctor(params, node), where params is the hash of parameters specified on the node,
// excluding data-dojo-type and data-dojo-mixins. Does not call startup(). Returns the widget.
// ctor: Function
// Widget constructor.
// node: DOMNode
// This node will be replaced/attached to by the widget. It also specifies the arguments to pass to ctor.
// mixin: Object?
// Attributes in this object will be passed as parameters to ctor,
// overriding attributes specified on the node.
// options: Object?
// An options object used to hold kwArgs for instantiation. See parse.options argument for details.
// scripts: DomNode[]?
// Array of `<script type="dojo/*">` DOMNodes. If not specified, will search for `<script>` tags inside node.
// inherited: Object?
// Settings from dir=rtl or lang=... on a node above this node. Overrides options.inherited.
var proto = ctor && ctor.prototype;
options = options || {};
// 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 = {};
if(options.defaults){
// settings for the document itself (or whatever subtree is being parsed)
dlang.mixin(params, options.defaults);
}
if(inherited){
// settings from dir=rtl or lang=... on a node above this node
dlang.mixin(params, inherited);
}
// Get list of attributes explicitly listed in the markup
var attributes;
if(has("dom-attributes-explicit")){
// Standard path to get list of user specified attributes
attributes = node.attributes;
}else if(has("dom-attributes-specified-flag")){
// Special processing needed for IE8, to skip a few faux values in attributes[]
attributes = darray.filter(node.attributes, function(a){ return a.specified;});
}else{
// Special path for IE6-7, avoid (sometimes >100) bogus entries in node.attributes
var clone = /^input$|^img$/i.test(node.nodeName) ? node : node.cloneNode(false),
attrs = clone.outerHTML.replace(/=[^\s"']+|="[^"]*"|='[^']*'/g, "").replace(/^\s*<[a-zA-Z0-9]*\s*/, "").replace(/\s*>.*$/, "");
attributes = darray.map(attrs.split(/\s+/), function(name){
var lcName = name.toLowerCase();
return {
name: name,
// getAttribute() doesn't work for button.value, returns innerHTML of button.
// but getAttributeNode().value doesn't work for the form.encType or li.value
value: (node.nodeName == "LI" && name == "value") || lcName == "enctype" ?
node.getAttribute(lcName) : node.getAttributeNode(lcName).value
};
});
}
// Hash to convert scoped attribute name (ex: data-dojo17-params) to something friendly (ex: data-dojo-params)
// TODO: remove scope for 2.0
var scope = options.scope || dojo._scopeName,
attrData = "data-" + scope + "-", // typically "data-dojo-"
hash = {};
if(scope !== "dojo"){
hash[attrData + "props"] = "data-dojo-props";
hash[attrData + "type"] = "data-dojo-type";
hash[attrData + "mixins"] = "data-dojo-mixins";
hash[scope + "type"] = "dojoType";
hash[attrData + "id"] = "data-dojo-id";
}
// Read in attributes and process them, including data-dojo-props, data-dojo-type,
// dojoAttachPoint, etc., as well as normal foo=bar attributes.
var i=0, item, funcAttrs=[], jsname, extra;
while(item = attributes[i++]){
var name = item.name,
lcName = name.toLowerCase(),
value = item.value;
switch(hash[lcName] || lcName){
// Already processed, just ignore
case "data-dojo-type":
case "dojotype":
case "data-dojo-mixins":
break;
// Data-dojo-props. Save for later to make sure it overrides direct foo=bar settings
case "data-dojo-props":
extra = value;
break;
// data-dojo-id or jsId. TODO: drop jsId in 2.0
case "data-dojo-id":
case "jsid":
jsname = value;
break;
// For the benefit of _Templated
case "data-dojo-attach-point":
case "dojoattachpoint":
params.dojoAttachPoint = value;
break;
case "data-dojo-attach-event":
case "dojoattachevent":
params.dojoAttachEvent = value;
break;
// Special parameter handling needed for IE
case "class":
params["class"] = node.className;
break;
case "style":
params["style"] = node.style && node.style.cssText;
break;
default:
// Normal attribute, ex: value="123"
// Find attribute in widget corresponding to specified name.
// May involve case conversion, ex: onclick --> onClick
if(!(name in proto)){
var map = getNameMap(ctor);
name = map[lcName] || name;
}
// Set params[name] to value, doing type conversion
if(name in proto){
switch(typeof proto[name]){
case "string":
params[name] = value;
break;
case "number":
params[name] = value.length ? Number(value) : NaN;
break;
case "boolean":
// for checked/disabled value might be "" or "checked". interpret as true.
params[name] = value.toLowerCase() != "false";
break;
case "function":
if(value === "" || value.search(/[^\w\.]+/i) != -1){
// The user has specified some text for a function like "return x+5"
params[name] = new Function(value);
}else{
// The user has specified the name of a global function like "myOnClick"
// or a single word function "return"
params[name] = dlang.getObject(value, false) || new Function(value);
}
funcAttrs.push(name); // prevent "double connect", see #15026
break;
default:
var pVal = proto[name];
params[name] =
(pVal && "length" in pVal) ? (value ? value.split(/\s*,\s*/) : []) : // array
(pVal instanceof Date) ?
(value == "" ? new Date("") : // the NaN of dates
value == "now" ? new Date() : // current date
dates.fromISOString(value)) :
(pVal instanceof _Url) ? (dojo.baseUrl + value) :
djson.fromJson(value);
}
}else{
params[name] = value;
}
}
}
// Remove function attributes from DOMNode to prevent "double connect" problem, see #15026.
// Do this as a separate loop since attributes[] is often a live collection (depends on the browser though).
for(var j=0; j<funcAttrs.length; j++){
var lcfname = funcAttrs[j].toLowerCase();
node.removeAttribute(lcfname);
node[lcfname] = null;
}
// Mix things found in data-dojo-props into the params, overriding any direct settings
if(extra){
try{
extra = djson.fromJson.call(options.propsThis, "{" + extra + "}");
dlang.mixin(params, extra);
}catch(e){
// give the user a pointer to their invalid parameters. FIXME: can we kill this in production?
throw new Error(e.toString() + " in data-dojo-props='" + extra + "'");
}
}
// Any parameters specified in "mixin" override everything else.
dlang.mixin(params, mixin);
// Get <script> nodes associated with this widget, if they weren't specified explicitly
if(!scripts){
scripts = (ctor && (ctor._noScript || proto._noScript) ? [] : query("> script[type^='dojo/']", node));
}
// 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" data-dojo-event="foo"> tags are dojo.connected after instantiation
// <script type="dojo/watch" data-dojo-prop="foo"> tags are dojo.watch after instantiation
// <script type="dojo/on" data-dojo-event="foo"> tags are dojo.on after instantiation
// note: dojo/* script tags cannot exist in self closing widgets, like <input />
var aspects = [], // aspects to connect after instantiation
calls = [], // functions to call after instantiation
watches = [], // functions to watch after instantiation
ons = []; // functions to on after instantiation
if(scripts){
for(i=0; i<scripts.length; i++){
var script = scripts[i];
node.removeChild(script);
// FIXME: drop event="" support in 2.0. use data-dojo-event="" instead
var event = (script.getAttribute(attrData + "event") || script.getAttribute("event")),
prop = script.getAttribute(attrData + "prop"),
method = script.getAttribute(attrData + "method"),
advice = script.getAttribute(attrData + "advice"),
scriptType = script.getAttribute("type"),
nf = this._functionFromScript(script, attrData);
if(event){
if(scriptType == "dojo/connect"){
aspects.push({ method: event, func: nf });
}else if(scriptType == "dojo/on"){
ons.push({ event: event, func: nf });
}else{
params[event] = nf;
}
}else if(scriptType == "dojo/aspect"){
aspects.push({ method: method, advice: advice, func: nf });
}else if(scriptType == "dojo/watch"){
watches.push({ prop: prop, func: nf });
}else{
calls.push(nf);
}
}
}
// create the instance
var markupFactory = ctor.markupFactory || proto.markupFactory;
var instance = markupFactory ? markupFactory(params, node, ctor) : new ctor(params, node);
// map it to the JS namespace if that makes sense
if(jsname){
dlang.setObject(jsname, instance);
}
// process connections and startup functions
for(i=0; i<aspects.length; i++){
aspect[aspects[i].advice || "after"](instance, aspects[i].method, dlang.hitch(instance, aspects[i].func), true);
}
for(i=0; i<calls.length; i++){
calls[i].call(instance);
}
for(i=0; i<watches.length; i++){
instance.watch(watches[i].prop, watches[i].func);
}
for(i=0; i<ons.length; i++){
don(instance, ons[i].event, ons[i].func);
}
return instance;
},
scan: function(root, options){
// summary:
// Scan a DOM tree and return an array of objects representing the DOMNodes
// that need to be turned into widgets.
// description:
// Search specified node (or document root node) recursively for class instances
// and return an array of objects that represent potential widgets to be
// instantiated. Searches for either data-dojo-type="MID" or dojoType="MID" where
// "MID" is a module ID like "dijit/form/Button" or a fully qualified Class name
// like "dijit/form/Button". If the MID is not currently available, scan will
// attempt to require() in the module.
//
// See parser.parse() for details of markup.
// root: DomNode?
// A default starting root node from which to start the parsing. Can be
// omitted, defaulting to the entire document. If omitted, the `options`
// object can be passed in this place. If the `options` object has a
// `rootNode` member, that is used.
// options: Object
// a kwArgs options object, see parse() for details
//
// returns: Promise
// A promise that is resolved with the nodes that have been parsed.
var list = [], // Output List
mids = [], // An array of modules that are not yet loaded
midsHash = {}; // Used to keep the mids array unique
var dojoType = (options.scope || dojo._scopeName) + "Type", // typically "dojoType"
attrData = "data-" + (options.scope || dojo._scopeName) + "-", // typically "data-dojo-"
dataDojoType = attrData + "type", // typically "data-dojo-type"
dataDojoTextDir = attrData + "textdir", // typically "data-dojo-textdir"
dataDojoMixins = attrData + "mixins"; // typically "data-dojo-mixins"
// Info on DOMNode currently being processed
var node = root.firstChild;
// Info on parent of DOMNode currently being processed
// - inherited: dir, lang, and textDir setting of parent, or inherited by parent
// - parent: pointer to identical structure for my parent (or null if no parent)
// - scripts: if specified, collects <script type="dojo/..."> type nodes from children
var inherited = options.inherited;
if(!inherited){
function findAncestorAttr(node, attr){
return (node.getAttribute && node.getAttribute(attr)) ||
(node.parentNode && findAncestorAttr(node.parentNode, attr));
}
inherited = {
dir: findAncestorAttr(root, "dir"),
lang: findAncestorAttr(root, "lang"),
textDir: findAncestorAttr(root, dataDojoTextDir)
};
for(var key in inherited){
if(!inherited[key]){ delete inherited[key]; }
}
}
// Metadata about parent node
var parent = {
inherited: inherited
};
// For collecting <script type="dojo/..."> type nodes (when null, we don't need to collect)
var scripts;
// when true, only look for <script type="dojo/..."> tags, and don't recurse to children
var scriptsOnly;
function getEffective(parent){
// summary:
// Get effective dir, lang, textDir settings for specified obj
// (matching "parent" object structure above), and do caching.
// Take care not to return null entries.
if(!parent.inherited){
parent.inherited = {};
var node = parent.node,
grandparent = getEffective(parent.parent);
var inherited = {
dir: node.getAttribute("dir") || grandparent.dir,
lang: node.getAttribute("lang") || grandparent.lang,
textDir: node.getAttribute(dataDojoTextDir) || grandparent.textDir
};
for(var key in inherited){
if(inherited[key]){
parent.inherited[key] = inherited[key];
}
}
}
return parent.inherited;
}
// DFS on DOM tree, collecting nodes with data-dojo-type specified.
while(true){
if(!node){
// Finished this level, continue to parent's next sibling
if(!parent || !parent.node){
break;
}
node = parent.node.nextSibling;
scriptsOnly = false;
parent = parent.parent;
scripts = parent.scripts;
continue;
}
if(node.nodeType != 1){
// Text or comment node, skip to next sibling
node = node.nextSibling;
continue;
}
if(scripts && node.nodeName.toLowerCase() == "script"){
// Save <script type="dojo/..."> for parent, then continue to next sibling
type = node.getAttribute("type");
if(type && /^dojo\/\w/i.test(type)){
scripts.push(node);
}
node = node.nextSibling;
continue;
}
if(scriptsOnly){
// scriptsOnly flag is set, we have already collected scripts if the parent wants them, so now we shouldn't
// continue further analysis of the node and will continue to the next sibling
node = node.nextSibling;
continue;
}
// Check for data-dojo-type attribute, fallback to backward compatible dojoType
// TODO: Remove dojoType in 2.0
var type = node.getAttribute(dataDojoType) || node.getAttribute(dojoType);
// Short circuit for leaf nodes containing nothing [but text]
var firstChild = node.firstChild;
if(!type && (!firstChild || (firstChild.nodeType == 3 && !firstChild.nextSibling))){
node = node.nextSibling;
continue;
}
// Meta data about current node
var current;
var ctor = null;
if(type){
// If dojoType/data-dojo-type specified, add to output array of nodes to instantiate.
var mixinsValue = node.getAttribute(dataDojoMixins),
types = mixinsValue ? [type].concat(mixinsValue.split(/\s*,\s*/)) : [type];
// Note: won't find classes declared via dojo/Declaration or any modules that haven't been
// loaded yet so use try/catch to avoid throw from require()
try{
ctor = getCtor(types);
}catch(e){}
// If the constructor was not found, check to see if it has modules that can be loaded
if(!ctor){
darray.forEach(types, function(t){
if(~t.indexOf('/') && !midsHash[t]){
// If the type looks like a MID and it currently isn't in the array of MIDs to load, add it.
midsHash[t] = true;
mids[mids.length] = t;
}
});
}
var childScripts = ctor && !ctor.prototype._noScript ? [] : null; // <script> nodes that are parent's children
// Setup meta data about this widget node, and save it to list of nodes to instantiate
current = {
types: types,
ctor: ctor,
parent: parent,
node: node,
scripts: childScripts
};
current.inherited = getEffective(current); // dir & lang settings for current node, explicit or inherited
list.push(current);
}else{
// Meta data about this non-widget node
current = {
node: node,
scripts: scripts,
parent: parent
};
}
// Recurse, collecting <script type="dojo/..."> children, and also looking for
// descendant nodes with dojoType specified (unless the widget has the stopParser flag).
// When finished with children, go to my next sibling.
node = firstChild;
scripts = childScripts;
scriptsOnly = ctor && ctor.prototype.stopParser && !(options.template);
parent = current;
}
var d = new Deferred();
// If there are modules to load then require them in
if(mids.length){
// Warn that there are modules being auto-required
if(has("dojo-debug-messages")){
console.warn("WARNING: Modules being Auto-Required: " + mids.join(", "));
}
require(mids, function(){
// Go through list of widget nodes, filling in missing constructors, and filtering out nodes that shouldn't
// be instantiated due to a stopParser flag on an ancestor that we belatedly learned about due to
// auto-require of a module like ContentPane. Assumes list is in DFS order.
d.resolve(darray.filter(list, function(widget){
if(!widget.ctor){
// Attempt to find the constructor again. Still won't find classes defined via
// dijit/Declaration so need to try/catch.
try{
widget.ctor = getCtor(widget.types);
}catch(e){}
}
// Get the parent widget
var parent = widget.parent;
while(parent && !parent.types){
parent = parent.parent;
}
// Return false if this node should be skipped due to stopParser on an ancestor.
// Since list[] is in DFS order, this loop will always set parent.instantiateChildren before
// trying to compute widget.instantiate.
var proto = widget.ctor && widget.ctor.prototype;
widget.instantiateChildren = !(proto && proto.stopParser && !(options.template));
widget.instantiate = !parent || (parent.instantiate && parent.instantiateChildren);
return widget.instantiate;
}));
});
}else{
// There were no modules to load, so just resolve with the parsed nodes. This separate code path is for
// efficiency, to avoid running the require() and the callback code above.
d.resolve(list);
}
// Return the promise
return d.promise;
},
_require: function(/*DOMNode*/ script){
// summary:
// Helper for _scanAMD(). Takes a `<script type=dojo/require>bar: "acme/bar", ...</script>` node,
// calls require() to load the specified modules and (asynchronously) assign them to the specified global
// variables, and returns a Promise for when that operation completes.
//
// In the example above, it is effectively doing a require(["acme/bar", ...], function(a){ bar = a; }).
var hash = djson.fromJson("{" + script.innerHTML + "}"),
vars = [],
mids = [],
d = new Deferred();
for(var name in hash){
vars.push(name);
mids.push(hash[name]);
}
require(mids, function(){
for(var i=0; i<vars.length; i++){
dlang.setObject(vars[i], arguments[i]);
}
d.resolve(arguments);
});
return d.promise;
},
_scanAmd: function(root){
// summary:
// Scans the DOM for any declarative requires and returns their values.
// description:
// Looks for `<script type=dojo/require>bar: "acme/bar", ...</script>` node, calls require() to load the
// specified modules and (asynchronously) assign them to the specified global variables,
// and returns a Promise for when those operations complete.
// root: DomNode
// The node to base the scan from.
// Promise that resolves when all the <script type=dojo/require> nodes have finished loading.
var deferred = new Deferred(),
promise = deferred.promise;
deferred.resolve(true);
var self = this;
query("script[type='dojo/require']", root).forEach(function(node){
// Fire off require() call for specified modules. Chain this require to fire after
// any previous requires complete, so that layers can be loaded before individual module require()'s fire.
promise = promise.then(function(){ return self._require(node); });
// Remove from DOM so it isn't seen again
node.parentNode.removeChild(node);
});
return promise;
},
parse: function(rootNode, options){
// 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 either data-dojo-type="Class" or
// dojoType="Class" where "Class" is a a fully qualified class name,
// like `dijit/form/Button`
//
// Using `data-dojo-type`:
// Attributes using can be mixed into the parameters used to instantiate the
// Class by using a `data-dojo-props` attribute on the node being converted.
// `data-dojo-props` should be a string attribute to be converted from JSON.
//
// Using `dojoType`:
// Attributes are read from the original domNode and converted to appropriate
// types by looking up the Class prototype values. This is the default behavior
// from Dojo 1.0 to Dojo 1.5. `dojoType` support is deprecated, and will
// go away in Dojo 2.0.
// rootNode: DomNode?
// A default starting root node from which to start the parsing. Can be
// omitted, defaulting to the entire document. If omitted, the `options`
// object can be passed in this place. If the `options` object has a
// `rootNode` member, that is used.
// options: Object?
// A hash of options.
//
// - 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 `options object.
// - template: Boolean:
// If true, ignores ContentPane's stopParser flag and parses contents inside of
// a ContentPane inside of a template. This allows dojoAttachPoint on widgets/nodes
// nested inside the ContentPane to work.
// - 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
// - scope: String:
// Root for attribute names to search for. If scopeName is dojo,
// will search for data-dojo-type (or dojoType). For backwards compatibility
// reasons defaults to dojo._scopeName (which is "dojo" except when
// multi-version support is used, when it will be something like dojo16, dojo20, etc.)
// - propsThis: Object:
// If specified, "this" referenced from data-dojo-props will refer to propsThis.
// Intended for use from the widgets-in-template feature of `dijit._WidgetsInTemplateMixin`
// returns: Mixed
// Returns a blended object that is an array of the instantiated objects, but also can include
// a promise that is resolved with the instantiated objects. This is done for backwards
// compatibility. If the parser auto-requires modules, it will always behave in a promise
// fashion and `parser.parse().then(function(instances){...})` should be used.
// example:
// Parse all widgets on a page:
// | parser.parse();
// example:
// Parse all classes within the node with id="foo"
// | parser.parse(dojo.byId('foo'));
// example:
// Parse all classes in a page, but do not call .startup() on any
// child
// | parser.parse({ noStart: true })
// example:
// Parse all classes in a node, but do not call .startup()
// | parser.parse(someNode, { noStart:true });
// | // or
// | parser.parse({ noStart:true, rootNode: someNode });
// determine the root node and options based on the passed arguments.
var root;
if(!options && rootNode && rootNode.rootNode){
options = rootNode;
root = options.rootNode;
}else if(rootNode && dlang.isObject(rootNode) && !("nodeType" in rootNode)){
options = rootNode;
}else{
root = rootNode;
}
root = root ? dhtml.byId(root) : dwindow.body();
options = options || {};
var mixin = options.template ? { template: true } : {},
instances = [],
self = this;
// First scan for any <script type=dojo/require> nodes, and execute.
// Then scan for all nodes with data-dojo-type, and load any unloaded modules.
// Then build the object instances. Add instances to already existing (but empty) instances[] array,
// which may already have been returned to caller. Also, use otherwise to collect and throw any errors
// that occur during the parse().
var p =
this._scanAmd(root, options).then(function(){
return self.scan(root, options);
}).then(function(parsedNodes){
return instances = instances.concat(self._instantiate(parsedNodes, mixin, options));
}).otherwise(function(e){
// TODO Modify to follow better pattern for promise error managment when available
console.error("dojo/parser::parse() error", e);
throw e;
});
// Blend the array with the promise
dlang.mixin(instances, p);
return instances;
}
};
if( 1 ){
dojo.parser = parser;
}
// Register the parser callback. It should be the first callback
// after the a11y test.
if(config.parseOnLoad){
ready(100, parser, "parse");
}
return parser;
});
},
'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n",
'dojo/dnd/Manager':function(){
define("dojo/dnd/Manager", [
"../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../_base/window",
"../dom-class", "../Evented", "../has", "../keys", "../on", "../topic", "../touch",
"./common", "./autoscroll", "./Avatar"
], function(array, declare, event, lang, win, domClass, Evented, has, keys, on, topic, touch,
dnd, autoscroll, Avatar){
// module:
// dojo/dnd/Manager
var Manager = declare("dojo.dnd.Manager", [Evented], {
// summary:
// the manager of DnD operations (usually a singleton)
constructor: function(){
this.avatar = null;
this.source = null;
this.nodes = [];
this.copy = true;
this.target = null;
this.canDropFlag = false;
this.events = [];
},
// avatar's offset from the mouse
OFFSET_X: has("touch") ? 0 : 16,
OFFSET_Y: has("touch") ? -64 : 16,
// methods
overSource: function(source){
// summary:
// called when a source detected a mouse-over condition
// source: Object
// the reporter
if(this.avatar){
this.target = (source && source.targetState != "Disabled") ? source : null;
this.canDropFlag = Boolean(this.target);
this.avatar.update();
}
topic.publish("/dnd/source/over", source);
},
outSource: function(source){
// summary:
// called when a source detected a mouse-out condition
// source: Object
// the reporter
if(this.avatar){
if(this.target == source){
this.target = null;
this.canDropFlag = false;
this.avatar.update();
topic.publish("/dnd/source/over", null);
}
}else{
topic.publish("/dnd/source/over", null);
}
},
startDrag: function(source, nodes, copy){
// summary:
// called to initiate the DnD operation
// source: Object
// the source which provides items
// nodes: Array
// the list of transferred items
// copy: Boolean
// copy items, if true, move items otherwise
// Tell autoscroll that a drag is starting
autoscroll.autoScrollStart(win.doc);
this.source = source;
this.nodes = nodes;
this.copy = Boolean(copy); // normalizing to true boolean
this.avatar = this.makeAvatar();
win.body().appendChild(this.avatar.node);
topic.publish("/dnd/start", source, nodes, this.copy);
this.events = [
on(win.doc, touch.move, lang.hitch(this, "onMouseMove")),
on(win.doc, touch.release, lang.hitch(this, "onMouseUp")),
on(win.doc, "keydown", lang.hitch(this, "onKeyDown")),
on(win.doc, "keyup", lang.hitch(this, "onKeyUp")),
// cancel text selection and text dragging
on(win.doc, "dragstart", event.stop),
on(win.body(), "selectstart", event.stop)
];
var c = "dojoDnd" + (copy ? "Copy" : "Move");
domClass.add(win.body(), c);
},
canDrop: function(flag){
// summary:
// called to notify if the current target can accept items
var canDropFlag = Boolean(this.target && flag);
if(this.canDropFlag != canDropFlag){
this.canDropFlag = canDropFlag;
this.avatar.update();
}
},
stopDrag: function(){
// summary:
// stop the DnD in progress
domClass.remove(win.body(), ["dojoDndCopy", "dojoDndMove"]);
array.forEach(this.events, function(handle){ handle.remove(); });
this.events = [];
this.avatar.destroy();
this.avatar = null;
this.source = this.target = null;
this.nodes = [];
},
makeAvatar: function(){
// summary:
// makes the avatar; it is separate to be overwritten dynamically, if needed
return new Avatar(this);
},
updateAvatar: function(){
// summary:
// updates the avatar; it is separate to be overwritten dynamically, if needed
this.avatar.update();
},
// mouse event processors
onMouseMove: function(e){
// summary:
// event processor for onmousemove
// e: Event
// mouse event
var a = this.avatar;
if(a){
autoscroll.autoScrollNodes(e);
//autoscroll.autoScroll(e);
var s = a.node.style;
s.left = (e.pageX + this.OFFSET_X) + "px";
s.top = (e.pageY + this.OFFSET_Y) + "px";
var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
if(this.copy != copy){
this._setCopyStatus(copy);
}
}
if(has("touch")){
// Prevent page from scrolling so that user can drag instead.
e.preventDefault();
}
},
onMouseUp: function(e){
// summary:
// event processor for onmouseup
// e: Event
// mouse event
if(this.avatar){
if(this.target && this.canDropFlag){
var copy = Boolean(this.source.copyState(dnd.getCopyKeyState(e)));
topic.publish("/dnd/drop/before", this.source, this.nodes, copy, this.target, e);
topic.publish("/dnd/drop", this.source, this.nodes, copy, this.target, e);
}else{
topic.publish("/dnd/cancel");
}
this.stopDrag();
}
},
// keyboard event processors
onKeyDown: function(e){
// summary:
// event processor for onkeydown:
// watching for CTRL for copy/move status, watching for ESCAPE to cancel the drag
// e: Event
// keyboard event
if(this.avatar){
switch(e.keyCode){
case keys.CTRL:
var copy = Boolean(this.source.copyState(true));
if(this.copy != copy){
this._setCopyStatus(copy);
}
break;
case keys.ESCAPE:
topic.publish("/dnd/cancel");
this.stopDrag();
break;
}
}
},
onKeyUp: function(e){
// summary:
// event processor for onkeyup, watching for CTRL for copy/move status
// e: Event
// keyboard event
if(this.avatar && e.keyCode == keys.CTRL){
var copy = Boolean(this.source.copyState(false));
if(this.copy != copy){
this._setCopyStatus(copy);
}
}
},
// utilities
_setCopyStatus: function(copy){
// summary:
// changes the copy status
// copy: Boolean
// the copy status
this.copy = copy;
this.source._markDndStatus(this.copy);
this.updateAvatar();
domClass.replace(win.body(),
"dojoDnd" + (this.copy ? "Copy" : "Move"),
"dojoDnd" + (this.copy ? "Move" : "Copy"));
}
});
// dnd._manager:
// The manager singleton variable. Can be overwritten if needed.
dnd._manager = null;
Manager.manager = dnd.manager = function(){
// summary:
// Returns the current DnD manager. Creates one if it is not created yet.
if(!dnd._manager){
dnd._manager = new Manager();
}
return dnd._manager; // Object
};
return Manager;
});
},
'dijit/form/ToggleButton':function(){
define("dijit/form/ToggleButton", [
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.deprecated
"./Button",
"./_ToggleButtonMixin"
], function(declare, kernel, Button, _ToggleButtonMixin){
// module:
// dijit/form/ToggleButton
return declare("dijit.form.ToggleButton", [Button, _ToggleButtonMixin], {
// summary:
// A templated button widget that can be in two states (checked or not).
// Can be base class for things like tabs or checkbox or radio buttons.
baseClass: "dijitToggleButton",
setChecked: function(/*Boolean*/ checked){
// summary:
// Deprecated. Use set('checked', true/false) instead.
kernel.deprecated("setChecked("+checked+") is deprecated. Use set('checked',"+checked+") instead.", "", "2.0");
this.set('checked', checked);
}
});
});
},
'dojo/date/stamp':function(){
define("dojo/date/stamp", ["../_base/lang", "../_base/array"], function(lang, array){
// module:
// dojo/date/stamp
var stamp = {
// summary:
// TODOC
};
lang.setObject("dojo.date.stamp", stamp);
// Methods to convert dates to or from a wire (string) format using well-known conventions
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(!stamp._isoRegExp){
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 = 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);
array.forEach(array.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
};
/*=====
var __Options = {
// 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
};
=====*/
stamp.toISOString = function(/*Date*/ dateObject, /*__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
};
return stamp;
});
},
'dojo/Stateful':function(){
define("dojo/Stateful", ["./_base/declare", "./_base/lang", "./_base/array", "dojo/when"], function(declare, lang, array, when){
// module:
// dojo/Stateful
return declare("dojo.Stateful", null, {
// summary:
// Base class for objects that provide named properties with optional getter/setter
// control and the ability to watch for property changes
//
// The class also provides the functionality to auto-magically manage getters
// and setters for object attributes/properties.
//
// Getters and Setters should follow the format of _xxxGetter or _xxxSetter where
// the xxx is a name of the attribute to handle. So an attribute of "foo"
// would have a custom getter of _fooGetter and a custom setter of _fooSetter.
//
// example:
// | var obj = new dojo.Stateful();
// | obj.watch("foo", function(){
// | console.log("foo changed to " + this.get("foo"));
// | });
// | obj.set("foo","bar");
// _attrPairNames: Hash
// Used across all instances a hash to cache attribute names and their getter
// and setter names.
_attrPairNames: {},
_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]; }
return (apn[name] = {
s: "_" + name + "Setter",
g: "_" + name + "Getter"
});
},
postscript: function(/*Object?*/ params){
// Automatic setting of params during construction
if (params){ this.set(params); }
},
_get: function(name, names){
// summary:
// Private function that does a get based off a hash of names
// names:
// Hash of names of custom attributes
return typeof this[names.g] === "function" ? this[names.g]() : this[name];
},
get: function(/*String*/name){
// summary:
// Get a property on a Stateful instance.
// name:
// The property to get.
// returns:
// The property value on this Stateful instance.
// description:
// Get a named property on a Stateful object. The property may
// potentially be retrieved via a getter method in subclasses. In the base class
// this just retrieves the object's property.
// For example:
// | stateful = new dojo.Stateful({foo: 3});
// | stateful.get("foo") // returns 3
// | stateful.foo // returns 3
return this._get(name, this._getAttrNames(name)); //Any
},
set: function(/*String*/name, /*Object*/value){
// summary:
// Set a property on a Stateful instance
// name:
// The property to set.
// value:
// The value to set in the property.
// returns:
// The function returns this dojo.Stateful instance.
// description:
// Sets named properties on a stateful object and notifies any watchers of
// the property. A programmatic setter may be defined in subclasses.
// For example:
// | stateful = new dojo.Stateful();
// | stateful.watch(function(name, oldValue, value){
// | // this will be called on the set below
// | }
// | stateful.set(foo, 5);
//
// set() may also be called with a hash of name/value pairs, ex:
// | myObj.set({
// | foo: "Howdy",
// | bar: 3
// | })
// This is equivalent to calling set(foo, "Howdy") and set(bar, 3)
// If an object is used, iterate through object
if(typeof name === "object"){
for(var x in name){
if(name.hasOwnProperty(x) && x !="_watchCallbacks"){
this.set(x, name[x]);
}
}
return this;
}
var names = this._getAttrNames(name),
oldValue = this._get(name, names),
setter = this[names.s],
result;
if(typeof setter === "function"){
// use the explicit setter
result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
}else{
// no setter so set attribute directly
this[name] = value;
}
if(this._watchCallbacks){
var self = this;
// If setter returned a promise, wait for it to complete, otherwise call watches immediatly
when(result, function(){
self._watchCallbacks(name, oldValue, value);
});
}
return this; // dojo/Stateful
},
_changeAttrValue: function(name, value){
// summary:
// Internal helper for directly changing an attribute value.
//
// name: String
// The property to set.
// value: Mixed
// The value to set in the property.
//
// description:
// Directly change the value of an attribute on an object, bypassing any
// accessor setter. Also handles the calling of watch and emitting events.
// It is designed to be used by descendent class when there are two values
// of attributes that are linked, but calling .set() is not appropriate.
var oldValue = this.get(name);
this[name] = value;
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
return this; // dojo/Stateful
},
watch: function(/*String?*/name, /*Function*/callback){
// summary:
// Watches a property for changes
// name:
// Indicates the property to watch. This is optional (the callback may be the
// only parameter), and if omitted, all the properties will be watched
// returns:
// An object handle for the watch. The unwatch method of this object
// can be used to discontinue watching this property:
// | var watchHandle = obj.watch("foo", callback);
// | watchHandle.unwatch(); // callback won't be called now
// callback:
// The function to execute when the property changes. This will be called after
// the property has been changed. The callback will be called with the |this|
// set to the instance, the first argument as the name of the property, the
// second argument as the old value and the third argument as the new value.
var callbacks = this._watchCallbacks;
if(!callbacks){
var self = this;
callbacks = this._watchCallbacks = function(name, oldValue, value, ignoreCatchall){
var notify = function(propertyCallbacks){
if(propertyCallbacks){
propertyCallbacks = propertyCallbacks.slice();
for(var i = 0, l = propertyCallbacks.length; i < l; i++){
propertyCallbacks[i].call(self, name, oldValue, value);
}
}
};
notify(callbacks['_' + name]);
if(!ignoreCatchall){
notify(callbacks["*"]); // the catch-all
}
}; // we use a function instead of an object so it will be ignored by JSON conversion
}
if(!callback && typeof name === "function"){
callback = name;
name = "*";
}else{
// prepend with dash to prevent name conflicts with function (like "name" property)
name = '_' + name;
}
var propertyCallbacks = callbacks[name];
if(typeof propertyCallbacks !== "object"){
propertyCallbacks = callbacks[name] = [];
}
propertyCallbacks.push(callback);
// TODO: Remove unwatch in 2.0
var handle = {};
handle.unwatch = handle.remove = function(){
var index = array.indexOf(propertyCallbacks, callback);
if(index > -1){
propertyCallbacks.splice(index, 1);
}
};
return handle; //Object
}
});
});
},
'dijit/layout/AccordionContainer':function(){
require({cache:{
'url:dijit/layout/templates/AccordionButton.html':"<div data-dojo-attach-event='onclick:_onTitleClick' class='dijitAccordionTitle' role=\"presentation\">\n\t<div data-dojo-attach-point='titleNode,focusNode' data-dojo-attach-event='onkeypress:_onTitleKeyPress'\n\t\t\tclass='dijitAccordionTitleFocus' role=\"tab\" aria-expanded=\"false\"\n\t\t><span class='dijitInline dijitAccordionArrow' role=\"presentation\"></span\n\t\t><span class='arrowTextUp' role=\"presentation\">+</span\n\t\t><span class='arrowTextDown' role=\"presentation\">-</span\n\t\t><img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon\" data-dojo-attach-point='iconNode' style=\"vertical-align: middle\" role=\"presentation\"/>\n\t\t<span role=\"presentation\" data-dojo-attach-point='titleTextNode' class='dijitAccordionText'></span>\n\t</div>\n</div>\n"}});
define("dijit/layout/AccordionContainer", [
"require",
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/_base/fx", // fx.Animation
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.attr
"dojo/dom-class", // domClass.remove
"dojo/dom-construct", // domConstruct.place
"dojo/dom-geometry",
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/sniff", // has("ie") has("dijit-legacy-requires")
"dojo/topic", // publish
"../focus", // focus.focus()
"../_base/manager", // manager.defaultDuration
"dojo/ready",
"../_Widget",
"../_Container",
"../_TemplatedMixin",
"../_CssStateMixin",
"./StackContainer",
"./ContentPane",
"dojo/text!./templates/AccordionButton.html"
], function(require, array, declare, event, fx, dom, domAttr, domClass, domConstruct, domGeometry,
keys, lang, has, topic, focus, manager, ready,
_Widget, _Container, _TemplatedMixin, _CssStateMixin, StackContainer, ContentPane, template){
// module:
// dijit/layout/AccordionContainer
// Design notes:
//
// An AccordionContainer is a StackContainer, but each child (typically ContentPane)
// is wrapped in a _AccordionInnerContainer. This is hidden from the caller.
//
// The resulting markup will look like:
//
// <div class=dijitAccordionContainer>
// <div class=dijitAccordionInnerContainer> (one pane)
// <div class=dijitAccordionTitle> (title bar) ... </div>
// <div class=dijtAccordionChildWrapper> (content pane) </div>
// </div>
// </div>
//
// Normally the dijtAccordionChildWrapper is hidden for all but one child (the shown
// child), so the space for the content pane is all the title bars + the one dijtAccordionChildWrapper,
// which on claro has a 1px border plus a 2px bottom margin.
//
// During animation there are two dijtAccordionChildWrapper's shown, so we need
// to compensate for that.
var AccordionButton = declare("dijit.layout._AccordionButton", [_Widget, _TemplatedMixin, _CssStateMixin], {
// summary:
// The title bar to click to open up an accordion pane.
// Internal widget used by AccordionContainer.
// tags:
// private
templateString: template,
// label: String
// Title of the pane
label: "",
_setLabelAttr: {node: "titleTextNode", type: "innerHTML" },
// title: String
// Tooltip that appears on hover
title: "",
_setTitleAttr: {node: "titleTextNode", type: "attribute", attribute: "title"},
// iconClassAttr: String
// CSS class for icon to left of label
iconClassAttr: "",
_setIconClassAttr: { node: "iconNode", type: "class" },
baseClass: "dijitAccordionTitle",
getParent: function(){
// summary:
// Returns the AccordionContainer parent.
// tags:
// private
return this.parent;
},
buildRendering: function(){
this.inherited(arguments);
var titleTextNodeId = this.id.replace(' ','_');
domAttr.set(this.titleTextNode, "id", titleTextNodeId+"_title");
this.focusNode.setAttribute("aria-labelledby", domAttr.get(this.titleTextNode, "id"));
dom.setSelectable(this.domNode, false);
},
getTitleHeight: function(){
// summary:
// Returns the height of the title dom node.
return domGeometry.getMarginSize(this.domNode).h; // Integer
},
// TODO: maybe the parent should set these methods directly rather than forcing the code
// into the button widget?
_onTitleClick: function(){
// summary:
// Callback when someone clicks my title.
var parent = this.getParent();
parent.selectChild(this.contentWidget, true);
focus.focus(this.focusNode);
},
_onTitleKeyPress: function(/*Event*/ evt){
return this.getParent()._onKeyPress(evt, this.contentWidget);
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this._set("selected", isSelected);
this.focusNode.setAttribute("aria-expanded", isSelected ? "true" : "false");
this.focusNode.setAttribute("aria-selected", isSelected ? "true" : "false");
this.focusNode.setAttribute("tabIndex", isSelected ? "0" : "-1");
}
});
var AccordionInnerContainer = declare("dijit.layout._AccordionInnerContainer", [_Widget, _CssStateMixin], {
// summary:
// Internal widget placed as direct child of AccordionContainer.containerNode.
// When other widgets are added as children to an AccordionContainer they are wrapped in
// this widget.
/*=====
// buttonWidget: Function|String
// Class to use to instantiate title
// (Wish we didn't have a separate widget for just the title but maintaining it
// for backwards compatibility, is it worth it?)
buttonWidget: null,
=====*/
/*=====
// contentWidget: dijit/_WidgetBase
// Pointer to the real child widget
contentWidget: null,
=====*/
baseClass: "dijitAccordionInnerContainer",
// tell nested layout widget that we will take care of sizing
isLayoutContainer: true,
buildRendering: function(){
// Builds a template like:
// <div class=dijitAccordionInnerContainer>
// Button
// <div class=dijitAccordionChildWrapper>
// ContentPane
// </div>
// </div>
// Create wrapper div, placed where the child is now
this.domNode = domConstruct.place("<div class='" + this.baseClass +
"' role='presentation'>", this.contentWidget.domNode, "after");
// wrapper div's first child is the button widget (ie, the title bar)
var child = this.contentWidget,
cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
this.button = child._buttonWidget = (new cls({
contentWidget: child,
label: child.title,
title: child.tooltip,
dir: child.dir,
lang: child.lang,
textDir: child.textDir,
iconClass: child.iconClass,
id: child.id + "_button",
parent: this.parent
})).placeAt(this.domNode);
// and then the actual content widget (changing it from prior-sibling to last-child),
// wrapped by a <div class=dijitAccordionChildWrapper>
this.containerNode = domConstruct.place("<div class='dijitAccordionChildWrapper' style='display:none'>", this.domNode);
domConstruct.place(this.contentWidget.domNode, this.containerNode);
},
postCreate: function(){
this.inherited(arguments);
// Map changes in content widget's title etc. to changes in the button
var button = this.button;
this._contentWidgetWatches = [
this.contentWidget.watch('title', lang.hitch(this, function(name, oldValue, newValue){
button.set("label", newValue);
})),
this.contentWidget.watch('tooltip', lang.hitch(this, function(name, oldValue, newValue){
button.set("title", newValue);
})),
this.contentWidget.watch('iconClass', lang.hitch(this, function(name, oldValue, newValue){
button.set("iconClass", newValue);
}))
];
},
_setSelectedAttr: function(/*Boolean*/ isSelected){
this._set("selected", isSelected);
this.button.set("selected", isSelected);
if(isSelected){
var cw = this.contentWidget;
if(cw.onSelected){ cw.onSelected(); }
}
},
startup: function(){
// Called by _Container.addChild()
this.contentWidget.startup();
},
destroy: function(){
this.button.destroyRecursive();
array.forEach(this._contentWidgetWatches || [], function(w){ w.unwatch(); });
delete this.contentWidget._buttonWidget;
delete this.contentWidget._wrapperWidget;
this.inherited(arguments);
},
destroyDescendants: function(/*Boolean*/ preserveDom){
// since getChildren isn't working for me, have to code this manually
this.contentWidget.destroyRecursive(preserveDom);
}
});
var AccordionContainer = declare("dijit.layout.AccordionContainer", StackContainer, {
// summary:
// Holds a set of panes where every pane's title is visible, but only one pane's content is visible at a time,
// and switching between panes is visualized by sliding the other panes up/down.
// example:
// | <div data-dojo-type="dijit/layout/AccordionContainer">
// | <div data-dojo-type="dijit/layout/ContentPane" title="pane 1">
// | </div>
// | <div data-dojo-type="dijit/layout/ContentPane" title="pane 2">
// | <p>This is some text</p>
// | </div>
// | </div>
// duration: Integer
// Amount of time (in ms) it takes to slide panes
duration: manager.defaultDuration,
// buttonWidget: [const] String
// The name of the widget used to display the title of each pane
buttonWidget: AccordionButton,
/*=====
// _verticalSpace: Number
// Pixels of space available for the open pane
// (my content box size minus the cumulative size of all the title bars)
_verticalSpace: 0,
=====*/
baseClass: "dijitAccordionContainer",
buildRendering: function(){
this.inherited(arguments);
this.domNode.style.overflow = "hidden"; // TODO: put this in dijit.css
this.domNode.setAttribute("role", "tablist"); // TODO: put this in template
},
startup: function(){
if(this._started){ return; }
this.inherited(arguments);
if(this.selectedChildWidget){
this.selectedChildWidget._wrapperWidget.set("selected", true);
}
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
// Set the height of the open pane based on what room remains.
var openPane = this.selectedChildWidget;
if(!openPane){ return;}
// space taken up by title, plus wrapper div (with border/margin) for open pane
var wrapperDomNode = openPane._wrapperWidget.domNode,
wrapperDomNodeMargin = domGeometry.getMarginExtents(wrapperDomNode),
wrapperDomNodePadBorder = domGeometry.getPadBorderExtents(wrapperDomNode),
wrapperContainerNode = openPane._wrapperWidget.containerNode,
wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
mySize = this._contentBox;
// get cumulative height of all the unselected title bars
var totalCollapsedHeight = 0;
array.forEach(this.getChildren(), function(child){
if(child != openPane){
// Using domGeometry.getMarginSize() rather than domGeometry.position() since claro has 1px bottom margin
// to separate accordion panes. Not sure that works perfectly, it's probably putting a 1px
// margin below the bottom pane (even though we don't want one).
totalCollapsedHeight += domGeometry.getMarginSize(child._wrapperWidget.domNode).h;
}
});
this._verticalSpace = mySize.h - totalCollapsedHeight - wrapperDomNodeMargin.h
- wrapperDomNodePadBorder.h - wrapperContainerNodeMargin.h - wrapperContainerNodePadBorder.h
- openPane._buttonWidget.getTitleHeight();
// Memo size to make displayed child
this._containerContentBox = {
h: this._verticalSpace,
w: this._contentBox.w - wrapperDomNodeMargin.w - wrapperDomNodePadBorder.w
- wrapperContainerNodeMargin.w - wrapperContainerNodePadBorder.w
};
if(openPane){
openPane.resize(this._containerContentBox);
}
},
_setupChild: function(child){
// Overrides _LayoutWidget._setupChild().
// Put wrapper widget around the child widget, showing title
child._wrapperWidget = AccordionInnerContainer({
contentWidget: child,
buttonWidget: this.buttonWidget,
id: child.id + "_wrapper",
dir: child.dir,
lang: child.lang,
textDir: child.textDir,
parent: this
});
this.inherited(arguments);
},
addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
// Overrides _LayoutWidget.addChild().
if(this._started){
// Adding a child to a started Accordion is complicated because children have
// wrapper widgets. Default code path (calling this.inherited()) would add
// the new child inside another child's wrapper.
// First add in child as a direct child of this AccordionContainer
var refNode = this.containerNode;
if(insertIndex && typeof insertIndex == "number"){
var children = _Widget.prototype.getChildren.call(this); // get wrapper panes
if(children && children.length >= insertIndex){
refNode = children[insertIndex-1].domNode;
insertIndex = "after";
}
}
domConstruct.place(child.domNode, refNode, insertIndex);
if(!child._started){
child.startup();
}
// Then stick the wrapper widget around the child widget
this._setupChild(child);
// Code below copied from StackContainer
topic.publish(this.id+"-addChild", child, insertIndex); // publish
this.layout();
if(!this.selectedChildWidget){
this.selectChild(child);
}
}else{
// We haven't been started yet so just add in the child widget directly,
// and the wrapper will be created on startup()
this.inherited(arguments);
}
},
removeChild: function(child){
// Overrides _LayoutWidget.removeChild().
// Destroy wrapper widget first, before StackContainer.getChildren() call.
// Replace wrapper widget with true child widget (ContentPane etc.).
// This step only happens if the AccordionContainer has been started; otherwise there's no wrapper.
if(child._wrapperWidget){
domConstruct.place(child.domNode, child._wrapperWidget.domNode, "after");
child._wrapperWidget.destroy();
delete child._wrapperWidget;
}
domClass.remove(child.domNode, "dijitHidden");
this.inherited(arguments);
},
getChildren: function(){
// Overrides _Container.getChildren() to return content panes rather than internal AccordionInnerContainer panes
return array.map(this.inherited(arguments), function(child){
return child.declaredClass == "dijit.layout._AccordionInnerContainer" ? child.contentWidget : child;
}, this);
},
destroy: function(){
if(this._animation){
this._animation.stop();
}
array.forEach(this.getChildren(), function(child){
// If AccordionContainer has been started, then each child has a wrapper widget which
// also needs to be destroyed.
if(child._wrapperWidget){
child._wrapperWidget.destroy();
}else{
child.destroyRecursive();
}
});
this.inherited(arguments);
},
_showChild: function(child){
// Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
child._wrapperWidget.containerNode.style.display="block";
return this.inherited(arguments);
},
_hideChild: function(child){
// Override StackContainer._showChild() to set visibility of _wrapperWidget.containerNode
child._wrapperWidget.containerNode.style.display="none";
this.inherited(arguments);
},
_transition: function(/*dijit/_WidgetBase?*/ newWidget, /*dijit/_WidgetBase?*/ oldWidget, /*Boolean*/ animate){
// Overrides StackContainer._transition() to provide sliding of title bars etc.
if(has("ie") < 8){
// workaround animation bugs by not animating; not worth supporting animation for IE6 & 7
animate = false;
}
if(this._animation){
// there's an in-progress animation. speedily end it so we can do the newly requested one
this._animation.stop(true);
delete this._animation;
}
var self = this;
if(newWidget){
newWidget._wrapperWidget.set("selected", true);
var d = this._showChild(newWidget); // prepare widget to be slid in
// Size the new widget, in case this is the first time it's being shown,
// or I have been resized since the last time it was shown.
// Note that page must be visible for resizing to work.
if(this.doLayout && newWidget.resize){
newWidget.resize(this._containerContentBox);
}
}
if(oldWidget){
oldWidget._wrapperWidget.set("selected", false);
if(!animate){
this._hideChild(oldWidget);
}
}
if(animate){
var newContents = newWidget._wrapperWidget.containerNode,
oldContents = oldWidget._wrapperWidget.containerNode;
// During the animation we will be showing two dijitAccordionChildWrapper nodes at once,
// which on claro takes up 4px extra space (compared to stable AccordionContainer).
// Have to compensate for that by immediately shrinking the pane being closed.
var wrapperContainerNode = newWidget._wrapperWidget.containerNode,
wrapperContainerNodeMargin = domGeometry.getMarginExtents(wrapperContainerNode),
wrapperContainerNodePadBorder = domGeometry.getPadBorderExtents(wrapperContainerNode),
animationHeightOverhead = wrapperContainerNodeMargin.h + wrapperContainerNodePadBorder.h;
oldContents.style.height = (self._verticalSpace - animationHeightOverhead) + "px";
this._animation = new fx.Animation({
node: newContents,
duration: this.duration,
curve: [1, this._verticalSpace - animationHeightOverhead - 1],
onAnimate: function(value){
value = Math.floor(value); // avoid fractional values
newContents.style.height = value + "px";
oldContents.style.height = (self._verticalSpace - animationHeightOverhead - value) + "px";
},
onEnd: function(){
delete self._animation;
newContents.style.height = "auto";
oldWidget._wrapperWidget.containerNode.style.display = "none";
oldContents.style.height = "auto";
self._hideChild(oldWidget);
}
});
this._animation.onStop = this._animation.onEnd;
this._animation.play();
}
return d; // If child has an href, promise that fires when the widget has finished loading
},
// note: we are treating the container as controller here
_onKeyPress: function(/*Event*/ e, /*dijit/_WidgetBase*/ fromTitle){
// summary:
// Handle keypress events
// description:
// This is called from a handler on AccordionContainer.domNode
// (setup in StackContainer), and is also called directly from
// the click handler for accordion labels
if(this.disabled || e.altKey || !(fromTitle || e.ctrlKey)){
return;
}
var c = e.charOrCode;
if((fromTitle && (c == keys.LEFT_ARROW || c == keys.UP_ARROW)) ||
(e.ctrlKey && c == keys.PAGE_UP)){
this._adjacent(false)._buttonWidget._onTitleClick();
event.stop(e);
}else if((fromTitle && (c == keys.RIGHT_ARROW || c == keys.DOWN_ARROW)) ||
(e.ctrlKey && (c == keys.PAGE_DOWN || c == keys.TAB))){
this._adjacent(true)._buttonWidget._onTitleClick();
event.stop(e);
}
}
});
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/layout/AccordionPane"];
require(requires); // use indirection so modules not rolled into a build
});
}
// For monkey patching
AccordionContainer._InnerContainer = AccordionInnerContainer;
AccordionContainer._Button = AccordionButton;
return AccordionContainer;
});
},
'dijit/form/_AutoCompleterMixin':function(){
define("dijit/form/_AutoCompleterMixin", [
"dojo/data/util/filter", // patternToRegExp
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.get
"dojo/_base/event", // event.stop
"dojo/keys",
"dojo/_base/lang", // lang.clone lang.hitch
"dojo/query", // query
"dojo/regexp", // regexp.escapeString
"dojo/sniff", // has("ie")
"dojo/string", // string.substitute
"./DataList",
"../registry", // registry.byId
"./_TextBoxMixin", // defines _TextBoxMixin.selectInputText
"./_SearchMixin"
], function(filter, declare, domAttr, event, keys, lang, query, regexp, has, string,
DataList, registry, _TextBoxMixin, SearchMixin){
// module:
// dijit/form/_AutoCompleterMixin
return declare("dijit.form._AutoCompleterMixin", SearchMixin, {
// summary:
// A mixin that implements the base functionality for `dijit/form/ComboBox`/`dijit/form/FilteringSelect`
// description:
// All widgets that mix in dijit/form/_AutoCompleterMixin must extend `dijit/form/_FormValueWidget`.
// tags:
// protected
// item: Object
// This is the item returned by the dojo/store/api/Store implementation that
// provides the data for this ComboBox, it's the currently selected item.
item: null,
// autoComplete: Boolean
// If user types in a partial string, and then tab out of the `<input>` box,
// automatically copy the first entry displayed in the drop down list to
// the `<input>` field
autoComplete: true,
// highlightMatch: String
// One of: "first", "all" or "none".
//
// If the ComboBox/FilteringSelect opens with the search results and the searched
// string can be found, it will be highlighted. If set to "all"
// then will probably want to change `queryExpr` parameter to '*${0}*'
//
// Highlighting is only performed when `labelType` is "text", so as to not
// interfere with any HTML markup an HTML label might contain.
highlightMatch: "first",
// labelAttr: String?
// The entries in the drop down list come from this attribute in the
// dojo.data items.
// If not specified, the searchAttr attribute is used instead.
labelAttr: "",
// labelType: String
// Specifies how to interpret the labelAttr in the data store items.
// Can be "html" or "text".
labelType: "text",
// Flags to _HasDropDown to limit height of drop down to make it fit in viewport
maxHeight: -1,
// For backwards compatibility let onClick events propagate, even clicks on the down arrow button
_stopClickEvents: false,
_getCaretPos: function(/*DomNode*/ element){
// khtml 3.5.2 has selection* methods as does webkit nightlies from 2005-06-22
var pos = 0;
if(typeof(element.selectionStart) == "number"){
// FIXME: this is totally borked on Moz < 1.3. Any recourse?
pos = element.selectionStart;
}else if(has("ie")){
// in the case of a mouse click in a popup being handled,
// then the win.doc.selection is not the textarea, but the popup
// var r = win.doc.selection.createRange();
// hack to get IE 6 to play nice. What a POS browser.
var tr = element.ownerDocument.selection.createRange().duplicate();
var ntr = element.createTextRange();
tr.move("character",0);
ntr.move("character",0);
try{
// If control doesn't have focus, you get an exception.
// Seems to happen on reverse-tab, but can also happen on tab (seems to be a race condition - only happens sometimes).
// There appears to be no workaround for this - googled for quite a while.
ntr.setEndPoint("EndToEnd", tr);
pos = String(ntr.text).replace(/\r/g,"").length;
}catch(e){
// If focus has shifted, 0 is fine for caret pos.
}
}
return pos;
},
_setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
location = parseInt(location);
_TextBoxMixin.selectInputText(element, location, location);
},
_setDisabledAttr: function(/*Boolean*/ value){
// Additional code to set disabled state of ComboBox node.
// Overrides _FormValueWidget._setDisabledAttr() or ValidationTextBox._setDisabledAttr().
this.inherited(arguments);
this.domNode.setAttribute("aria-disabled", value ? "true" : "false");
},
_onKey: function(/*Event*/ evt){
// summary:
// Handles keyboard events
if(evt.charCode >= 32){ return; } // alphanumeric reserved for searching
var key = evt.charCode || evt.keyCode;
// except for cutting/pasting case - ctrl + x/v
if(key == keys.ALT || key == keys.CTRL || key == keys.META || key == keys.SHIFT){
return; // throw out spurious events
}
var pw = this.dropDown;
var highlighted = null;
this._abortQuery();
// _HasDropDown will do some of the work:
//
// 1. when drop down is not yet shown:
// - if user presses the down arrow key, call loadDropDown()
// 2. when drop down is already displayed:
// - on ESC key, call closeDropDown()
// - otherwise, call dropDown.handleKey() to process the keystroke
this.inherited(arguments);
if(evt.altKey || evt.ctrlKey || evt.metaKey){ return; } // don't process keys with modifiers - but we want shift+TAB
if(this._opened){
highlighted = pw.getHighlightedOption();
}
switch(key){
case keys.PAGE_DOWN:
case keys.DOWN_ARROW:
case keys.PAGE_UP:
case keys.UP_ARROW:
// Keystroke caused ComboBox_menu to move to a different item.
// Copy new item to <input> box.
if(this._opened){
this._announceOption(highlighted);
}
event.stop(evt);
break;
case keys.ENTER:
// prevent submitting form if user presses enter. Also
// prevent accepting the value if either Next or Previous
// are selected
if(highlighted){
// only stop event on prev/next
if(highlighted == pw.nextButton){
this._nextSearch(1);
event.stop(evt); // prevent submit
break;
}else if(highlighted == pw.previousButton){
this._nextSearch(-1);
event.stop(evt); // prevent submit
break;
}
event.stop(evt); // prevent submit if ENTER was to choose an item
}else{
// Update 'value' (ex: KY) according to currently displayed text
this._setBlurValue(); // set value if needed
this._setCaretPos(this.focusNode, this.focusNode.value.length); // move cursor to end and cancel highlighting
}
// fall through
case keys.TAB:
var newvalue = this.get('displayedValue');
// if the user had More Choices selected fall into the
// _onBlur handler
if(pw && (
newvalue == pw._messages["previousMessage"] ||
newvalue == pw._messages["nextMessage"])
){
break;
}
if(highlighted){
this._selectOption(highlighted);
}
// fall through
case keys.ESCAPE:
if(this._opened){
this._lastQuery = null; // in case results come back later
this.closeDropDown();
}
break;
}
},
_autoCompleteText: function(/*String*/ text){
// summary:
// Fill in the textbox with the first item from the drop down
// list, and highlight the characters that were
// auto-completed. For example, if user typed "CA" and the
// drop down list appeared, the textbox would be changed to
// "California" and "ifornia" would be highlighted.
var fn = this.focusNode;
// IE7: clear selection so next highlight works all the time
_TextBoxMixin.selectInputText(fn, fn.value.length);
// does text autoComplete the value in the textbox?
var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
if(text[caseFilter](0).indexOf(this.focusNode.value[caseFilter](0)) == 0){
var cpos = this.autoComplete ? this._getCaretPos(fn) : fn.value.length;
// only try to extend if we added the last character at the end of the input
if((cpos+1) > fn.value.length){
// only add to input node as we would overwrite Capitalisation of chars
// actually, that is ok
fn.value = text;//.substr(cpos);
// visually highlight the autocompleted characters
_TextBoxMixin.selectInputText(fn, cpos);
}
}else{
// text does not autoComplete; replace the whole value and highlight
fn.value = text;
_TextBoxMixin.selectInputText(fn);
}
},
_openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
// summary:
// Callback when a search completes.
// description:
// 1. generates drop-down list and calls _showResultList() to display it
// 2. if this result list is from user pressing "more choices"/"previous choices"
// then tell screen reader to announce new option
var wasSelected = this.dropDown.getHighlightedOption();
this.dropDown.clearResultList();
if(!results.length && options.start == 0){ // if no results and not just the previous choices button
this.closeDropDown();
return;
}
this._nextSearch = this.dropDown.onPage = lang.hitch(this, function(direction){
results.nextPage(direction !== -1);
this.focus();
});
// Fill in the textbox with the first item from the drop down list,
// and highlight the characters that were auto-completed. For
// example, if user typed "CA" and the drop down list appeared, the
// textbox would be changed to "California" and "ifornia" would be
// highlighted.
this.dropDown.createOptions(
results,
options,
lang.hitch(this, "_getMenuLabelFromItem")
);
// show our list (only if we have content, else nothing)
this._showResultList();
// #4091:
// tell the screen reader that the paging callback finished by
// shouting the next choice
if("direction" in options){
if(options.direction){
this.dropDown.highlightFirstOption();
}else if(!options.direction){
this.dropDown.highlightLastOption();
}
if(wasSelected){
this._announceOption(this.dropDown.getHighlightedOption());
}
}else if(this.autoComplete && !this._prev_key_backspace
// when the user clicks the arrow button to show the full list,
// startSearch looks for "*".
// it does not make sense to autocomplete
// if they are just previewing the options available.
&& !/^[*]+$/.test(query[this.searchAttr].toString())){
this._announceOption(this.dropDown.containerNode.firstChild.nextSibling); // 1st real item
}
},
_showResultList: function(){
// summary:
// Display the drop down if not already displayed, or if it is displayed, then
// reposition it if necessary (reposition may be necessary if drop down's height changed).
this.closeDropDown(true);
this.openDropDown();
this.domNode.setAttribute("aria-expanded", "true");
},
loadDropDown: function(/*Function*/ /*===== callback =====*/){
// Overrides _HasDropDown.loadDropDown().
// This is called when user has pressed button icon or pressed the down arrow key
// to open the drop down.
this._startSearchAll();
},
isLoaded: function(){
// signal to _HasDropDown that it needs to call loadDropDown() to load the
// drop down asynchronously before displaying it
return false;
},
closeDropDown: function(){
// Overrides _HasDropDown.closeDropDown(). Closes the drop down (assuming that it's open).
// This method is the callback when the user types ESC or clicking
// the button icon while the drop down is open. It's also called by other code.
this._abortQuery();
if(this._opened){
this.inherited(arguments);
this.domNode.setAttribute("aria-expanded", "false");
this.focusNode.removeAttribute("aria-activedescendant");
}
},
_setBlurValue: function(){
// if the user clicks away from the textbox OR tabs away, set the
// value to the textbox value
// #4617:
// if value is now more choices or previous choices, revert
// the value
var newvalue = this.get('displayedValue');
var pw = this.dropDown;
if(pw && (
newvalue == pw._messages["previousMessage"] ||
newvalue == pw._messages["nextMessage"]
)
){
this._setValueAttr(this._lastValueReported, true);
}else if(typeof this.item == "undefined"){
// Update 'value' (ex: KY) according to currently displayed text
this.item = null;
this.set('displayedValue', newvalue);
}else{
if(this.value != this._lastValueReported){
this._handleOnChange(this.value, true);
}
this._refreshState();
}
},
_setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
// summary:
// Set the displayed valued in the input box, and the hidden value
// that gets submitted, based on a dojo.data store item.
// description:
// Users shouldn't call this function; they should be calling
// set('item', value)
// tags:
// private
var value = '';
if(item){
if(!displayedValue){
displayedValue = this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
this.store.getValue(item, this.searchAttr) : item[this.searchAttr];
}
value = this._getValueField() != this.searchAttr ? this.store.getIdentity(item) : displayedValue;
}
this.set('value', value, priorityChange, displayedValue, item);
},
_announceOption: function(/*Node*/ node){
// summary:
// a11y code that puts the highlighted option in the textbox.
// This way screen readers will know what is happening in the
// menu.
if(!node){
return;
}
// pull the text value from the item attached to the DOM node
var newValue;
if(node == this.dropDown.nextButton ||
node == this.dropDown.previousButton){
newValue = node.innerHTML;
this.item = undefined;
this.value = '';
}else{
var item = this.dropDown.items[node.getAttribute("item")];
newValue = (this.store._oldAPI ? // remove getValue() for 2.0 (old dojo.data API)
this.store.getValue(item, this.searchAttr) : item[this.searchAttr]).toString();
this.set('item', item, false, newValue);
}
// get the text that the user manually entered (cut off autocompleted text)
this.focusNode.value = this.focusNode.value.substring(0, this._lastInput.length);
// set up ARIA activedescendant
this.focusNode.setAttribute("aria-activedescendant", domAttr.get(node, "id"));
// autocomplete the rest of the option to announce change
this._autoCompleteText(newValue);
},
_selectOption: function(/*DomNode*/ target){
// summary:
// Menu callback function, called when an item in the menu is selected.
this.closeDropDown();
if(target){
this._announceOption(target);
}
this._setCaretPos(this.focusNode, this.focusNode.value.length);
this._handleOnChange(this.value, true);
},
_startSearchAll: function(){
this._startSearch('');
},
_startSearchFromInput: function(){
this.item = undefined; // undefined means item needs to be set
this.inherited(arguments);
},
_startSearch: function(/*String*/ key){
// summary:
// Starts a search for elements matching key (key=="" means to return all items),
// and calls _openResultList() when the search completes, to display the results.
if(!this.dropDown){
var popupId = this.id + "_popup",
dropDownConstructor = lang.isString(this.dropDownClass) ?
lang.getObject(this.dropDownClass, false) : this.dropDownClass;
this.dropDown = new dropDownConstructor({
onChange: lang.hitch(this, this._selectOption),
id: popupId,
dir: this.dir,
textDir: this.textDir
});
this.focusNode.removeAttribute("aria-activedescendant");
this.textbox.setAttribute("aria-owns",popupId); // associate popup with textbox
}
this._lastInput = key; // Store exactly what was entered by the user.
this.inherited(arguments);
},
_getValueField: function(){
// summary:
// Helper for postMixInProperties() to set this.value based on data inlined into the markup.
// Returns the attribute name in the item (in dijit/form/_ComboBoxDataStore) to use as the value.
return this.searchAttr;
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
postMixInProperties: function(){
this.inherited(arguments);
if(!this.store){
var srcNodeRef = this.srcNodeRef;
// if user didn't specify store, then assume there are option tags
this.store = new DataList({}, srcNodeRef);
// if there is no value set and there is an option list, set
// the value to the first value to be consistent with native Select
// Firefox and Safari set value
// IE6 and Opera set selectedIndex, which is automatically set
// by the selected attribute of an option tag
// IE6 does not set value, Opera sets value = selectedIndex
if(!("value" in this.params)){
var item = (this.item = this.store.fetchSelectedItem());
if(item){
var valueField = this._getValueField();
// remove getValue() for 2.0 (old dojo.data API)
this.value = this.store._oldAPI ? this.store.getValue(item, valueField) : item[valueField];
}
}
}
},
postCreate: function(){
// summary:
// Subclasses must call this method from their postCreate() methods
// tags:
// protected
// find any associated label element and add to ComboBox node.
var label=query('label[for="'+this.id+'"]');
if(label.length){
if(!label[0].id){ label[0].id = this.id + "_label"; }
this.domNode.setAttribute("aria-labelledby", label[0].id);
}
this.inherited(arguments);
this.connect(this, "onSearch", "_openResultList");
},
_getMenuLabelFromItem: function(/*Item*/ item){
var label = this.labelFunc(item, this.store),
labelType = this.labelType;
// If labelType is not "text" we don't want to screw any markup ot whatever.
if(this.highlightMatch != "none" && this.labelType == "text" && this._lastInput){
label = this.doHighlight(label, this._lastInput);
labelType = "html";
}
return {html: labelType == "html", label: label};
},
doHighlight: function(/*String*/ label, /*String*/ find){
// summary:
// Highlights the string entered by the user in the menu. By default this
// highlights the first occurrence found. Override this method
// to implement your custom highlighting.
// tags:
// protected
var
// Add (g)lobal modifier when this.highlightMatch == "all" and (i)gnorecase when this.ignoreCase == true
modifiers = (this.ignoreCase ? "i" : "") + (this.highlightMatch == "all" ? "g" : ""),
i = this.queryExpr.indexOf("${0}");
find = regexp.escapeString(find); // escape regexp special chars
//If < appears in label, and user presses t, we don't want to highlight the t in the escaped "&lt;"
//first find out every occurences of "find", wrap each occurence in a pair of "\uFFFF" characters (which
//should not appear in any string). then html escape the whole string, and replace '\uFFFF" with the
//HTML highlight markup.
return this._escapeHtml(label.replace(
new RegExp((i == 0 ? "^" : "") + "("+ find +")" + (i == (this.queryExpr.length - 4) ? "$" : ""), modifiers),
'\uFFFF$1\uFFFF')).replace(
/\uFFFF([^\uFFFF]+)\uFFFF/g, '<span class="dijitComboBoxHighlightMatch">$1</span>'
); // returns String, (almost) valid HTML (entities encoded)
},
_escapeHtml: function(/*String*/ str){
// TODO Should become dojo.html.entities(), when exists use instead
// summary:
// Adds escape sequences for special characters in XML: `&<>"'`
str = String(str).replace(/&/gm, "&amp;").replace(/</gm, "&lt;")
.replace(/>/gm, "&gt;").replace(/"/gm, "&quot;"); //balance"
return str; // string
},
reset: function(){
// Overrides the _FormWidget.reset().
// Additionally reset the .item (to clean up).
this.item = null;
this.inherited(arguments);
},
labelFunc: function(item, store){
// summary:
// Computes the label to display based on the dojo.data store item.
// item: Object
// The item from the store
// store: dojo/store/api/Store
// The store.
// returns:
// The label that the ComboBox should display
// tags:
// private
// Use toString() because XMLStore returns an XMLItem whereas this
// method is expected to return a String (#9354).
// Remove getValue() for 2.0 (old dojo.data API)
return (store._oldAPI ? store.getValue(item, this.labelAttr || this.searchAttr) :
item[this.labelAttr || this.searchAttr]).toString(); // String
},
_setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
// summary:
// Hook so set('value', value) works.
// description:
// Sets the value of the select.
this._set("item", item||null); // value not looked up in store
if(value == null /* or undefined */){ value = ''; } // null translates to blank
this.inherited(arguments);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir, needed for the dropDown's textDir update.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this.inherited(arguments);
// update the drop down also (_ComboBoxMenuMixin)
if(this.dropDown){
this.dropDown._set("textDir", textDir);
}
}
});
});
},
'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n",
'url:dijit/layout/templates/_ScrollingTabControllerButton.html':"<div data-dojo-attach-event=\"onclick:_onClick\" class=\"dijitTabInnerDiv dijitTabContent dijitButtonContents\" data-dojo-attach-point=\"focusNode\">\n\t<img role=\"presentation\" alt=\"\" src=\"${_blankGif}\" class=\"dijitTabStripIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t<span data-dojo-attach-point=\"containerNode,titleNode\" class=\"dijitButtonText\"></span>\n</div>",
'dijit/form/MappedTextBox':function(){
define("dijit/form/MappedTextBox", [
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"./ValidationTextBox"
], function(declare, domConstruct, ValidationTextBox){
// module:
// dijit/form/MappedTextBox
return declare("dijit.form.MappedTextBox", ValidationTextBox, {
// summary:
// A dijit/form/ValidationTextBox subclass which provides a base class for widgets that have
// a visible formatted display value, and a serializable
// value in a hidden input field which is actually sent to the server.
// description:
// The visible display may
// be locale-dependent and interactive. The value sent to the server is stored in a hidden
// input field which uses the `name` attribute declared by the original widget. That value sent
// to the server is defined by the dijit/form/MappedTextBox.serialize() method and is typically
// locale-neutral.
// tags:
// protected
postMixInProperties: function(){
this.inherited(arguments);
// we want the name attribute to go to the hidden <input>, not the displayed <input>,
// so override _FormWidget.postMixInProperties() setting of nameAttrSetting
this.nameAttrSetting = "";
},
// Override default behavior to assign name to focusNode
_setNameAttr: null,
serialize: function(val /*=====, options =====*/){
// summary:
// Overridable function used to convert the get('value') result to a canonical
// (non-localized) string. For example, will print dates in ISO format, and
// numbers the same way as they are represented in javascript.
// val: anything
// options: Object?
// tags:
// protected extension
return val.toString ? val.toString() : ""; // String
},
toString: function(){
// summary:
// Returns widget as a printable string using the widget's value
// tags:
// protected
var val = this.filter(this.get('value')); // call filter in case value is nonstring and filter has been customized
return val != null ? (typeof val == "string" ? val : this.serialize(val, this.constraints)) : ""; // String
},
validate: function(){
// Overrides `dijit/form/TextBox.validate`
this.valueNode.value = this.toString();
return this.inherited(arguments);
},
buildRendering: function(){
// Overrides `dijit/_TemplatedMixin/buildRendering`
this.inherited(arguments);
// Create a hidden <input> node with the serialized value used for submit
// (as opposed to the displayed value).
// Passing in name as markup rather than calling domConstruct.create() with an attrs argument
// to make query(input[name=...]) work on IE. (see #8660)
this.valueNode = domConstruct.place("<input type='hidden'" + (this.name ? ' name="' + this.name.replace(/"/g, "&quot;") + '"' : "") + "/>", this.textbox, "after");
},
reset: function(){
// Overrides `dijit/form/ValidationTextBox.reset` to
// reset the hidden textbox value to ''
this.valueNode.value = '';
this.inherited(arguments);
}
});
});
},
'dijit/form/ComboBoxMixin':function(){
require({cache:{
'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"button presentation\" aria-hidden=\"true\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n"}});
define("dijit/form/ComboBoxMixin", [
"dojo/_base/declare", // declare
"dojo/_base/Deferred",
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.mixin
"dojo/store/util/QueryResults",
"./_AutoCompleterMixin",
"./_ComboBoxMenu",
"../_HasDropDown",
"dojo/text!./templates/DropDownBox.html"
], function(declare, Deferred, kernel, lang, QueryResults, _AutoCompleterMixin, _ComboBoxMenu, _HasDropDown, template){
// module:
// dijit/form/ComboBoxMixin
return declare("dijit.form.ComboBoxMixin", [_HasDropDown, _AutoCompleterMixin], {
// summary:
// Provides main functionality of ComboBox widget
// dropDownClass: [protected extension] Function String
// Dropdown widget class used to select a date/time.
// Subclasses should specify this.
dropDownClass: _ComboBoxMenu,
// hasDownArrow: Boolean
// Set this textbox to have a down arrow button, to display the drop down list.
// Defaults to true.
hasDownArrow: true,
templateString: template,
baseClass: "dijitTextBox dijitComboBox",
/*=====
// store: [const] dojo/store/api/Store|dojo/data/api/Read
// Reference to data provider object used by this ComboBox.
//
// Should be dojo/store/api/Store, but dojo/data/api/Read supported
// for backwards compatibility.
store: null,
=====*/
// Set classes like dijitDownArrowButtonHover depending on
// mouse action over button node
cssStateNodes: {
"_buttonNode": "dijitDownArrowButton"
},
_setHasDownArrowAttr: function(/*Boolean*/ val){
this._set("hasDownArrow", val);
this._buttonNode.style.display = val ? "" : "none";
},
_showResultList: function(){
// hide the tooltip
this.displayMessage("");
this.inherited(arguments);
},
_setStoreAttr: function(store){
// For backwards-compatibility, accept dojo.data store in addition to dojo/store/api/Store. Remove in 2.0.
if(!store.get){
lang.mixin(store, {
_oldAPI: true,
get: function(id){
// summary:
// Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
// Like dojo/store/DataStore.get() except returns native item.
var deferred = new Deferred();
this.fetchItemByIdentity({
identity: id,
onItem: function(object){
deferred.resolve(object);
},
onError: function(error){
deferred.reject(error);
}
});
return deferred.promise;
},
query: function(query, options){
// summary:
// Queries the store for objects. Like dojo/store/DataStore.query()
// except returned Deferred contains array of native items.
var deferred = new Deferred(function(){ fetchHandle.abort && fetchHandle.abort(); });
deferred.total = new Deferred();
var fetchHandle = this.fetch(lang.mixin({
query: query,
onBegin: function(count){
deferred.total.resolve(count);
},
onComplete: function(results){
deferred.resolve(results);
},
onError: function(error){
deferred.reject(error);
}
}, options));
return QueryResults(deferred);
}
});
}
this._set("store", store);
},
postMixInProperties: function(){
// Since _setValueAttr() depends on this.store, _setStoreAttr() needs to execute first.
// Unfortunately, without special code, it ends up executing second.
var store = this.params.store || this.store;
if(store){
this._setStoreAttr(store);
}
this.inherited(arguments);
// User may try to access this.store.getValue() etc. in a custom labelFunc() function.
// It's not available with the new data store for handling inline <option> tags, so add it.
if(!this.params.store && !this.store._oldAPI){
var clazz = this.declaredClass;
lang.mixin(this.store, {
getValue: function(item, attr){
kernel.deprecated(clazz + ".store.getValue(item, attr) is deprecated for builtin store. Use item.attr directly", "", "2.0");
return item[attr];
},
getLabel: function(item){
kernel.deprecated(clazz + ".store.getLabel(item) is deprecated for builtin store. Use item.label directly", "", "2.0");
return item.name;
},
fetch: function(args){
kernel.deprecated(clazz + ".store.fetch() is deprecated for builtin store.", "Use store.query()", "2.0");
var shim = ["dojo/data/ObjectStore"]; // indirection so it doesn't get rolled into a build
require(shim, lang.hitch(this, function(ObjectStore){
new ObjectStore({objectStore: this}).fetch(args);
}));
}
});
}
}
});
});
},
'dijit/form/_TextBoxMixin':function(){
define("dijit/form/_TextBoxMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/_base/event", // event.stop
"dojo/keys", // keys.ALT keys.CAPS_LOCK keys.CTRL keys.META keys.SHIFT
"dojo/_base/lang", // lang.mixin
"dojo/on", // on
"../main" // for exporting dijit._setSelectionRange, dijit.selectInputText
], function(array, declare, dom, event, keys, lang, on, dijit){
// module:
// dijit/form/_TextBoxMixin
var _TextBoxMixin = declare("dijit.form._TextBoxMixin", null, {
// summary:
// A mixin for textbox form input widgets
// trim: Boolean
// Removes leading and trailing whitespace if true. Default is false.
trim: false,
// uppercase: Boolean
// Converts all characters to uppercase if true. Default is false.
uppercase: false,
// lowercase: Boolean
// Converts all characters to lowercase if true. Default is false.
lowercase: false,
// propercase: Boolean
// Converts the first character of each word to uppercase if true.
propercase: false,
// maxLength: String
// HTML INPUT tag maxLength declaration.
maxLength: "",
// selectOnClick: [const] Boolean
// If true, all text will be selected when focused with mouse
selectOnClick: false,
// placeHolder: String
// Defines a hint to help users fill out the input field (as defined in HTML 5).
// This should only contain plain text (no html markup).
placeHolder: "",
_getValueAttr: function(){
// summary:
// Hook so get('value') works as we like.
// description:
// For `dijit/form/TextBox` this basically returns the value of the `<input>`.
//
// For `dijit/form/MappedTextBox` subclasses, which have both
// a "displayed value" and a separate "submit value",
// This treats the "displayed value" as the master value, computing the
// submit value from it via this.parse().
return this.parse(this.get('displayedValue'), this.constraints);
},
_setValueAttr: function(value, /*Boolean?*/ priorityChange, /*String?*/ formattedValue){
// summary:
// Hook so set('value', ...) works.
//
// description:
// Sets the value of the widget to "value" which can be of
// any type as determined by the widget.
//
// value:
// The visual element value is also set to a corresponding,
// but not necessarily the same, value.
//
// formattedValue:
// If specified, used to set the visual element value,
// otherwise a computed visual value is used.
//
// priorityChange:
// If true, an onChange event is fired immediately instead of
// waiting for the next blur event.
var filteredValue;
if(value !== undefined){
// TODO: this is calling filter() on both the display value and the actual value.
// I added a comment to the filter() definition about this, but it should be changed.
filteredValue = this.filter(value);
if(typeof formattedValue != "string"){
if(filteredValue !== null && ((typeof filteredValue != "number") || !isNaN(filteredValue))){
formattedValue = this.filter(this.format(filteredValue, this.constraints));
}else{ formattedValue = ''; }
}
}
if(formattedValue != null /* and !undefined */ && ((typeof formattedValue) != "number" || !isNaN(formattedValue)) && this.textbox.value != formattedValue){
this.textbox.value = formattedValue;
this._set("displayedValue", this.get("displayedValue"));
}
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, formattedValue);
}
this.inherited(arguments, [filteredValue, priorityChange]);
},
// displayedValue: String
// For subclasses like ComboBox where the displayed value
// (ex: Kentucky) and the serialized value (ex: KY) are different,
// this represents the displayed value.
//
// Setting 'displayedValue' through set('displayedValue', ...)
// updates 'value', and vice-versa. Otherwise 'value' is updated
// from 'displayedValue' periodically, like onBlur etc.
//
// TODO: move declaration to MappedTextBox?
// Problem is that ComboBox references displayedValue,
// for benefit of FilteringSelect.
displayedValue: "",
_getDisplayedValueAttr: function(){
// summary:
// Hook so get('displayedValue') works.
// description:
// Returns the displayed value (what the user sees on the screen),
// after filtering (ie, trimming spaces etc.).
//
// For some subclasses of TextBox (like ComboBox), the displayed value
// is different from the serialized value that's actually
// sent to the server (see `dijit/form/ValidationTextBox.serialize()`)
// TODO: maybe we should update this.displayedValue on every keystroke so that we don't need
// this method
// TODO: this isn't really the displayed value when the user is typing
return this.filter(this.textbox.value);
},
_setDisplayedValueAttr: function(/*String*/ value){
// summary:
// Hook so set('displayedValue', ...) works.
// description:
// Sets the value of the visual element to the string "value".
// The widget value is also set to a corresponding,
// but not necessarily the same, value.
if(value == null /* or undefined */){ value = '' }
else if(typeof value != "string"){ value = String(value) }
this.textbox.value = value;
// sets the serialized value to something corresponding to specified displayedValue
// (if possible), and also updates the textbox.value, for example converting "123"
// to "123.00"
this._setValueAttr(this.get('value'), undefined);
this._set("displayedValue", this.get('displayedValue'));
// textDir support
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, value);
}
},
format: function(value /*=====, constraints =====*/){
// summary:
// Replaceable function to convert a value to a properly formatted string.
// value: String
// constraints: Object
// tags:
// protected extension
return value == null /* or undefined */ ? "" : (value.toString ? value.toString() : value);
},
parse: function(value /*=====, constraints =====*/){
// summary:
// Replaceable function to convert a formatted string to a value
// value: String
// constraints: Object
// tags:
// protected extension
return value; // String
},
_refreshState: function(){
// summary:
// After the user types some characters, etc., this method is
// called to check the field for validity etc. The base method
// in `dijit/form/TextBox` does nothing, but subclasses override.
// tags:
// protected
},
/*=====
onInput: function(event){
// summary:
// Connect to this function to receive notifications of various user data-input events.
// Return false to cancel the event and prevent it from being processed.
// event:
// keydown | keypress | cut | paste | input
// tags:
// callback
},
=====*/
onInput: function(){},
__skipInputEvent: false,
_onInput: function(/*Event*/ evt){
// summary:
// Called AFTER the input event has happened
// set text direction according to textDir that was defined in creation
if(this.textDir == "auto"){
this.applyTextDir(this.focusNode, this.focusNode.value);
}
this._processInput(evt);
},
_processInput: function(/*Event*/ evt){
// summary:
// Default action handler for user input events
this._refreshState();
// In case someone is watch()'ing for changes to displayedValue
this._set("displayedValue", this.get("displayedValue"));
},
postCreate: function(){
// setting the value here is needed since value="" in the template causes "undefined"
// and setting in the DOM (instead of the JS object) helps with form reset actions
this.textbox.setAttribute("value", this.textbox.value); // DOM and JS values should be the same
this.inherited(arguments);
// normalize input events to reduce spurious event processing
// onkeydown: do not forward modifier keys
// set charOrCode to numeric keycode
// onkeypress: do not forward numeric charOrCode keys (already sent through onkeydown)
// onpaste & oncut: set charOrCode to 229 (IME)
// oninput: if primary event not already processed, set charOrCode to 229 (IME), else do not forward
var handleEvent = function(e){
var charOrCode;
if(e.type == "keydown"){
charOrCode = e.keyCode;
switch(charOrCode){ // ignore state keys
case keys.SHIFT:
case keys.ALT:
case keys.CTRL:
case keys.META:
case keys.CAPS_LOCK:
case keys.NUM_LOCK:
case keys.SCROLL_LOCK:
return;
}
if(!e.ctrlKey && !e.metaKey && !e.altKey){ // no modifiers
switch(charOrCode){ // ignore location keys
case keys.NUMPAD_0:
case keys.NUMPAD_1:
case keys.NUMPAD_2:
case keys.NUMPAD_3:
case keys.NUMPAD_4:
case keys.NUMPAD_5:
case keys.NUMPAD_6:
case keys.NUMPAD_7:
case keys.NUMPAD_8:
case keys.NUMPAD_9:
case keys.NUMPAD_MULTIPLY:
case keys.NUMPAD_PLUS:
case keys.NUMPAD_ENTER:
case keys.NUMPAD_MINUS:
case keys.NUMPAD_PERIOD:
case keys.NUMPAD_DIVIDE:
return;
}
if((charOrCode >= 65 && charOrCode <= 90) || (charOrCode >= 48 && charOrCode <= 57) || charOrCode == keys.SPACE){
return; // keypress will handle simple non-modified printable keys
}
var named = false;
for(var i in keys){
if(keys[i] === e.keyCode){
named = true;
break;
}
}
if(!named){ return; } // only allow named ones through
}
}
charOrCode = e.charCode >= 32 ? String.fromCharCode(e.charCode) : e.charCode;
if(!charOrCode){
charOrCode = (e.keyCode >= 65 && e.keyCode <= 90) || (e.keyCode >= 48 && e.keyCode <= 57) || e.keyCode == keys.SPACE ? String.fromCharCode(e.keyCode) : e.keyCode;
}
if(!charOrCode){
charOrCode = 229; // IME
}
if(e.type == "keypress"){
if(typeof charOrCode != "string"){ return; }
if((charOrCode >= 'a' && charOrCode <= 'z') || (charOrCode >= 'A' && charOrCode <= 'Z') || (charOrCode >= '0' && charOrCode <= '9') || (charOrCode === ' ')){
if(e.ctrlKey || e.metaKey || e.altKey){ return; } // can only be stopped reliably in keydown
}
}
if(e.type == "input"){
if(this.__skipInputEvent){ // duplicate event
this.__skipInputEvent = false;
return;
}
}else{
this.__skipInputEvent = true;
}
// create fake event to set charOrCode and to know if preventDefault() was called
var faux = { faux: true }, attr;
for(attr in e){
if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
var v = e[attr];
if(typeof v != "function" && typeof v != "undefined"){ faux[attr] = v; }
}
}
lang.mixin(faux, {
charOrCode: charOrCode,
_wasConsumed: false,
preventDefault: function(){
faux._wasConsumed = true;
e.preventDefault();
},
stopPropagation: function(){ e.stopPropagation(); }
});
// give web page author a chance to consume the event
//console.log(faux.type + ', charOrCode = (' + (typeof charOrCode) + ') ' + charOrCode + ', ctrl ' + !!faux.ctrlKey + ', alt ' + !!faux.altKey + ', meta ' + !!faux.metaKey + ', shift ' + !!faux.shiftKey);
if(this.onInput(faux) === false){ // return false means stop
faux.preventDefault();
faux.stopPropagation();
}
if(faux._wasConsumed){ return; } // if preventDefault was called
this.defer(function(){ this._onInput(faux); }); // widget notification after key has posted
};
this.own(on(this.textbox, "keydown, keypress, paste, cut, input, compositionend", lang.hitch(this, handleEvent)));
},
_blankValue: '', // if the textbox is blank, what value should be reported
filter: function(val){
// summary:
// Auto-corrections (such as trimming) that are applied to textbox
// value on blur or form submit.
// description:
// For MappedTextBox subclasses, this is called twice
//
// - once with the display value
// - once the value as set/returned by set('value', ...)
//
// and get('value'), ex: a Number for NumberTextBox.
//
// In the latter case it does corrections like converting null to NaN. In
// the former case the NumberTextBox.filter() method calls this.inherited()
// to execute standard trimming code in TextBox.filter().
//
// TODO: break this into two methods in 2.0
//
// tags:
// protected extension
if(val === null){ return this._blankValue; }
if(typeof val != "string"){ return val; }
if(this.trim){
val = lang.trim(val);
}
if(this.uppercase){
val = val.toUpperCase();
}
if(this.lowercase){
val = val.toLowerCase();
}
if(this.propercase){
val = val.replace(/[^\s]+/g, function(word){
return word.substring(0,1).toUpperCase() + word.substring(1);
});
}
return val;
},
_setBlurValue: function(){
this._setValueAttr(this.get('value'), true);
},
_onBlur: function(e){
if(this.disabled){ return; }
this._setBlurValue();
this.inherited(arguments);
},
_isTextSelected: function(){
return this.textbox.selectionStart != this.textbox.selectionEnd;
},
_onFocus: function(/*String*/ by){
if(this.disabled || this.readOnly){ return; }
// Select all text on focus via click if nothing already selected.
// Since mouse-up will clear the selection, need to defer selection until after mouse-up.
// Don't do anything on focus by tabbing into the widget since there's no associated mouse-up event.
if(this.selectOnClick && by == "mouse"){
this._selectOnClickHandle = this.connect(this.domNode, "onmouseup", function(){
// Only select all text on first click; otherwise users would have no way to clear
// the selection.
this.disconnect(this._selectOnClickHandle);
this._selectOnClickHandle = null;
// Check if the user selected some text manually (mouse-down, mouse-move, mouse-up)
// and if not, then select all the text
if(!this._isTextSelected()){
_TextBoxMixin.selectInputText(this.textbox);
}
});
// in case the mouseup never comes
this.defer(function(){
if(this._selectOnClickHandle){
this.disconnect(this._selectOnClickHandle);
this._selectOnClickHandle = null;
}
}, 500); // if mouseup not received soon, then treat it as some gesture
}
// call this.inherited() before refreshState(), since this.inherited() will possibly scroll the viewport
// (to scroll the TextBox into view), which will affect how _refreshState() positions the tooltip
this.inherited(arguments);
this._refreshState();
},
reset: function(){
// Overrides `dijit/_FormWidget/reset()`.
// Additionally resets the displayed textbox value to ''
this.textbox.value = '';
this.inherited(arguments);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
// only if new textDir is different from the old one
// and on widgets creation.
if(!this._created
|| this.textDir != textDir){
this._set("textDir", textDir);
// so the change of the textDir will take place immediately.
this.applyTextDir(this.focusNode, this.focusNode.value);
}
}
});
_TextBoxMixin._setSelectionRange = dijit._setSelectionRange = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
if(element.setSelectionRange){
element.setSelectionRange(start, stop);
}
};
_TextBoxMixin.selectInputText = dijit.selectInputText = function(/*DomNode*/ element, /*Number?*/ start, /*Number?*/ stop){
// summary:
// Select text in the input element argument, from start (default 0), to stop (default end).
// TODO: use functions in _editor/selection.js?
element = dom.byId(element);
if(isNaN(start)){ start = 0; }
if(isNaN(stop)){ stop = element.value ? element.value.length : 0; }
try{
element.focus();
_TextBoxMixin._setSelectionRange(element, start, stop);
}catch(e){ /* squelch random errors (esp. on IE) from unexpected focus changes or DOM nodes being hidden */ }
};
return _TextBoxMixin;
});
},
'dijit/form/SimpleTextarea':function(){
define("dijit/form/SimpleTextarea", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add
"dojo/sniff", // has("ie") has("opera")
"./TextBox"
], function(declare, domClass, has, TextBox){
// module:
// dijit/form/SimpleTextarea
return declare("dijit.form.SimpleTextarea", TextBox, {
// summary:
// A simple textarea that degrades, and responds to
// minimal LayoutContainer usage, and works with dijit/form/Form.
// Doesn't automatically size according to input, like Textarea.
//
// example:
// | <textarea data-dojo-type="dijit/form/SimpleTextarea" name="foo" value="bar" rows=30 cols=40></textarea>
//
// example:
// | new SimpleTextarea({ rows:20, cols:30 }, "foo");
baseClass: "dijitTextBox dijitTextArea",
// rows: Number
// The number of rows of text.
rows: "3",
// rows: Number
// The number of characters per line.
cols: "20",
templateString: "<textarea ${!nameAttrSetting} data-dojo-attach-point='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
postMixInProperties: function(){
// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
// TODO: parser will handle this in 2.0
if(!this.value && this.srcNodeRef){
this.value = this.srcNodeRef.value;
}
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
if(has("ie") && this.cols){ // attribute selectors is not supported in IE6
domClass.add(this.textbox, "dijitTextAreaCols");
}
},
filter: function(/*String*/ value){
// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
// as \r\n instead of just \n
if(value){
value = value.replace(/\r/g,"");
}
return this.inherited(arguments);
},
_onInput: function(/*Event?*/ e){
// Override TextBox._onInput() to enforce maxLength restriction
if(this.maxLength){
var maxLength = parseInt(this.maxLength);
var value = this.textbox.value.replace(/\r/g,'');
var overflow = value.length - maxLength;
if(overflow > 0){
var textarea = this.textbox;
if(textarea.selectionStart){
var pos = textarea.selectionStart;
var cr = 0;
if(has("opera")){
cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
}
this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
textarea.setSelectionRange(pos-overflow, pos-overflow);
}else if(this.ownerDocument.selection){ //IE
textarea.focus();
var range = this.ownerDocument.selection.createRange();
// delete overflow characters
range.moveStart("character", -overflow);
range.text = '';
// show cursor
range.select();
}
}
}
this.inherited(arguments);
}
});
});
},
'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n",
'dijit/_base/window':function(){
define("dijit/_base/window", [
"dojo/window", // windowUtils.get
"../main" // export symbol to dijit
], function(windowUtils, dijit){
// module:
// dijit/_base/window
/*=====
return {
// summary:
// Back compatibility module, new code should use windowUtils directly instead of using this module.
};
=====*/
dijit.getDocumentWindow = function(doc){
return windowUtils.get(doc);
};
});
},
'dojo/hccss':function(){
define("dojo/hccss", [
"require", // require.toUrl
"./_base/config", // config.blankGif
"./dom-class", // domClass.add
"./dom-style", // domStyle.getComputedStyle
"./has",
"./ready", // ready
"./_base/window" // win.body
], function(require, config, domClass, domStyle, has, ready, win){
// module:
// dojo/hccss
/*=====
return function(){
// summary:
// Test if computer is in high contrast mode (i.e. if browser is not displaying background images).
// Defines `has("highcontrast")` and sets `dj_a11y` CSS class on `<body>` if machine is in high contrast mode.
// Returns `has()` method;
};
=====*/
// Has() test for when background images aren't displayed. Don't call has("highcontrast") before dojo/domReady!.
has.add("highcontrast", function(){
// note: if multiple documents, doesn't matter which one we use
var div = win.doc.createElement("div");
div.style.cssText = "border: 1px solid; border-color:red green; position: absolute; height: 5px; top: -999px;" +
"background-image: url(" + (config.blankGif || require.toUrl("./resources/blank.gif")) + ");";
win.body().appendChild(div);
var cs = domStyle.getComputedStyle(div),
bkImg = cs.backgroundImage,
hc = (cs.borderTopColor == cs.borderRightColor) ||
(bkImg && (bkImg == "none" || bkImg == "url(invalid-url:)" ));
if(has("ie") <= 8){
div.outerHTML = ""; // prevent mixed-content warning, see http://support.microsoft.com/kb/925014
}else{
win.body().removeChild(div);
}
return hc;
});
// Priority is 90 to run ahead of parser priority of 100. For 2.0, remove the ready() call and instead
// change this module to depend on dojo/domReady!
ready(90, function(){
if(has("highcontrast")){
domClass.add(win.body(), "dj_a11y");
}
});
return has;
});
},
'dijit/form/RadioButton':function(){
define("dijit/form/RadioButton", [
"dojo/_base/declare", // declare
"./CheckBox",
"./_RadioButtonMixin"
], function(declare, CheckBox, _RadioButtonMixin){
// module:
// dijit/form/RadioButton
return declare("dijit.form.RadioButton", [CheckBox, _RadioButtonMixin], {
// summary:
// Same as an HTML radio, but with fancy styling.
baseClass: "dijitRadio"
});
});
},
'dijit/main':function(){
define("dijit/main", [
"dojo/_base/kernel"
], function(dojo){
// module:
// dijit/main
/*=====
return {
// summary:
// The dijit package main module.
// Deprecated. Users should access individual modules (ex: dijit/registry) directly.
};
=====*/
return dojo.dijit;
});
},
'dijit/_OnDijitClickMixin':function(){
define("dijit/_OnDijitClickMixin", [
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"dojo/_base/unload", // unload.addOnWindowUnload
"dojo/_base/window", // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
"./a11yclick"
], function(on, array, keys, declare, has, unload, win, a11yclick){
// module:
// dijit/_OnDijitClickMixin
var ret = declare("dijit._OnDijitClickMixin", null, {
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 connect.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.
// Events connected with `this.connect` are disconnected upon
// destruction.
// returns:
// A handle that can be passed to `disconnect` in order to disconnect before
// the widget is destroyed.
// example:
// | var btn = new 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
return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
}
});
ret.a11yclick = a11yclick; // back compat
return ret;
});
},
'dijit/InlineEditBox':function(){
require({cache:{
'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n"}});
define("dijit/InlineEditBox", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set domAttr.get
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-style", // domStyle.getComputedStyle domStyle.set domStyle.get
"dojo/_base/event", // event.stop
"dojo/i18n", // i18n.getLocalization
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // keys.ENTER keys.ESCAPE
"dojo/_base/lang", // lang.getObject
"dojo/sniff", // has("ie")
"dojo/when",
"./focus",
"./_Widget",
"./_TemplatedMixin",
"./_WidgetsInTemplateMixin",
"./_Container",
"./form/Button",
"./form/_TextBoxMixin",
"./form/TextBox",
"dojo/text!./templates/InlineEditBox.html",
"dojo/i18n!./nls/common"
], function(require, array, declare, domAttr, domClass, domConstruct, domStyle, event, i18n, kernel, keys, lang, has, when,
fm, _Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _Container, Button, _TextBoxMixin, TextBox, template){
// module:
// dijit/InlineEditBox
var InlineEditor = declare("dijit._InlineEditor", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin], {
// summary:
// Internal widget used by InlineEditBox, displayed when in editing mode
// to display the editor and maybe save/cancel buttons. Calling code should
// connect to save/cancel methods to detect when editing is finished
//
// Has mainly the same parameters as InlineEditBox, plus these values:
//
// style: Object
// Set of CSS attributes of display node, to replicate in editor
//
// value: String
// Value as an HTML string or plain text string, depending on renderAsHTML flag
templateString: template,
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit", "common", this.lang);
array.forEach(["buttonSave", "buttonCancel"], function(prop){
if(!this[prop]){
this[prop] = this.messages[prop];
}
}, this);
},
buildRendering: function(){
this.inherited(arguments);
// Create edit widget in place in the template
// TODO: remove getObject() for 2.0
var Cls = typeof this.editor == "string" ? (lang.getObject(this.editor) || require(this.editor)) : this.editor;
// Copy the style from the source
// Don't copy ALL properties though, just the necessary/applicable ones.
// wrapperStyle/destStyle code is to workaround IE bug where getComputedStyle().fontSize
// is a relative value like 200%, rather than an absolute value like 24px, and
// the 200% can refer *either* to a setting on the node or it's ancestor (see #11175)
var srcStyle = this.sourceStyle,
editStyle = "line-height:" + srcStyle.lineHeight + ";",
destStyle = domStyle.getComputedStyle(this.domNode);
array.forEach(["Weight", "Family", "Size", "Style"], function(prop){
var textStyle = srcStyle["font" + prop],
wrapperStyle = destStyle["font" + prop];
if(wrapperStyle != textStyle){
editStyle += "font-" + prop + ":" + srcStyle["font" + prop] + ";";
}
}, this);
array.forEach(["marginTop", "marginBottom", "marginLeft", "marginRight", "position", "left", "top", "right", "bottom", "float", "clear", "display"], function(prop){
this.domNode.style[prop] = srcStyle[prop];
}, this);
var width = this.inlineEditBox.width;
if(width == "100%"){
// block mode
editStyle += "width:100%;";
this.domNode.style.display = "block";
}else{
// inline-block mode
editStyle += "width:" + (width + (Number(width) == width ? "px" : "")) + ";";
}
var editorParams = lang.delegate(this.inlineEditBox.editorParams, {
style: editStyle,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
});
editorParams[ "displayedValue" in Cls.prototype ? "displayedValue" : "value"] = this.value;
this.editWidget = new Cls(editorParams, this.editorPlaceholder);
if(this.inlineEditBox.autoSave){
// Remove the save/cancel buttons since saving is done by simply tabbing away or
// selecting a value from the drop down list
domConstruct.destroy(this.buttonContainer);
}
},
postCreate: function(){
this.inherited(arguments);
var ew = this.editWidget;
if(this.inlineEditBox.autoSave){
// Selecting a value from a drop down list causes an onChange event and then we save
this.connect(ew, "onChange", "_onChange");
// ESC and TAB should cancel and save. Note that edit widgets do a stopEvent() on ESC key (to
// prevent Dialog from closing when the user just wants to revert the value in the edit widget),
// so this is the only way we can see the key press event.
this.connect(ew, "onKeyPress", "_onKeyPress");
}else{
// If possible, enable/disable save button based on whether the user has changed the value
if("intermediateChanges" in ew){
ew.set("intermediateChanges", true);
this.connect(ew, "onChange", "_onIntermediateChange");
this.saveButton.set("disabled", true);
}
}
},
startup: function(){
this.editWidget.startup();
this.inherited(arguments);
},
_onIntermediateChange: function(/*===== val =====*/){
// summary:
// Called for editor widgets that support the intermediateChanges=true flag as a way
// to detect when to enable/disabled the save button
this.saveButton.set("disabled", (this.getValue() == this._resetValue) || !this.enableSave());
},
destroy: function(){
this.editWidget.destroy(true); // let the parent wrapper widget clean up the DOM
this.inherited(arguments);
},
getValue: function(){
// summary:
// Return the [display] value of the edit widget
var ew = this.editWidget;
return String(ew.get("displayedValue" in ew ? "displayedValue" : "value"));
},
_onKeyPress: function(e){
// summary:
// Handler for keypress in the edit box in autoSave mode.
// description:
// For autoSave widgets, if Esc/Enter, call cancel/save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(e.altKey || e.ctrlKey){
return;
}
// If Enter/Esc pressed, treat as save/cancel.
if(e.charOrCode == keys.ESCAPE){
event.stop(e);
this.cancel(true); // sets editing=false which short-circuits _onBlur processing
}else if(e.charOrCode == keys.ENTER && e.target.tagName == "INPUT"){
event.stop(e);
this._onChange(); // fire _onBlur and then save
}
// _onBlur will handle TAB automatically by allowing
// the TAB to change focus before we mess with the DOM: #6227
// Expounding by request:
// The current focus is on the edit widget input field.
// save() will hide and destroy this widget.
// We want the focus to jump from the currently hidden
// displayNode, but since it's hidden, it's impossible to
// unhide it, focus it, and then have the browser focus
// away from it to the next focusable element since each
// of these events is asynchronous and the focus-to-next-element
// is already queued.
// So we allow the browser time to unqueue the move-focus event
// before we do all the hide/show stuff.
}
},
_onBlur: function(){
// summary:
// Called when focus moves outside the editor
// tags:
// private
this.inherited(arguments);
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing){
if(this.getValue() == this._resetValue){
this.cancel(false);
}else if(this.enableSave()){
this.save(false);
}
}
},
_onChange: function(){
// summary:
// Called when the underlying widget fires an onChange event,
// such as when the user selects a value from the drop down list of a ComboBox,
// which means that the user has finished entering the value and we should save.
// tags:
// private
if(this.inlineEditBox.autoSave && this.inlineEditBox.editing && this.enableSave()){
fm.focus(this.inlineEditBox.displayNode); // fires _onBlur which will save the formatted value
}
},
enableSave: function(){
// summary:
// User overridable function returning a Boolean to indicate
// if the Save button should be enabled or not - usually due to invalid conditions
// tags:
// extension
return this.editWidget.isValid ? this.editWidget.isValid() : true;
},
focus: function(){
// summary:
// Focus the edit widget.
// tags:
// protected
this.editWidget.focus();
if(this.editWidget.focusNode){
// IE can take 30ms to report the focus event, but focus manager needs to know before a 0ms timeout.
fm._onFocusNode(this.editWidget.focusNode);
if(this.editWidget.focusNode.tagName == "INPUT"){
this.defer(function(){
_TextBoxMixin.selectInputText(this.editWidget.focusNode);
});
}
}
}
});
var InlineEditBox = declare("dijit.InlineEditBox", _Widget, {
// summary:
// An element with in-line edit capabilities
//
// description:
// Behavior for an existing node (`<p>`, `<div>`, `<span>`, etc.) so that
// when you click it, an editor shows up in place of the original
// text. Optionally, Save and Cancel button are displayed below the edit widget.
// When Save is clicked, the text is pulled from the edit
// widget and redisplayed and the edit widget is again hidden.
// By default a plain Textarea widget is used as the editor (or for
// inline values a TextBox), but you can specify an editor such as
// dijit.Editor (for editing HTML) or a Slider (for adjusting a number).
// An edit widget must support the following API to be used:
//
// - displayedValue or value as initialization parameter,
// and available through set('displayedValue') / set('value')
// - void focus()
// - DOM-node focusNode = node containing editable text
// editing: [readonly] Boolean
// Is the node currently in edit mode?
editing: false,
// autoSave: Boolean
// Changing the value automatically saves it; don't have to push save button
// (and save button isn't even displayed)
autoSave: true,
// buttonSave: String
// Save button label
buttonSave: "",
// buttonCancel: String
// Cancel button label
buttonCancel: "",
// renderAsHtml: Boolean
// Set this to true if the specified Editor's value should be interpreted as HTML
// rather than plain text (ex: `dijit.Editor`)
renderAsHtml: false,
// editor: String|Function
// MID (ex: "dijit/form/TextBox") or constructor for editor widget
editor: TextBox,
// editorWrapper: String|Function
// Class name (or reference to the Class) for widget that wraps the editor widget, displaying save/cancel
// buttons.
editorWrapper: InlineEditor,
// editorParams: Object
// Set of parameters for editor, like {required: true}
editorParams: {},
// disabled: Boolean
// If true, clicking the InlineEditBox to edit it will have no effect.
disabled: false,
onChange: function(/*===== value =====*/){
// summary:
// Set this handler to be notified of changes to value.
// tags:
// callback
},
onCancel: function(){
// summary:
// Set this handler to be notified when editing is cancelled.
// tags:
// callback
},
// width: String
// Width of editor. By default it's width=100% (ie, block mode).
width: "100%",
// value: String
// The display value of the widget in read-only mode
value: "",
// noValueIndicator: [const] String
// The text that gets displayed when there is no value (so that the user has a place to click to edit)
noValueIndicator: has("ie") <= 6 ? // font-family needed on IE6 but it messes up IE8
"<span style='font-family: wingdings; text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>" :
"<span style='text-decoration: underline;'>&#160;&#160;&#160;&#160;&#x270d;&#160;&#160;&#160;&#160;</span>", // &#160; == &nbsp;
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my value
// - replace srcNodeRef with my generated DOM tree
this.editorParams = {};
},
postMixInProperties: function(){
this.inherited(arguments);
// save pointer to original source node, since Widget nulls-out srcNodeRef
this.displayNode = this.srcNodeRef;
// connect handlers to the display node
var events = {
ondijitclick: "_onClick",
onmouseover: "_onMouseOver",
onmouseout: "_onMouseOut",
onfocus: "_onMouseOver",
onblur: "_onMouseOut"
};
for(var name in events){
this.connect(this.displayNode, name, events[name]);
}
this.displayNode.setAttribute("role", "button");
if(!this.displayNode.getAttribute("tabIndex")){
this.displayNode.setAttribute("tabIndex", 0);
}
if(!this.value && !("value" in this.params)){ // "" is a good value if specified directly so check params){
this.value = lang.trim(this.renderAsHtml ? this.displayNode.innerHTML :
(this.displayNode.innerText || this.displayNode.textContent || ""));
}
if(!this.value){
this.displayNode.innerHTML = this.noValueIndicator;
}
domClass.add(this.displayNode, 'dijitInlineEditBoxDisplayMode');
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ disabled){
// summary:
// Hook to make set("disabled", ...) work.
// Set disabled state of widget.
this.domNode.setAttribute("aria-disabled", disabled ? "true" : "false");
if(disabled){
this.displayNode.removeAttribute("tabIndex");
}else{
this.displayNode.setAttribute("tabIndex", 0);
}
domClass.toggle(this.displayNode, "dijitInlineEditBoxDisplayModeDisabled", disabled);
this._set("disabled", disabled);
},
_onMouseOver: function(){
// summary:
// Handler for onmouseover and onfocus event.
// tags:
// private
if(!this.disabled){
domClass.add(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
}
},
_onMouseOut: function(){
// summary:
// Handler for onmouseout and onblur event.
// tags:
// private
domClass.remove(this.displayNode, "dijitInlineEditBoxDisplayModeHover");
},
_onClick: function(/*Event*/ e){
// summary:
// Handler for onclick event.
// tags:
// private
if(this.disabled){
return;
}
if(e){
event.stop(e);
}
this._onMouseOut();
// Since FF gets upset if you move a node while in an event handler for that node...
this.defer("edit");
},
edit: function(){
// summary:
// Display the editor widget in place of the original (read only) markup.
// tags:
// private
if(this.disabled || this.editing){
return;
}
this._set('editing', true);
// save some display node values that can be restored later
this._savedTabIndex = domAttr.get(this.displayNode, "tabIndex") || "0";
if(this.wrapperWidget){
var ew = this.wrapperWidget.editWidget;
ew.set("displayedValue" in ew ? "displayedValue" : "value", this.value);
}else{
// Placeholder for edit widget
// Put place holder (and eventually editWidget) before the display node so that it's positioned correctly
// when Calendar dropdown appears, which happens automatically on focus.
var placeholder = domConstruct.create("span", null, this.domNode, "before");
// Create the editor wrapper (the thing that holds the editor widget and the save/cancel buttons)
var Ewc = typeof this.editorWrapper == "string" ? lang.getObject(this.editorWrapper) : this.editorWrapper;
this.wrapperWidget = new Ewc({
value: this.value,
buttonSave: this.buttonSave,
buttonCancel: this.buttonCancel,
dir: this.dir,
lang: this.lang,
tabIndex: this._savedTabIndex,
editor: this.editor,
inlineEditBox: this,
sourceStyle: domStyle.getComputedStyle(this.displayNode),
save: lang.hitch(this, "save"),
cancel: lang.hitch(this, "cancel"),
textDir: this.textDir
}, placeholder);
if(!this.wrapperWidget._started){
this.wrapperWidget.startup();
}
if(!this._started){
this.startup();
}
}
var ww = this.wrapperWidget;
// to avoid screen jitter, we first create the editor with position: absolute, visibility: hidden,
// and then when it's finished rendering, we switch from display mode to editor
// position: absolute releases screen space allocated to the display node
// opacity:0 is the same as visibility: hidden but is still focusable
// visibility: hidden removes focus outline
domClass.add(this.displayNode, "dijitOffScreen");
domClass.remove(ww.domNode, "dijitOffScreen");
domStyle.set(ww.domNode, { visibility: "visible" });
domAttr.set(this.displayNode, "tabIndex", "-1"); // needed by WebKit for TAB from editor to skip displayNode
// After edit widget has finished initializing (in particular need to wait for dijit.Editor),
// or immediately if there is no onLoadDeferred Deferred,
// replace the display widget with edit widget, leaving them both displayed for a brief time so that
// focus can be shifted without incident.
when(ww.editWidget.onLoadDeferred, lang.hitch(ww, function(){
this.defer(function(){ // defer needed so that the change of focus doesn't happen on mousedown which also sets focus
this.focus(); // both nodes are showing, so we can switch focus safely
this._resetValue = this.getValue();
});
}));
},
_onBlur: function(){
// summary:
// Called when focus moves outside the InlineEditBox.
// Performs garbage collection.
// tags:
// private
this.inherited(arguments);
if(!this.editing){
/* causes IE focus problems, see TooltipDialog_a11y.html...
this.defer(function(){
if(this.wrapperWidget){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
});
*/
}
},
destroy: function(){
if(this.wrapperWidget && !this.wrapperWidget._destroyed){
this.wrapperWidget.destroy();
delete this.wrapperWidget;
}
this.inherited(arguments);
},
_showText: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, and optionally focus on display node
// tags:
// private
var ww = this.wrapperWidget;
domStyle.set(ww.domNode, { visibility: "hidden" }); // hide the editor from mouse/keyboard events
domClass.add(ww.domNode, "dijitOffScreen");
domClass.remove(this.displayNode, "dijitOffScreen");
domAttr.set(this.displayNode, "tabIndex", this._savedTabIndex);
if(focus){
fm.focus(this.displayNode);
}
},
save: function(/*Boolean*/ focus){
// summary:
// Save the contents of the editor and revert to display mode.
// focus: Boolean
// Focus on the display mode text
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
var ww = this.wrapperWidget;
var value = ww.getValue();
this.set('value', value); // display changed, formatted value
this._showText(focus); // set focus as needed
},
setValue: function(/*String*/ val){
// summary:
// Deprecated. Use set('value', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.setValue() is deprecated. Use set('value', ...) instead.", "", "2.0");
return this.set("value", val);
},
_setValueAttr: function(/*String*/ val){
// summary:
// Hook to make set("value", ...) work.
// Inserts specified HTML value into this node, or an "input needed" character if node is blank.
val = lang.trim(val);
var renderVal = this.renderAsHtml ? val : val.replace(/&/gm, "&amp;").replace(/</gm, "&lt;").replace(/>/gm, "&gt;").replace(/"/gm, "&quot;").replace(/\n/g, "<br>");
this.displayNode.innerHTML = renderVal || this.noValueIndicator;
this._set("value", val);
if(this._started){
// tell the world that we have changed
this.defer(function(){
this.onChange(val);
}); // defer prevents browser freeze for long-running event handlers
}
// contextual (auto) text direction depends on the text value
if(this.textDir == "auto"){
this.applyTextDir(this.displayNode, this.displayNode.innerText);
}
},
getValue: function(){
// summary:
// Deprecated. Use get('value') instead.
// tags:
// deprecated
kernel.deprecated("dijit.InlineEditBox.getValue() is deprecated. Use get('value') instead.", "", "2.0");
return this.get("value");
},
cancel: function(/*Boolean*/ focus){
// summary:
// Revert to display mode, discarding any changes made in the editor
// tags:
// private
if(this.disabled || !this.editing){
return;
}
this._set('editing', false);
// tell the world that we have no changes
this.defer("onCancel"); // defer prevents browser freeze for long-running event handlers
this._showText(focus);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
this.applyTextDir(this.displayNode, this.displayNode.innerText);
this.displayNode.align = this.dir == "rtl" ? "right" : "left"; //fix the text alignment
}
}
});
InlineEditBox._InlineEditor = InlineEditor; // for monkey patching
return InlineEditBox;
});
},
'dojo/selector/acme':function(){
define("dojo/selector/acme", [
"../dom", "../sniff", "../_base/array", "../_base/lang", "../_base/window"
], function(dom, has, array, lang, win){
// module:
// dojo/selector/acme
/*
acme architectural overview:
acme is a relatively full-featured CSS3 query library. It is
designed to take any valid CSS3 selector and return the nodes matching
the selector. To do this quickly, it processes queries in several
steps, applying caching where profitable.
The steps (roughly in reverse order of the way they appear in the code):
1.) check to see if we already have a "query dispatcher"
- if so, use that with the given parameterization. Skip to step 4.
2.) attempt to determine which branch to dispatch the query to:
- JS (optimized DOM iteration)
- native (FF3.1+, Safari 3.1+, IE 8+)
3.) tokenize and convert to executable "query dispatcher"
- this is where the lion's share of the complexity in the
system lies. In the DOM version, the query dispatcher is
assembled as a chain of "yes/no" test functions pertaining to
a section of a simple query statement (".blah:nth-child(odd)"
but not "div div", which is 2 simple statements). Individual
statement dispatchers are cached (to prevent re-definition)
as are entire dispatch chains (to make re-execution of the
same query fast)
4.) the resulting query dispatcher is called in the passed scope
(by default the top-level document)
- for DOM queries, this results in a recursive, top-down
evaluation of nodes based on each simple query section
- for native implementations, this may mean working around spec
bugs. So be it.
5.) matched nodes are pruned to ensure they are unique (if necessary)
*/
////////////////////////////////////////////////////////////////////////
// Toolkit aliases
////////////////////////////////////////////////////////////////////////
// if you are extracting acme for use in your own system, you will
// need to provide these methods and properties. No other porting should be
// necessary, save for configuring the system to use a class other than
// dojo/NodeList as the return instance instantiator
var trim = lang.trim;
var each = array.forEach;
var getDoc = function(){ return win.doc; };
// NOTE(alex): the spec is idiotic. CSS queries should ALWAYS be case-sensitive, but nooooooo
var cssCaseBug = (getDoc().compatMode) == "BackCompat";
////////////////////////////////////////////////////////////////////////
// Global utilities
////////////////////////////////////////////////////////////////////////
var specials = ">~+";
// global thunk to determine whether we should treat the current query as
// case sensitive or not. This switch is flipped by the query evaluator
// based on the document passed as the context to search.
var caseSensitive = false;
// how high?
var yesman = function(){ return true; };
////////////////////////////////////////////////////////////////////////
// Tokenizer
////////////////////////////////////////////////////////////////////////
var getQueryParts = function(query){
// summary:
// state machine for query tokenization
// description:
// instead of using a brittle and slow regex-based CSS parser,
// acme implements an AST-style query representation. This
// representation is only generated once per query. For example,
// the same query run multiple times or under different root nodes
// does not re-parse the selector expression but instead uses the
// cached data structure. The state machine implemented here
// terminates on the last " " (space) character and returns an
// ordered array of query component structures (or "parts"). Each
// part represents an operator or a simple CSS filtering
// expression. The structure for parts is documented in the code
// below.
// NOTE:
// this code is designed to run fast and compress well. Sacrifices
// to readability and maintainability have been made. Your best
// bet when hacking the tokenizer is to put The Donnas on *really*
// loud (may we recommend their "Spend The Night" release?) and
// just assume you're gonna make mistakes. Keep the unit tests
// open and run them frequently. Knowing is half the battle ;-)
if(specials.indexOf(query.slice(-1)) >= 0){
// if we end with a ">", "+", or "~", that means we're implicitly
// searching all children, so make it explicit
query += " * ";
}else{
// if you have not provided a terminator, one will be provided for
// you...
query += " ";
}
var ts = function(/*Integer*/ s, /*Integer*/ e){
// trim and slice.
// take an index to start a string slice from and an end position
// and return a trimmed copy of that sub-string
return trim(query.slice(s, e));
};
// the overall data graph of the full query, as represented by queryPart objects
var queryParts = [];
// state keeping vars
var inBrackets = -1, inParens = -1, inMatchFor = -1,
inPseudo = -1, inClass = -1, inId = -1, inTag = -1, currentQuoteChar,
lc = "", cc = "", pStart;
// iteration vars
var x = 0, // index in the query
ql = query.length,
currentPart = null, // data structure representing the entire clause
_cp = null; // the current pseudo or attr matcher
// several temporary variables are assigned to this structure during a
// potential sub-expression match:
// attr:
// a string representing the current full attribute match in a
// bracket expression
// type:
// if there's an operator in a bracket expression, this is
// used to keep track of it
// value:
// the internals of parenthetical expression for a pseudo. for
// :nth-child(2n+1), value might be "2n+1"
var endTag = function(){
// called when the tokenizer hits the end of a particular tag name.
// Re-sets state variables for tag matching and sets up the matcher
// to handle the next type of token (tag or operator).
if(inTag >= 0){
var tv = (inTag == x) ? null : ts(inTag, x); // .toLowerCase();
currentPart[ (specials.indexOf(tv) < 0) ? "tag" : "oper" ] = tv;
inTag = -1;
}
};
var endId = function(){
// called when the tokenizer might be at the end of an ID portion of a match
if(inId >= 0){
currentPart.id = ts(inId, x).replace(/\\/g, "");
inId = -1;
}
};
var endClass = function(){
// called when the tokenizer might be at the end of a class name
// match. CSS allows for multiple classes, so we augment the
// current item with another class in its list
if(inClass >= 0){
currentPart.classes.push(ts(inClass + 1, x).replace(/\\/g, ""));
inClass = -1;
}
};
var endAll = function(){
// at the end of a simple fragment, so wall off the matches
endId();
endTag();
endClass();
};
var endPart = function(){
endAll();
if(inPseudo >= 0){
currentPart.pseudos.push({ name: ts(inPseudo + 1, x) });
}
// hint to the selector engine to tell it whether or not it
// needs to do any iteration. Many simple selectors don't, and
// we can avoid significant construction-time work by advising
// the system to skip them
currentPart.loops = (
currentPart.pseudos.length ||
currentPart.attrs.length ||
currentPart.classes.length );
currentPart.oquery = currentPart.query = ts(pStart, x); // save the full expression as a string
// otag/tag are hints to suggest to the system whether or not
// it's an operator or a tag. We save a copy of otag since the
// tag name is cast to upper-case in regular HTML matches. The
// system has a global switch to figure out if the current
// expression needs to be case sensitive or not and it will use
// otag or tag accordingly
currentPart.otag = currentPart.tag = (currentPart["oper"]) ? null : (currentPart.tag || "*");
if(currentPart.tag){
// if we're in a case-insensitive HTML doc, we likely want
// the toUpperCase when matching on element.tagName. If we
// do it here, we can skip the string op per node
// comparison
currentPart.tag = currentPart.tag.toUpperCase();
}
// add the part to the list
if(queryParts.length && (queryParts[queryParts.length-1].oper)){
// operators are always infix, so we remove them from the
// list and attach them to the next match. The evaluator is
// responsible for sorting out how to handle them.
currentPart.infixOper = queryParts.pop();
currentPart.query = currentPart.infixOper.query + " " + currentPart.query;
/*
console.debug( "swapping out the infix",
currentPart.infixOper,
"and attaching it to",
currentPart);
*/
}
queryParts.push(currentPart);
currentPart = null;
};
// iterate over the query, character by character, building up a
// list of query part objects
for(; lc=cc, cc=query.charAt(x), x < ql; x++){
// cc: the current character in the match
// lc: the last character (if any)
// someone is trying to escape something, so don't try to match any
// fragments. We assume we're inside a literal.
if(lc == "\\"){ continue; }
if(!currentPart){ // a part was just ended or none has yet been created
// NOTE: I hate all this alloc, but it's shorter than writing tons of if's
pStart = x;
// rules describe full CSS sub-expressions, like:
// #someId
// .className:first-child
// but not:
// thinger > div.howdy[type=thinger]
// the indidual components of the previous query would be
// split into 3 parts that would be represented a structure like:
// [
// {
// query: "thinger",
// tag: "thinger",
// },
// {
// query: "div.howdy[type=thinger]",
// classes: ["howdy"],
// infixOper: {
// query: ">",
// oper: ">",
// }
// },
// ]
currentPart = {
query: null, // the full text of the part's rule
pseudos: [], // CSS supports multiple pseud-class matches in a single rule
attrs: [], // CSS supports multi-attribute match, so we need an array
classes: [], // class matches may be additive, e.g.: .thinger.blah.howdy
tag: null, // only one tag...
oper: null, // ...or operator per component. Note that these wind up being exclusive.
id: null, // the id component of a rule
getTag: function(){
return caseSensitive ? this.otag : this.tag;
}
};
// if we don't have a part, we assume we're going to start at
// the beginning of a match, which should be a tag name. This
// might fault a little later on, but we detect that and this
// iteration will still be fine.
inTag = x;
}
// Skip processing all quoted characters.
// If we are inside quoted text then currentQuoteChar stores the character that began the quote,
// thus that character that will end it.
if(currentQuoteChar){
if(cc == currentQuoteChar){
currentQuoteChar = null;
}
continue;
}else if (cc == "'" || cc == '"'){
currentQuoteChar = cc;
continue;
}
if(inBrackets >= 0){
// look for a the close first
if(cc == "]"){ // if we're in a [...] clause and we end, do assignment
if(!_cp.attr){
// no attribute match was previously begun, so we
// assume this is an attribute existence match in the
// form of [someAttributeName]
_cp.attr = ts(inBrackets+1, x);
}else{
// we had an attribute already, so we know that we're
// matching some sort of value, as in [attrName=howdy]
_cp.matchFor = ts((inMatchFor||inBrackets+1), x);
}
var cmf = _cp.matchFor;
if(cmf){
// try to strip quotes from the matchFor value. We want
// [attrName=howdy] to match the same
// as [attrName = 'howdy' ]
if( (cmf.charAt(0) == '"') || (cmf.charAt(0) == "'") ){
_cp.matchFor = cmf.slice(1, -1);
}
}
// remove backslash escapes from an attribute match, since DOM
// querying will get attribute values without backslashes
if(_cp.matchFor){
_cp.matchFor = _cp.matchFor.replace(/\\/g, "");
}
// end the attribute by adding it to the list of attributes.
currentPart.attrs.push(_cp);
_cp = null; // necessary?
inBrackets = inMatchFor = -1;
}else if(cc == "="){
// if the last char was an operator prefix, make sure we
// record it along with the "=" operator.
var addToCc = ("|~^$*".indexOf(lc) >=0 ) ? lc : "";
_cp.type = addToCc+cc;
_cp.attr = ts(inBrackets+1, x-addToCc.length);
inMatchFor = x+1;
}
// now look for other clause parts
}else if(inParens >= 0){
// if we're in a parenthetical expression, we need to figure
// out if it's attached to a pseudo-selector rule like
// :nth-child(1)
if(cc == ")"){
if(inPseudo >= 0){
_cp.value = ts(inParens+1, x);
}
inPseudo = inParens = -1;
}
}else if(cc == "#"){
// start of an ID match
endAll();
inId = x+1;
}else if(cc == "."){
// start of a class match
endAll();
inClass = x;
}else if(cc == ":"){
// start of a pseudo-selector match
endAll();
inPseudo = x;
}else if(cc == "["){
// start of an attribute match.
endAll();
inBrackets = x;
// provide a new structure for the attribute match to fill-in
_cp = {
/*=====
attr: null, type: null, matchFor: null
=====*/
};
}else if(cc == "("){
// we really only care if we've entered a parenthetical
// expression if we're already inside a pseudo-selector match
if(inPseudo >= 0){
// provide a new structure for the pseudo match to fill-in
_cp = {
name: ts(inPseudo+1, x),
value: null
};
currentPart.pseudos.push(_cp);
}
inParens = x;
}else if(
(cc == " ") &&
// if it's a space char and the last char is too, consume the
// current one without doing more work
(lc != cc)
){
endPart();
}
}
return queryParts;
};
////////////////////////////////////////////////////////////////////////
// DOM query infrastructure
////////////////////////////////////////////////////////////////////////
var agree = function(first, second){
// the basic building block of the yes/no chaining system. agree(f1,
// f2) generates a new function which returns the boolean results of
// both of the passed functions to a single logical-anded result. If
// either are not passed, the other is used exclusively.
if(!first){ return second; }
if(!second){ return first; }
return function(){
return first.apply(window, arguments) && second.apply(window, arguments);
};
};
var getArr = function(i, arr){
// helps us avoid array alloc when we don't need it
var r = arr||[]; // FIXME: should this be 'new d._NodeListCtor()' ?
if(i){ r.push(i); }
return r;
};
var _isElement = function(n){ return (1 == n.nodeType); };
// FIXME: need to coalesce _getAttr with defaultGetter
var blank = "";
var _getAttr = function(elem, attr){
if(!elem){ return blank; }
if(attr == "class"){
return elem.className || blank;
}
if(attr == "for"){
return elem.htmlFor || blank;
}
if(attr == "style"){
return elem.style.cssText || blank;
}
return (caseSensitive ? elem.getAttribute(attr) : elem.getAttribute(attr, 2)) || blank;
};
var attrs = {
"*=": function(attr, value){
return function(elem){
// E[foo*="bar"]
// an E element whose "foo" attribute value contains
// the substring "bar"
return (_getAttr(elem, attr).indexOf(value)>=0);
};
},
"^=": function(attr, value){
// E[foo^="bar"]
// an E element whose "foo" attribute value begins exactly
// with the string "bar"
return function(elem){
return (_getAttr(elem, attr).indexOf(value)==0);
};
},
"$=": function(attr, value){
// E[foo$="bar"]
// an E element whose "foo" attribute value ends exactly
// with the string "bar"
return function(elem){
var ea = " "+_getAttr(elem, attr);
var lastIndex = ea.lastIndexOf(value);
return lastIndex > -1 && (lastIndex==(ea.length-value.length));
};
},
"~=": function(attr, value){
// E[foo~="bar"]
// an E element whose "foo" attribute value is a list of
// space-separated values, one of which is exactly equal
// to "bar"
// return "[contains(concat(' ',@"+attr+",' '), ' "+ value +" ')]";
var tval = " "+value+" ";
return function(elem){
var ea = " "+_getAttr(elem, attr)+" ";
return (ea.indexOf(tval)>=0);
};
},
"|=": function(attr, value){
// E[hreflang|="en"]
// an E element whose "hreflang" attribute has a
// hyphen-separated list of values beginning (from the
// left) with "en"
var valueDash = value+"-";
return function(elem){
var ea = _getAttr(elem, attr);
return (
(ea == value) ||
(ea.indexOf(valueDash)==0)
);
};
},
"=": function(attr, value){
return function(elem){
return (_getAttr(elem, attr) == value);
};
}
};
// avoid testing for node type if we can. Defining this in the negative
// here to avoid negation in the fast path.
var _noNES = (typeof getDoc().firstChild.nextElementSibling == "undefined");
var _ns = !_noNES ? "nextElementSibling" : "nextSibling";
var _ps = !_noNES ? "previousElementSibling" : "previousSibling";
var _simpleNodeTest = (_noNES ? _isElement : yesman);
var _lookLeft = function(node){
// look left
while(node = node[_ps]){
if(_simpleNodeTest(node)){ return false; }
}
return true;
};
var _lookRight = function(node){
// look right
while(node = node[_ns]){
if(_simpleNodeTest(node)){ return false; }
}
return true;
};
var getNodeIndex = function(node){
var root = node.parentNode;
root = root.nodeType != 7 ? root : root.nextSibling; // PROCESSING_INSTRUCTION_NODE
var i = 0,
tret = root.children || root.childNodes,
ci = (node["_i"]||node.getAttribute("_i")||-1),
cl = (root["_l"]|| (typeof root.getAttribute !== "undefined" ? root.getAttribute("_l") : -1));
if(!tret){ return -1; }
var l = tret.length;
// we calculate the parent length as a cheap way to invalidate the
// cache. It's not 100% accurate, but it's much more honest than what
// other libraries do
if( cl == l && ci >= 0 && cl >= 0 ){
// if it's legit, tag and release
return ci;
}
// else re-key things
if(has("ie") && typeof root.setAttribute !== "undefined"){
root.setAttribute("_l", l);
}else{
root["_l"] = l;
}
ci = -1;
for(var te = root["firstElementChild"]||root["firstChild"]; te; te = te[_ns]){
if(_simpleNodeTest(te)){
if(has("ie")){
te.setAttribute("_i", ++i);
}else{
te["_i"] = ++i;
}
if(node === te){
// NOTE:
// shortcutting the return at this step in indexing works
// very well for benchmarking but we avoid it here since
// it leads to potential O(n^2) behavior in sequential
// getNodexIndex operations on a previously un-indexed
// parent. We may revisit this at a later time, but for
// now we just want to get the right answer more often
// than not.
ci = i;
}
}
}
return ci;
};
var isEven = function(elem){
return !((getNodeIndex(elem)) % 2);
};
var isOdd = function(elem){
return ((getNodeIndex(elem)) % 2);
};
var pseudos = {
"checked": function(name, condition){
return function(elem){
return !!("checked" in elem ? elem.checked : elem.selected);
};
},
"disabled": function(name, condition){
return function(elem){
return elem.disabled;
};
},
"enabled": function(name, condition){
return function(elem){
return !elem.disabled;
};
},
"first-child": function(){ return _lookLeft; },
"last-child": function(){ return _lookRight; },
"only-child": function(name, condition){
return function(node){
return _lookLeft(node) && _lookRight(node);
};
},
"empty": function(name, condition){
return function(elem){
// DomQuery and jQuery get this wrong, oddly enough.
// The CSS 3 selectors spec is pretty explicit about it, too.
var cn = elem.childNodes;
var cnl = elem.childNodes.length;
// if(!cnl){ return true; }
for(var x=cnl-1; x >= 0; x--){
var nt = cn[x].nodeType;
if((nt === 1)||(nt == 3)){ return false; }
}
return true;
};
},
"contains": function(name, condition){
var cz = condition.charAt(0);
if( cz == '"' || cz == "'" ){ //remove quote
condition = condition.slice(1, -1);
}
return function(elem){
return (elem.innerHTML.indexOf(condition) >= 0);
};
},
"not": function(name, condition){
var p = getQueryParts(condition)[0];
var ignores = { el: 1 };
if(p.tag != "*"){
ignores.tag = 1;
}
if(!p.classes.length){
ignores.classes = 1;
}
var ntf = getSimpleFilterFunc(p, ignores);
return function(elem){
return (!ntf(elem));
};
},
"nth-child": function(name, condition){
var pi = parseInt;
// avoid re-defining function objects if we can
if(condition == "odd"){
return isOdd;
}else if(condition == "even"){
return isEven;
}
// FIXME: can we shorten this?
if(condition.indexOf("n") != -1){
var tparts = condition.split("n", 2);
var pred = tparts[0] ? ((tparts[0] == '-') ? -1 : pi(tparts[0])) : 1;
var idx = tparts[1] ? pi(tparts[1]) : 0;
var lb = 0, ub = -1;
if(pred > 0){
if(idx < 0){
idx = (idx % pred) && (pred + (idx % pred));
}else if(idx>0){
if(idx >= pred){
lb = idx - idx % pred;
}
idx = idx % pred;
}
}else if(pred<0){
pred *= -1;
// idx has to be greater than 0 when pred is negative;
// shall we throw an error here?
if(idx > 0){
ub = idx;
idx = idx % pred;
}
}
if(pred > 0){
return function(elem){
var i = getNodeIndex(elem);
return (i>=lb) && (ub<0 || i<=ub) && ((i % pred) == idx);
};
}else{
condition = idx;
}
}
var ncount = pi(condition);
return function(elem){
return (getNodeIndex(elem) == ncount);
};
}
};
var defaultGetter = (has("ie") < 9 || has("ie") == 9 && has("quirks")) ? function(cond){
var clc = cond.toLowerCase();
if(clc == "class"){ cond = "className"; }
return function(elem){
return (caseSensitive ? elem.getAttribute(cond) : elem[cond]||elem[clc]);
};
} : function(cond){
return function(elem){
return (elem && elem.getAttribute && elem.hasAttribute(cond));
};
};
var getSimpleFilterFunc = function(query, ignores){
// generates a node tester function based on the passed query part. The
// query part is one of the structures generated by the query parser
// when it creates the query AST. The "ignores" object specifies which
// (if any) tests to skip, allowing the system to avoid duplicating
// work where it may have already been taken into account by other
// factors such as how the nodes to test were fetched in the first
// place
if(!query){ return yesman; }
ignores = ignores||{};
var ff = null;
if(!("el" in ignores)){
ff = agree(ff, _isElement);
}
if(!("tag" in ignores)){
if(query.tag != "*"){
ff = agree(ff, function(elem){
return (elem && ((caseSensitive ? elem.tagName : elem.tagName.toUpperCase()) == query.getTag()));
});
}
}
if(!("classes" in ignores)){
each(query.classes, function(cname, idx, arr){
// get the class name
/*
var isWildcard = cname.charAt(cname.length-1) == "*";
if(isWildcard){
cname = cname.substr(0, cname.length-1);
}
// I dislike the regex thing, even if memoized in a cache, but it's VERY short
var re = new RegExp("(?:^|\\s)" + cname + (isWildcard ? ".*" : "") + "(?:\\s|$)");
*/
var re = new RegExp("(?:^|\\s)" + cname + "(?:\\s|$)");
ff = agree(ff, function(elem){
return re.test(elem.className);
});
ff.count = idx;
});
}
if(!("pseudos" in ignores)){
each(query.pseudos, function(pseudo){
var pn = pseudo.name;
if(pseudos[pn]){
ff = agree(ff, pseudos[pn](pn, pseudo.value));
}
});
}
if(!("attrs" in ignores)){
each(query.attrs, function(attr){
var matcher;
var a = attr.attr;
// type, attr, matchFor
if(attr.type && attrs[attr.type]){
matcher = attrs[attr.type](a, attr.matchFor);
}else if(a.length){
matcher = defaultGetter(a);
}
if(matcher){
ff = agree(ff, matcher);
}
});
}
if(!("id" in ignores)){
if(query.id){
ff = agree(ff, function(elem){
return (!!elem && (elem.id == query.id));
});
}
}
if(!ff){
if(!("default" in ignores)){
ff = yesman;
}
}
return ff;
};
var _nextSibling = function(filterFunc){
return function(node, ret, bag){
while(node = node[_ns]){
if(_noNES && (!_isElement(node))){ continue; }
if(
(!bag || _isUnique(node, bag)) &&
filterFunc(node)
){
ret.push(node);
}
break;
}
return ret;
};
};
var _nextSiblings = function(filterFunc){
return function(root, ret, bag){
var te = root[_ns];
while(te){
if(_simpleNodeTest(te)){
if(bag && !_isUnique(te, bag)){
break;
}
if(filterFunc(te)){
ret.push(te);
}
}
te = te[_ns];
}
return ret;
};
};
// get an array of child *elements*, skipping text and comment nodes
var _childElements = function(filterFunc){
filterFunc = filterFunc||yesman;
return function(root, ret, bag){
// get an array of child elements, skipping text and comment nodes
var te, x = 0, tret = root.children || root.childNodes;
while(te = tret[x++]){
if(
_simpleNodeTest(te) &&
(!bag || _isUnique(te, bag)) &&
(filterFunc(te, x))
){
ret.push(te);
}
}
return ret;
};
};
// test to see if node is below root
var _isDescendant = function(node, root){
var pn = node.parentNode;
while(pn){
if(pn == root){
break;
}
pn = pn.parentNode;
}
return !!pn;
};
var _getElementsFuncCache = {};
var getElementsFunc = function(query){
var retFunc = _getElementsFuncCache[query.query];
// if we've got a cached dispatcher, just use that
if(retFunc){ return retFunc; }
// else, generate a new on
// NOTE:
// this function returns a function that searches for nodes and
// filters them. The search may be specialized by infix operators
// (">", "~", or "+") else it will default to searching all
// descendants (the " " selector). Once a group of children is
// found, a test function is applied to weed out the ones we
// don't want. Many common cases can be fast-pathed. We spend a
// lot of cycles to create a dispatcher that doesn't do more work
// than necessary at any point since, unlike this function, the
// dispatchers will be called every time. The logic of generating
// efficient dispatchers looks like this in pseudo code:
//
// # if it's a purely descendant query (no ">", "+", or "~" modifiers)
// if infixOperator == " ":
// if only(id):
// return def(root):
// return d.byId(id, root);
//
// elif id:
// return def(root):
// return filter(d.byId(id, root));
//
// elif cssClass && getElementsByClassName:
// return def(root):
// return filter(root.getElementsByClassName(cssClass));
//
// elif only(tag):
// return def(root):
// return root.getElementsByTagName(tagName);
//
// else:
// # search by tag name, then filter
// return def(root):
// return filter(root.getElementsByTagName(tagName||"*"));
//
// elif infixOperator == ">":
// # search direct children
// return def(root):
// return filter(root.children);
//
// elif infixOperator == "+":
// # search next sibling
// return def(root):
// return filter(root.nextElementSibling);
//
// elif infixOperator == "~":
// # search rightward siblings
// return def(root):
// return filter(nextSiblings(root));
var io = query.infixOper;
var oper = (io ? io.oper : "");
// the default filter func which tests for all conditions in the query
// part. This is potentially inefficient, so some optimized paths may
// re-define it to test fewer things.
var filterFunc = getSimpleFilterFunc(query, { el: 1 });
var qt = query.tag;
var wildcardTag = ("*" == qt);
var ecs = getDoc()["getElementsByClassName"];
if(!oper){
// if there's no infix operator, then it's a descendant query. ID
// and "elements by class name" variants can be accelerated so we
// call them out explicitly:
if(query.id){
// testing shows that the overhead of yesman() is acceptable
// and can save us some bytes vs. re-defining the function
// everywhere.
filterFunc = (!query.loops && wildcardTag) ?
yesman :
getSimpleFilterFunc(query, { el: 1, id: 1 });
retFunc = function(root, arr){
var te = dom.byId(query.id, (root.ownerDocument||root));
if(!te || !filterFunc(te)){ return; }
if(9 == root.nodeType){ // if root's a doc, we just return directly
return getArr(te, arr);
}else{ // otherwise check ancestry
if(_isDescendant(te, root)){
return getArr(te, arr);
}
}
};
}else if(
ecs &&
// isAlien check. Workaround for Prototype.js being totally evil/dumb.
/\{\s*\[native code\]\s*\}/.test(String(ecs)) &&
query.classes.length &&
!cssCaseBug
){
// it's a class-based query and we've got a fast way to run it.
// ignore class and ID filters since we will have handled both
filterFunc = getSimpleFilterFunc(query, { el: 1, classes: 1, id: 1 });
var classesString = query.classes.join(" ");
retFunc = function(root, arr, bag){
var ret = getArr(0, arr), te, x=0;
var tret = root.getElementsByClassName(classesString);
while((te = tret[x++])){
if(filterFunc(te, root) && _isUnique(te, bag)){
ret.push(te);
}
}
return ret;
};
}else if(!wildcardTag && !query.loops){
// it's tag only. Fast-path it.
retFunc = function(root, arr, bag){
var ret = getArr(0, arr), te, x=0;
var tag = query.getTag(),
tret = tag ? root.getElementsByTagName(tag) : [];
while((te = tret[x++])){
if(_isUnique(te, bag)){
ret.push(te);
}
}
return ret;
};
}else{
// the common case:
// a descendant selector without a fast path. By now it's got
// to have a tag selector, even if it's just "*" so we query
// by that and filter
filterFunc = getSimpleFilterFunc(query, { el: 1, tag: 1, id: 1 });
retFunc = function(root, arr, bag){
var ret = getArr(0, arr), te, x=0;
// we use getTag() to avoid case sensitivity issues
var tag = query.getTag(),
tret = tag ? root.getElementsByTagName(tag) : [];
while((te = tret[x++])){
if(filterFunc(te, root) && _isUnique(te, bag)){
ret.push(te);
}
}
return ret;
};
}
}else{
// the query is scoped in some way. Instead of querying by tag we
// use some other collection to find candidate nodes
var skipFilters = { el: 1 };
if(wildcardTag){
skipFilters.tag = 1;
}
filterFunc = getSimpleFilterFunc(query, skipFilters);
if("+" == oper){
retFunc = _nextSibling(filterFunc);
}else if("~" == oper){
retFunc = _nextSiblings(filterFunc);
}else if(">" == oper){
retFunc = _childElements(filterFunc);
}
}
// cache it and return
return _getElementsFuncCache[query.query] = retFunc;
};
var filterDown = function(root, queryParts){
// NOTE:
// this is the guts of the DOM query system. It takes a list of
// parsed query parts and a root and finds children which match
// the selector represented by the parts
var candidates = getArr(root), qp, x, te, qpl = queryParts.length, bag, ret;
for(var i = 0; i < qpl; i++){
ret = [];
qp = queryParts[i];
x = candidates.length - 1;
if(x > 0){
// if we have more than one root at this level, provide a new
// hash to use for checking group membership but tell the
// system not to post-filter us since we will already have been
// guaranteed to be unique
bag = {};
ret.nozip = true;
}
var gef = getElementsFunc(qp);
for(var j = 0; (te = candidates[j]); j++){
// for every root, get the elements that match the descendant
// selector, adding them to the "ret" array and filtering them
// via membership in this level's bag. If there are more query
// parts, then this level's return will be used as the next
// level's candidates
gef(te, ret, bag);
}
if(!ret.length){ break; }
candidates = ret;
}
return ret;
};
////////////////////////////////////////////////////////////////////////
// the query runner
////////////////////////////////////////////////////////////////////////
// these are the primary caches for full-query results. The query
// dispatcher functions are generated then stored here for hash lookup in
// the future
var _queryFuncCacheDOM = {},
_queryFuncCacheQSA = {};
// this is the second level of splitting, from full-length queries (e.g.,
// "div.foo .bar") into simple query expressions (e.g., ["div.foo",
// ".bar"])
var getStepQueryFunc = function(query){
var qparts = getQueryParts(trim(query));
// if it's trivial, avoid iteration and zipping costs
if(qparts.length == 1){
// we optimize this case here to prevent dispatch further down the
// chain, potentially slowing things down. We could more elegantly
// handle this in filterDown(), but it's slower for simple things
// that need to be fast (e.g., "#someId").
var tef = getElementsFunc(qparts[0]);
return function(root){
var r = tef(root, []);
if(r){ r.nozip = true; }
return r;
};
}
// otherwise, break it up and return a runner that iterates over the parts recursively
return function(root){
return filterDown(root, qparts);
};
};
// NOTES:
// * we can't trust QSA for anything but document-rooted queries, so
// caching is split into DOM query evaluators and QSA query evaluators
// * caching query results is dirty and leak-prone (or, at a minimum,
// prone to unbounded growth). Other toolkits may go this route, but
// they totally destroy their own ability to manage their memory
// footprint. If we implement it, it should only ever be with a fixed
// total element reference # limit and an LRU-style algorithm since JS
// has no weakref support. Caching compiled query evaluators is also
// potentially problematic, but even on large documents the size of the
// query evaluators is often < 100 function objects per evaluator (and
// LRU can be applied if it's ever shown to be an issue).
// * since IE's QSA support is currently only for HTML documents and even
// then only in IE 8's "standards mode", we have to detect our dispatch
// route at query time and keep 2 separate caches. Ugg.
// we need to determine if we think we can run a given query via
// querySelectorAll or if we'll need to fall back on DOM queries to get
// there. We need a lot of information about the environment and the query
// to make the determination (e.g. does it support QSA, does the query in
// question work in the native QSA impl, etc.).
// IE QSA queries may incorrectly include comment nodes, so we throw the
// zipping function into "remove" comments mode instead of the normal "skip
// it" which every other QSA-clued browser enjoys
var noZip = has("ie") ? "commentStrip" : "nozip";
var qsa = "querySelectorAll";
var qsaAvail = !!getDoc()[qsa];
//Don't bother with n+3 type of matches, IE complains if we modify those.
var infixSpaceRe = /\\[>~+]|n\+\d|([^ \\])?([>~+])([^ =])?/g;
var infixSpaceFunc = function(match, pre, ch, post){
return ch ? (pre ? pre + " " : "") + ch + (post ? " " + post : "") : /*n+3*/ match;
};
//Don't apply the infixSpaceRe to attribute value selectors
var attRe = /([^[]*)([^\]]*])?/g;
var attFunc = function(match, nonAtt, att){
return nonAtt.replace(infixSpaceRe, infixSpaceFunc) + (att||"");
};
var getQueryFunc = function(query, forceDOM){
//Normalize query. The CSS3 selectors spec allows for omitting spaces around
//infix operators, >, ~ and +
//Do the work here since detection for spaces is used as a simple "not use QSA"
//test below.
query = query.replace(attRe, attFunc);
if(qsaAvail){
// if we've got a cached variant and we think we can do it, run it!
var qsaCached = _queryFuncCacheQSA[query];
if(qsaCached && !forceDOM){ return qsaCached; }
}
// else if we've got a DOM cached variant, assume that we already know
// all we need to and use it
var domCached = _queryFuncCacheDOM[query];
if(domCached){ return domCached; }
// TODO:
// today we're caching DOM and QSA branches separately so we
// recalc useQSA every time. If we had a way to tag root+query
// efficiently, we'd be in good shape to do a global cache.
var qcz = query.charAt(0);
var nospace = (-1 == query.indexOf(" "));
// byId searches are wicked fast compared to QSA, even when filtering
// is required
if( (query.indexOf("#") >= 0) && (nospace) ){
forceDOM = true;
}
var useQSA = (
qsaAvail && (!forceDOM) &&
// as per CSS 3, we can't currently start w/ combinator:
// http://www.w3.org/TR/css3-selectors/#w3cselgrammar
(specials.indexOf(qcz) == -1) &&
// IE's QSA impl sucks on pseudos
(!has("ie") || (query.indexOf(":") == -1)) &&
(!(cssCaseBug && (query.indexOf(".") >= 0))) &&
// FIXME:
// need to tighten up browser rules on ":contains" and "|=" to
// figure out which aren't good
// Latest webkit (around 531.21.8) does not seem to do well with :checked on option
// elements, even though according to spec, selected options should
// match :checked. So go nonQSA for it:
// http://bugs.dojotoolkit.org/ticket/5179
(query.indexOf(":contains") == -1) && (query.indexOf(":checked") == -1) &&
(query.indexOf("|=") == -1) // some browsers don't grok it
);
// TODO:
// if we've got a descendant query (e.g., "> .thinger" instead of
// just ".thinger") in a QSA-able doc, but are passed a child as a
// root, it should be possible to give the item a synthetic ID and
// trivially rewrite the query to the form "#synid > .thinger" to
// use the QSA branch
if(useQSA){
var tq = (specials.indexOf(query.charAt(query.length-1)) >= 0) ?
(query + " *") : query;
return _queryFuncCacheQSA[query] = function(root){
try{
// the QSA system contains an egregious spec bug which
// limits us, effectively, to only running QSA queries over
// entire documents. See:
// http://ejohn.org/blog/thoughts-on-queryselectorall/
// despite this, we can also handle QSA runs on simple
// selectors, but we don't want detection to be expensive
// so we're just checking for the presence of a space char
// right now. Not elegant, but it's cheaper than running
// the query parser when we might not need to
if(!((9 == root.nodeType) || nospace)){ throw ""; }
var r = root[qsa](tq);
// skip expensive duplication checks and just wrap in a NodeList
r[noZip] = true;
return r;
}catch(e){
// else run the DOM branch on this query, ensuring that we
// default that way in the future
return getQueryFunc(query, true)(root);
}
};
}else{
// DOM branch
var parts = query.match(/([^\s,](?:"(?:\\.|[^"])+"|'(?:\\.|[^'])+'|[^,])*)/g);
return _queryFuncCacheDOM[query] = ((parts.length < 2) ?
// if not a compound query (e.g., ".foo, .bar"), cache and return a dispatcher
getStepQueryFunc(query) :
// if it *is* a complex query, break it up into its
// constituent parts and return a dispatcher that will
// merge the parts when run
function(root){
var pindex = 0, // avoid array alloc for every invocation
ret = [],
tp;
while((tp = parts[pindex++])){
ret = ret.concat(getStepQueryFunc(tp)(root));
}
return ret;
}
);
}
};
var _zipIdx = 0;
// NOTE:
// this function is Moo inspired, but our own impl to deal correctly
// with XML in IE
var _nodeUID = has("ie") ? function(node){
if(caseSensitive){
// XML docs don't have uniqueID on their nodes
return (node.getAttribute("_uid") || node.setAttribute("_uid", ++_zipIdx) || _zipIdx);
}else{
return node.uniqueID;
}
} :
function(node){
return (node._uid || (node._uid = ++_zipIdx));
};
// determine if a node in is unique in a "bag". In this case we don't want
// to flatten a list of unique items, but rather just tell if the item in
// question is already in the bag. Normally we'd just use hash lookup to do
// this for us but IE's DOM is busted so we can't really count on that. On
// the upside, it gives us a built in unique ID function.
var _isUnique = function(node, bag){
if(!bag){ return 1; }
var id = _nodeUID(node);
if(!bag[id]){ return bag[id] = 1; }
return 0;
};
// attempt to efficiently determine if an item in a list is a dupe,
// returning a list of "uniques", hopefully in document order
var _zipIdxName = "_zipIdx";
var _zip = function(arr){
if(arr && arr.nozip){
return arr;
}
var ret = [];
if(!arr || !arr.length){ return ret; }
if(arr[0]){
ret.push(arr[0]);
}
if(arr.length < 2){ return ret; }
_zipIdx++;
// we have to fork here for IE and XML docs because we can't set
// expandos on their nodes (apparently). *sigh*
var x, te;
if(has("ie") && caseSensitive){
var szidx = _zipIdx+"";
arr[0].setAttribute(_zipIdxName, szidx);
for(x = 1; te = arr[x]; x++){
if(arr[x].getAttribute(_zipIdxName) != szidx){
ret.push(te);
}
te.setAttribute(_zipIdxName, szidx);
}
}else if(has("ie") && arr.commentStrip){
try{
for(x = 1; te = arr[x]; x++){
if(_isElement(te)){
ret.push(te);
}
}
}catch(e){ /* squelch */ }
}else{
if(arr[0]){ arr[0][_zipIdxName] = _zipIdx; }
for(x = 1; te = arr[x]; x++){
if(arr[x][_zipIdxName] != _zipIdx){
ret.push(te);
}
te[_zipIdxName] = _zipIdx;
}
}
return ret;
};
// the main executor
var query = function(/*String*/ query, /*String|DOMNode?*/ root){
// summary:
// Returns nodes which match the given CSS3 selector, searching the
// entire document by default but optionally taking a node to scope
// the search by. Returns an array.
// description:
// dojo.query() is the swiss army knife of DOM node manipulation in
// Dojo. Much like Prototype's "$$" (bling-bling) function or JQuery's
// "$" function, dojo.query provides robust, high-performance
// CSS-based node selector support with the option of scoping searches
// to a particular sub-tree of a document.
//
// Supported Selectors:
// --------------------
//
// acme supports a rich set of CSS3 selectors, including:
//
// - class selectors (e.g., `.foo`)
// - node type selectors like `span`
// - ` ` descendant selectors
// - `>` child element selectors
// - `#foo` style ID selectors
// - `*` universal selector
// - `~`, the preceded-by sibling selector
// - `+`, the immediately preceded-by sibling selector
// - attribute queries:
// - `[foo]` attribute presence selector
// - `[foo='bar']` attribute value exact match
// - `[foo~='bar']` attribute value list item match
// - `[foo^='bar']` attribute start match
// - `[foo$='bar']` attribute end match
// - `[foo*='bar']` attribute substring match
// - `:first-child`, `:last-child`, and `:only-child` positional selectors
// - `:empty` content emtpy selector
// - `:checked` pseudo selector
// - `:nth-child(n)`, `:nth-child(2n+1)` style positional calculations
// - `:nth-child(even)`, `:nth-child(odd)` positional selectors
// - `:not(...)` negation pseudo selectors
//
// Any legal combination of these selectors will work with
// `dojo.query()`, including compound selectors ("," delimited).
// Very complex and useful searches can be constructed with this
// palette of selectors and when combined with functions for
// manipulation presented by dojo/NodeList, many types of DOM
// manipulation operations become very straightforward.
//
// Unsupported Selectors:
// ----------------------
//
// While dojo.query handles many CSS3 selectors, some fall outside of
// what's reasonable for a programmatic node querying engine to
// handle. Currently unsupported selectors include:
//
// - namespace-differentiated selectors of any form
// - all `::` pseduo-element selectors
// - certain pseudo-selectors which don't get a lot of day-to-day use:
// - `:root`, `:lang()`, `:target`, `:focus`
// - all visual and state selectors:
// - `:root`, `:active`, `:hover`, `:visited`, `:link`,
// `:enabled`, `:disabled`
// - `:*-of-type` pseudo selectors
//
// dojo.query and XML Documents:
// -----------------------------
//
// `dojo.query` (as of dojo 1.2) supports searching XML documents
// in a case-sensitive manner. If an HTML document is served with
// a doctype that forces case-sensitivity (e.g., XHTML 1.1
// Strict), dojo.query() will detect this and "do the right
// thing". Case sensitivity is dependent upon the document being
// searched and not the query used. It is therefore possible to
// use case-sensitive queries on strict sub-documents (iframes,
// etc.) or XML documents while still assuming case-insensitivity
// for a host/root document.
//
// Non-selector Queries:
// ---------------------
//
// If something other than a String is passed for the query,
// `dojo.query` will return a new `dojo/NodeList` instance
// constructed from that parameter alone and all further
// processing will stop. This means that if you have a reference
// to a node or NodeList, you can quickly construct a new NodeList
// from the original by calling `dojo.query(node)` or
// `dojo.query(list)`.
//
// query:
// The CSS3 expression to match against. For details on the syntax of
// CSS3 selectors, see <http://www.w3.org/TR/css3-selectors/#selectors>
// root:
// A DOMNode (or node id) to scope the search from. Optional.
// returns: Array
// example:
// search the entire document for elements with the class "foo":
// | dojo.query(".foo");
// these elements will match:
// | <span class="foo"></span>
// | <span class="foo bar"></span>
// | <p class="thud foo"></p>
// example:
// search the entire document for elements with the classes "foo" *and* "bar":
// | dojo.query(".foo.bar");
// these elements will match:
// | <span class="foo bar"></span>
// while these will not:
// | <span class="foo"></span>
// | <p class="thud foo"></p>
// example:
// find `<span>` elements which are descendants of paragraphs and
// which have a "highlighted" class:
// | dojo.query("p span.highlighted");
// the innermost span in this fragment matches:
// | <p class="foo">
// | <span>...
// | <span class="highlighted foo bar">...</span>
// | </span>
// | </p>
// example:
// set an "odd" class on all odd table rows inside of the table
// `#tabular_data`, using the `>` (direct child) selector to avoid
// affecting any nested tables:
// | dojo.query("#tabular_data > tbody > tr:nth-child(odd)").addClass("odd");
// example:
// remove all elements with the class "error" from the document
// and store them in a list:
// | var errors = dojo.query(".error").orphan();
// example:
// add an onclick handler to every submit button in the document
// which causes the form to be sent via Ajax instead:
// | dojo.query("input[type='submit']").onclick(function(e){
// | dojo.stopEvent(e); // prevent sending the form
// | var btn = e.target;
// | dojo.xhrPost({
// | form: btn.form,
// | load: function(data){
// | // replace the form with the response
// | var div = dojo.doc.createElement("div");
// | dojo.place(div, btn.form, "after");
// | div.innerHTML = data;
// | dojo.style(btn.form, "display", "none");
// | }
// | });
// | });
root = root || getDoc();
// throw the big case sensitivity switch
var od = root.ownerDocument || root; // root is either Document or a node inside the document
caseSensitive = (od.createElement("div").tagName === "div");
// NOTE:
// adding "true" as the 2nd argument to getQueryFunc is useful for
// testing the DOM branch without worrying about the
// behavior/performance of the QSA branch.
var r = getQueryFunc(query)(root);
// FIXME:
// need to investigate this branch WRT #8074 and #8075
if(r && r.nozip){
return r;
}
return _zip(r); // dojo/NodeList
};
query.filter = function(/*Node[]*/ nodeList, /*String*/ filter, /*String|DOMNode?*/ root){
// summary:
// function for filtering a NodeList based on a selector, optimized for simple selectors
var tmpNodeList = [],
parts = getQueryParts(filter),
filterFunc =
(parts.length == 1 && !/[^\w#\.]/.test(filter)) ?
getSimpleFilterFunc(parts[0]) :
function(node){
return array.indexOf(query(filter, dom.byId(root)), node) != -1;
};
for(var x = 0, te; te = nodeList[x]; x++){
if(filterFunc(te)){ tmpNodeList.push(te); }
}
return tmpNodeList;
};
return query;
});
},
'dojo/dnd/autoscroll':function(){
define("dojo/dnd/autoscroll", ["../_base/lang", "../sniff", "../_base/window", "../dom-geometry", "../dom-style", "../window"],
function(lang, has, win, domGeom, domStyle, winUtils){
// module:
// dojo/dnd/autoscroll
var exports = {
// summary:
// Used by dojo/dnd/Manager to scroll document or internal node when the user
// drags near the edge of the viewport or a scrollable node
};
lang.setObject("dojo.dnd.autoscroll", exports);
exports.getViewport = winUtils.getBox;
exports.V_TRIGGER_AUTOSCROLL = 32;
exports.H_TRIGGER_AUTOSCROLL = 32;
exports.V_AUTOSCROLL_VALUE = 16;
exports.H_AUTOSCROLL_VALUE = 16;
// These are set by autoScrollStart().
// Set to default values in case autoScrollStart() isn't called. (back-compat, remove for 2.0)
var viewport,
doc = win.doc,
maxScrollTop = Infinity,
maxScrollLeft = Infinity;
exports.autoScrollStart = function(d){
// summary:
// Called at the start of a drag.
// d: Document
// The document of the node being dragged.
doc = d;
viewport = winUtils.getBox(doc);
// Save height/width of document at start of drag, before it gets distorted by a user dragging an avatar past
// the document's edge
var html = win.body(doc).parentNode;
maxScrollTop = Math.max(html.scrollHeight - viewport.h, 0);
maxScrollLeft = Math.max(html.scrollWidth - viewport.w, 0); // usually 0
};
exports.autoScroll = function(e){
// summary:
// a handler for mousemove and touchmove events, which scrolls the window, if
// necessary
// e: Event
// mousemove/touchmove event
// FIXME: needs more docs!
var v = viewport || winUtils.getBox(doc), // getBox() call for back-compat, in case autoScrollStart() wasn't called
html = win.body(doc).parentNode,
dx = 0, dy = 0;
if(e.clientX < exports.H_TRIGGER_AUTOSCROLL){
dx = -exports.H_AUTOSCROLL_VALUE;
}else if(e.clientX > v.w - exports.H_TRIGGER_AUTOSCROLL){
dx = Math.min(exports.H_AUTOSCROLL_VALUE, maxScrollLeft - html.scrollLeft); // don't scroll past edge of doc
}
if(e.clientY < exports.V_TRIGGER_AUTOSCROLL){
dy = -exports.V_AUTOSCROLL_VALUE;
}else if(e.clientY > v.h - exports.V_TRIGGER_AUTOSCROLL){
dy = Math.min(exports.V_AUTOSCROLL_VALUE, maxScrollTop - html.scrollTop); // don't scroll past edge of doc
}
window.scrollBy(dx, dy);
};
exports._validNodes = {"div": 1, "p": 1, "td": 1};
exports._validOverflow = {"auto": 1, "scroll": 1};
exports.autoScrollNodes = function(e){
// summary:
// a handler for mousemove and touchmove events, which scrolls the first available
// Dom element, it falls back to exports.autoScroll()
// e: Event
// mousemove/touchmove event
// FIXME: needs more docs!
var b, t, w, h, rx, ry, dx = 0, dy = 0, oldLeft, oldTop;
for(var n = e.target; n;){
if(n.nodeType == 1 && (n.tagName.toLowerCase() in exports._validNodes)){
var s = domStyle.getComputedStyle(n),
overflow = (s.overflow.toLowerCase() in exports._validOverflow),
overflowX = (s.overflowX.toLowerCase() in exports._validOverflow),
overflowY = (s.overflowY.toLowerCase() in exports._validOverflow);
if(overflow || overflowX || overflowY){
b = domGeom.getContentBox(n, s);
t = domGeom.position(n, true);
}
// overflow-x
if(overflow || overflowX){
w = Math.min(exports.H_TRIGGER_AUTOSCROLL, b.w / 2);
rx = e.pageX - t.x;
if(has("webkit") || has("opera")){
// FIXME: this code should not be here, it should be taken into account
// either by the event fixing code, or the domGeom.position()
// FIXME: this code doesn't work on Opera 9.5 Beta
rx += win.body().scrollLeft;
}
dx = 0;
if(rx > 0 && rx < b.w){
if(rx < w){
dx = -w;
}else if(rx > b.w - w){
dx = w;
}
oldLeft = n.scrollLeft;
n.scrollLeft = n.scrollLeft + dx;
}
}
// overflow-y
if(overflow || overflowY){
//console.log(b.l, b.t, t.x, t.y, n.scrollLeft, n.scrollTop);
h = Math.min(exports.V_TRIGGER_AUTOSCROLL, b.h / 2);
ry = e.pageY - t.y;
if(has("webkit") || has("opera")){
// FIXME: this code should not be here, it should be taken into account
// either by the event fixing code, or the domGeom.position()
// FIXME: this code doesn't work on Opera 9.5 Beta
ry += win.body().scrollTop;
}
dy = 0;
if(ry > 0 && ry < b.h){
if(ry < h){
dy = -h;
}else if(ry > b.h - h){
dy = h;
}
oldTop = n.scrollTop;
n.scrollTop = n.scrollTop + dy;
}
}
if(dx || dy){ return; }
}
try{
n = n.parentNode;
}catch(x){
n = null;
}
}
exports.autoScroll(e);
};
return exports;
});
},
'dijit/form/_RadioButtonMixin':function(){
define("dijit/form/_RadioButtonMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event", // event.stop
"dojo/_base/lang", // lang.hitch
"dojo/query", // query
"../registry" // registry.getEnclosingWidget
], function(array, declare, domAttr, event, lang, query, registry){
// module:
// dijit/form/_RadioButtonMixin
return declare("dijit.form._RadioButtonMixin", null, {
// summary:
// Mixin to provide widget functionality for an HTML radio button
// type: [private] String
// type attribute on `<input>` node.
// Users should not change this value.
type: "radio",
_getRelatedWidgets: function(){
// Private function needed to help iterate over all radio buttons in a group.
var ary = [];
query("input[type=radio]", this.focusNode.form || this.ownerDocument).forEach( // can't use name= since query doesn't support [] in the name
lang.hitch(this, function(inputNode){
if(inputNode.name == this.name && inputNode.form == this.focusNode.form){
var widget = registry.getEnclosingWidget(inputNode);
if(widget){
ary.push(widget);
}
}
})
);
return ary;
},
_setCheckedAttr: function(/*Boolean*/ value){
// If I am being checked then have to deselect currently checked radio button
this.inherited(arguments);
if(!this._created){ return; }
if(value){
array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
if(widget != this && widget.checked){
widget.set('checked', false);
}
}));
}
},
_getSubmitValue: function(/*String*/ value){
return value === null ? "on" : value;
},
_onClick: function(/*Event*/ e){
if(this.checked || this.disabled){ // nothing to do
event.stop(e);
return false;
}
if(this.readOnly){ // ignored by some browsers so we have to resync the DOM elements with widget values
event.stop(e);
array.forEach(this._getRelatedWidgets(), lang.hitch(this, function(widget){
domAttr.set(this.focusNode || this.domNode, 'checked', widget.checked);
}));
return false;
}
return this.inherited(arguments);
}
});
});
},
'dojo/data/ItemFileWriteStore':function(){
define("dojo/data/ItemFileWriteStore", ["../_base/lang", "../_base/declare", "../_base/array", "../_base/json", "../_base/kernel",
"./ItemFileReadStore", "../date/stamp"
], function(lang, declare, arrayUtil, jsonUtil, kernel, ItemFileReadStore, dateStamp){
// module:
// dojo/data/ItemFileWriteStore
return declare("dojo.data.ItemFileWriteStore", ItemFileReadStore, {
// summary:
// TODOC
constructor: function(/* object */ keywordParameters){
// keywordParameters:
// The structure of the typeMap object is as follows:
// | {
// | type0: function || object,
// | type1: function || object,
// | ...
// | typeN: function || object
// | }
// Where if it is a function, it is assumed to be an object constructor that takes the
// value of _value as the initialization parameters. It is serialized assuming object.toString()
// serialization. If it is an object, then it is assumed
// to be an object of general form:
// | {
// | type: function, //constructor.
// | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
// | serialize: function(object) //The function that converts the object back into the proper file format form.
// | }
// ItemFileWriteStore extends ItemFileReadStore to implement these additional dojo.data APIs
this._features['dojo.data.api.Write'] = true;
this._features['dojo.data.api.Notification'] = true;
// For keeping track of changes so that we can implement isDirty and revert
this._pending = {
_newItems:{},
_modifiedItems:{},
_deletedItems:{}
};
if(!this._datatypeMap['Date'].serialize){
this._datatypeMap['Date'].serialize = function(obj){
return dateStamp.toISOString(obj, {zulu:true});
};
}
//Disable only if explicitly set to false.
if(keywordParameters && (keywordParameters.referenceIntegrity === false)){
this.referenceIntegrity = false;
}
// this._saveInProgress is set to true, briefly, from when save() is first called to when it completes
this._saveInProgress = false;
},
referenceIntegrity: true, //Flag that defaultly enabled reference integrity tracking. This way it can also be disabled pogrammatially or declaratively.
_assert: function(/* boolean */ condition){
if(!condition){
throw new Error("assertion failed in ItemFileWriteStore");
}
},
_getIdentifierAttribute: function(){
// this._assert((identifierAttribute === Number) || (dojo.isString(identifierAttribute)));
return this.getFeatures()['dojo.data.api.Identity'];
},
/* dojo/data/api/Write */
newItem: function(/* Object? */ keywordArgs, /* Object? */ parentInfo){
// summary:
// See dojo/data/api/Write.newItem()
this._assert(!this._saveInProgress);
if(!this._loadFinished){
// We need to do this here so that we'll be able to find out what
// identifierAttribute was specified in the data file.
this._forceLoad();
}
if(typeof keywordArgs != "object" && typeof keywordArgs != "undefined"){
throw new Error("newItem() was passed something other than an object");
}
var newIdentity = null;
var identifierAttribute = this._getIdentifierAttribute();
if(identifierAttribute === Number){
newIdentity = this._arrayOfAllItems.length;
}else{
newIdentity = keywordArgs[identifierAttribute];
if(typeof newIdentity === "undefined"){
throw new Error("newItem() was not passed an identity for the new item");
}
if(lang.isArray(newIdentity)){
throw new Error("newItem() was not passed an single-valued identity");
}
}
// make sure this identity is not already in use by another item, if identifiers were
// defined in the file. Otherwise it would be the item count,
// which should always be unique in this case.
if(this._itemsByIdentity){
this._assert(typeof this._itemsByIdentity[newIdentity] === "undefined");
}
this._assert(typeof this._pending._newItems[newIdentity] === "undefined");
this._assert(typeof this._pending._deletedItems[newIdentity] === "undefined");
var newItem = {};
newItem[this._storeRefPropName] = this;
newItem[this._itemNumPropName] = this._arrayOfAllItems.length;
if(this._itemsByIdentity){
this._itemsByIdentity[newIdentity] = newItem;
//We have to set the identifier now, otherwise we can't look it
//up at calls to setValueorValues in parentInfo handling.
newItem[identifierAttribute] = [newIdentity];
}
this._arrayOfAllItems.push(newItem);
//We need to construct some data for the onNew call too...
var pInfo = null;
// Now we need to check to see where we want to assign this thingm if any.
if(parentInfo && parentInfo.parent && parentInfo.attribute){
pInfo = {
item: parentInfo.parent,
attribute: parentInfo.attribute,
oldValue: undefined
};
//See if it is multi-valued or not and handle appropriately
//Generally, all attributes are multi-valued for this store
//So, we only need to append if there are already values present.
var values = this.getValues(parentInfo.parent, parentInfo.attribute);
if(values && values.length > 0){
var tempValues = values.slice(0, values.length);
if(values.length === 1){
pInfo.oldValue = values[0];
}else{
pInfo.oldValue = values.slice(0, values.length);
}
tempValues.push(newItem);
this._setValueOrValues(parentInfo.parent, parentInfo.attribute, tempValues, false);
pInfo.newValue = this.getValues(parentInfo.parent, parentInfo.attribute);
}else{
this._setValueOrValues(parentInfo.parent, parentInfo.attribute, newItem, false);
pInfo.newValue = newItem;
}
}else{
//Toplevel item, add to both top list as well as all list.
newItem[this._rootItemPropName]=true;
this._arrayOfTopLevelItems.push(newItem);
}
this._pending._newItems[newIdentity] = newItem;
//Clone over the properties to the new item
for(var key in keywordArgs){
if(key === this._storeRefPropName || key === this._itemNumPropName){
// Bummer, the user is trying to do something like
// newItem({_S:"foo"}). Unfortunately, our superclass,
// ItemFileReadStore, is already using _S in each of our items
// to hold private info. To avoid a naming collision, we
// need to move all our private info to some other property
// of all the items/objects. So, we need to iterate over all
// the items and do something like:
// item.__S = item._S;
// item._S = undefined;
// But first we have to make sure the new "__S" variable is
// not in use, which means we have to iterate over all the
// items checking for that.
throw new Error("encountered bug in ItemFileWriteStore.newItem");
}
var value = keywordArgs[key];
if(!lang.isArray(value)){
value = [value];
}
newItem[key] = value;
if(this.referenceIntegrity){
for(var i = 0; i < value.length; i++){
var val = value[i];
if(this.isItem(val)){
this._addReferenceToMap(val, newItem, key);
}
}
}
}
this.onNew(newItem, pInfo); // dojo/data/api/Notification call
return newItem; // item
},
_removeArrayElement: function(/* Array */ array, /* anything */ element){
var index = arrayUtil.indexOf(array, element);
if(index != -1){
array.splice(index, 1);
return true;
}
return false;
},
deleteItem: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Write.deleteItem()
this._assert(!this._saveInProgress);
this._assertIsItem(item);
// Remove this item from the _arrayOfAllItems, but leave a null value in place
// of the item, so as not to change the length of the array, so that in newItem()
// we can still safely do: newIdentity = this._arrayOfAllItems.length;
var indexInArrayOfAllItems = item[this._itemNumPropName];
var identity = this.getIdentity(item);
//If we have reference integrity on, we need to do reference cleanup for the deleted item
if(this.referenceIntegrity){
//First scan all the attributes of this items for references and clean them up in the map
//As this item is going away, no need to track its references anymore.
//Get the attributes list before we generate the backup so it
//doesn't pollute the attributes list.
var attributes = this.getAttributes(item);
//Backup the map, we'll have to restore it potentially, in a revert.
if(item[this._reverseRefMap]){
item["backup_" + this._reverseRefMap] = lang.clone(item[this._reverseRefMap]);
}
//TODO: This causes a reversion problem. This list won't be restored on revert since it is
//attached to the 'value'. item, not ours. Need to back tese up somehow too.
//Maybe build a map of the backup of the entries and attach it to the deleted item to be restored
//later. Or just record them and call _addReferenceToMap on them in revert.
arrayUtil.forEach(attributes, function(attribute){
arrayUtil.forEach(this.getValues(item, attribute), function(value){
if(this.isItem(value)){
//We have to back up all the references we had to others so they can be restored on a revert.
if(!item["backupRefs_" + this._reverseRefMap]){
item["backupRefs_" + this._reverseRefMap] = [];
}
item["backupRefs_" + this._reverseRefMap].push({id: this.getIdentity(value), attr: attribute});
this._removeReferenceFromMap(value, item, attribute);
}
}, this);
}, this);
//Next, see if we have references to this item, if we do, we have to clean them up too.
var references = item[this._reverseRefMap];
if(references){
//Look through all the items noted as references to clean them up.
for(var itemId in references){
var containingItem = null;
if(this._itemsByIdentity){
containingItem = this._itemsByIdentity[itemId];
}else{
containingItem = this._arrayOfAllItems[itemId];
}
//We have a reference to a containing item, now we have to process the
//attributes and clear all references to the item being deleted.
if(containingItem){
for(var attribute in references[itemId]){
var oldValues = this.getValues(containingItem, attribute) || [];
var newValues = arrayUtil.filter(oldValues, function(possibleItem){
return !(this.isItem(possibleItem) && this.getIdentity(possibleItem) == identity);
}, this);
//Remove the note of the reference to the item and set the values on the modified attribute.
this._removeReferenceFromMap(item, containingItem, attribute);
if(newValues.length < oldValues.length){
this._setValueOrValues(containingItem, attribute, newValues, true);
}
}
}
}
}
}
this._arrayOfAllItems[indexInArrayOfAllItems] = null;
item[this._storeRefPropName] = null;
if(this._itemsByIdentity){
delete this._itemsByIdentity[identity];
}
this._pending._deletedItems[identity] = item;
//Remove from the toplevel items, if necessary...
if(item[this._rootItemPropName]){
this._removeArrayElement(this._arrayOfTopLevelItems, item);
}
this.onDelete(item); // dojo/data/api/Notification call
return true;
},
setValue: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* almost anything */ value){
// summary:
// See dojo/data/api/Write.set()
return this._setValueOrValues(item, attribute, value, true); // boolean
},
setValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* array */ values){
// summary:
// See dojo/data/api/Write.setValues()
return this._setValueOrValues(item, attribute, values, true); // boolean
},
unsetAttribute: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
// summary:
// See dojo/data/api/Write.unsetAttribute()
return this._setValueOrValues(item, attribute, [], true);
},
_setValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute, /* anything */ newValueOrValues, /*boolean?*/ callOnSet){
this._assert(!this._saveInProgress);
// Check for valid arguments
this._assertIsItem(item);
this._assert(lang.isString(attribute));
this._assert(typeof newValueOrValues !== "undefined");
// Make sure the user isn't trying to change the item's identity
var identifierAttribute = this._getIdentifierAttribute();
if(attribute == identifierAttribute){
throw new Error("ItemFileWriteStore does not have support for changing the value of an item's identifier.");
}
// To implement the Notification API, we need to make a note of what
// the old attribute value was, so that we can pass that info when
// we call the onSet method.
var oldValueOrValues = this._getValueOrValues(item, attribute);
var identity = this.getIdentity(item);
if(!this._pending._modifiedItems[identity]){
// Before we actually change the item, we make a copy of it to
// record the original state, so that we'll be able to revert if
// the revert method gets called. If the item has already been
// modified then there's no need to do this now, since we already
// have a record of the original state.
var copyOfItemState = {};
for(var key in item){
if((key === this._storeRefPropName) || (key === this._itemNumPropName) || (key === this._rootItemPropName)){
copyOfItemState[key] = item[key];
}else if(key === this._reverseRefMap){
copyOfItemState[key] = lang.clone(item[key]);
}else{
copyOfItemState[key] = item[key].slice(0, item[key].length);
}
}
// Now mark the item as dirty, and save the copy of the original state
this._pending._modifiedItems[identity] = copyOfItemState;
}
// Okay, now we can actually change this attribute on the item
var success = false;
if(lang.isArray(newValueOrValues) && newValueOrValues.length === 0){
// If we were passed an empty array as the value, that counts
// as "unsetting" the attribute, so we need to remove this
// attribute from the item.
success = delete item[attribute];
newValueOrValues = undefined; // used in the onSet Notification call below
if(this.referenceIntegrity && oldValueOrValues){
var oldValues = oldValueOrValues;
if(!lang.isArray(oldValues)){
oldValues = [oldValues];
}
for(var i = 0; i < oldValues.length; i++){
var value = oldValues[i];
if(this.isItem(value)){
this._removeReferenceFromMap(value, item, attribute);
}
}
}
}else{
var newValueArray;
if(lang.isArray(newValueOrValues)){
// Unfortunately, it's not safe to just do this:
// newValueArray = newValueOrValues;
// Instead, we need to copy the array, which slice() does very nicely.
// This is so that our internal data structure won't
// get corrupted if the user mucks with the values array *after*
// calling setValues().
newValueArray = newValueOrValues.slice(0, newValueOrValues.length);
}else{
newValueArray = [newValueOrValues];
}
//We need to handle reference integrity if this is on.
//In the case of set, we need to see if references were added or removed
//and update the reference tracking map accordingly.
if(this.referenceIntegrity){
if(oldValueOrValues){
var oldValues = oldValueOrValues;
if(!lang.isArray(oldValues)){
oldValues = [oldValues];
}
//Use an associative map to determine what was added/removed from the list.
//Should be O(n) performant. First look at all the old values and make a list of them
//Then for any item not in the old list, we add it. If it was already present, we remove it.
//Then we pass over the map and any references left it it need to be removed (IE, no match in
//the new values list).
var map = {};
arrayUtil.forEach(oldValues, function(possibleItem){
if(this.isItem(possibleItem)){
var id = this.getIdentity(possibleItem);
map[id.toString()] = true;
}
}, this);
arrayUtil.forEach(newValueArray, function(possibleItem){
if(this.isItem(possibleItem)){
var id = this.getIdentity(possibleItem);
if(map[id.toString()]){
delete map[id.toString()];
}else{
this._addReferenceToMap(possibleItem, item, attribute);
}
}
}, this);
for(var rId in map){
var removedItem;
if(this._itemsByIdentity){
removedItem = this._itemsByIdentity[rId];
}else{
removedItem = this._arrayOfAllItems[rId];
}
this._removeReferenceFromMap(removedItem, item, attribute);
}
}else{
//Everything is new (no old values) so we have to just
//insert all the references, if any.
for(var i = 0; i < newValueArray.length; i++){
var value = newValueArray[i];
if(this.isItem(value)){
this._addReferenceToMap(value, item, attribute);
}
}
}
}
item[attribute] = newValueArray;
success = true;
}
// Now we make the dojo/data/api/Notification call
if(callOnSet){
this.onSet(item, attribute, oldValueOrValues, newValueOrValues);
}
return success; // boolean
},
_addReferenceToMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
// summary:
// Method to add an reference map entry for an item and attribute.
// description:
// Method to add an reference map entry for an item and attribute.
// refItem:
// The item that is referenced.
// parentItem:
// The item that holds the new reference to refItem.
// attribute:
// The attribute on parentItem that contains the new reference.
var parentId = this.getIdentity(parentItem);
var references = refItem[this._reverseRefMap];
if(!references){
references = refItem[this._reverseRefMap] = {};
}
var itemRef = references[parentId];
if(!itemRef){
itemRef = references[parentId] = {};
}
itemRef[attribute] = true;
},
_removeReferenceFromMap: function(/* dojo/data/api/Item */ refItem, /* dojo/data/api/Item */ parentItem, /* string */ attribute){
// summary:
// Method to remove an reference map entry for an item and attribute.
// description:
// Method to remove an reference map entry for an item and attribute. This will
// also perform cleanup on the map such that if there are no more references at all to
// the item, its reference object and entry are removed.
// refItem:
// The item that is referenced.
// parentItem:
// The item holding a reference to refItem.
// attribute:
// The attribute on parentItem that contains the reference.
var identity = this.getIdentity(parentItem);
var references = refItem[this._reverseRefMap];
var itemId;
if(references){
for(itemId in references){
if(itemId == identity){
delete references[itemId][attribute];
if(this._isEmpty(references[itemId])){
delete references[itemId];
}
}
}
if(this._isEmpty(references)){
delete refItem[this._reverseRefMap];
}
}
},
_dumpReferenceMap: function(){
// summary:
// Function to dump the reverse reference map of all items in the store for debug purposes.
// description:
// Function to dump the reverse reference map of all items in the store for debug purposes.
var i;
for(i = 0; i < this._arrayOfAllItems.length; i++){
var item = this._arrayOfAllItems[i];
if(item && item[this._reverseRefMap]){
console.log("Item: [" + this.getIdentity(item) + "] is referenced by: " + jsonUtil.toJson(item[this._reverseRefMap]));
}
}
},
_getValueOrValues: function(/* dojo/data/api/Item */ item, /* attribute-name-string */ attribute){
var valueOrValues = undefined;
if(this.hasAttribute(item, attribute)){
var valueArray = this.getValues(item, attribute);
if(valueArray.length == 1){
valueOrValues = valueArray[0];
}else{
valueOrValues = valueArray;
}
}
return valueOrValues;
},
_flatten: function(/* anything */ value){
if(this.isItem(value)){
// Given an item, return an serializable object that provides a
// reference to the item.
// For example, given kermit:
// var kermit = store.newItem({id:2, name:"Kermit"});
// we want to return
// {_reference:2}
return {_reference: this.getIdentity(value)};
}else{
if(typeof value === "object"){
for(var type in this._datatypeMap){
var typeMap = this._datatypeMap[type];
if(lang.isObject(typeMap) && !lang.isFunction(typeMap)){
if(value instanceof typeMap.type){
if(!typeMap.serialize){
throw new Error("ItemFileWriteStore: No serializer defined for type mapping: [" + type + "]");
}
return {_type: type, _value: typeMap.serialize(value)};
}
}else if(value instanceof typeMap){
//SImple mapping, therefore, return as a toString serialization.
return {_type: type, _value: value.toString()};
}
}
}
return value;
}
},
_getNewFileContentString: function(){
// summary:
// Generate a string that can be saved to a file.
// The result should look similar to:
// http://trac.dojotoolkit.org/browser/dojo/trunk/tests/data/countries.json
var serializableStructure = {};
var identifierAttribute = this._getIdentifierAttribute();
if(identifierAttribute !== Number){
serializableStructure.identifier = identifierAttribute;
}
if(this._labelAttr){
serializableStructure.label = this._labelAttr;
}
serializableStructure.items = [];
for(var i = 0; i < this._arrayOfAllItems.length; ++i){
var item = this._arrayOfAllItems[i];
if(item !== null){
var serializableItem = {};
for(var key in item){
if(key !== this._storeRefPropName && key !== this._itemNumPropName && key !== this._reverseRefMap && key !== this._rootItemPropName){
var valueArray = this.getValues(item, key);
if(valueArray.length == 1){
serializableItem[key] = this._flatten(valueArray[0]);
}else{
var serializableArray = [];
for(var j = 0; j < valueArray.length; ++j){
serializableArray.push(this._flatten(valueArray[j]));
serializableItem[key] = serializableArray;
}
}
}
}
serializableStructure.items.push(serializableItem);
}
}
var prettyPrint = true;
return jsonUtil.toJson(serializableStructure, prettyPrint);
},
_isEmpty: function(something){
// summary:
// Function to determine if an array or object has no properties or values.
// something:
// The array or object to examine.
var empty = true;
if(lang.isObject(something)){
var i;
for(i in something){
empty = false;
break;
}
}else if(lang.isArray(something)){
if(something.length > 0){
empty = false;
}
}
return empty; //boolean
},
save: function(/* object */ keywordArgs){
// summary:
// See dojo/data/api/Write.save()
this._assert(!this._saveInProgress);
// this._saveInProgress is set to true, briefly, from when save is first called to when it completes
this._saveInProgress = true;
var self = this;
var saveCompleteCallback = function(){
self._pending = {
_newItems:{},
_modifiedItems:{},
_deletedItems:{}
};
self._saveInProgress = false; // must come after this._pending is cleared, but before any callbacks
if(keywordArgs && keywordArgs.onComplete){
var scope = keywordArgs.scope || kernel.global;
keywordArgs.onComplete.call(scope);
}
};
var saveFailedCallback = function(err){
self._saveInProgress = false;
if(keywordArgs && keywordArgs.onError){
var scope = keywordArgs.scope || kernel.global;
keywordArgs.onError.call(scope, err);
}
};
if(this._saveEverything){
var newFileContentString = this._getNewFileContentString();
this._saveEverything(saveCompleteCallback, saveFailedCallback, newFileContentString);
}
if(this._saveCustom){
this._saveCustom(saveCompleteCallback, saveFailedCallback);
}
if(!this._saveEverything && !this._saveCustom){
// Looks like there is no user-defined save-handler function.
// That's fine, it just means the datastore is acting as a "mock-write"
// store -- changes get saved in memory but don't get saved to disk.
saveCompleteCallback();
}
},
revert: function(){
// summary:
// See dojo/data/api/Write.revert()
this._assert(!this._saveInProgress);
var identity;
for(identity in this._pending._modifiedItems){
// find the original item and the modified item that replaced it
var copyOfItemState = this._pending._modifiedItems[identity];
var modifiedItem = null;
if(this._itemsByIdentity){
modifiedItem = this._itemsByIdentity[identity];
}else{
modifiedItem = this._arrayOfAllItems[identity];
}
// Restore the original item into a full-fledged item again, we want to try to
// keep the same object instance as if we don't it, causes bugs like #9022.
copyOfItemState[this._storeRefPropName] = this;
for(var key in modifiedItem){
delete modifiedItem[key];
}
lang.mixin(modifiedItem, copyOfItemState);
}
var deletedItem;
for(identity in this._pending._deletedItems){
deletedItem = this._pending._deletedItems[identity];
deletedItem[this._storeRefPropName] = this;
var index = deletedItem[this._itemNumPropName];
//Restore the reverse refererence map, if any.
if(deletedItem["backup_" + this._reverseRefMap]){
deletedItem[this._reverseRefMap] = deletedItem["backup_" + this._reverseRefMap];
delete deletedItem["backup_" + this._reverseRefMap];
}
this._arrayOfAllItems[index] = deletedItem;
if(this._itemsByIdentity){
this._itemsByIdentity[identity] = deletedItem;
}
if(deletedItem[this._rootItemPropName]){
this._arrayOfTopLevelItems.push(deletedItem);
}
}
//We have to pass through it again and restore the reference maps after all the
//undeletes have occurred.
for(identity in this._pending._deletedItems){
deletedItem = this._pending._deletedItems[identity];
if(deletedItem["backupRefs_" + this._reverseRefMap]){
arrayUtil.forEach(deletedItem["backupRefs_" + this._reverseRefMap], function(reference){
var refItem;
if(this._itemsByIdentity){
refItem = this._itemsByIdentity[reference.id];
}else{
refItem = this._arrayOfAllItems[reference.id];
}
this._addReferenceToMap(refItem, deletedItem, reference.attr);
}, this);
delete deletedItem["backupRefs_" + this._reverseRefMap];
}
}
for(identity in this._pending._newItems){
var newItem = this._pending._newItems[identity];
newItem[this._storeRefPropName] = null;
// null out the new item, but don't change the array index so
// so we can keep using _arrayOfAllItems.length.
this._arrayOfAllItems[newItem[this._itemNumPropName]] = null;
if(newItem[this._rootItemPropName]){
this._removeArrayElement(this._arrayOfTopLevelItems, newItem);
}
if(this._itemsByIdentity){
delete this._itemsByIdentity[identity];
}
}
this._pending = {
_newItems:{},
_modifiedItems:{},
_deletedItems:{}
};
return true; // boolean
},
isDirty: function(/* item? */ item){
// summary:
// See dojo/data/api/Write.isDirty()
if(item){
// return true if the item is dirty
var identity = this.getIdentity(item);
return new Boolean(this._pending._newItems[identity] ||
this._pending._modifiedItems[identity] ||
this._pending._deletedItems[identity]).valueOf(); // boolean
}else{
// return true if the store is dirty -- which means return true
// if there are any new items, dirty items, or modified items
return !this._isEmpty(this._pending._newItems) ||
!this._isEmpty(this._pending._modifiedItems) ||
!this._isEmpty(this._pending._deletedItems); // boolean
}
},
/* dojo/data/api/Notification */
onSet: function(/* dojo/data/api/Item */ item,
/*attribute-name-string*/ attribute,
/*object|array*/ oldValue,
/*object|array*/ newValue){
// summary:
// See dojo/data/api/Notification.onSet()
// No need to do anything. This method is here just so that the
// client code can connect observers to it.
},
onNew: function(/* dojo/data/api/Item */ newItem, /*object?*/ parentInfo){
// summary:
// See dojo/data/api/Notification.onNew()
// No need to do anything. This method is here just so that the
// client code can connect observers to it.
},
onDelete: function(/* dojo/data/api/Item */ deletedItem){
// summary:
// See dojo/data/api/Notification.onDelete()
// No need to do anything. This method is here just so that the
// client code can connect observers to it.
},
close: function(/* object? */ request){
// summary:
// Over-ride of base close function of ItemFileReadStore to add in check for store state.
// description:
// Over-ride of base close function of ItemFileReadStore to add in check for store state.
// If the store is still dirty (unsaved changes), then an error will be thrown instead of
// clearing the internal state for reload from the url.
//Clear if not dirty ... or throw an error
if(this.clearOnClose){
if(!this.isDirty()){
this.inherited(arguments);
}else{
//Only throw an error if the store was dirty and we were loading from a url (cannot reload from url until state is saved).
throw new Error("dojo.data.ItemFileWriteStore: There are unsaved changes present in the store. Please save or revert the changes before invoking close.");
}
}
}
});
});
},
'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
'dojo/dnd/TimedMoveable':function(){
define("dojo/dnd/TimedMoveable", ["../_base/declare", "./Moveable" /*=====, "./Mover" =====*/], function(declare, Moveable /*=====, Mover =====*/){
// module:
// dojo/dnd/TimedMoveable
/*=====
var __TimedMoveableArgs = declare([Moveable.__MoveableArgs], {
// timeout: Number
// delay move by this number of ms,
// accumulating position changes during the timeout
timeout: 0
});
=====*/
// precalculate long expressions
var oldOnMove = Moveable.prototype.onMove;
return declare("dojo.dnd.TimedMoveable", Moveable, {
// summary:
// A specialized version of Moveable to support an FPS throttling.
// This class puts an upper restriction on FPS, which may reduce
// the CPU load. The additional parameter "timeout" regulates
// the delay before actually moving the moveable object.
// object attributes (for markup)
timeout: 40, // in ms, 40ms corresponds to 25 fps
constructor: function(node, params){
// summary:
// an object that makes a node moveable with a timer
// node: Node||String
// a node (or node's id) to be moved
// params: __TimedMoveableArgs
// object with additional parameters.
// sanitize parameters
if(!params){ params = {}; }
if(params.timeout && typeof params.timeout == "number" && params.timeout >= 0){
this.timeout = params.timeout;
}
},
onMoveStop: function(/*Mover*/ mover){
if(mover._timer){
// stop timer
clearTimeout(mover._timer);
// reflect the last received position
oldOnMove.call(this, mover, mover._leftTop);
}
Moveable.prototype.onMoveStop.apply(this, arguments);
},
onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
mover._leftTop = leftTop;
if(!mover._timer){
var _t = this; // to avoid using dojo.hitch()
mover._timer = setTimeout(function(){
// we don't have any pending requests
mover._timer = null;
// reflect the last received position
oldOnMove.call(_t, mover, mover._leftTop);
}, this.timeout);
}
}
});
});
},
'dojo/NodeList-fx':function(){
define("dojo/NodeList-fx", ["./query", "./_base/lang", "./_base/connect", "./_base/fx", "./fx"],
function(query, lang, connectLib, baseFx, coreFx){
// module:
// dojo/NodeList-fx
/*=====
return function(){
// summary:
// Adds dojo.fx animation support to dojo.query() by extending the NodeList class
// with additional FX functions. NodeList is the array-like object used to hold query results.
};
=====*/
var NodeList = query.NodeList;
lang.extend(NodeList, {
_anim: function(obj, method, args){
args = args||{};
var a = coreFx.combine(
this.map(function(item){
var tmpArgs = { node: item };
lang.mixin(tmpArgs, args);
return obj[method](tmpArgs);
})
);
return args.auto ? a.play() && this : a; // dojo/_base/fx.Animation|dojo/NodeList
},
wipeIn: function(args){
// summary:
// wipe in all elements of this NodeList via `dojo/fx.wipeIn()`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// Fade in all tables with class "blah":
// | dojo.query("table.blah").wipeIn().play();
//
// example:
// Utilizing `auto` to get the NodeList back:
// | dojo.query(".titles").wipeIn({ auto:true }).onclick(someFunction);
//
return this._anim(coreFx, "wipeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
},
wipeOut: function(args){
// summary:
// wipe out all elements of this NodeList via `dojo/fx.wipeOut()`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// Wipe out all tables with class "blah":
// | dojo.query("table.blah").wipeOut().play();
return this._anim(coreFx, "wipeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
},
slideTo: function(args){
// summary:
// slide all elements of the node list to the specified place via `dojo/fx.slideTo()`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// | Move all tables with class "blah" to 300/300:
// | dojo.query("table.blah").slideTo({
// | left: 40,
// | top: 50
// | }).play();
return this._anim(coreFx, "slideTo", args); // dojo/_base/fx.Animation|dojo/NodeList
},
fadeIn: function(args){
// summary:
// fade in all elements of this NodeList via `dojo.fadeIn`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// Fade in all tables with class "blah":
// | dojo.query("table.blah").fadeIn().play();
return this._anim(baseFx, "fadeIn", args); // dojo/_base/fx.Animation|dojo/NodeList
},
fadeOut: function(args){
// summary:
// fade out all elements of this NodeList via `dojo.fadeOut`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// Fade out all elements with class "zork":
// | dojo.query(".zork").fadeOut().play();
// example:
// Fade them on a delay and do something at the end:
// | var fo = dojo.query(".zork").fadeOut();
// | dojo.connect(fo, "onEnd", function(){ /*...*/ });
// | fo.play();
// example:
// Using `auto`:
// | dojo.query("li").fadeOut({ auto:true }).filter(filterFn).forEach(doit);
//
return this._anim(baseFx, "fadeOut", args); // dojo/_base/fx.Animation|dojo/NodeList
},
animateProperty: function(args){
// summary:
// Animate all elements of this NodeList across the properties specified.
// syntax identical to `dojo.animateProperty`
//
// args: Object?
// Additional dojo/_base/fx.Animation arguments to mix into this set with the addition of
// an `auto` parameter.
//
// returns: dojo/_base/fx.Animation|dojo/NodeList
// A special args member `auto` can be passed to automatically play the animation.
// If args.auto is present, the original dojo/NodeList will be returned for further
// chaining. Otherwise the dojo/_base/fx.Animation instance is returned and must be .play()'ed
//
// example:
// | dojo.query(".zork").animateProperty({
// | duration: 500,
// | properties: {
// | color: { start: "black", end: "white" },
// | left: { end: 300 }
// | }
// | }).play();
//
// example:
// | dojo.query(".grue").animateProperty({
// | auto:true,
// | properties: {
// | height:240
// | }
// | }).onclick(handler);
return this._anim(baseFx, "animateProperty", args); // dojo/_base/fx.Animation|dojo/NodeList
},
anim: function( /*Object*/ properties,
/*Integer?*/ duration,
/*Function?*/ easing,
/*Function?*/ onEnd,
/*Integer?*/ delay){
// summary:
// Animate one or more CSS properties for all nodes in this list.
// The returned animation object will already be playing when it
// is returned. See the docs for `dojo.anim` for full details.
// properties: Object
// the properties to animate. does NOT support the `auto` parameter like other
// NodeList-fx methods.
// duration: Integer?
// Optional. The time to run the animations for
// easing: Function?
// Optional. The easing function to use.
// onEnd: Function?
// A function to be called when the animation ends
// delay:
// how long to delay playing the returned animation
// example:
// Another way to fade out:
// | dojo.query(".thinger").anim({ opacity: 0 });
// example:
// animate all elements with the "thigner" class to a width of 500
// pixels over half a second
// | dojo.query(".thinger").anim({ width: 500 }, 700);
var canim = coreFx.combine(
this.map(function(item){
return baseFx.animateProperty({
node: item,
properties: properties,
duration: duration||350,
easing: easing
});
})
);
if(onEnd){
connectLib.connect(canim, "onEnd", onEnd);
}
return canim.play(delay||0); // dojo/_base/fx.Animation
}
});
return NodeList;
});
},
'dijit/form/_ListMouseMixin':function(){
define("dijit/form/_ListMouseMixin", [
"dojo/_base/declare", // declare
"dojo/mouse",
"dojo/on",
"dojo/touch",
"./_ListBase"
], function(declare, mouse, on, touch, _ListBase){
// module:
// dijit/form/_ListMouseMixin
return declare( "dijit.form._ListMouseMixin", _ListBase, {
// summary:
// a Mixin to handle mouse or touch events for a focus-less menu
// Abstract methods that must be defined externally:
//
// - onClick: item was chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
// tags:
// private
postCreate: function(){
this.inherited(arguments);
this.own(on(this.domNode, touch.press, function(evt){ evt.preventDefault(); })); // prevent focus shift on list scrollbar press
this._listConnect(touch.press, "_onMouseDown");
this._listConnect(touch.release, "_onMouseUp");
this._listConnect(mouse.enter, "_onMouseOver");
this._listConnect(mouse.leave, "_onMouseOut");
},
_onMouseDown: function(/*Event*/ evt, /*DomNode*/ target){
if(this._hoveredNode){
this.onUnhover(this._hoveredNode);
this._hoveredNode = null;
}
this._isDragging = true;
this._setSelectedAttr(target);
},
_onMouseUp: function(/*Event*/ evt, /*DomNode*/ target){
this._isDragging = false;
var selectedNode = this.selected;
var hoveredNode = this._hoveredNode;
if(selectedNode && target == selectedNode){
this.onClick(selectedNode);
}else if(hoveredNode && target == hoveredNode){ // drag to select
this._setSelectedAttr(hoveredNode);
this.onClick(hoveredNode);
}
},
_onMouseOut: function(/*Event*/ evt, /*DomNode*/ target){
if(this._hoveredNode){
this.onUnhover(this._hoveredNode);
this._hoveredNode = null;
}
if(this._isDragging){
this._cancelDrag = (new Date()).getTime() + 1000; // cancel in 1 second if no _onMouseOver fires
}
},
_onMouseOver: function(/*Event*/ evt, /*DomNode*/ target){
if(this._cancelDrag){
var time = (new Date()).getTime();
if(time > this._cancelDrag){
this._isDragging = false;
}
this._cancelDrag = null;
}
this._hoveredNode = target;
this.onHover(target);
if(this._isDragging){
this._setSelectedAttr(target);
}
}
});
});
},
'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n",
'dojo/cookie':function(){
define("dojo/cookie", ["./_base/kernel", "./regexp"], function(dojo, regexp){
// module:
// dojo/cookie
/*=====
var __cookieProps = {
// expires: Date|String|Number?
// If a number, the number of days from today at which the cookie
// will expire. If a date, the date past which the cookie will expire.
// If expires is in the past, the cookie will be deleted.
// If expires is omitted or is 0, the cookie will expire when the browser closes.
// path: String?
// The path to use for the cookie.
// domain: String?
// The domain to use for the cookie.
// secure: Boolean?
// Whether to only send the cookie on secure connections
};
=====*/
dojo.cookie = function(/*String*/name, /*String?*/ value, /*__cookieProps?*/ props){
// summary:
// Get or set a cookie.
// description:
// If one argument is passed, returns the value of the cookie
// For two or more arguments, acts as a setter.
// name:
// Name of the cookie
// value:
// Value for the cookie
// props:
// Properties for the cookie
// example:
// set a cookie with the JSON-serialized contents of an object which
// will expire 5 days from now:
// | require(["dojo/cookie", "dojo/json"], function(cookie, json){
// | cookie("configObj", json.stringify(config, {expires: 5 }));
// | });
//
// example:
// de-serialize a cookie back into a JavaScript object:
// | require(["dojo/cookie", "dojo/json"], function(cookie, json){
// | config = json.parse(cookie("configObj"));
// | });
//
// example:
// delete a cookie:
// | require(["dojo/cookie"], function(cookie){
// | cookie("configObj", null, {expires: -1});
// | });
var c = document.cookie, ret;
if(arguments.length == 1){
var matches = c.match(new RegExp("(?:^|; )" + regexp.escapeString(name) + "=([^;]*)"));
ret = matches ? decodeURIComponent(matches[1]) : undefined;
}else{
props = props || {};
// FIXME: expires=0 seems to disappear right away, not on close? (FF3) Change docs?
var exp = props.expires;
if(typeof exp == "number"){
var d = new Date();
d.setTime(d.getTime() + exp*24*60*60*1000);
exp = props.expires = d;
}
if(exp && exp.toUTCString){ props.expires = exp.toUTCString(); }
value = encodeURIComponent(value);
var updatedCookie = name + "=" + value, propName;
for(propName in props){
updatedCookie += "; " + propName;
var propValue = props[propName];
if(propValue !== true){ updatedCookie += "=" + propValue; }
}
document.cookie = updatedCookie;
}
return ret; // String|undefined
};
dojo.cookie.isSupported = function(){
// summary:
// Use to determine if the current browser supports cookies or not.
//
// Returns true if user allows cookies.
// Returns false if user doesn't allow cookies.
if(!("cookieEnabled" in navigator)){
this("__djCookieTest__", "CookiesAllowed");
navigator.cookieEnabled = this("__djCookieTest__") == "CookiesAllowed";
if(navigator.cookieEnabled){
this("__djCookieTest__", "", {expires: -1});
}
}
return navigator.cookieEnabled;
};
return dojo.cookie;
});
},
'dojo/cache':function(){
define("dojo/cache", ["./_base/kernel", "./text"], function(dojo){
// module:
// dojo/cache
// dojo.cache is defined in dojo/text
return dojo.cache;
});
},
'url:dijit/form/templates/DropDownBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\"\n\trole=\"combobox\"\n\t><div class='dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer'\n\t\tdata-dojo-attach-point=\"_buttonNode, _popupStateNode\" role=\"presentation\"\n\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"button presentation\" aria-hidden=\"true\"\n\t\t\t${_buttonInputDisabled}\n\t/></div\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class='dijitReset dijitInputInner' ${!nameAttrSetting} type=\"text\" autocomplete=\"off\"\n\t\t\tdata-dojo-attach-point=\"textbox,focusNode\" role=\"textbox\" aria-haspopup=\"true\"\n\t/></div\n></div>\n",
'dijit/ProgressBar':function(){
require({cache:{
'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n"}});
define("dijit/ProgressBar", [
"require", // require.toUrl
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/_base/lang", // lang.mixin
"dojo/number", // number.format
"./_Widget",
"./_TemplatedMixin",
"dojo/text!./templates/ProgressBar.html"
], function(require, declare, domClass, lang, number, _Widget, _TemplatedMixin, template){
// module:
// dijit/ProgressBar
return declare("dijit.ProgressBar", [_Widget, _TemplatedMixin], {
// summary:
// A progress indication widget, showing the amount completed
// (often the percentage completed) of a task.
//
// example:
// | <div data-dojo-type="ProgressBar"
// | places="0"
// | value="..." maximum="...">
// | </div>
// progress: [const] String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// Deprecated. Use "value" instead.
progress: "0",
// value: String (Percentage or Number)
// Number or percentage indicating amount of task completed.
// With "%": percentage value, 0% <= progress <= 100%, or
// without "%": absolute value, 0 <= progress <= maximum.
// Infinity means that the progress bar is indeterminate.
value: "",
// maximum: [const] Float
// Max sample number
maximum: 100,
// places: [const] Number
// Number of places to show in values; 0 by default
places: 0,
// indeterminate: [const] Boolean
// If false: show progress value (number or percentage).
// If true: show that a process is underway but that the amount completed is unknown.
// Deprecated. Use "value" instead.
indeterminate: false,
// label: String?
// Label on progress bar. Defaults to percentage for determinate progress bar and
// blank for indeterminate progress bar.
label:"",
// name: String
// this is the field name (for a form) if set. This needs to be set if you want to use
// this widget in a dijit/form/Form widget (such as dijit/Dialog)
name: '',
templateString: template,
// _indeterminateHighContrastImagePath: [private] URL
// URL to image to use for indeterminate progress bar when display is in high contrast mode
_indeterminateHighContrastImagePath:
require.toUrl("./themes/a11y/indeterminate_progress.gif"),
postMixInProperties: function(){
this.inherited(arguments);
// Back-compat for when constructor specifies indeterminate or progress, rather than value. Remove for 2.0.
if(!(this.params && "value" in this.params)){
this.value = this.indeterminate ? Infinity : this.progress;
}
},
buildRendering: function(){
this.inherited(arguments);
this.indeterminateHighContrastImage.setAttribute("src",
this._indeterminateHighContrastImagePath.toString());
this.update();
},
update: function(/*Object?*/attributes){
// summary:
// Internal method to change attributes of ProgressBar, similar to set(hash). Users should call
// set("value", ...) rather than calling this method directly.
// attributes:
// May provide progress and/or maximum properties on this parameter;
// see attribute specs for details.
// example:
// | myProgressBar.update({'indeterminate': true});
// | myProgressBar.update({'progress': 80});
// | myProgressBar.update({'indeterminate': true, label:"Loading ..." })
// tags:
// private
// TODO: deprecate this method and use set() instead
lang.mixin(this, attributes || {});
var tip = this.internalProgress, ap = this.domNode;
var percent = 1;
if(this.indeterminate){
ap.removeAttribute("aria-valuenow");
}else{
if(String(this.progress).indexOf("%") != -1){
percent = Math.min(parseFloat(this.progress)/100, 1);
this.progress = percent * this.maximum;
}else{
this.progress = Math.min(this.progress, this.maximum);
percent = this.maximum ? this.progress / this.maximum : 0;
}
ap.setAttribute("aria-valuenow", this.progress);
}
// Even indeterminate ProgressBars should have these attributes
ap.setAttribute("aria-describedby", this.labelNode.id);
ap.setAttribute("aria-valuemin", 0);
ap.setAttribute("aria-valuemax", this.maximum);
this.labelNode.innerHTML = this.report(percent);
domClass.toggle(this.domNode, "dijitProgressBarIndeterminate", this.indeterminate);
tip.style.width = (percent * 100) + "%";
this.onChange();
},
_setValueAttr: function(v){
this._set("value", v);
if(v == Infinity){
this.update({indeterminate:true});
}else{
this.update({indeterminate:false, progress:v});
}
},
_setLabelAttr: function(label){
this._set("label", label);
this.update();
},
_setIndeterminateAttr: function(indeterminate){
// Deprecated, use set("value", ...) instead
this.indeterminate = indeterminate;
this.update();
},
report: function(/*float*/percent){
// summary:
// Generates message to show inside progress bar (normally indicating amount of task completed).
// May be overridden.
// tags:
// extension
return this.label ? this.label :
(this.indeterminate ? "&#160;" : number.format(percent, { type: "percent", places: this.places, locale: this.lang }));
},
onChange: function(){
// summary:
// Callback fired when progress updates.
// tags:
// extension
}
});
});
},
'dijit/_base/popup':function(){
define("dijit/_base/popup", [
"dojo/dom-class", // domClass.contains
"dojo/_base/window",
"../popup",
"../BackgroundIframe" // just loading for back-compat, in case client code is referencing it
], function(domClass, win, popup){
// module:
// dijit/_base/popup
/*=====
return {
// summary:
// Deprecated. Old module for popups, new code should use dijit/popup directly.
};
=====*/
// Hack support for old API passing in node instead of a widget (to various methods)
var origCreateWrapper = popup._createWrapper;
popup._createWrapper = function(widget){
if(!widget.declaredClass){
// make fake widget to pass to new API
widget = {
_popupWrapper: (widget.parentNode && domClass.contains(widget.parentNode, "dijitPopup")) ?
widget.parentNode : null,
domNode: widget,
destroy: function(){},
ownerDocument: widget.ownerDocument,
ownerDocumentBody: win.body(widget.ownerDocument)
};
}
return origCreateWrapper.call(this, widget);
};
// Support old format of orient parameter
var origOpen = popup.open;
popup.open = function(/*__OpenArgs*/ args){
// Convert old hash structure (ex: {"BL": "TL", ...}) of orient to format compatible w/new popup.open() API.
// Don't do conversion for:
// - null parameter (that means to use the default positioning)
// - "R" or "L" strings used to indicate positioning for context menus (when there is no around node)
// - new format, ex: ["below", "above"]
// - return value from deprecated dijit.getPopupAroundAlignment() method,
// ex: ["below", "above"]
if(args.orient && typeof args.orient != "string" && !("length" in args.orient)){
var ary = [];
for(var key in args.orient){
ary.push({aroundCorner: key, corner: args.orient[key]});
}
args.orient = ary;
}
return origOpen.call(this, args);
};
return popup;
});
},
'dijit/ColorPalette':function(){
require({cache:{
'url:dijit/templates/ColorPalette.html':"<div class=\"dijitInline dijitColorPalette\">\n\t<table dojoAttachPoint=\"paletteTableNode\" class=\"dijitPaletteTable\" cellSpacing=\"0\" cellPadding=\"0\" role=\"grid\">\n\t\t<tbody data-dojo-attach-point=\"gridNode\"></tbody>\n\t</table>\n</div>\n"}});
define("dijit/ColorPalette", [
"require", // require.toUrl
"dojo/text!./templates/ColorPalette.html",
"./_Widget", // used also to load dijit/hccss for setting has("highcontrast")
"./_TemplatedMixin",
"./_PaletteMixin",
"./hccss", // has("highcontrast")
"dojo/i18n", // i18n.getLocalization
"dojo/_base/Color", // dojo.Color dojo.Color.named
"dojo/_base/declare", // declare
"dojo/dom-construct", // domConstruct.place
"dojo/string", // string.substitute
"dojo/i18n!dojo/nls/colors", // translations
"dojo/colors" // extend dojo.Color w/names of other colors
], function(require, template, _Widget, _TemplatedMixin, _PaletteMixin, has, i18n, Color,
declare, domConstruct, string){
// module:
// dijit/ColorPalette
var ColorPalette = declare("dijit.ColorPalette", [_Widget, _TemplatedMixin, _PaletteMixin], {
// summary:
// A keyboard accessible color-picking widget
// description:
// Grid showing various colors, so the user can pick a certain color.
// Can be used standalone, or as a popup.
//
// example:
// | <div data-dojo-type="dijit/ColorPalette"></div>
//
// example:
// | var picker = new dijit.ColorPalette({ },srcNode);
// | picker.startup();
// palette: [const] String
// Size of grid, either "7x10" or "3x4".
palette: "7x10",
// _palettes: [protected] Map
// This represents the value of the colors.
// The first level is a hashmap of the different palettes available.
// The next two dimensions represent the columns and rows of colors.
_palettes: {
"7x10": [["white", "seashell", "cornsilk", "lemonchiffon","lightyellow", "palegreen", "paleturquoise", "lightcyan", "lavender", "plum"],
["lightgray", "pink", "bisque", "moccasin", "khaki", "lightgreen", "lightseagreen", "lightskyblue", "cornflowerblue", "violet"],
["silver", "lightcoral", "sandybrown", "orange", "palegoldenrod", "chartreuse", "mediumturquoise", "skyblue", "mediumslateblue","orchid"],
["gray", "red", "orangered", "darkorange", "yellow", "limegreen", "darkseagreen", "royalblue", "slateblue", "mediumorchid"],
["dimgray", "crimson", "chocolate", "coral", "gold", "forestgreen", "seagreen", "blue", "blueviolet", "darkorchid"],
["darkslategray","firebrick","saddlebrown", "sienna", "olive", "green", "darkcyan", "mediumblue","darkslateblue", "darkmagenta" ],
["black", "darkred", "maroon", "brown", "darkolivegreen", "darkgreen", "midnightblue", "navy", "indigo", "purple"]],
"3x4": [["white", "lime", "green", "blue"],
["silver", "yellow", "fuchsia", "navy"],
["gray", "red", "purple", "black"]]
},
// templateString: String
// The template of this widget.
templateString: template,
baseClass: "dijitColorPalette",
_dyeFactory: function(value, row, col, title){
// Overrides _PaletteMixin._dyeFactory().
return new this._dyeClass(value, row, col, title);
},
buildRendering: function(){
// Instantiate the template, which makes a skeleton into which we'll insert a bunch of
// <img> nodes
this.inherited(arguments);
// Creates customized constructor for dye class (color of a single cell) for
// specified palette and high-contrast vs. normal mode. Used in _getDye().
this._dyeClass = declare(ColorPalette._Color, {
palette: this.palette
});
// Creates <img> nodes in each cell of the template.
this._preparePalette(
this._palettes[this.palette],
i18n.getLocalization("dojo", "colors", this.lang));
}
});
ColorPalette._Color = declare("dijit._Color", Color, {
// summary:
// Object associated with each cell in a ColorPalette palette.
// Implements dijit/Dye.
// Template for each cell in normal (non-high-contrast mode). Each cell contains a wrapper
// node for showing the border (called dijitPaletteImg for back-compat), and dijitColorPaletteSwatch
// for showing the color.
template:
"<span class='dijitInline dijitPaletteImg'>" +
"<img src='${blankGif}' alt='${alt}' title='${title}' class='dijitColorPaletteSwatch' style='background-color: ${color}'/>" +
"</span>",
// Template for each cell in high contrast mode. Each cell contains an image with the whole palette,
// but scrolled and clipped to show the correct color only
hcTemplate:
"<span class='dijitInline dijitPaletteImg' style='position: relative; overflow: hidden; height: 12px; width: 14px;'>" +
"<img src='${image}' alt='${alt}' title='${title}' style='position: absolute; left: ${left}px; top: ${top}px; ${size}'/>" +
"</span>",
// _imagePaths: [protected] Map
// This is stores the path to the palette images used for high-contrast mode display
_imagePaths: {
"7x10": require.toUrl("./themes/a11y/colors7x10.png"),
"3x4": require.toUrl("./themes/a11y/colors3x4.png")
},
constructor: function(alias, row, col, title){
// summary:
// Constructor for ColorPalette._Color
// alias: String
// English name of the color.
// row: Number
// Vertical position in grid.
// column: Number
// Horizontal position in grid.
// title: String
// Localized name of the color.
this._title = title;
this._row = row;
this._col = col;
this.setColor(Color.named[alias]);
},
getValue: function(){
// summary:
// Note that although dijit._Color is initialized with a value like "white" getValue() always
// returns a hex value
return this.toHex();
},
fillCell: function(/*DOMNode*/ cell, /*String*/ blankGif){
var html = string.substitute(has("highcontrast") ? this.hcTemplate : this.template, {
// substitution variables for normal mode
color: this.toHex(),
blankGif: blankGif,
alt: this._title,
title: this._title,
// variables used for high contrast mode
image: this._imagePaths[this.palette].toString(),
left: this._col * -20 - 5,
top: this._row * -20 - 5,
size: this.palette == "7x10" ? "height: 145px; width: 206px" : "height: 64px; width: 86px"
});
domConstruct.place(html, cell);
}
});
return ColorPalette;
});
},
'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n",
'dojo/_base/url':function(){
define("dojo/_base/url", ["./kernel"], function(dojo){
// module:
// dojo/url
var
ore = new RegExp("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\?([^#]*))?(#(.*))?$"),
ire = new RegExp("^((([^\\[:]+):)?([^@]+)@)?(\\[([^\\]]+)\\]|([^\\[:]*))(:([0-9]+))?$"),
_Url = function(){
var n = null,
_a = arguments,
uri = [_a[0]];
// resolve uri components relative to each other
for(var i = 1; i<_a.length; i++){
if(!_a[i]){ continue; }
// Safari doesn't support this.constructor so we have to be explicit
// FIXME: Tracked (and fixed) in Webkit bug 3537.
// http://bugs.webkit.org/show_bug.cgi?id=3537
var relobj = new _Url(_a[i]+""),
uriobj = new _Url(uri[0]+"");
if(
relobj.path == "" &&
!relobj.scheme &&
!relobj.authority &&
!relobj.query
){
if(relobj.fragment != n){
uriobj.fragment = relobj.fragment;
}
relobj = uriobj;
}else if(!relobj.scheme){
relobj.scheme = uriobj.scheme;
if(!relobj.authority){
relobj.authority = uriobj.authority;
if(relobj.path.charAt(0) != "/"){
var path = uriobj.path.substring(0,
uriobj.path.lastIndexOf("/") + 1) + relobj.path;
var segs = path.split("/");
for(var j = 0; j < segs.length; j++){
if(segs[j] == "."){
// flatten "./" references
if(j == segs.length - 1){
segs[j] = "";
}else{
segs.splice(j, 1);
j--;
}
}else if(j > 0 && !(j == 1 && segs[0] == "") &&
segs[j] == ".." && segs[j-1] != ".."){
// flatten "../" references
if(j == (segs.length - 1)){
segs.splice(j, 1);
segs[j - 1] = "";
}else{
segs.splice(j - 1, 2);
j -= 2;
}
}
}
relobj.path = segs.join("/");
}
}
}
uri = [];
if(relobj.scheme){
uri.push(relobj.scheme, ":");
}
if(relobj.authority){
uri.push("//", relobj.authority);
}
uri.push(relobj.path);
if(relobj.query){
uri.push("?", relobj.query);
}
if(relobj.fragment){
uri.push("#", relobj.fragment);
}
}
this.uri = uri.join("");
// break the uri into its main components
var r = this.uri.match(ore);
this.scheme = r[2] || (r[1] ? "" : n);
this.authority = r[4] || (r[3] ? "" : n);
this.path = r[5]; // can never be undefined
this.query = r[7] || (r[6] ? "" : n);
this.fragment = r[9] || (r[8] ? "" : n);
if(this.authority != n){
// server based naming authority
r = this.authority.match(ire);
this.user = r[3] || n;
this.password = r[4] || n;
this.host = r[6] || r[7]; // ipv6 || ipv4
this.port = r[9] || n;
}
};
_Url.prototype.toString = function(){ return this.uri; };
return dojo._Url = _Url;
});
},
'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n",
'dojo/text':function(){
define("dojo/text", ["./_base/kernel", "require", "./has", "./_base/xhr"], function(dojo, require, has, xhr){
// module:
// dojo/text
var getText;
if( 1 ){
getText= function(url, sync, load){
xhr("GET", {url: url, sync:!!sync, load: load, headers: dojo.config.textPluginHeaders || {}});
};
}else{
// TODOC: only works for dojo AMD loader
if(require.getText){
getText= require.getText;
}else{
console.error("dojo/text plugin failed to load because loader does not support getText");
}
}
var
theCache = {},
strip= function(text){
//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.
if(text){
text= text.replace(/^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, "");
var matches= text.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
if(matches){
text= matches[1];
}
}else{
text = "";
}
return text;
},
notFound = {},
pending = {};
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:
// If module is a string that contains slashes, then it is interpretted as a fully
// resolved path (typically a result returned by require.toUrl), and url should not be
// provided. This is the preferred signature. If module is a string that does not
// contain slashes, then url must also be provided and module and url are used to
// call `dojo.moduleUrl()` to generate a module URL. This signature is deprecated.
// 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 with slashes, a fully resolved path; if a String without slashes, 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 demonstrates 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});
// * (string string [value]) => (module, url, value)
// * (object [value]) => (module, value), url defaults to ""
//
// * if module is an object, then it must be convertable to a string
// * (module, url) module + (url ? ("/" + url) : "") must be a legal argument to require.toUrl
// * value may be a string or an object; if an object then may have the properties "value" and/or "sanitize"
var key;
if(typeof module=="string"){
if(/\//.test(module)){
// module is a version 1.7+ resolved path
key = module;
value = url;
}else{
// module is a version 1.6- argument to dojo.moduleUrl
key = require.toUrl(module.replace(/\./g, "/") + (url ? ("/" + url) : ""));
}
}else{
key = module + "";
value = url;
}
var
val = (value != undefined && typeof value != "string") ? value.value : value,
sanitize = value && value.sanitize;
if(typeof val == "string"){
//We have a string, set cache value
theCache[key] = val;
return sanitize ? strip(val) : val;
}else if(val === null){
//Remove cached value
delete theCache[key];
return null;
}else{
//Allow cache values to be empty strings. If key property does
//not exist, fetch it.
if(!(key in theCache)){
getText(key, true, function(text){
theCache[key]= text;
});
}
return sanitize ? strip(theCache[key]) : theCache[key];
}
};
return {
// summary:
// This module implements the dojo/text! plugin and the dojo.cache API.
// description:
// We choose to include our own plugin to leverage functionality already contained in dojo
// and thereby reduce the size of the plugin compared to various foreign loader implementations.
// Also, this allows foreign AMD loaders to be used without their plugins.
//
// CAUTION: this module is designed to optionally function synchronously to support the dojo v1.x synchronous
// loader. This feature is outside the scope of the CommonJS plugins specification.
// the dojo/text caches it's own resources because of dojo.cache
dynamic: true,
normalize: function(id, toAbsMid){
// id is something like (path may be relative):
//
// "path/to/text.html"
// "path/to/text.html!strip"
var parts= id.split("!"),
url= parts[0];
return (/^\./.test(url) ? toAbsMid(url) : url) + (parts[1] ? "!" + parts[1] : "");
},
load: function(id, require, load){
// id: String
// Path to the resource.
// require: Function
// Object that include the function toUrl with given id returns a valid URL from which to load the text.
// load: Function
// Callback function which will be called, when the loading finished.
// id is something like (path is always absolute):
//
// "path/to/text.html"
// "path/to/text.html!strip"
var
parts= id.split("!"),
stripFlag= parts.length>1,
absMid= parts[0],
url = require.toUrl(parts[0]),
requireCacheUrl = "url:" + url,
text = notFound,
finish = function(text){
load(stripFlag ? strip(text) : text);
};
if(absMid in theCache){
text = theCache[absMid];
}else if(requireCacheUrl in require.cache){
text = require.cache[requireCacheUrl];
}else if(url in theCache){
text = theCache[url];
}
if(text===notFound){
if(pending[url]){
pending[url].push(finish);
}else{
var pendingList = pending[url] = [finish];
getText(url, !require.async, function(text){
theCache[absMid]= theCache[url]= text;
for(var i = 0; i<pendingList.length;){
pendingList[i++](text);
}
delete pending[url];
});
}
}else{
finish(text);
}
}
};
});
},
'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n",
'dojo/uacss':function(){
define("dojo/uacss", ["./dom-geometry", "./_base/lang", "./ready", "./sniff", "./_base/window"],
function(geometry, lang, ready, has, baseWindow){
// module:
// dojo/uacss
/*=====
return {
// 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.
//
// Returns the has() method.
};
=====*/
var
html = baseWindow.doc.documentElement,
ie = has("ie"),
opera = has("opera"),
maj = Math.floor,
ff = has("ff"),
boxModel = geometry.boxModel.replace(/-/,''),
classes = {
"dj_quirks": has("quirks"),
// NOTE: Opera not supported by dijit
"dj_opera": opera,
"dj_khtml": has("khtml"),
"dj_webkit": has("webkit"),
"dj_safari": has("safari"),
"dj_chrome": has("chrome"),
"dj_gecko": has("mozilla")
}; // no dojo unsupported browsers
if(ie){
classes["dj_ie"] = true;
classes["dj_ie" + maj(ie)] = true;
classes["dj_iequirks"] = has("quirks");
}
if(ff){
classes["dj_ff" + maj(ff)] = true;
}
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 = lang.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).
// priority is 90 to run ahead of parser priority of 100
ready(90, function(){
if(!geometry.isBodyLtr()){
var rtlClassStr = "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl ");
html.className = lang.trim(html.className + " " + rtlClassStr + "dj_rtl dijitRtl " + classStr.replace(/ /g, "-rtl "));
}
});
return has;
});
},
'dijit/Tooltip':function(){
require({cache:{
'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n"}});
define("dijit/Tooltip", [
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/dom", // dom.byId
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/_base/lang", // lang.hitch lang.isArrayLike
"dojo/mouse",
"dojo/on",
"dojo/sniff", // has("ie")
"./_base/manager", // manager.defaultDuration
"./place",
"./_Widget",
"./_TemplatedMixin",
"./BackgroundIframe",
"dojo/text!./templates/Tooltip.html",
"./main" // sets dijit.showTooltip etc. for back-compat
], function(array, declare, fx, dom, domClass, domGeometry, domStyle, lang, mouse, on, has,
manager, place, _Widget, _TemplatedMixin, BackgroundIframe, template, dijit){
// module:
// dijit/Tooltip
// TODO: Tooltip should really share more positioning code with TooltipDialog, like:
// - the orient() method
// - the connector positioning code in show()
// - the dijitTooltip[Dialog] class
//
// The problem is that Tooltip's implementation supplies it's own <iframe> and interacts directly
// with dijit/place, rather than going through dijit/popup like TooltipDialog and other popups (ex: Menu).
var MasterTooltip = declare("dijit._MasterTooltip", [_Widget, _TemplatedMixin], {
// summary:
// Internal widget that holds the actual tooltip markup,
// which occurs once per page.
// Called by Tooltip widgets which are just containers to hold
// the markup
// tags:
// protected
// duration: Integer
// Milliseconds to fade in/fade out
duration: manager.defaultDuration,
templateString: template,
postCreate: function(){
this.ownerDocumentBody.appendChild(this.domNode);
this.bgIframe = new BackgroundIframe(this.domNode);
// Setup fade-in and fade-out functions.
this.fadeIn = fx.fadeIn({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onShow") });
this.fadeOut = fx.fadeOut({ node: this.domNode, duration: this.duration, onEnd: lang.hitch(this, "_onHide") });
},
show: function(innerHTML, aroundNode, position, rtl, textDir){
// summary:
// Display tooltip w/specified contents to right of specified node
// (To left if there's no space on the right, or if rtl == true)
// innerHTML: String
// Contents of the tooltip
// aroundNode: DomNode|dijit/place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
if(this.aroundNode && this.aroundNode === aroundNode && this.containerNode.innerHTML == innerHTML){
return;
}
if(this.fadeOut.status() == "playing"){
// previous tooltip is being hidden; wait until the hide completes then show new one
this._onDeck=arguments;
return;
}
this.containerNode.innerHTML=innerHTML;
if(textDir){
this.set("textDir", textDir);
}
this.containerNode.align = rtl? "right" : "left"; //fix the text alignment
var pos = place.around(this.domNode, aroundNode,
position && position.length ? position : Tooltip.defaultPosition, !rtl, lang.hitch(this, "orient"));
// Position the tooltip connector for middle alignment.
// This could not have been done in orient() since the tooltip wasn't positioned at that time.
var aroundNodeCoords = pos.aroundNodePos;
if(pos.corner.charAt(0) == 'M' && pos.aroundCorner.charAt(0) == 'M'){
this.connectorNode.style.top = aroundNodeCoords.y + ((aroundNodeCoords.h - this.connectorNode.offsetHeight) >> 1) - pos.y + "px";
this.connectorNode.style.left = "";
}else if(pos.corner.charAt(1) == 'M' && pos.aroundCorner.charAt(1) == 'M'){
this.connectorNode.style.left = aroundNodeCoords.x + ((aroundNodeCoords.w - this.connectorNode.offsetWidth) >> 1) - pos.x + "px";
}else{
// Not *-centered, but just above/below/after/before
this.connectorNode.style.left = "";
this.connectorNode.style.top = "";
}
// show it
domStyle.set(this.domNode, "opacity", 0);
this.fadeIn.play();
this.isShowingNow = true;
this.aroundNode = aroundNode;
},
orient: function(/*DomNode*/ node, /*String*/ aroundCorner, /*String*/ tooltipCorner, /*Object*/ spaceAvailable, /*Object*/ aroundNodeCoords){
// summary:
// Private function to set CSS for tooltip node based on which position it's in.
// This is called by the dijit popup code. It will also reduce the tooltip's
// width to whatever width is available
// tags:
// protected
this.connectorNode.style.top = ""; //reset to default
var heightAvailable = spaceAvailable.h,
widthAvailable = spaceAvailable.w;
node.className = "dijitTooltip " +
{
"MR-ML": "dijitTooltipRight",
"ML-MR": "dijitTooltipLeft",
"TM-BM": "dijitTooltipAbove",
"BM-TM": "dijitTooltipBelow",
"BL-TL": "dijitTooltipBelow dijitTooltipABLeft",
"TL-BL": "dijitTooltipAbove dijitTooltipABLeft",
"BR-TR": "dijitTooltipBelow dijitTooltipABRight",
"TR-BR": "dijitTooltipAbove dijitTooltipABRight",
"BR-BL": "dijitTooltipRight",
"BL-BR": "dijitTooltipLeft"
}[aroundCorner + "-" + tooltipCorner];
// reset width; it may have been set by orient() on a previous tooltip show()
this.domNode.style.width = "auto";
// Reduce tooltip's width to the amount of width available, so that it doesn't overflow screen.
// Note that sometimes widthAvailable is negative, but we guard against setting style.width to a
// negative number since that causes an exception on IE.
var size = domGeometry.position(this.domNode);
if(has("ie") == 9){
// workaround strange IE9 bug where setting width to offsetWidth causes words to wrap
size.w += 2;
}
var width = Math.min((Math.max(widthAvailable,1)), size.w);
domGeometry.setMarginBox(this.domNode, {w: width});
// Reposition the tooltip connector.
if(tooltipCorner.charAt(0) == 'B' && aroundCorner.charAt(0) == 'B'){
var bb = domGeometry.position(node);
var tooltipConnectorHeight = this.connectorNode.offsetHeight;
if(bb.h > heightAvailable){
// The tooltip starts at the top of the page and will extend past the aroundNode
var aroundNodePlacement = heightAvailable - ((aroundNodeCoords.h + tooltipConnectorHeight) >> 1);
this.connectorNode.style.top = aroundNodePlacement + "px";
this.connectorNode.style.bottom = "";
}else{
// Align center of connector with center of aroundNode, except don't let bottom
// of connector extend below bottom of tooltip content, or top of connector
// extend past top of tooltip content
this.connectorNode.style.bottom = Math.min(
Math.max(aroundNodeCoords.h/2 - tooltipConnectorHeight/2, 0),
bb.h - tooltipConnectorHeight) + "px";
this.connectorNode.style.top = "";
}
}else{
// reset the tooltip back to the defaults
this.connectorNode.style.top = "";
this.connectorNode.style.bottom = "";
}
return Math.max(0, size.w - widthAvailable);
},
_onShow: function(){
// summary:
// Called at end of fade-in operation
// tags:
// protected
if(has("ie")){
// the arrow won't show up on a node w/an opacity filter
this.domNode.style.filter="";
}
},
hide: function(aroundNode){
// summary:
// Hide the tooltip
if(this._onDeck && this._onDeck[1] == aroundNode){
// this hide request is for a show() that hasn't even started yet;
// just cancel the pending show()
this._onDeck=null;
}else if(this.aroundNode === aroundNode){
// this hide request is for the currently displayed tooltip
this.fadeIn.stop();
this.isShowingNow = false;
this.aroundNode = null;
this.fadeOut.play();
}else{
// just ignore the call, it's for a tooltip that has already been erased
}
},
_onHide: function(){
// summary:
// Called at end of fade-out operation
// tags:
// protected
this.domNode.style.cssText=""; // to position offscreen again
this.containerNode.innerHTML="";
if(this._onDeck){
// a show request has been queued up; do it now
this.show.apply(this, this._onDeck);
this._onDeck=null;
}
},
_setAutoTextDir: function(/*Object*/node){
// summary:
// Resolve "auto" text direction for children nodes
// tags:
// private
this.applyTextDir(node, has("ie") ? node.outerText : node.textContent);
array.forEach(node.children, function(child){this._setAutoTextDir(child); }, this);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
this._set("textDir", textDir);
if (textDir == "auto"){
this._setAutoTextDir(this.containerNode);
}else{
this.containerNode.dir = this.textDir;
}
}
});
dijit.showTooltip = function(innerHTML, aroundNode, position, rtl, textDir){
// summary:
// Static method to display tooltip w/specified contents in specified position.
// See description of dijit/Tooltip.defaultPosition for details on position parameter.
// If position is not specified then dijit/Tooltip.defaultPosition is used.
// innerHTML: String
// Contents of the tooltip
// aroundNode: place.__Rectangle
// Specifies that tooltip should be next to this node / area
// position: String[]?
// List of positions to try to position tooltip (ex: ["right", "above"])
// rtl: Boolean?
// Corresponds to `WidgetBase.dir` attribute, where false means "ltr" and true
// means "rtl"; specifies GUI direction, not text direction.
// textDir: String?
// Corresponds to `WidgetBase.textdir` attribute; specifies direction of text.
// After/before don't work, but for back-compat convert them to the working after-centered, before-centered.
// Possibly remove this in 2.0. Alternately, get before/after to work.
if(position){
position = array.map(position, function(val){
return {after: "after-centered", before: "before-centered"}[val] || val;
});
}
if(!Tooltip._masterTT){ dijit._masterTT = Tooltip._masterTT = new MasterTooltip(); }
return Tooltip._masterTT.show(innerHTML, aroundNode, position, rtl, textDir);
};
dijit.hideTooltip = function(aroundNode){
// summary:
// Static method to hide the tooltip displayed via showTooltip()
return Tooltip._masterTT && Tooltip._masterTT.hide(aroundNode);
};
var Tooltip = declare("dijit.Tooltip", _Widget, {
// summary:
// Pops up a tooltip (a help message) when you hover over a node.
// Also provides static show() and hide() methods that can be used without instantiating a dijit/Tooltip.
// label: String
// Text to display in the tooltip.
// Specified as innerHTML when creating the widget from markup.
label: "",
// showDelay: Integer
// Number of milliseconds to wait after hovering over/focusing on the object, before
// the tooltip is displayed.
showDelay: 400,
// connectId: String|String[]|DomNode|DomNode[]
// Id of domNode(s) to attach the tooltip to.
// When user hovers over specified dom node(s), the tooltip will appear.
connectId: [],
// position: String[]
// See description of `dijit/Tooltip.defaultPosition` for details on position parameter.
position: [],
// selector: String?
// CSS expression to apply this Tooltip to descendants of connectIds, rather than to
// the nodes specified by connectIds themselves. Useful for applying a Tooltip to
// a range of rows in a table, tree, etc. Use in conjunction with getContent() parameter.
// Ex: connectId: myTable, selector: "tr", getContent: function(node){ return ...; }
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple connectIds. selector gives the same effect.
// So, change connectId to a "", remove addTarget()/removeTarget(), etc.
_setConnectIdAttr: function(/*String|String[]}DomNode|DomNode[]*/ newId){
// summary:
// Connect to specified node(s)
// Remove connections to old nodes (if there are any)
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
// Make array of id's to connect to, excluding entries for nodes that don't exist yet, see startup()
this._connectIds = array.filter(lang.isArrayLike(newId) ? newId : (newId ? [newId] : []),
function(id){ return dom.byId(id, this.ownerDocument); }, this);
// Make connections
this._connections = array.map(this._connectIds, function(id){
var node = dom.byId(id, this.ownerDocument),
selector = this.selector,
delegatedEvent = selector ?
function(eventType){ return on.selector(selector, eventType); } :
function(eventType){ return eventType; },
self = this;
return [
on(node, delegatedEvent(mouse.enter), function(){
self._onHover(this);
}),
on(node, delegatedEvent("focusin"), function(){
self._onHover(this);
}),
on(node, delegatedEvent(mouse.leave), lang.hitch(self, "_onUnHover")),
on(node, delegatedEvent("focusout"), lang.hitch(self, "_onUnHover"))
];
}, this);
this._set("connectId", newId);
},
addTarget: function(/*OomNode|String*/ node){
// summary:
// Attach tooltip to specified node if it's not already connected
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node;
if(array.indexOf(this._connectIds, id) == -1){
this.set("connectId", this._connectIds.concat(id));
}
},
removeTarget: function(/*DomNode|String*/ node){
// summary:
// Detach tooltip from specified node
// TODO: remove in 2.0 and just use set("connectId", ...) interface
var id = node.id || node, // map from DOMNode back to plain id string
idx = array.indexOf(this._connectIds, id);
if(idx >= 0){
// remove id (modifies original this._connectIds but that's OK in this case)
this._connectIds.splice(idx, 1);
this.set("connectId", this._connectIds);
}
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode,"dijitTooltipData");
},
startup: function(){
this.inherited(arguments);
// If this tooltip was created in a template, or for some other reason the specified connectId[s]
// didn't exist during the widget's initialization, then connect now.
var ids = this.connectId;
array.forEach(lang.isArrayLike(ids) ? ids : [ids], this.addTarget, this);
},
getContent: function(/*DomNode*/ node){
// summary:
// User overridable function that return the text to display in the tooltip.
// tags:
// extension
return this.label || this.domNode.innerHTML;
},
_onHover: function(/*DomNode*/ target){
// summary:
// Despite the name of this method, it actually handles both hover and focus
// events on the target node, setting a timer to show the tooltip.
// tags:
// private
if(!this._showTimer){
this._showTimer = this.defer(function(){ this.open(target); }, this.showDelay);
}
},
_onUnHover: function(){
// summary:
// Despite the name of this method, it actually handles both mouseleave and blur
// events on the target node, hiding the tooltip.
// tags:
// private
if(this._showTimer){
this._showTimer.remove();
delete this._showTimer;
}
this.close();
},
open: function(/*DomNode*/ target){
// summary:
// Display the tooltip; usually not called directly.
// tags:
// private
if(this._showTimer){
this._showTimer.remove();
delete this._showTimer;
}
var content = this.getContent(target);
if(!content){
return;
}
Tooltip.show(content, target, this.position, !this.isLeftToRight(), this.textDir);
this._connectNode = target; // _connectNode means "tooltip currently displayed for this node"
this.onShow(target, this.position);
},
close: function(){
// summary:
// Hide the tooltip or cancel timer for show of tooltip
// tags:
// private
if(this._connectNode){
// if tooltip is currently shown
Tooltip.hide(this._connectNode);
delete this._connectNode;
this.onHide();
}
if(this._showTimer){
// if tooltip is scheduled to be shown (after a brief delay)
this._showTimer.remove();
delete this._showTimer;
}
},
onShow: function(/*===== target, position =====*/){
// summary:
// Called when the tooltip is shown
// tags:
// callback
},
onHide: function(){
// summary:
// Called when the tooltip is hidden
// tags:
// callback
},
destroy: function(){
this.close();
// Remove connections manually since they aren't registered to be removed by _WidgetBase
array.forEach(this._connections || [], function(nested){
array.forEach(nested, function(handle){ handle.remove(); });
}, this);
this.inherited(arguments);
}
});
Tooltip._MasterTooltip = MasterTooltip; // for monkey patching
Tooltip.show = dijit.showTooltip; // export function through module return value
Tooltip.hide = dijit.hideTooltip; // export function through module return value
Tooltip.defaultPosition = ["after-centered", "before-centered"];
/*=====
lang.mixin(Tooltip, {
// defaultPosition: String[]
// This variable controls the position of tooltips, if the position is not specified to
// the Tooltip widget or *TextBox widget itself. It's an array of strings with the values
// possible for `dijit/place.around()`. The recommended values are:
//
// - before-centered: centers tooltip to the left of the anchor node/widget, or to the right
// in the case of RTL scripts like Hebrew and Arabic
// - after-centered: centers tooltip to the right of the anchor node/widget, or to the left
// in the case of RTL scripts like Hebrew and Arabic
// - above-centered: tooltip is centered above anchor node
// - below-centered: tooltip is centered above anchor node
//
// The list is positions is tried, in order, until a position is found where the tooltip fits
// within the viewport.
//
// Be careful setting this parameter. A value of "above-centered" may work fine until the user scrolls
// the screen so that there's no room above the target node. Nodes with drop downs, like
// DropDownButton or FilteringSelect, are especially problematic, in that you need to be sure
// that the drop down and tooltip don't overlap, even when the viewport is scrolled so that there
// is only room below (or above) the target node, but not both.
});
=====*/
return Tooltip;
});
},
'dojo/string':function(){
define("dojo/string", [
"./_base/kernel", // kernel.global
"./_base/lang"
], function(kernel, lang){
// module:
// dojo/string
var string = {
// summary:
// String utilities for Dojo
};
lang.setObject("dojo.string", string);
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
};
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++++++".
// | string.pad("Dojo", 10, "+", true);
if(!ch){
ch = '0';
}
var out = String(text),
pad = string.rep(ch, Math.ceil((size - out.length) / ch.length));
return end ? out + pad : pad + out; // String
};
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
// | 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.
// | 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'."
// | 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"
// | string.substitute(
// | "${0:postfix}", ["thinger"], null, {
// | postfix: function(value, key){
// | return value + " -- howdy";
// | }
// | }
// | );
thisObject = thisObject || kernel.global;
transform = transform ?
lang.hitch(thisObject, transform) : function(v){ return v; };
return template.replace(/\$\{([^\s\:\}]+)(?:\:([^\s\:\}]+))?\}/g,
function(match, key, format){
var value = lang.getObject(key, false, map);
if(format){
value = lang.getObject(format, false, thisObject).call(thisObject, value, key);
}
return transform(value, key).toString();
}); // String
};
string.trim = String.prototype.trim ?
lang.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;
};
/*=====
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
};
=====*/
return string;
});
},
'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>",
'dijit/dijit':function(){
define("dijit/dijit", [
"./main",
"./_base",
"dojo/parser",
"./_Widget",
"./_TemplatedMixin",
"./_Container",
"./layout/_LayoutWidget",
"./form/_FormWidget",
"./form/_FormValueWidget"
], function(dijit){
// module:
// dijit/dijit
/*=====
return {
// summary:
// A roll-up for common dijit methods
// 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
};
=====*/
return dijit;
});
},
'dijit/form/DropDownButton':function(){
require({cache:{
'url:dijit/form/templates/DropDownButton.html':"<span class=\"dijit dijitReset dijitInline\"\n\t><span class='dijitReset dijitInline dijitButtonNode'\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" data-dojo-attach-point=\"_buttonNode\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"focusNode,titleNode,_arrowWrapperNode\"\n\t\t\trole=\"button\" aria-haspopup=\"true\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\"\n\t\t\t\tdata-dojo-attach-point=\"iconNode\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode,_popupStateNode\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonInner\"></span\n\t\t\t><span class=\"dijitReset dijitInline dijitArrowButtonChar\">&#9660;</span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\" tabIndex=\"-1\"\n\t\tdata-dojo-attach-point=\"valueNode\" role=\"presentation\"\n/></span>\n"}});
define("dijit/form/DropDownButton", [
"dojo/_base/declare", // declare
"dojo/_base/lang", // hitch
"dojo/query", // query
"../registry", // registry.byNode
"../popup", // dijit.popup2.hide
"./Button",
"../_Container",
"../_HasDropDown",
"dojo/text!./templates/DropDownButton.html"
], function(declare, lang, query, registry, popup, Button, _Container, _HasDropDown, template){
// module:
// dijit/form/DropDownButton
return declare("dijit.form.DropDownButton", [Button, _Container, _HasDropDown], {
// summary:
// A button with a drop down
//
// example:
// | <button data-dojo-type="dijit/form/DropDownButton">
// | Hello world
// | <div data-dojo-type="dijit/Menu">...</div>
// | </button>
//
// example:
// | var button1 = new DropDownButton({ label: "hi", dropDown: new dijit.Menu(...) });
// | win.body().appendChild(button1);
//
baseClass : "dijitDropDownButton",
templateString: template,
_fillContent: function(){
// Overrides Button._fillContent().
//
// My inner HTML contains both the button contents and a drop down widget, like
// <DropDownButton> <span>push me</span> <Menu> ... </Menu> </DropDownButton>
// The first node is assumed to be the button content. The widget is the popup.
if(this.srcNodeRef){ // programatically created buttons might not define srcNodeRef
//FIXME: figure out how to filter out the widget and use all remaining nodes as button
// content, not just nodes[0]
var nodes = query("*", this.srcNodeRef);
this.inherited(arguments, [nodes[0]]);
// save pointer to srcNode so we can grab the drop down widget after it's instantiated
this.dropDownContainer = this.srcNodeRef;
}
},
startup: function(){
if(this._started){ return; }
// the child widget from srcNodeRef is the dropdown widget. Insert it in the page DOM,
// make it invisible, and store a reference to pass to the popup code.
if(!this.dropDown && this.dropDownContainer){
var dropDownNode = query("[widgetId]", this.dropDownContainer)[0];
this.dropDown = registry.byNode(dropDownNode);
delete this.dropDownContainer;
}
if(this.dropDown){
popup.hide(this.dropDown);
}
this.inherited(arguments);
},
isLoaded: function(){
// Returns whether or not we are loaded - if our dropdown has an href,
// then we want to check that.
var dropDown = this.dropDown;
return (!!dropDown && (!dropDown.href || dropDown.isLoaded));
},
loadDropDown: function(/*Function*/ callback){
// Default implementation assumes that drop down already exists,
// but hasn't loaded it's data (ex: ContentPane w/href).
// App must override if the drop down is lazy-created.
var dropDown = this.dropDown;
var handler = dropDown.on("load", lang.hitch(this, function(){
handler.remove();
callback();
}));
dropDown.refresh(); // tell it to load
},
isFocusable: function(){
// Overridden so that focus is handled by the _HasDropDown mixin, not by
// the _FormWidget mixin.
return this.inherited(arguments) && !this._mouseDown;
}
});
});
},
'dijit/form/_FormValueMixin':function(){
define("dijit/form/_FormValueMixin", [
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/keys", // keys.ESCAPE
"dojo/sniff", // has("ie"), has("quirks")
"./_FormWidgetMixin"
], function(declare, domAttr, keys, has, _FormWidgetMixin){
// module:
// dijit/form/_FormValueMixin
return declare("dijit.form._FormValueMixin", _FormWidgetMixin, {
// summary:
// Mixin for widgets corresponding to native HTML elements such as `<input>` or `<select>`
// that have user changeable values.
// description:
// Each _FormValueMixin 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.
// 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,
_setReadOnlyAttr: function(/*Boolean*/ value){
domAttr.set(this.focusNode, 'readOnly', value);
this._set("readOnly", value);
},
postCreate: function(){
this.inherited(arguments);
if(has("ie")){ // 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._lastValueReported = this._resetValue = this.value;
}
},
_setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('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._handleOnChange(newValue, priorityChange);
},
_handleOnChange: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
// summary:
// Called when the value of the widget has changed. Saves the new value in this.value,
// and calls onChange() if appropriate. See _FormWidget._handleOnChange() for details.
this._set("value", newValue);
this.inherited(arguments);
},
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 == keys.ESCAPE && !(e.ctrlKey || e.altKey || e.metaKey)){
if(has("ie") < 9 || (has("ie") && has("quirks"))){
e.preventDefault(); // default behavior needs to be stopped here since keypress is too late
var node = e.srcElement,
te = node.ownerDocument.createEventObject();
te.keyCode = keys.ESCAPE;
te.shiftKey = e.shiftKey;
node.fireEvent('onkeypress', te);
}
}
}
});
});
},
'dijit/form/_FormWidgetMixin':function(){
define("dijit/form/_FormWidgetMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/dom-style", // domStyle.get
"dojo/_base/lang", // lang.hitch lang.isArray
"dojo/mouse", // mouse.isLeft
"dojo/sniff", // has("webkit")
"dojo/window", // winUtils.scrollIntoView
"../a11y" // a11y.hasDefaultTabStop
], function(array, declare, domAttr, domStyle, lang, mouse, has, winUtils, a11y){
// module:
// dijit/form/_FormWidgetMixin
return declare("dijit.form._FormWidgetMixin", null, {
// summary:
// Mixin 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/_WidgetBase.set()`.
//
// They also share some common methods.
// name: [const] 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: [const] String
// Corresponds to the native HTML `<input>` element's attribute.
type: "text",
// type: String
// Apply aria-label in markup to the widget's focusNode
"aria-label": "focusNode",
// tabIndex: String
// Order fields are traversed when user hits the tab key
tabIndex: "0",
_setTabIndexAttr: "focusNode", // force copy even when tabIndex default value, needed since Button is <span>
// 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,
// Override _WidgetBase mapping id to this.domNode, needs to be on focusNode so <label> etc.
// works with screen reader
_setIdAttr: "focusNode",
_setDisabledAttr: function(/*Boolean*/ value){
this._set("disabled", value);
domAttr.set(this.focusNode, 'disabled', value);
if(this.valueNode){
domAttr.set(this.valueNode, 'disabled', value);
}
this.focusNode.setAttribute("aria-disabled", value ? "true" : "false");
if(value){
// reset these, because after the domNode is disabled, we can no longer receive
// mouse related events, see #4200
this._set("hovering", false);
this._set("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 :
("_setTabIndexAttr" in this) ? this._setTabIndexAttr : "focusNode";
array.forEach(lang.isArray(attachPointNames) ? attachPointNames : [attachPointNames], function(attachPointName){
var node = this[attachPointName];
// complex code because tabIndex=-1 on a <div> doesn't work on FF
if(has("webkit") || a11y.hasDefaultTabStop(node)){ // see #11064 about webkit bug
node.setAttribute('tabIndex', "-1");
}else{
node.removeAttribute('tabIndex');
}
}, this);
}else{
if(this.tabIndex != ""){
this.set('tabIndex', this.tabIndex);
}
}
},
_onFocus: function(/*String*/ by){
// If user clicks on the widget, even if the mouse is released outside of it,
// this widget's focusNode should get focus (to mimic native browser hehavior).
// Browsers often need help to make sure the focus via mouse actually gets to the focusNode.
if(by == "mouse" && this.isFocusable()){
// IE exhibits strange scrolling behavior when refocusing a node so only do it when !focused.
var focusConnector = this.connect(this.focusNode, "onfocus", function(){
this.disconnect(mouseUpConnector);
this.disconnect(focusConnector);
});
// 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(this.ownerDocumentBody, "onmouseup", function(){
this.disconnect(mouseUpConnector);
this.disconnect(focusConnector);
// if here, then the mousedown did not focus the focusNode as the default action
if(this.focused){
this.focus();
}
});
}
if(this.scrollOnFocus){
this.defer(function(){ winUtils.scrollIntoView(this.domNode); }); // without defer, the input caret position can change on mouse click
}
this.inherited(arguments);
},
isFocusable: function(){
// summary:
// Tells if this widget is focusable or not. Used internally by dijit.
// tags:
// protected
return !this.disabled && this.focusNode && (domStyle.get(this.domNode, "display") != "none");
},
focus: function(){
// summary:
// Put focus on this widget
if(!this.disabled && this.focusNode.focus){
try{ this.focusNode.focus(); }catch(e){}/*squelch errors from hidden nodes*/
}
},
compare: function(/*anything*/ val1, /*anything*/ val2){
// summary:
// Compare 2 values (as returned by get('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==false,
// onChange is only called form priorityChange=true events.
// tags:
// private
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;
}
this._pendingOnChange = this._pendingOnChange
|| (typeof newValue != typeof this._lastValueReported)
|| (this.compare(newValue, this._lastValueReported) != 0);
if((this.intermediateChanges || priorityChange || priorityChange === undefined) && this._pendingOnChange){
this._lastValueReported = newValue;
this._pendingOnChange = false;
if(this._onChangeActive){
if(this._onChangeHandle){
this._onChangeHandle.remove();
}
// defer allows hidden value processing to run and
// also the onChange handler can safely adjust focus, etc
this._onChangeHandle = this.defer(
function(){
this._onChangeHandle = null;
this.onChange(newValue);
}); // 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
this._onChangeHandle.remove();
this.onChange(this._lastValueReported);
}
this.inherited(arguments);
}
});
});
},
'dijit/a11yclick':function(){
define("dijit/a11yclick", [
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"dojo/_base/unload", // unload.addOnWindowUnload
"dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
], function(on, array, keys, declare, has, unload, win){
// module:
// dijit/a11yclick
// 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
var lastKeyDownNode = null;
if(has("dom-addeventlistener")){
win.doc.addEventListener('keydown', function(evt){
lastKeyDownNode = evt.target;
}, true);
}else{
// Fallback path for IE6-8
(function(){
var keydownCallback = function(evt){
lastKeyDownNode = evt.srcElement;
};
win.doc.attachEvent('onkeydown', keydownCallback);
unload.addOnWindowUnload(function(){
win.doc.detachEvent('onkeydown', keydownCallback);
});
})();
}
function clickKey(/*Event*/ e){
return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
!e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
}
return function(node, listener){
// summary:
// Custom a11yclick (a.k.a. ondijitclick) event
// which triggers on a mouse click, touch, or space/enter keyup.
if(/input|button/i.test(node.nodeName)){
// pass through, the browser already generates click event on SPACE/ENTER key
return on(node, "click", listener);
}else{
// Don't fire the click event unless both the keydown and keyup occur on this node.
// Avoids problems where focus shifted to this node or away from the node on keydown,
// either causing this node to process a stray keyup event, or causing another node
// to get a stray keyup event.
var handles = [
on(node, "keydown", function(e){
//console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e)){
// needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
lastKeyDownNode = e.target;
// Prevent viewport scrolling on space key in IE<9.
// (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
e.preventDefault();
}
}),
on(node, "keyup", function(e){
//console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
//need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
lastKeyDownNode = null;
on.emit(e.target, "click", {
cancelable: true,
bubbles: true
});
}
}),
on(node, "click", function(e){
// catch mouse clicks, plus the on.emit() calls from above and below
listener.call(this, e);
})
];
if(has("touch")){
// touchstart-->touchend will automatically generate a click event, but there are problems
// on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
// if click doesn't fire naturally.
var clickTimer;
handles.push(
on(node, "touchend", function(e){
var target = e.target;
clickTimer = setTimeout(function(){
clickTimer = null;
on.emit(target, "click", {
cancelable: true,
bubbles: true
});
}, 600);
}),
on(node, "click", function(e){
// If browser generates a click naturally, clear the timer to fire a synthetic click event
if(clickTimer){
clearTimeout(clickTimer);
}
})
// TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
// event <300ms after the touchend event, then clear the synthetic click timer, because user
// is doing a zoom. Alternately monitor screen.deviceXDPI (or something similar) to see if
// zoom level has changed.
);
}
return {
remove: function(){
array.forEach(handles, function(h){ h.remove(); });
if(clickTimer){
clearTimeout(clickTimer);
clickTimer = null;
}
}
};
}
};
return ret;
});
},
'url:dijit/templates/ProgressBar.html':"<div class=\"dijitProgressBar dijitProgressBarEmpty\" role=\"progressbar\"\n\t><div data-dojo-attach-point=\"internalProgress\" class=\"dijitProgressBarFull\"\n\t\t><div class=\"dijitProgressBarTile\" role=\"presentation\"></div\n\t\t><span style=\"visibility:hidden\">&#160;</span\n\t></div\n\t><div data-dojo-attach-point=\"labelNode\" class=\"dijitProgressBarLabel\" id=\"${id}_label\"></div\n\t><img data-dojo-attach-point=\"indeterminateHighContrastImage\" class=\"dijitProgressBarIndeterminateHighContrastImage\" alt=\"\"\n/></div>\n",
'dijit/Destroyable':function(){
define("dijit/Destroyable", [
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/declare"
], function(array, aspect, declare){
// module:
// dijit/Destroyable
return declare("dijit.Destroyable", null, {
// summary:
// Mixin to track handles and release them when instance is destroyed.
// description:
// Call this.own(...) on list of handles (returned from dojo/aspect, dojo/on,
// dojo/Stateful::watch, or any class (including widgets) with a destroyRecursive() or destroy() method.
// Then call destroy() later to destroy this instance and release the resources.
destroy: function(/*Boolean*/ preserveDom){
// summary:
// Destroy this class, releasing any resources registered via own().
this._destroyed = true;
},
own: function(){
// summary:
// Track specified handles and remove/destroy them when this instance is destroyed, unless they were
// already removed/destroyed manually.
// tags:
// protected
// returns:
// The array of specified handles, so you can do for example:
// | var handle = this.own(on(...))[0];
array.forEach(arguments, function(handle){
var destroyMethodName =
"destroyRecursive" in handle ? "destroyRecursive" : // remove "destroyRecursive" for 2.0
"destroy" in handle ? "destroy" :
"remove";
// When this.destroy() is called, destroy handle. Since I'm using aspect.before(),
// the handle will be destroyed before a subclass's destroy() method starts running, before it calls
// this.inherited() or even if it doesn't call this.inherited() at all. If that's an issue, make an
// onDestroy() method and connect to that instead.
var odh = aspect.before(this, "destroy", function(preserveDom){
handle[destroyMethodName](preserveDom);
});
// If handle is destroyed manually before this.destroy() is called, remove the listener set directly above.
var hdh = aspect.after(handle, destroyMethodName, function(){
odh.remove();
hdh.remove();
}, true);
}, this);
return arguments; // handle
}
});
});
},
'dijit/layout/_ContentPaneResizeMixin':function(){
define("dijit/layout/_ContentPaneResizeMixin", [
"dojo/_base/array", // array.filter array.forEach
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.contains domClass.toggle
"dojo/dom-geometry",// domGeometry.contentBox domGeometry.marginBox
"dojo/dom-style",
"dojo/_base/lang", // lang.mixin
"dojo/query", // query
"dojo/sniff", // has("ie")
"../registry", // registry.byId
"../Viewport",
"./utils" // marginBox2contextBox
], function(array, declare, domClass, domGeometry, domStyle, lang, query, has,
registry, Viewport, layoutUtils){
// module:
// dijit/layout/_ContentPaneResizeMixin
return declare("dijit.layout._ContentPaneResizeMixin", null, {
// summary:
// Resize() functionality of ContentPane. If there's a single layout widget
// child then it will call resize() with the same dimensions as the ContentPane.
// Otherwise just calls resize on each child.
//
// Also implements basic startup() functionality, where starting the parent
// will start the children
// doLayout: Boolean
// - false - don't adjust size of children
// - true - if there is a single visible child widget, set it's size to however big the ContentPane is
doLayout: true,
// isLayoutContainer: [protected] Boolean
// Indicates that this widget will call resize() on it's child widgets
// when they become visible.
isLayoutContainer: true,
startup: function(){
// summary:
// See `dijit/layout/_LayoutWidget.startup()` for description.
// Although ContentPane doesn't extend _LayoutWidget, it does implement
// the same API.
if(this._started){ return; }
var parent = this.getParent();
this._childOfLayoutWidget = parent && parent.isLayoutContainer;
// I need to call resize() on my child/children (when I become visible), unless
// I'm the child of a layout widget in which case my parent will call resize() on me and I'll do it then.
this._needLayout = !this._childOfLayoutWidget;
this.inherited(arguments);
if(this._isShown()){
this._onShow();
}
if(!this._childOfLayoutWidget){
// 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 viewport size changes so that I can re-layout.
// This is more for subclasses of ContentPane than ContentPane itself, although it
// could be useful for a ContentPane if it has a single child widget inheriting ContentPane's size.
this.own(Viewport.on("resize", lang.hitch(this, "resize")));
}
},
_checkIfSingleChild: function(){
// summary:
// Test if we have exactly one visible widget as a child,
// and if so assume that we are a container for that widget,
// and should propagate startup() and resize() calls to it.
// Skips over things like data stores since they aren't visible.
var candidateWidgets = [],
otherVisibleNodes = false;
query("> *", this.containerNode).some(function(node){
var widget = registry.byNode(node);
if(widget && widget.resize){
candidateWidgets.push(widget);
}else if(node.offsetHeight){
otherVisibleNodes = true;
}
});
this._singleChild = candidateWidgets.length == 1 && !otherVisibleNodes ?
candidateWidgets[0] : null;
// So we can set overflow: hidden to avoid a safari bug w/scrollbars showing up (#9449)
domClass.toggle(this.containerNode, this.baseClass + "SingleChild", !!this._singleChild);
},
resize: function(changeSize, resultSize){
// summary:
// See `dijit/layout/_LayoutWidget.resize()` for description.
// Although ContentPane doesn't extend _LayoutWidget, it does implement
// the same API.
this._resizeCalled = true;
this._scheduleLayout(changeSize, resultSize);
},
_scheduleLayout: function(changeSize, resultSize){
// summary:
// Resize myself, and call resize() on each of my child layout widgets, either now
// (if I'm currently visible) or when I become visible
if(this._isShown()){
this._layout(changeSize, resultSize);
}else{
this._needLayout = true;
this._changeSize = changeSize;
this._resultSize = resultSize;
}
},
_layout: function(changeSize, resultSize){
// summary:
// Resize myself according to optional changeSize/resultSize parameters, like a layout widget.
// Also, since I am an isLayoutContainer widget, each of my children expects me to
// call resize() or layout() on it.
//
// Should be called on initialization and also whenever we get new content
// (from an href, or from set('content', ...))... but deferred until
// the ContentPane is visible
delete this._needLayout;
// For the TabContainer --> BorderContainer --> ContentPane case, _onShow() is
// never called directly, so resize() is our trigger to do the initial href download (see [20099]).
// However, don't load href for closed TitlePanes.
if(!this._wasShown && this.open !== false){
this._onShow();
}
// Set margin box size, unless it wasn't specified, in which case use current size.
if(changeSize){
domGeometry.setMarginBox(this.domNode, changeSize);
}
// Compute content box size of containerNode in case we [later] need to size our single child.
var cn = this.containerNode;
if(cn === this.domNode){
// If changeSize or resultSize was passed to this method and this.containerNode ==
// this.domNode then we can compute the content-box size without querying the node,
// which is more reliable (similar to LayoutWidget.resize) (see for example #9449).
var mb = resultSize || {};
lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
if(!("h" in mb) || !("w" in mb)){
mb = lang.mixin(domGeometry.getMarginBox(cn), mb); // just use domGeometry.setMarginBox() to fill in missing values
}
this._contentBox = layoutUtils.marginBox2contentBox(cn, mb);
}else{
this._contentBox = domGeometry.getContentBox(cn);
}
this._layoutChildren();
},
_layoutChildren: function(){
// Call _checkIfSingleChild() again in case app has manually mucked w/the content
// of the ContentPane (rather than changing it through the set("content", ...) API.
if(this.doLayout){
this._checkIfSingleChild();
}
if(this._singleChild && this._singleChild.resize){
var cb = this._contentBox || domGeometry.getContentBox(this.containerNode);
// note: if widget has padding this._contentBox will have l and t set,
// but don't pass them to resize() or it will doubly-offset the child
this._singleChild.resize({w: cb.w, h: cb.h});
}else{
// All my child widgets are independently sized (rather than matching my size),
// but I still need to call resize() on each child to make it layout.
array.forEach(this.getChildren(), function(widget){
if(widget.resize){
widget.resize();
}
});
}
},
_isShown: function(){
// summary:
// Returns true if the content is currently shown.
// description:
// If I am a child of a layout widget then it actually returns true if I've ever been visible,
// not whether I'm currently visible, since that's much faster than tracing up the DOM/widget
// tree every call, and at least solves the performance problem on page load by deferring loading
// hidden ContentPanes until they are first shown
if(this._childOfLayoutWidget){
// If we are TitlePane, etc - we return that only *IF* we've been resized
if(this._resizeCalled && "open" in this){
return this.open;
}
return this._resizeCalled;
}else if("open" in this){
return this.open; // for TitlePane, etc.
}else{
var node = this.domNode, parent = this.domNode.parentNode;
return (node.style.display != 'none') && (node.style.visibility != 'hidden') && !domClass.contains(node, "dijitHidden") &&
parent && parent.style && (parent.style.display != 'none');
}
},
_onShow: function(){
// summary:
// Called when the ContentPane is made visible
// description:
// For a plain ContentPane, this is called on initialization, from startup().
// If the ContentPane is a hidden pane of a TabContainer etc., then it's
// called whenever the pane is made visible.
//
// Does layout/resize of child widget(s)
// Need to keep track of whether ContentPane has been shown (which is different than
// whether or not it's currently visible).
this._wasShown = true;
if(this._needLayout){
// If a layout has been scheduled for when we become visible, do it now
this._layout(this._changeSize, this._resultSize);
}
this.inherited(arguments);
}
});
});
},
'dijit/WidgetSet':function(){
define("dijit/WidgetSet", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.global
"./registry" // to add functions to dijit.registry
], function(array, declare, kernel, registry){
// module:
// dijit/WidgetSet
var WidgetSet = declare("dijit.WidgetSet", null, {
// summary:
// A set of widgets indexed by id.
// Deprecated, will be removed in 2.0.
//
// example:
// Create a small list of widgets:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("one"));
// | ws.add(registry.byId("two"));
// | // destroy both:
// | ws.forEach(function(w){ w.destroy(); });
// | });
constructor: function(){
this._hash = {};
this.length = 0;
},
add: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Add a widget to this list. If a duplicate ID is detected, a error is thrown.
//
// widget: dijit/_WidgetBase
// Any dijit/_WidgetBase 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 `array.forEach`.
//
// thisObj:
// An optional scope parameter
//
// example:
// Using the default `dijit.registry` instance:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.forEach(function(widget){
// | console.log(widget.declaredClass);
// | });
// | });
//
// returns:
// Returns self, in order to allow for further chaining.
thisObj = thisObj || kernel.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 `array.filter` and `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
// |
// |
// |
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.filter(function(w, i){
// | return i % 2 == 0;
// | }).forEach(function(w){ /* odd ones */ });
// | });
thisObj = thisObj || kernel.global;
var res = new 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
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var ws = new WidgetSet();
// | ws.add(registry.byId("bar"));
// | var t = ws.byId("bar") // returns a widget
// | var x = ws.byId("foo"); // returns undefined
// | });
return this._hash[id]; // dijit/_WidgetBase
},
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:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | registry.byClass("dijit.TitlePane").forEach(function(tp){ tp.close(); });
// | });
var res = new 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
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | array.map(registry.toArray(), function(w){ return w.domNode; });
// | });
var ar = [];
for(var id in this._hash){
ar.push(this._hash[id]);
}
return ar; // dijit/_WidgetBase[]
},
map: function(/* Function */func, /* Object? */thisObj){
// summary:
// Create a new Array from this WidgetSet, following the same rules as `array.map`
// example:
// | require(["dijit/WidgetSet", "dijit/registry"],
// | function(WidgetSet, registry){
// | var nodes = registry.map(function(w){ return w.domNode; });
// | });
//
// returns:
// A new array of the returned values.
return array.map(this.toArray(), func, thisObj); // Array
},
every: function(func, thisObj){
// summary:
// A synthetic clone of `array.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 || kernel.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 `array.some` acting explicitly 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 || kernel.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
}
});
// Add in 1.x compatibility methods to dijit/registry.
// These functions won't show up in the API doc but since they are deprecated anyway,
// that's probably for the best.
array.forEach(["forEach", "filter", "byClass", "map", "every", "some"], function(func){
registry[func] = WidgetSet.prototype[func];
});
return WidgetSet;
});
},
'dojo/dnd/Moveable':function(){
define("dojo/dnd/Moveable", [
"../_base/array", "../_base/declare", "../_base/event", "../_base/lang",
"../dom", "../dom-class", "../Evented", "../on", "../topic", "../touch", "./common", "./Mover", "../_base/window"
], function(array, declare, event, lang, dom, domClass, Evented, on, topic, touch, dnd, Mover, win){
// module:
// dojo/dnd/Moveable
var Moveable = declare("dojo.dnd.Moveable", [Evented], {
// summary:
// an object, which makes a node movable
// object attributes (for markup)
handle: "",
delay: 0,
skip: false,
constructor: function(node, params){
// node: Node
// a node (or node's id) to be moved
// params: Moveable.__MoveableArgs?
// optional parameters
this.node = dom.byId(node);
if(!params){ params = {}; }
this.handle = params.handle ? dom.byId(params.handle) : null;
if(!this.handle){ this.handle = this.node; }
this.delay = params.delay > 0 ? params.delay : 0;
this.skip = params.skip;
this.mover = params.mover ? params.mover : Mover;
this.events = [
on(this.handle, touch.press, lang.hitch(this, "onMouseDown")),
// cancel text selection and text dragging
on(this.handle, "dragstart", lang.hitch(this, "onSelectStart")),
on(this.handle, "selectstart", lang.hitch(this, "onSelectStart"))
];
},
// markup methods
markupFactory: function(params, node, Ctor){
return new Ctor(node, params);
},
// methods
destroy: function(){
// summary:
// stops watching for possible move, deletes all references, so the object can be garbage-collected
array.forEach(this.events, function(handle){ handle.remove(); });
this.events = this.node = this.handle = null;
},
// mouse event processors
onMouseDown: function(e){
// summary:
// event processor for onmousedown/ontouchstart, creates a Mover for the node
// e: Event
// mouse/touch event
if(this.skip && dnd.isFormElement(e)){ return; }
if(this.delay){
this.events.push(
on(this.handle, touch.move, lang.hitch(this, "onMouseMove")),
on(this.handle, touch.release, lang.hitch(this, "onMouseUp"))
);
this._lastX = e.pageX;
this._lastY = e.pageY;
}else{
this.onDragDetected(e);
}
event.stop(e);
},
onMouseMove: function(e){
// summary:
// event processor for onmousemove/ontouchmove, used only for delayed drags
// e: Event
// mouse/touch event
if(Math.abs(e.pageX - this._lastX) > this.delay || Math.abs(e.pageY - this._lastY) > this.delay){
this.onMouseUp(e);
this.onDragDetected(e);
}
event.stop(e);
},
onMouseUp: function(e){
// summary:
// event processor for onmouseup, used only for delayed drags
// e: Event
// mouse event
for(var i = 0; i < 2; ++i){
this.events.pop().remove();
}
event.stop(e);
},
onSelectStart: function(e){
// summary:
// event processor for onselectevent and ondragevent
// e: Event
// mouse event
if(!this.skip || !dnd.isFormElement(e)){
event.stop(e);
}
},
// local events
onDragDetected: function(/*Event*/ e){
// summary:
// called when the drag is detected;
// responsible for creation of the mover
new this.mover(this.node, e, this);
},
onMoveStart: function(/*Mover*/ mover){
// summary:
// called before every move operation
topic.publish("/dnd/move/start", mover);
domClass.add(win.body(), "dojoMove");
domClass.add(this.node, "dojoMoveItem");
},
onMoveStop: function(/*Mover*/ mover){
// summary:
// called after every move operation
topic.publish("/dnd/move/stop", mover);
domClass.remove(win.body(), "dojoMove");
domClass.remove(this.node, "dojoMoveItem");
},
onFirstMove: function(/*===== mover, e =====*/){
// summary:
// called during the very first move notification;
// can be used to initialize coordinates, can be overwritten.
// mover: Mover
// e: Event
// default implementation does nothing
},
onMove: function(mover, leftTop /*=====, e =====*/){
// summary:
// called during every move notification;
// should actually move the node; can be overwritten.
// mover: Mover
// leftTop: Object
// e: Event
this.onMoving(mover, leftTop);
var s = mover.node.style;
s.left = leftTop.l + "px";
s.top = leftTop.t + "px";
this.onMoved(mover, leftTop);
},
onMoving: function(/*===== mover, leftTop =====*/){
// summary:
// called before every incremental move; can be overwritten.
// mover: Mover
// leftTop: Object
// default implementation does nothing
},
onMoved: function(/*===== mover, leftTop =====*/){
// summary:
// called after every incremental move; can be overwritten.
// mover: Mover
// leftTop: Object
// default implementation does nothing
}
});
/*=====
Moveable.__MoveableArgs = declare([], {
// handle: Node||String
// A node (or node's id), which is used as a mouse handle.
// If omitted, the node itself is used as a handle.
handle: null,
// delay: Number
// delay move by this number of pixels
delay: 0,
// skip: Boolean
// skip move of form elements
skip: false,
// mover: Object
// a constructor of custom Mover
mover: dnd.Mover
});
=====*/
return Moveable;
});
},
'dojo/store/util/SimpleQueryEngine':function(){
define("dojo/store/util/SimpleQueryEngine", ["../../_base/array" /*=====, "../api/Store" =====*/], function(arrayUtil /*=====, Store =====*/){
// module:
// dojo/store/util/SimpleQueryEngine
return function(query, options){
// summary:
// Simple query engine that matches using filter functions, named filter
// functions or objects by name-value on a query object hash
//
// description:
// The SimpleQueryEngine provides a way of getting a QueryResults through
// the use of a simple object hash as a filter. The hash will be used to
// match properties on data objects with the corresponding value given. In
// other words, only exact matches will be returned.
//
// This function can be used as a template for more complex query engines;
// for example, an engine can be created that accepts an object hash that
// contains filtering functions, or a string that gets evaluated, etc.
//
// When creating a new dojo.store, simply set the store's queryEngine
// field as a reference to this function.
//
// query: Object
// An object hash with fields that may match fields of items in the store.
// Values in the hash will be compared by normal == operator, but regular expressions
// or any object that provides a test() method are also supported and can be
// used to match strings by more complex expressions
// (and then the regex's or object's test() method will be used to match values).
//
// options: dojo/store/api/Store.QueryOptions?
// An object that contains optional information such as sort, start, and count.
//
// returns: Function
// A function that caches the passed query under the field "matches". See any
// of the "query" methods on dojo.stores.
//
// example:
// Define a store with a reference to this engine, and set up a query method.
//
// | var myStore = function(options){
// | // ...more properties here
// | this.queryEngine = SimpleQueryEngine;
// | // define our query method
// | this.query = function(query, options){
// | return QueryResults(this.queryEngine(query, options)(this.data));
// | };
// | };
// create our matching query function
switch(typeof query){
default:
throw new Error("Can not query with a " + typeof query);
case "object": case "undefined":
var queryObject = query;
query = function(object){
for(var key in queryObject){
var required = queryObject[key];
if(required && required.test){
// an object can provide a test method, which makes it work with regex
if(!required.test(object[key], object)){
return false;
}
}else if(required != object[key]){
return false;
}
}
return true;
};
break;
case "string":
// named query
if(!this[query]){
throw new Error("No filter function " + query + " was found in store");
}
query = this[query];
// fall through
case "function":
// fall through
}
function execute(array){
// execute the whole query, first we filter
var results = arrayUtil.filter(array, query);
// next we sort
var sortSet = options && options.sort;
if(sortSet){
results.sort(typeof sortSet == "function" ? sortSet : function(a, b){
for(var sort, i=0; sort = sortSet[i]; i++){
var aValue = a[sort.attribute];
var bValue = b[sort.attribute];
if (aValue != bValue){
return !!sort.descending == (aValue == null || aValue > bValue) ? -1 : 1;
}
}
return 0;
});
}
// now we paginate
if(options && (options.start || options.count)){
var total = results.length;
results = results.slice(options.start || 0, (options.start || 0) + (options.count || Infinity));
results.total = total;
}
return results;
}
execute.matches = query;
return execute;
};
});
},
'dijit/typematic':function(){
define("dijit/typematic", [
"dojo/_base/array", // array.forEach
"dojo/_base/connect", // connect.connect
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.mixin, lang.hitch
"dojo/on",
"dojo/sniff", // has("ie")
"./main" // setting dijit.typematic global
], function(array, connect, event, kernel, lang, on, has, dijit){
// module:
// dijit/typematic
var 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(lang.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:
// 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:
// the number of milliseconds until the 2nd event occurs, default=500ms
// minDelay:
// 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._node = node;
this._currentTimeout = -1;
this._count = -1;
this._callback = lang.hitch(_this, callback);
this._evt = { faux: true };
for(var attr in evt){
if(attr != "layerX" && attr != "layerY"){ // prevent WebKit warnings
var v = evt[attr];
if(typeof v != "function" && typeof v != "undefined"){ this._evt[attr] = v }
}
}
this._fireEventAndReload();
}
},
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:
// a connection handle
if(keyObject.keyCode){
keyObject.charOrCode = keyObject.keyCode;
kernel.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);
kernel.deprecated("charCode attribute parameter for dijit.typematic.addKeyListener is deprecated. Use charOrCode instead.", "", "2.0");
}
var handles = [
on(node, connect._keypress, lang.hitch(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)){
event.stop(evt);
typematic.trigger(evt, _this, node, callback, keyObject, subsequentDelay, initialDelay, minDelay);
}else if(typematic._obj == keyObject){
typematic.stop();
}
})),
on(node, "keyup", lang.hitch(this, function(){
if(typematic._obj == keyObject){
typematic.stop();
}
}))
];
return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
},
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:
// a connection handle
var handles = [
on(node, "mousedown", lang.hitch(this, function(evt){
evt.preventDefault();
typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
})),
on(node, "mouseup", lang.hitch(this, function(evt){
if(this._obj){
evt.preventDefault();
}
typematic.stop();
})),
on(node, "mouseout", lang.hitch(this, function(evt){
if(this._obj){
evt.preventDefault();
}
typematic.stop();
})),
on(node, "dblclick", lang.hitch(this, function(evt){
evt.preventDefault();
if(has("ie") < 9){
typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
setTimeout(lang.hitch(this, typematic.stop), 50);
}
}))
];
return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
},
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:
// a connection handle
var handles = [
this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay),
this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)
];
return { remove: function(){ array.forEach(handles, function(h){ h.remove(); }); } };
}
});
return typematic;
});
},
'dijit/MenuItem':function(){
require({cache:{
'url:dijit/templates/MenuItem.html':"<tr class=\"dijitReset dijitMenuItem\" data-dojo-attach-point=\"focusNode\" role=\"menuitem\" tabIndex=\"-1\">\n\t<td class=\"dijitReset dijitMenuItemIconCell\" role=\"presentation\">\n\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitMenuItemIcon\" data-dojo-attach-point=\"iconNode\"/>\n\t</td>\n\t<td class=\"dijitReset dijitMenuItemLabel\" colspan=\"2\" data-dojo-attach-point=\"containerNode\"></td>\n\t<td class=\"dijitReset dijitMenuItemAccelKey\" style=\"display: none\" data-dojo-attach-point=\"accelKeyNode\"></td>\n\t<td class=\"dijitReset dijitMenuArrowCell\" role=\"presentation\">\n\t\t<div data-dojo-attach-point=\"arrowWrapper\" style=\"visibility: hidden\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitMenuExpand\"/>\n\t\t\t<span class=\"dijitMenuExpandA11y\">+</span>\n\t\t</div>\n\t</td>\n</tr>\n"}});
define("dijit/MenuItem", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // kernel.deprecated
"dojo/sniff", // has("ie")
"./_Widget",
"./_TemplatedMixin",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/MenuItem.html"
], function(declare, dom, domAttr, domClass, kernel, has,
_Widget, _TemplatedMixin, _Contained, _CssStateMixin, template){
// module:
// dijit/MenuItem
return declare("dijit.MenuItem",
[_Widget, _TemplatedMixin, _Contained, _CssStateMixin],
{
// summary:
// A line item in a Menu Widget
// Make 3 columns
// icon, label, and expand arrow (BiDi-dependent) indicating sub-menu
templateString: template,
baseClass: "dijitMenuItem",
// label: String
// Menu text
label: "",
_setLabelAttr: function(val){
this.containerNode.innerHTML = val;
this._set("label", val);
if(this.textDir === "auto"){
this.applyTextDir(this.focusNode, this.label);
}
},
// iconClass: String
// Class to apply to DOMNode to make it display an icon.
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
// accelKey: String
// Text for the accelerator (shortcut) key combination.
// Note that although Menu can display accelerator keys there
// is no infrastructure to actually catch and execute these
// accelerators.
accelKey: "",
// disabled: Boolean
// If true, the menu item is disabled.
// If false, the menu item is enabled.
disabled: false,
_fillContent: function(/*DomNode*/ source){
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
if(source && !("label" in this.params)){
this.set('label', source.innerHTML);
}
},
buildRendering: function(){
this.inherited(arguments);
var label = this.id+"_text";
domAttr.set(this.containerNode, "id", label);
if(this.accelKeyNode){
domAttr.set(this.accelKeyNode, "id", this.id + "_accel");
label += " " + this.id + "_accel";
}
this.domNode.setAttribute("aria-labelledby", label);
dom.setSelectable(this.domNode, false);
},
onClick: function(/*Event*/){
// summary:
// User defined function to handle clicks
// tags:
// callback
},
focus: function(){
// summary:
// Focus on this MenuItem
try{
if(has("ie") == 8){
// needed for IE8 which won't scroll TR tags into view on focus yet calling scrollIntoView creates flicker (#10275)
this.containerNode.focus();
}
this.focusNode.focus();
}catch(e){
// this throws on IE (at least) in some scenarios
}
},
_onFocus: function(){
// summary:
// This is called by the focus manager when focus
// goes to this MenuItem or a child menu.
// tags:
// protected
this._setSelected(true);
this.getParent()._onItemFocus(this);
this.inherited(arguments);
},
_setSelected: function(selected){
// summary:
// Indicate that this node is the currently selected one
// tags:
// private
/***
* TODO: remove this method and calls to it, when _onBlur() is working for MenuItem.
* Currently _onBlur() gets called when focus is moved from the MenuItem to a child menu.
* That's not supposed to happen, but the problem is:
* In order to allow dijit.popup's getTopPopup() to work,a sub menu's popupParent
* points to the parent Menu, bypassing the parent MenuItem... thus the
* MenuItem is not in the chain of active widgets and gets a premature call to
* _onBlur()
*/
domClass.toggle(this.domNode, "dijitMenuItemSelected", selected);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
// tags:
// deprecated
kernel.deprecated("dijit.MenuItem.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', bool) instead.
// tags:
// deprecated
kernel.deprecated("dijit.Menu.setDisabled() is deprecated. Use set('disabled', bool) instead.", "", "2.0");
this.set('disabled', disabled);
},
_setDisabledAttr: function(/*Boolean*/ value){
// summary:
// Hook for attr('disabled', ...) to work.
// Enable or disable this menu item.
this.focusNode.setAttribute('aria-disabled', value ? 'true' : 'false');
this._set("disabled", value);
},
_setAccelKeyAttr: function(/*String*/ value){
// summary:
// Hook for attr('accelKey', ...) to work.
// Set accelKey on this menu item.
this.accelKeyNode.style.display=value?"":"none";
this.accelKeyNode.innerHTML=value;
//have to use colSpan to make it work in IE
domAttr.set(this.containerNode,'colSpan',value?"1":"2");
this._set("accelKey", value);
},
_setTextDirAttr: function(/*String*/ textDir){
// summary:
// Setter for textDir.
// description:
// Users shouldn't call this function; they should be calling
// set('textDir', value)
// tags:
// private
// only if new textDir is different from the old one
// and on widgets creation.
if(!this._created || this.textDir != textDir){
this._set("textDir", textDir);
this.applyTextDir(this.focusNode, this.label);
}
}
});
});
},
'dijit/layout/TabController':function(){
require({cache:{
'url:dijit/layout/templates/_TabButton.html':"<div role=\"presentation\" data-dojo-attach-point=\"titleNode,innerDiv,tabContent\" class=\"dijitTabInner dijitTabContent\">\n\t<img src=\"${_blankGif}\" alt=\"\" class=\"dijitIcon dijitTabButtonIcon\" data-dojo-attach-point='iconNode'/>\n\t<span data-dojo-attach-point='containerNode,focusNode' class='tabLabel'></span>\n\t<span class=\"dijitInline dijitTabCloseButton dijitTabCloseIcon\" data-dojo-attach-point='closeNode'\n\t\t role=\"presentation\">\n\t\t<span data-dojo-attach-point='closeText' class='dijitTabCloseText'>[x]</span\n\t\t\t\t></span>\n</div>\n"}});
define("dijit/layout/TabController", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-attr", // domAttr.attr
"dojo/dom-class", // domClass.toggle
"dojo/i18n", // i18n.getLocalization
"dojo/_base/lang", // lang.hitch lang.trim
"./StackController",
"../registry",
"../Menu",
"../MenuItem",
"dojo/text!./templates/_TabButton.html",
"dojo/i18n!../nls/common"
], function(declare, dom, domAttr, domClass, i18n, lang, StackController, registry, Menu, MenuItem, template){
// module:
// dijit/layout/TabController
var TabButton = declare("dijit.layout._TabButton", StackController.StackButton, {
// summary:
// A tab (the thing you click to select a pane).
// description:
// Contains the title of the pane, and optionally a close-button to destroy the pane.
// This is an internal widget and should not be instantiated directly.
// tags:
// private
// baseClass: String
// The CSS class applied to the domNode.
baseClass: "dijitTab",
// Apply dijitTabCloseButtonHover when close button is hovered
cssStateNodes: {
closeNode: "dijitTabCloseButton"
},
templateString: template,
// Override _FormWidget.scrollOnFocus.
// Don't scroll the whole tab container into view when the button is focused.
scrollOnFocus: false,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.containerNode, false);
},
startup: function(){
this.inherited(arguments);
var n = this.domNode;
// Required to give IE6 a kick, as it initially hides the
// tabs until they are focused on.
this.defer(function(){
n.className = n.className;
}, 1);
},
_setCloseButtonAttr: function(/*Boolean*/ disp){
// summary:
// Hide/show close button
this._set("closeButton", disp);
domClass.toggle(this.domNode, "dijitClosable", disp);
this.closeNode.style.display = disp ? "" : "none";
if(disp){
var _nlsResources = i18n.getLocalization("dijit", "common");
if(this.closeNode){
domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
}
}
},
_setDisabledAttr: function(/*Boolean*/ disabled){
// summary:
// Make tab selected/unselectable
this.inherited(arguments);
// Don't show tooltip for close button when tab is disabled
if(this.closeNode){
if(disabled){
domAttr.remove(this.closeNode, "title");
}else{
var _nlsResources = i18n.getLocalization("dijit", "common");
domAttr.set(this.closeNode, "title", _nlsResources.itemClose);
}
}
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// takes an HTML string.
// Inherited ToggleButton implementation will Set the label (text) of the button;
// Need to set the alt attribute of icon on tab buttons if no label displayed
this.inherited(arguments);
if(!this.showLabel && !this.params.title){
this.iconNode.alt = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
}
});
var TabController = declare("dijit.layout.TabController", StackController, {
// summary:
// Set of tabs (the things with titles and a close button, that you click to show a tab panel).
// Used internally by `dijit/layout/TabContainer`.
// description:
// Lets the user select the currently shown pane in a TabContainer or StackContainer.
// TabController also monitors the TabContainer, and whenever a pane is
// added or deleted updates itself accordingly.
// tags:
// private
baseClass: "dijitTabController",
templateString: "<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'></div>",
// tabPosition: String
// Defines where tabs go relative to the content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
// buttonWidget: Constructor
// The tab widget to create to correspond to each page
buttonWidget: TabButton,
// buttonWidgetCloseClass: String
// Class of [x] close icon, used by event delegation code to tell when close button was clicked
buttonWidgetCloseClass: "dijitTabCloseButton",
postCreate: function(){
this.inherited(arguments);
// Setup a close menu to be shared between all the closable tabs (excluding disabled tabs)
var closeMenu = new Menu({
id: this.id+"_Menu",
ownerDocument: this.ownerDocument,
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
targetNodeIds: [this.domNode],
selector: function(node){
return domClass.contains(node, "dijitClosable") && !domClass.contains(node, "dijitTabDisabled");
}
});
this.own(closeMenu);
var _nlsResources = i18n.getLocalization("dijit", "common"),
controller = this;
closeMenu.addChild(new MenuItem({
label: _nlsResources.itemClose,
ownerDocument: this.ownerDocument,
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
onClick: function(evt){
var button = registry.byNode(this.getParent().currentTarget);
controller.onCloseButtonClick(button.page);
}
}));
}
});
TabController.TabButton = TabButton; // for monkey patching
return TabController;
});
},
'dijit/layout/_LayoutWidget':function(){
define("dijit/layout/_LayoutWidget", [
"dojo/_base/lang", // lang.mixin
"../_Widget",
"../_Container",
"../_Contained",
"../Viewport",
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox
"dojo/dom-style" // domStyle.getComputedStyle
], function(lang, _Widget, _Container, _Contained, Viewport,
declare, domClass, domGeometry, domStyle){
// module:
// dijit/layout/_LayoutWidget
return declare("dijit.layout._LayoutWidget", [_Widget, _Container, _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,
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitContainer");
},
startup: function(){
// summary:
// Called after all the widgets have been instantiated and their
// dom nodes have been inserted somewhere under win.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 viewport size changes so that I can re-layout.
this.own(Viewport.on("resize", lang.hitch(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 re-layout 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 re-layout 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){
domGeometry.setMarginBox(node, changeSize);
}
// 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 || {};
lang.mixin(mb, changeSize || {}); // changeSize overrides resultSize
if( !("h" in mb) || !("w" in mb) ){
mb = lang.mixin(domGeometry.getMarginBox(node), mb); // just use domGeometry.marginBox() to fill in missing values
}
// Compute and save the size of my border box and content box
// (w/out calling domGeometry.getContentBox() since that may fail if size was recently set)
var cs = domStyle.getComputedStyle(node);
var me = domGeometry.getMarginExtents(node, cs);
var be = domGeometry.getBorderExtents(node, cs);
var bb = (this._borderBox = {
w: mb.w - (me.w + be.w),
h: mb.h - (me.h + be.h)
});
var pe = domGeometry.getPadExtents(node, cs);
this._contentBox = {
l: domStyle.toPixelValue(node, cs.paddingLeft),
t: domStyle.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/_WidgetBase*/child){
// summary:
// Common setup for initial children and children which are added after startup
// tags:
// protected extension
var cls = this.baseClass + "-child "
+ (child.baseClass ? this.baseClass + "-" + child.baseClass : "");
domClass.add(child.domNode, cls);
},
addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
// Overrides _Container.addChild() to call _setupChild()
this.inherited(arguments);
if(this._started){
this._setupChild(child);
}
},
removeChild: function(/*dijit/_WidgetBase*/ child){
// Overrides _Container.removeChild() to remove class added by _setupChild()
var cls = this.baseClass + "-child"
+ (child.baseClass ?
" " + this.baseClass + "-" + child.baseClass : "");
domClass.remove(child.domNode, cls);
this.inherited(arguments);
}
});
});
},
'dijit/popup':function(){
define("dijit/popup", [
"dojo/_base/array", // array.forEach array.some
"dojo/aspect",
"dojo/_base/connect", // connect._keypress
"dojo/_base/declare", // declare
"dojo/dom", // dom.isDescendant
"dojo/dom-attr", // domAttr.set
"dojo/dom-construct", // domConstruct.create domConstruct.destroy
"dojo/dom-geometry", // domGeometry.isBodyLtr
"dojo/dom-style", // domStyle.set
"dojo/_base/event", // event.stop
"dojo/keys",
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie") has("mozilla")
"./place",
"./BackgroundIframe",
"./main" // dijit (defining dijit.popup to match API doc)
], function(array, aspect, connect, declare, dom, domAttr, domConstruct, domGeometry, domStyle, event, keys, lang, on, has,
place, BackgroundIframe, dijit){
// module:
// dijit/popup
/*=====
var __OpenArgs = {
// 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 a list of positions to try, ex:
// | [ "below", "above" ]
// For backwards compatibility it can also be an (ordered) hash of tuples of the form
// (around-node-corner, popup-node-corner), ex:
// | { "BL": "TL", "TL": "BL" }
// where BL means "bottom left" and "TL" means "top left", etc.
//
// dijit/popup.open() tries to position the popup according to each specified position, in order,
// until the popup appears fully within the viewport.
//
// The default value is ["below", "above"]
//
// 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: place.__Position
// adding a buffer around the opening position. This is only useful when around is not set.
};
=====*/
function destroyWrapper(){
// summary:
// Function to destroy wrapper when popup widget is destroyed.
// Left in this scope to avoid memory leak on IE8 on refresh page, see #15206.
if(this._popupWrapper){
domConstruct.destroy(this._popupWrapper);
delete this._popupWrapper;
}
}
var PopupManager = declare(null, {
// summary:
// Used to show drop downs (ex: the select list of a ComboBox)
// or popups (ex: right-click context menus).
// _stack: dijit/_WidgetBase[]
// 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,
_createWrapper: function(/*Widget*/ widget){
// summary:
// Initialization for widgets that will be used as popups.
// Puts widget inside a wrapper DIV (if not already in one),
// and returns pointer to that wrapper DIV.
var wrapper = widget._popupWrapper,
node = widget.domNode;
if(!wrapper){
// Create wrapper <div> for when this widget [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
wrapper = domConstruct.create("div", {
"class":"dijitPopup",
style:{ display: "none"},
role: "presentation"
}, widget.ownerDocumentBody);
wrapper.appendChild(node);
var s = node.style;
s.display = "";
s.visibility = "";
s.position = "";
s.top = "0px";
widget._popupWrapper = wrapper;
aspect.after(widget, "destroy", destroyWrapper, true);
}
return wrapper;
},
moveOffScreen: function(/*Widget*/ widget){
// summary:
// Moves the popup widget off-screen.
// Do not use this method to hide popups when not in use, because
// that will create an accessibility issue: the offscreen popup is
// still in the tabbing order.
// Create wrapper if not already there
var wrapper = this._createWrapper(widget);
domStyle.set(wrapper, {
visibility: "hidden",
top: "-9999px", // prevent transient scrollbar causing misalign (#5776), and initial flash in upper left (#10111)
display: ""
});
},
hide: function(/*Widget*/ widget){
// summary:
// Hide this popup widget (until it is ready to be shown).
// Initialization for widgets that will be used as popups
//
// Also puts widget inside a wrapper DIV (if not already in one)
//
// If popup widget needs to layout it should
// do so when it is made visible, and popup._onShow() is called.
// Create wrapper if not already there
var wrapper = this._createWrapper(widget);
domStyle.set(wrapper, "display", "none");
},
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(/*__OpenArgs*/ args){
// summary:
// Popup the widget at the specified position
//
// example:
// opening at the mouse position
// | popup.open({popup: menuWidget, x: evt.pageX, y: evt.pageY});
//
// example:
// opening the widget as a dropdown
// | 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 || ["below", "below-alt", "above", "above-alt"],
ltr = args.parent ? args.parent.isLeftToRight() : domGeometry.isBodyLtr(widget.ownerDocument),
around = args.around,
id = (args.around && args.around.id) ? (args.around.id+"_dropdown") : ("popup_"+this._idGen++);
// If we are opening a new popup that isn't a child of a currently opened popup, then
// close currently opened popup(s). This should happen automatically when the old popups
// gets the _onBlur() event, except that the _onBlur() event isn't reliable on IE, see [22198].
while(stack.length && (!args.parent || !dom.isDescendant(args.parent.domNode, stack[stack.length-1].widget.domNode))){
this.close(stack[stack.length-1].widget);
}
// Get pointer to popup wrapper, and create wrapper if it doesn't exist
var wrapper = this._createWrapper(widget);
domAttr.set(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(has("ie") || has("mozilla")){
if(!widget.bgIframe){
// setting widget.bgIframe triggers cleanup in _Widget.destroy()
widget.bgIframe = new BackgroundIframe(wrapper);
}
}
// position the wrapper node and make it visible
var best = around ?
place.around(wrapper, around, orient, ltr, widget.orient ? lang.hitch(widget, "orient") : null) :
place.at(wrapper, args, orient == 'R' ? ['TR','BR','TL','BL'] : ['TL','BL','TR','BR'], args.padding);
wrapper.style.display = "";
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(on(wrapper, connect._keypress, lang.hitch(this, function(evt){
if(evt.charOrCode == keys.ESCAPE && args.onCancel){
event.stop(evt);
args.onCancel();
}else if(evt.charOrCode === keys.TAB){
event.stop(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 && args.onCancel){
handlers.push(widget.on("cancel", args.onCancel));
}
handlers.push(widget.on(widget.onExecute ? "execute" : "change", lang.hitch(this, function(){
var topPopup = this.getTopPopup();
if(topPopup && topPopup.onExecute){
topPopup.onExecute();
}
})));
stack.push({
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(/*Widget?*/ popup){
// summary:
// Close specified popup and any popups that it parented.
// If no popup is specified, closes all popups.
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((popup && array.some(stack, function(elem){return elem.widget == popup;})) ||
(!popup && stack.length)){
var top = stack.pop(),
widget = top.widget,
onClose = top.onClose;
if(widget.onClose){
// TODO: in 2.0 standardize onHide() (used by StackContainer) and onClose() (used here)
widget.onClose();
}
var h;
while(h = top.handlers.pop()){ h.remove(); }
// Hide the widget and it's wrapper unless it has already been destroyed in above onClose() etc.
if(widget && widget.domNode){
this.hide(widget);
}
if(onClose){
onClose();
}
}
}
});
return (dijit.popup = new PopupManager());
});
},
'dijit/_base/manager':function(){
define("dijit/_base/manager", [
"dojo/_base/array",
"dojo/_base/config", // defaultDuration
"dojo/_base/lang",
"../registry",
"../main" // for setting exports to dijit namespace
], function(array, config, lang, registry, dijit){
// module:
// dijit/_base/manager
var exports = {
// summary:
// Deprecated. Shim to methods on registry, plus a few other declarations.
// New code should access dijit/registry directly when possible.
};
array.forEach(["byId", "getUniqueId", "findWidgets", "_destroyAll", "byNode", "getEnclosingWidget"], function(name){
exports[name] = registry[name];
});
lang.mixin(exports, {
// defaultDuration: Integer
// The default fx.animation speed (in ms) to use for all Dijit
// transitional fx.animations, unless otherwise specified
// on a per-instance basis. Defaults to 200, overrided by
// `djConfig.defaultDuration`
defaultDuration: config["defaultDuration"] || 200
});
lang.mixin(dijit, exports);
/*===== return exports; =====*/
return dijit; // for back compat :-(
});
},
'dijit/layout/StackController':function(){
define("dijit/layout/StackController", [
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/dom-class",
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject
"dojo/on",
"../focus", // focus.focus()
"../registry", // registry.byId
"../_Widget",
"../_TemplatedMixin",
"../_Container",
"../form/ToggleButton",
"dojo/i18n!../nls/common"
], function(array, declare, domClass, event, keys, lang, on,
focus, registry, _Widget, _TemplatedMixin, _Container, ToggleButton){
// module:
// dijit/layout/StackController
var StackButton = declare("dijit.layout._StackButton", ToggleButton, {
// summary:
// Internal widget used by StackContainer.
// description:
// The button-like or tab-like object you click to select or delete a page
// tags:
// private
// Override _FormWidget.tabIndex.
// StackContainer buttons are not in the tab order by default.
// Probably we should be calling this.startupKeyNavChildren() instead.
tabIndex: "-1",
// closeButton: Boolean
// When true, display close button for this tab
closeButton: false,
_aria_attr: "aria-selected",
buildRendering: function(/*Event*/ evt){
this.inherited(arguments);
(this.focusNode || this.domNode).setAttribute("role", "tab");
}
});
var StackController = declare("dijit.layout.StackController", [_Widget, _TemplatedMixin, _Container], {
// summary:
// Set of buttons to select a page in a `dijit/layout/StackContainer`
// description:
// Monitors the specified StackContainer, and whenever a page is
// added, deleted, or selected, updates itself accordingly.
baseClass: "dijitStackController",
templateString: "<span role='tablist' data-dojo-attach-event='onkeypress'></span>",
// containerId: [const] String
// The id of the page container that I point to
containerId: "",
// buttonWidget: [const] Constructor
// The button widget to create to correspond to each page
buttonWidget: StackButton,
// buttonWidgetCloseClass: String
// CSS class of [x] close icon, used by event delegation code to tell when close button was clicked
buttonWidgetCloseClass: "dijitStackCloseButton",
constructor: function(params /*===== , srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
this.pane2button = {}; // mapping from pane id to buttons
},
postCreate: function(){
this.inherited(arguments);
// Listen to notifications from StackContainer.
// TODO: do this through bubbled events instead of topics
this.subscribe(this.containerId+"-startup", "onStartup");
this.subscribe(this.containerId+"-addChild", "onAddChild");
this.subscribe(this.containerId+"-removeChild", "onRemoveChild");
this.subscribe(this.containerId+"-selectChild", "onSelectChild");
this.subscribe(this.containerId+"-containerKeyPress", "onContainerKeyPress");
// Listen for click events to select or close tabs.
// No need to worry about ENTER/SPACE key handling: tabs are selected via left/right arrow keys,
// and closed via shift-F10 (to show the close menu).
this.connect(this.containerNode, 'click', function(evt){
var button = registry.getEnclosingWidget(evt.target);
if(button != this.containerNode && !button.disabled && button.page){
for(var target = evt.target; target !== this.containerNode; target = target.parentNode){
if(domClass.contains(target, this.buttonWidgetCloseClass)){
this.onCloseButtonClick(button.page);
break;
}else if(target == button.domNode){
this.onButtonClick(button.page);
break;
}
}
}
});
},
onStartup: function(/*Object*/ info){
// summary:
// Called after StackContainer has finished initializing
// tags:
// private
array.forEach(info.children, this.onAddChild, this);
if(info.selected){
// Show button corresponding to selected pane (unless selected
// is null because there are no panes)
this.onSelectChild(info.selected);
}
// Reflect events like page title changes to tab buttons
var containerNode = registry.byId(this.containerId).containerNode,
pane2button = this.pane2button,
paneToButtonAttr = {
"title": "label",
"showtitle": "showLabel",
"iconclass": "iconClass",
"closable": "closeButton",
"tooltip": "title",
"disabled": "disabled"
},
connectFunc = function(attr, buttonAttr){
return on(containerNode, "attrmodified-" + attr, function(evt){
var button = pane2button[evt.detail && evt.detail.widget && evt.detail.widget.id];
if(button){
button.set(buttonAttr, evt.detail.newValue);
}
});
};
for(var attr in paneToButtonAttr){
this.own(connectFunc(attr, paneToButtonAttr[attr]));
}
},
destroy: function(){
// Since the buttons are internal to the StackController widget, destroy() should remove them, which is
// done by calling onRemoveChild().
for(var pane in this.pane2button){
this.onRemoveChild(registry.byId(pane));
}
// TODO: destroyRecursive() will call destroy() on each child button twice. Once from the above code,
// and once because _WidgetBase.destroyDescendants() deletes anything inside of this.containerNode.
// Probably shouldn't attach that DOMNode as this.containerNode.
this.inherited(arguments);
},
onAddChild: function(/*dijit/_WidgetBase*/ page, /*Integer?*/ insertIndex){
// summary:
// Called whenever a page is added to the container.
// Create button corresponding to the page.
// tags:
// private
// create an instance of the button widget
// (remove typeof buttonWidget == string support in 2.0)
var Cls = lang.isString(this.buttonWidget) ? lang.getObject(this.buttonWidget) : this.buttonWidget;
var button = new Cls({
id: this.id + "_" + page.id,
name: this.id + "_" + page.id,
label: page.title,
disabled: page.disabled,
ownerDocument: this.ownerDocument,
dir: page.dir,
lang: page.lang,
textDir: page.textDir,
showLabel: page.showTitle,
iconClass: page.iconClass,
closeButton: page.closable,
title: page.tooltip,
page: page
});
this.addChild(button, insertIndex);
this.pane2button[page.id] = button;
page.controlButton = button; // this value might be overwritten if two tabs point to same container
if(!this._currentChild){
// If this is the first child then StackContainer will soon publish that it's selected,
// but before that StackContainer calls layout(), and before layout() is called the
// StackController needs to have the proper height... which means that the button needs
// to be marked as selected now. See test_TabContainer_CSS.html for test.
this.onSelectChild(page);
}
},
onRemoveChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Called whenever a page is removed from the container.
// Remove the button corresponding to the page.
// tags:
// private
if(this._currentChild === page){ this._currentChild = null; }
var button = this.pane2button[page.id];
if(button){
this.removeChild(button);
delete this.pane2button[page.id];
button.destroy();
}
delete page.controlButton;
},
onSelectChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Called when a page has been selected in the StackContainer, either by me or by another StackController
// tags:
// private
if(!page){ return; }
if(this._currentChild){
var oldButton=this.pane2button[this._currentChild.id];
oldButton.set('checked', false);
oldButton.focusNode.setAttribute("tabIndex", "-1");
}
var newButton=this.pane2button[page.id];
newButton.set('checked', true);
this._currentChild = page;
newButton.focusNode.setAttribute("tabIndex", "0");
var container = registry.byId(this.containerId);
container.containerNode.setAttribute("aria-labelledby", newButton.id);
},
onButtonClick: function(/*dijit/_WidgetBase*/ page){
// summary:
// Called whenever one of my child buttons is pressed in an attempt to select a page
// tags:
// private
var button = this.pane2button[page.id];
// For TabContainer where the tabs are <span>, need to set focus explicitly when left/right arrow
focus.focus(button.focusNode);
if(this._currentChild && this._currentChild.id === page.id) {
//In case the user clicked the checked button, keep it in the checked state because it remains to be the selected stack page.
button.set('checked', true);
}
var container = registry.byId(this.containerId);
container.selectChild(page);
},
onCloseButtonClick: function(/*dijit/_WidgetBase*/ page){
// summary:
// Called whenever one of my child buttons [X] is pressed in an attempt to close a page
// tags:
// private
var container = registry.byId(this.containerId);
container.closeChild(page);
if(this._currentChild){
var b = this.pane2button[this._currentChild.id];
if(b){
focus.focus(b.focusNode || b.domNode);
}
}
},
// TODO: this is a bit redundant with forward, back api in StackContainer
adjacent: function(/*Boolean*/ forward){
// summary:
// Helper for onkeypress to find next/previous button
// tags:
// private
if(!this.isLeftToRight() && (!this.tabPosition || /top|bottom/.test(this.tabPosition))){ forward = !forward; }
// find currently focused button in children array
var children = this.getChildren();
var idx = array.indexOf(children, this.pane2button[this._currentChild.id]),
current = children[idx];
// Pick next/previous non-disabled button to focus on. If we get back to the original button it means
// that all buttons must be disabled, so return current child to avoid an infinite loop.
var child;
do{
idx = (idx + (forward ? 1 : children.length - 1)) % children.length;
child = children[idx];
}while(child.disabled && child != current);
return child; // dijit/_WidgetBase
},
onkeypress: function(/*Event*/ e){
// summary:
// Handle keystrokes on the page list, for advancing to next/previous button
// and closing the current page if the page is closable.
// tags:
// private
if(this.disabled || e.altKey ){ return; }
var forward = null;
if(e.ctrlKey || !e._djpage){
switch(e.charOrCode){
case keys.LEFT_ARROW:
case keys.UP_ARROW:
if(!e._djpage){ forward = false; }
break;
case keys.PAGE_UP:
if(e.ctrlKey){ forward = false; }
break;
case keys.RIGHT_ARROW:
case keys.DOWN_ARROW:
if(!e._djpage){ forward = true; }
break;
case keys.PAGE_DOWN:
if(e.ctrlKey){ forward = true; }
break;
case keys.HOME:
// Navigate to first non-disabled child
var children = this.getChildren();
for(var idx = 0; idx < children.length; idx++){
var child = children[idx];
if(!child.disabled){
this.onButtonClick(child.page);
break;
}
}
event.stop(e);
break;
case keys.END:
// Navigate to last non-disabled child
var children = this.getChildren();
for(var idx = children.length-1; idx >= 0; idx--){
var child = children[idx];
if(!child.disabled){
this.onButtonClick(child.page);
break;
}
}
event.stop(e);
break;
case keys.DELETE:
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
event.stop(e);
break;
default:
if(e.ctrlKey){
if(e.charOrCode === keys.TAB){
this.onButtonClick(this.adjacent(!e.shiftKey).page);
event.stop(e);
}else if(e.charOrCode == "w"){
if(this._currentChild.closable){
this.onCloseButtonClick(this._currentChild);
}
event.stop(e); // avoid browser tab closing.
}
}
}
// handle next/previous page navigation (left/right arrow, etc.)
if(forward !== null){
this.onButtonClick(this.adjacent(forward).page);
event.stop(e);
}
}
},
onContainerKeyPress: function(/*Object*/ info){
// summary:
// Called when there was a keypress on the container
// tags:
// private
info.e._djpage = info.page;
this.onkeypress(info.e);
}
});
StackController.StackButton = StackButton; // for monkey patching
return StackController;
});
},
'dojo/dnd/Mover':function(){
define("dojo/dnd/Mover", [
"../_base/array", "../_base/declare", "../_base/event", "../_base/lang", "../sniff", "../_base/window",
"../dom", "../dom-geometry", "../dom-style", "../Evented", "../on", "../touch", "./common", "./autoscroll"
], function(array, declare, event, lang, has, win, dom, domGeom, domStyle, Evented, on, touch, dnd, autoscroll){
// module:
// dojo/dnd/Mover
return declare("dojo.dnd.Mover", [Evented], {
// summary:
// an object which makes a node follow the mouse, or touch-drag on touch devices.
// Used as a default mover, and as a base class for custom movers.
constructor: function(node, e, host){
// node: Node
// a node (or node's id) to be moved
// e: Event
// a mouse event, which started the move;
// only pageX and pageY properties are used
// host: Object?
// object which implements the functionality of the move,
// and defines proper events (onMoveStart and onMoveStop)
this.node = dom.byId(node);
this.marginBox = {l: e.pageX, t: e.pageY};
this.mouseButton = e.button;
var h = (this.host = host), d = node.ownerDocument;
this.events = [
// At the start of a drag, onFirstMove is called, and then the following
// listener is disconnected.
on(d, touch.move, lang.hitch(this, "onFirstMove")),
// These are called continually during the drag
on(d, touch.move, lang.hitch(this, "onMouseMove")),
// And these are called at the end of the drag
on(d, touch.release, lang.hitch(this, "onMouseUp")),
// cancel text selection and text dragging
on(d, "dragstart", event.stop),
on(d.body, "selectstart", event.stop)
];
// Tell autoscroll that a drag is starting
autoscroll.autoScrollStart(d);
// notify that the move has started
if(h && h.onMoveStart){
h.onMoveStart(this);
}
},
// mouse event processors
onMouseMove: function(e){
// summary:
// event processor for onmousemove/ontouchmove
// e: Event
// mouse/touch event
autoscroll.autoScroll(e);
var m = this.marginBox;
this.host.onMove(this, {l: m.l + e.pageX, t: m.t + e.pageY}, e);
event.stop(e);
},
onMouseUp: function(e){
if(has("webkit") && has("mac") && this.mouseButton == 2 ?
e.button == 0 : this.mouseButton == e.button){ // TODO Should condition be met for touch devices, too?
this.destroy();
}
event.stop(e);
},
// utilities
onFirstMove: function(e){
// summary:
// makes the node absolute; it is meant to be called only once.
// relative and absolutely positioned nodes are assumed to use pixel units
var s = this.node.style, l, t, h = this.host;
switch(s.position){
case "relative":
case "absolute":
// assume that left and top values are in pixels already
l = Math.round(parseFloat(s.left)) || 0;
t = Math.round(parseFloat(s.top)) || 0;
break;
default:
s.position = "absolute"; // enforcing the absolute mode
var m = domGeom.getMarginBox(this.node);
// event.pageX/pageY (which we used to generate the initial
// margin box) includes padding and margin set on the body.
// However, setting the node's position to absolute and then
// doing domGeom.marginBox on it *doesn't* take that additional
// space into account - so we need to subtract the combined
// padding and margin. We use getComputedStyle and
// _getMarginBox/_getContentBox to avoid the extra lookup of
// the computed style.
var b = win.doc.body;
var bs = domStyle.getComputedStyle(b);
var bm = domGeom.getMarginBox(b, bs);
var bc = domGeom.getContentBox(b, bs);
l = m.l - (bc.l - bm.l);
t = m.t - (bc.t - bm.t);
break;
}
this.marginBox.l = l - this.marginBox.l;
this.marginBox.t = t - this.marginBox.t;
if(h && h.onFirstMove){
h.onFirstMove(this, e);
}
// Disconnect touch.move that call this function
this.events.shift().remove();
},
destroy: function(){
// summary:
// stops the move, deletes all references, so the object can be garbage-collected
array.forEach(this.events, function(handle){ handle.remove(); });
// undo global settings
var h = this.host;
if(h && h.onMoveStop){
h.onMoveStop(this);
}
// destroy objects
this.events = this.node = this.host = null;
}
});
});
},
'dijit/layout/TabContainer':function(){
define("dijit/layout/TabContainer", [
"dojo/_base/lang", // lang.getObject
"dojo/_base/declare", // declare
"./_TabContainerBase",
"./TabController",
"./ScrollingTabController"
], function(lang, declare, _TabContainerBase, TabController, ScrollingTabController){
// module:
// dijit/layout/TabContainer
return declare("dijit.layout.TabContainer", _TabContainerBase, {
// summary:
// A Container with tabs to select each child (only one of which is displayed at a time).
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
//
// See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
// children of a `TabContainer`.
// useMenu: [const] Boolean
// True if a menu should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useMenu: true,
// useSlider: [const] Boolean
// True if a slider should be used to select tabs when they are too
// wide to fit the TabContainer, false otherwise.
useSlider: true,
// controllerWidget: Class
// An optional parameter to override the widget used to display the tab labels
controllerWidget: "",
_makeController: function(/*DomNode*/ srcNode){
// summary:
// Instantiate tablist controller widget and return reference to it.
// Callback from _TabContainerBase.postCreate().
// tags:
// protected extension
// "string" branch for back-compat, remove for 2.0
var cls = this.baseClass + "-tabs" + (this.doLayout ? "" : " dijitTabNoLayout"),
TabController = typeof this.controllerWidget == "string" ? lang.getObject(this.controllerWidget) :
this.controllerWidget;
return new TabController({
id: this.id + "_tablist",
ownerDocument: this.ownerDocument,
dir: this.dir,
lang: this.lang,
textDir: this.textDir,
tabPosition: this.tabPosition,
doLayout: this.doLayout,
containerId: this.id,
"class": cls,
nested: this.nested,
useMenu: this.useMenu,
useSlider: this.useSlider,
tabStripClass: this.tabStrip ? this.baseClass + (this.tabStrip ? "":"No") + "Strip": null
}, srcNode);
},
postMixInProperties: function(){
this.inherited(arguments);
// Scrolling controller only works for horizontal non-nested tabs
if(!this.controllerWidget){
this.controllerWidget = (this.tabPosition == "top" || this.tabPosition == "bottom") && !this.nested ?
ScrollingTabController : TabController;
}
}
});
});
},
'dijit/BackgroundIframe':function(){
define("dijit/BackgroundIframe", [
"require", // require.toUrl
"./main", // to export dijit.BackgroundIframe
"dojo/_base/config",
"dojo/dom-construct", // domConstruct.create
"dojo/dom-style", // domStyle.set
"dojo/_base/lang", // lang.extend lang.hitch
"dojo/on",
"dojo/sniff", // has("ie"), has("mozilla"), has("quirks")
"dojo/_base/window" // win.doc.createElement
], function(require, dijit, config, domConstruct, domStyle, lang, on, has, win){
// module:
// dijit/BackgroundIFrame
// TODO: remove _frames, it isn't being used much, since popups never release their
// iframes (see [22236])
var _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(has("ie") < 9){
var burl = config["dojoBlankHtmlUrl"] || require.toUrl("dojo/resources/blank.html") || "javascript:\"\"";
var html="<iframe src='" + burl + "' role='presentation'"
+ " style='position: absolute; left: 0px; top: 0px;"
+ "z-index: -1; filter:Alpha(Opacity=\"0\");'>";
iframe = win.doc.createElement(html);
}else{
iframe = domConstruct.create("iframe");
iframe.src = 'javascript:""';
iframe.className = "dijitBackgroundIframe";
iframe.setAttribute("role", "presentation");
domStyle.set(iframe, "opacity", 0.1);
}
iframe.tabIndex = -1; // Magic to prevent iframe from getting focus on tab keypress - as style didn't work.
}
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(has("ie") || has("mozilla")){
var iframe = (this.iframe = _frames.pop());
node.appendChild(iframe);
if(has("ie")<7 || has("quirks")){
this.resize(node);
this._conn = on(node, 'resize', lang.hitch(this, function(){
this.resize(node);
}));
}else{
domStyle.set(iframe, {
width: '100%',
height: '100%'
});
}
}
};
lang.extend(dijit.BackgroundIframe, {
resize: function(node){
// summary:
// Resize the iframe so it's the same size as node.
// Needed on IE6 and IE/quirks because height:100% doesn't work right.
if(this.iframe){
domStyle.set(this.iframe, {
width: node.offsetWidth + 'px',
height: node.offsetHeight + 'px'
});
}
},
destroy: function(){
// summary:
// destroy the iframe
if(this._conn){
this._conn.remove();
this._conn = null;
}
if(this.iframe){
_frames.push(this.iframe);
delete this.iframe;
}
}
});
return dijit.BackgroundIframe;
});
},
'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n",
'dojo/dnd/Avatar':function(){
define("dojo/dnd/Avatar", [
"../_base/declare",
"../_base/window",
"../dom",
"../dom-attr",
"../dom-class",
"../dom-construct",
"../hccss",
"../query"
], function(declare, win, dom, domAttr, domClass, domConstruct, has, query){
// module:
// dojo/dnd/Avatar
return declare("dojo.dnd.Avatar", null, {
// summary:
// Object that represents transferred DnD items visually
// manager: Object
// a DnD manager object
constructor: function(manager){
this.manager = manager;
this.construct();
},
// methods
construct: function(){
// summary:
// constructor function;
// it is separate so it can be (dynamically) overwritten in case of need
var a = domConstruct.create("table", {
"class": "dojoDndAvatar",
style: {
position: "absolute",
zIndex: "1999",
margin: "0px"
}
}),
source = this.manager.source, node,
b = domConstruct.create("tbody", null, a),
tr = domConstruct.create("tr", null, b),
td = domConstruct.create("td", null, tr),
k = Math.min(5, this.manager.nodes.length), i = 0;
if(has("highcontrast")){
domConstruct.create("span", {
id : "a11yIcon",
innerHTML : this.manager.copy ? '+' : "<"
}, td)
}
domConstruct.create("span", {
innerHTML: source.generateText ? this._generateText() : ""
}, td);
// we have to set the opacity on IE only after the node is live
domAttr.set(tr, {
"class": "dojoDndAvatarHeader",
style: {opacity: 0.9}
});
for(; i < k; ++i){
if(source.creator){
// create an avatar representation of the node
node = source._normalizedCreator(source.getItem(this.manager.nodes[i].id).data, "avatar").node;
}else{
// or just clone the node and hope it works
node = this.manager.nodes[i].cloneNode(true);
if(node.tagName.toLowerCase() == "tr"){
// insert extra table nodes
var table = domConstruct.create("table"),
tbody = domConstruct.create("tbody", null, table);
tbody.appendChild(node);
node = table;
}
}
node.id = "";
tr = domConstruct.create("tr", null, b);
td = domConstruct.create("td", null, tr);
td.appendChild(node);
domAttr.set(tr, {
"class": "dojoDndAvatarItem",
style: {opacity: (9 - i) / 10}
});
}
this.node = a;
},
destroy: function(){
// summary:
// destructor for the avatar; called to remove all references so it can be garbage-collected
domConstruct.destroy(this.node);
this.node = false;
},
update: function(){
// summary:
// updates the avatar to reflect the current DnD state
domClass.toggle(this.node, "dojoDndAvatarCanDrop", this.manager.canDropFlag);
if(has("highcontrast")){
var icon = dom.byId("a11yIcon");
var text = '+'; // assume canDrop && copy
if (this.manager.canDropFlag && !this.manager.copy){
text = '< '; // canDrop && move
}else if (!this.manager.canDropFlag && !this.manager.copy){
text = "o"; //!canDrop && move
}else if(!this.manager.canDropFlag){
text = 'x'; // !canDrop && copy
}
icon.innerHTML=text;
}
// replace text
query(("tr.dojoDndAvatarHeader td span" +(has("highcontrast") ? " span" : "")), this.node).forEach(
function(node){
node.innerHTML = this.manager.source.generateText ? this._generateText() : "";
}, this);
},
_generateText: function(){
// summary:
// generates a proper text to reflect copying or moving of items
return this.manager.nodes.length.toString();
}
});
});
},
'dijit/form/Button':function(){
require({cache:{
'url:dijit/form/templates/Button.html':"<span class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><span class=\"dijitReset dijitInline dijitButtonNode\"\n\t\tdata-dojo-attach-event=\"ondijitclick:_onClick\" role=\"presentation\"\n\t\t><span class=\"dijitReset dijitStretch dijitButtonContents\"\n\t\t\tdata-dojo-attach-point=\"titleNode,focusNode\"\n\t\t\trole=\"button\" aria-labelledby=\"${id}_label\"\n\t\t\t><span class=\"dijitReset dijitInline dijitIcon\" data-dojo-attach-point=\"iconNode\"></span\n\t\t\t><span class=\"dijitReset dijitToggleButtonIconChar\">&#x25CF;</span\n\t\t\t><span class=\"dijitReset dijitInline dijitButtonText\"\n\t\t\t\tid=\"${id}_label\"\n\t\t\t\tdata-dojo-attach-point=\"containerNode\"\n\t\t\t></span\n\t\t></span\n\t></span\n\t><input ${!nameAttrSetting} type=\"${type}\" value=\"${value}\" class=\"dijitOffScreen\"\n\t\ttabIndex=\"-1\" role=\"presentation\" data-dojo-attach-point=\"valueNode\"\n/></span>\n"}});
define("dijit/form/Button", [
"require",
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.toggle
"dojo/has", // has("dijit-legacy-requires")
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.trim
"dojo/ready",
"./_FormWidget",
"./_ButtonMixin",
"dojo/text!./templates/Button.html"
], function(require, declare, domClass, has, kernel, lang, ready, _FormWidget, _ButtonMixin, template){
// module:
// dijit/form/Button
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/form/DropDownButton", "dijit/form/ComboButton", "dijit/form/ToggleButton"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.form.Button", [_FormWidget, _ButtonMixin], {
// summary:
// Basically the same thing as a normal HTML button, but with special styling.
// description:
// Buttons can display a label, an icon, or both.
// A label should always be specified (through innerHTML) or the label
// attribute. It can be hidden via showLabel=false.
// example:
// | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
//
// example:
// | var button1 = new Button({label: "hello world", onClick: foo});
// | dojo.body().appendChild(button1.domNode);
// showLabel: Boolean
// Set this to true to hide the label text and display only the icon.
// (If showLabel=false then iconClass must be specified.)
// Especially useful for toolbars.
// If showLabel=true, the label will become the title (a.k.a. tooltip/hint) of the icon.
//
// The exception case is for computers in high-contrast mode, where the label
// will still be displayed, since the icon doesn't appear.
showLabel: true,
// iconClass: String
// Class to apply to DOMNode in button to make it display an icon
iconClass: "dijitNoIcon",
_setIconClassAttr: { node: "iconNode", type: "class" },
baseClass: "dijitButton",
templateString: template,
// Map widget attributes to DOMNode attributes.
_setValueAttr: "valueNode",
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions
var ok = this.inherited(arguments);
if(ok){
if(this.valueNode){
this.valueNode.click();
e.preventDefault(); // cancel BUTTON click and continue with hidden INPUT click
e.stopPropagation(); // avoid two events bubbling from Button widget
// leave ok = true so that subclasses can do what they need to do
}
}
return ok;
},
_fillContent: function(/*DomNode*/ source){
// Overrides _Templated._fillContent().
// If button label is specified as srcNodeRef.innerHTML rather than
// this.params.label, handle it here.
// TODO: remove the method in 2.0, parser will do it all for me
if(source && (!this.params || !("label" in this.params))){
var sourceLabel = lang.trim(source.innerHTML);
if(sourceLabel){
this.label = sourceLabel; // _applyAttributes will be called after buildRendering completes to update the DOM
}
}
},
_setShowLabelAttr: function(val){
if(this.containerNode){
domClass.toggle(this.containerNode, "dijitDisplayNone", !val);
}
this._set("showLabel", val);
},
setLabel: function(/*String*/ content){
// summary:
// Deprecated. Use set('label', ...) instead.
kernel.deprecated("dijit.form.Button.setLabel() is deprecated. Use set('label', ...) instead.", "", "2.0");
this.set("label", content);
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// Set the label (text) of the button; takes an HTML string.
// If the label is hidden (showLabel=false) then and no title has
// been specified, then label is also set as title attribute of icon.
this.inherited(arguments);
if(!this.showLabel && !("title" in this.params)){
this.titleNode.title = lang.trim(this.containerNode.innerText || this.containerNode.textContent || '');
}
}
});
});
},
'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n",
'dojo/dnd/move':function(){
define("dojo/dnd/move", [
"../_base/declare",
"../dom-geometry", "../dom-style",
"./common", "./Mover", "./Moveable"
], function(declare, domGeom, domStyle, dnd, Mover, Moveable){
// module:
// dojo/dnd/move
/*=====
var __constrainedMoveableArgs = declare([Moveable.__MoveableArgs], {
// constraints: Function
// Calculates a constraint box.
// It is called in a context of the moveable object.
constraints: function(){},
// within: Boolean
// restrict move within boundaries.
within: false
});
=====*/
var constrainedMoveable = declare("dojo.dnd.move.constrainedMoveable", Moveable, {
// object attributes (for markup)
constraints: function(){},
within: false,
constructor: function(node, params){
// summary:
// an object that makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: __constrainedMoveableArgs?
// an optional object with additional parameters;
// the rest is passed to the base class
if(!params){ params = {}; }
this.constraints = params.constraints;
this.within = params.within;
},
onFirstMove: function(/*Mover*/ mover){
// summary:
// called during the very first move notification;
// can be used to initialize coordinates, can be overwritten.
var c = this.constraintBox = this.constraints.call(this, mover);
c.r = c.l + c.w;
c.b = c.t + c.h;
if(this.within){
var mb = domGeom.getMarginSize(mover.node);
c.r -= mb.w;
c.b -= mb.h;
}
},
onMove: function(/*Mover*/ mover, /*Object*/ leftTop){
// summary:
// called during every move notification;
// should actually move the node; can be overwritten.
var c = this.constraintBox, s = mover.node.style;
this.onMoving(mover, leftTop);
leftTop.l = leftTop.l < c.l ? c.l : c.r < leftTop.l ? c.r : leftTop.l;
leftTop.t = leftTop.t < c.t ? c.t : c.b < leftTop.t ? c.b : leftTop.t;
s.left = leftTop.l + "px";
s.top = leftTop.t + "px";
this.onMoved(mover, leftTop);
}
});
/*=====
var __boxConstrainedMoveableArgs = declare([__constrainedMoveableArgs], {
// box: Object
// a constraint box
box: {}
});
=====*/
var boxConstrainedMoveable = declare("dojo.dnd.move.boxConstrainedMoveable", constrainedMoveable, {
// box:
// object attributes (for markup)
box: {},
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: __boxConstrainedMoveableArgs?
// an optional object with parameters
var box = params && params.box;
this.constraints = function(){ return box; };
}
});
/*=====
var __parentConstrainedMoveableArgs = declare( [__constrainedMoveableArgs], {
// area: String
// A parent's area to restrict the move.
// Can be "margin", "border", "padding", or "content".
area: ""
});
=====*/
var parentConstrainedMoveable = declare("dojo.dnd.move.parentConstrainedMoveable", constrainedMoveable, {
// area:
// object attributes (for markup)
area: "content",
constructor: function(node, params){
// summary:
// an object, which makes a node moveable
// node: Node
// a node (or node's id) to be moved
// params: __parentConstrainedMoveableArgs?
// an optional object with parameters
var area = params && params.area;
this.constraints = function(){
var n = this.node.parentNode,
s = domStyle.getComputedStyle(n),
mb = domGeom.getMarginBox(n, s);
if(area == "margin"){
return mb; // Object
}
var t = domGeom.getMarginExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
if(area == "border"){
return mb; // Object
}
t = domGeom.getBorderExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
if(area == "padding"){
return mb; // Object
}
t = domGeom.getPadExtents(n, s);
mb.l += t.l, mb.t += t.t, mb.w -= t.w, mb.h -= t.h;
return mb; // Object
};
}
});
return {
// summary:
// TODOC
constrainedMoveable: constrainedMoveable,
boxConstrainedMoveable: boxConstrainedMoveable,
parentConstrainedMoveable: parentConstrainedMoveable
};
});
},
'dijit/_WidgetBase':function(){
define("dijit/_WidgetBase", [
"require", // require.toUrl
"dojo/_base/array", // array.forEach array.map
"dojo/aspect",
"dojo/_base/config", // config.blankGif
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.set domAttr.remove
"dojo/dom-class", // domClass.add domClass.replace
"dojo/dom-construct", // domConstruct.destroy domConstruct.place
"dojo/dom-geometry", // isBodyLtr
"dojo/dom-style", // domStyle.set, domStyle.get
"dojo/has",
"dojo/_base/kernel",
"dojo/_base/lang", // mixin(), isArray(), etc.
"dojo/on",
"dojo/ready",
"dojo/Stateful", // Stateful
"dojo/topic",
"dojo/_base/window", // win.doc, win.body()
"./Destroyable",
"./registry" // registry.getUniqueId(), registry.findWidgets()
], function(require, array, aspect, config, connect, declare,
dom, domAttr, domClass, domConstruct, domGeometry, domStyle, has, kernel,
lang, on, ready, Stateful, topic, win, Destroyable, registry){
// module:
// dijit/_WidgetBase
// Flag to make dijit load modules the app didn't explicitly request, for backwards compatibility
has.add("dijit-legacy-requires", !kernel.isAsync);
// For back-compat, remove in 2.0.
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/_base/manager"];
require(requires); // use indirection so modules not rolled into a build
});
}
// Nested hash listing attributes for each tag, all strings in lowercase.
// ex: {"div": {"style": true, "tabindex" true}, "form": { ...
var tagAttrs = {};
function getAttrs(obj){
var ret = {};
for(var attr in obj){
ret[attr.toLowerCase()] = true;
}
return ret;
}
function nonEmptyAttrToDom(attr){
// summary:
// Returns a setter function that copies the attribute to this.domNode,
// or removes the attribute from this.domNode, depending on whether the
// value is defined or not.
return function(val){
domAttr[val ? "set" : "remove"](this.domNode, attr, val);
this._set(attr, val);
};
}
return declare("dijit._WidgetBase", [Stateful, Destroyable], {
// summary:
// Future base class for all Dijit widgets.
// description:
// Future base class for all Dijit widgets.
// _Widget extends this class adding support for various features needed by desktop.
//
// Provides stubs for widget lifecycle methods for subclasses to extend, like postMixInProperties(), buildRendering(),
// postCreate(), startup(), and destroy(), and also public API methods like set(), get(), and watch().
//
// Widgets can provide custom setters/getters for widget attributes, which are called automatically by set(name, value).
// For an attribute XXX, define methods _setXXXAttr() and/or _getXXXAttr().
//
// _setXXXAttr can also be a string/hash/array mapping from a widget attribute XXX to the widget's DOMNodes:
//
// - DOM node attribute
// | _setFocusAttr: {node: "focusNode", type: "attribute"}
// | _setFocusAttr: "focusNode" (shorthand)
// | _setFocusAttr: "" (shorthand, maps to this.domNode)
// Maps this.focus to this.focusNode.focus, or (last example) this.domNode.focus
//
// - DOM node innerHTML
// | _setTitleAttr: { node: "titleNode", type: "innerHTML" }
// Maps this.title to this.titleNode.innerHTML
//
// - DOM node innerText
// | _setTitleAttr: { node: "titleNode", type: "innerText" }
// Maps this.title to this.titleNode.innerText
//
// - DOM node CSS class
// | _setMyClassAttr: { node: "domNode", type: "class" }
// Maps this.myClass to this.domNode.className
//
// If the value of _setXXXAttr is an array, then each element in the array matches one of the
// formats of the above list.
//
// If the custom setter is null, no action is performed other than saving the new value
// in the widget (in this).
//
// If no custom setter is defined for an attribute, then it will be copied
// to this.focusNode (if the widget defines a focusNode), or this.domNode otherwise.
// That's only done though for attributes that match DOMNode attributes (title,
// alt, aria-labelledby, etc.)
// 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: "",
_setIdAttr: "domNode", // to copy to this.domNode even for auto-generated id's
// 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: "",
// set on domNode even when there's a focus node. but don't set lang="", since that's invalid.
_setLangAttr: nonEmptyAttrToDom("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: "",
// set on domNode even when there's a focus node. but don't set dir="", since that's invalid.
_setDirAttr: nonEmptyAttrToDom("dir"), // to set on domNode even when there's a focus node
// textDir: String
// Bi-directional support, the main variable which is responsible for the direction of the text.
// The text direction can be different than the GUI direction by using this parameter in creation
// of a widget.
//
// Allowed values:
//
// 1. "ltr"
// 2. "rtl"
// 3. "auto" - contextual the direction of a text defined by first strong letter.
//
// By default is as the page direction.
textDir: "",
// class: String
// HTML class attribute
"class": "",
_setClassAttr: { node: "domNode", type: "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 data-dojo-attach-point 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 data-dojo-type=myWidget>
// | <b> here's a plain DOM node
// | <span data-dojo-type=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 data-dojo-type=subWidget>and a widget</span>
// | <i> and another plain DOM node </i>
//
// In templated widgets, "containerNode" is set via a
// data-dojo-attach-point 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,
// ownerDocument: [const] Document?
// The document this widget belongs to. If not specified to constructor, will default to
// srcNodeRef.ownerDocument, or if no sourceRef specified, then to dojo/_base/window::doc
ownerDocument: null,
_setOwnerDocumentAttr: function(val){
// this setter is merely to avoid automatically trying to set this.domNode.ownerDocument
this._set("ownerDocument", val);
},
/*=====
// _started: [readonly] Boolean
// startup() has completed.
_started: false,
=====*/
// attributeMap: [protected] Object
// Deprecated. Instead of attributeMap, widget should have a _setXXXAttr attribute
// for each XXX attribute to be mapped to the DOM.
//
// 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 set('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: {},
// _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: config.blankGif || require.toUrl("dojo/resources/blank.gif"),
//////////// INITIALIZATION METHODS ///////////////////////////////////////
/*=====
constructor: function(params, srcNodeRef){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// 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
},
=====*/
postscript: function(/*Object?*/params, /*DomNode|String*/srcNodeRef){
// summary:
// Kicks off widget instantiation. See create() for details.
// tags:
// private
this.create(params, srcNodeRef);
},
create: function(params, srcNodeRef){
// summary:
// Kick off the life-cycle of a widget
// description:
// Create calls a number of widget methods (postMixInProperties, buildRendering, postCreate,
// etc.), some of which of you'll want to override. See http://dojotoolkit.org/reference-guide/dijit/_WidgetBase.html
// 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.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// 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
// tags:
// private
// store pointer to original DOM tree
this.srcNodeRef = dom.byId(srcNodeRef);
// No longer used, remove for 2.0.
this._connects = [];
this._supportingWidgets = [];
// this is here for back-compat, remove in 2.0 (but check NodeList-instantiate.html test)
if(this.srcNodeRef && (typeof this.srcNodeRef.id == "string")){ this.id = this.srcNodeRef.id; }
// mix in our passed parameters
if(params){
this.params = params;
lang.mixin(this, params);
}
this.postMixInProperties();
// Generate an id for the widget if one wasn't specified, or it was specified as id: undefined.
// Do this before buildRendering() because it might expect the id to be there.
if(!this.id){
this.id = registry.getUniqueId(this.declaredClass.replace(/\./g,"_"));
if(this.params){
// if params contains {id: undefined}, prevent _applyAttributes() from processing it
delete this.params.id;
}
}
// The document and <body> node this widget is associated with
this.ownerDocument = this.ownerDocument || (this.srcNodeRef ? this.srcNodeRef.ownerDocument : win.doc);
this.ownerDocumentBody = win.body(this.ownerDocument);
registry.add(this);
this.buildRendering();
var deleteSrcNodeRef;
if(this.domNode){
// Copy attributes listed in attributeMap into the [newly created] DOM for the widget.
// Also calls custom setters for all attributes with custom setters.
this._applyAttributes();
// If srcNodeRef was specified, then swap out original srcNode for this widget's DOM tree.
// For 2.0, move this after postCreate(). postCreate() shouldn't depend on the
// widget being attached to the DOM since it isn't when a widget is created programmatically like
// new MyWidget({}). See #11635.
var source = this.srcNodeRef;
if(source && source.parentNode && this.domNode !== source){
source.parentNode.replaceChild(this.domNode, source);
deleteSrcNodeRef = true;
}
// Note: for 2.0 may want to rename widgetId to dojo._scopeName + "_widgetId",
// assuming that dojo._scopeName even exists in 2.0
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.
// I think for back-compatibility it isn't deleting srcNodeRef until after postCreate() has run.
if(deleteSrcNodeRef){
delete this.srcNodeRef;
}
this._created = true;
},
_applyAttributes: function(){
// summary:
// Step during widget creation to copy widget attributes to the
// DOM according to attributeMap and _setXXXAttr objects, and also to call
// custom _setXXXAttr() methods.
//
// 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.
//
// For backwards-compatibility reasons attributeMap overrides _setXXXAttr when
// _setXXXAttr is a hash/string/array, but _setXXXAttr as a functions override attributeMap.
// tags:
// private
// Get list of attributes where this.set(name, value) will do something beyond
// setting this[name] = value. Specifically, attributes that have:
// - associated _setXXXAttr() method/hash/string/array
// - entries in attributeMap (remove this for 2.0);
var ctor = this.constructor,
list = ctor._setterAttrs;
if(!list){
list = (ctor._setterAttrs = []);
for(var attr in this.attributeMap){
list.push(attr);
}
var proto = ctor.prototype;
for(var fxName in proto){
if(fxName in this.attributeMap){ continue; }
var setterName = "_set" + fxName.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); }) + "Attr";
if(setterName in proto){
list.push(fxName);
}
}
}
// Call this.set() for each property that was either specified as parameter to constructor,
// or is in the list found above. For correlated properties like value and displayedValue, the one
// specified as a parameter should take precedence.
// Particularly important for new DateTextBox({displayedValue: ...}) since DateTextBox's default value is
// NaN and thus is not ignored like a default value of "".
// Step 1: Save the current values of the widget properties that were specified as parameters to the constructor.
// Generally this.foo == this.params.foo, except if postMixInProperties() changed the value of this.foo.
var params = {};
for(var key in this.params || {}){
params[key] = this[key];
}
// Step 2: Call set() for each property that wasn't passed as a parameter to the constructor
array.forEach(list, function(attr){
if(attr in params){
// skip this one, do it below
}else if(this[attr]){
this.set(attr, this[attr]);
}
}, this);
// Step 3: Call set() for each property that was specified as parameter to constructor.
// Use params hash created above to ignore side effects from step #2 above.
for(key in params){
this.set(key, params[key]);
}
},
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.
// Most widgets will mixin `dijit._TemplatedMixin`, which implements this method.
// tags:
// protected
if(!this.domNode){
// Create root node if it wasn't created by _Templated
this.domNode = this.srcNodeRef || this.ownerDocument.createElement("div");
}
// baseClass is a single class name or occasionally a space-separated list of names.
// Add those classes to the DOMNode. If RTL mode then also add with Rtl suffix.
// TODO: make baseClass custom setter
if(this.baseClass){
var classes = this.baseClass.split(" ");
if(!this.isLeftToRight()){
classes = classes.concat( array.map(classes, function(name){ return name+"Rtl"; }));
}
domClass.add(this.domNode, classes);
}
},
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
},
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.
if(this._started){ return; }
this._started = true;
array.forEach(this.getChildren(), function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._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();
function destroy(w){
if(w.destroyRecursive){
w.destroyRecursive(preserveDom);
}else if(w.destroy){
w.destroy(preserveDom);
}
}
// Back-compat, remove for 2.0
array.forEach(this._connects, lang.hitch(this, "disconnect"));
array.forEach(this._supportingWidgets, destroy);
// Destroy supporting widgets, but not child widgets under this.containerNode (for 2.0, destroy child widgets
// here too). if() statement is to guard against exception if destroy() called multiple times (see #15815).
if(this.domNode){
array.forEach(registry.findWidgets(this.domNode, this.containerNode), destroy);
}
this.destroyRendering(preserveDom);
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){
domAttr.remove(this.domNode, "widgetId");
}else{
domConstruct.destroy(this.domNode);
}
delete this.domNode;
}
if(this.srcNodeRef){
if(!preserveDom){
domConstruct.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
array.forEach(this.getChildren(), function(widget){
if(widget.destroyRecursive){
widget.destroyRecursive(preserveDom);
}
});
},
uninitialize: function(){
// summary:
// Deprecated. Override destroy() instead to implement custom widget tear-down
// behavior.
// tags:
// protected
return false;
},
////////////////// GET/SET, CUSTOM SETTERS, ETC. ///////////////////
_setStyleAttr: function(/*String||Object*/ value){
// summary:
// Sets the style attribute 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.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(lang.isObject(value)){
domStyle.set(mapNode, value);
}else{
if(mapNode.style.cssText){
mapNode.style.cssText += "; " + value;
}else{
mapNode.style.cssText = value;
}
}
this._set("style", value);
},
_attrToDom: function(/*String*/ attr, /*String*/ value, /*Object?*/ commands){
// summary:
// Reflect a widget attribute (title, tabIndex, duration etc.) to
// the widget DOM, as specified by commands parameter.
// If commands isn't specified then it's looked up from attributeMap.
// Note some attributes like "type"
// cannot be processed this way as they are not mutable.
// attr:
// Name of member variable (ex: "focusNode" maps to this.focusNode) pointing
// to DOMNode inside the widget, or alternately pointing to a subwidget
// tags:
// private
commands = arguments.length >= 3 ? commands : this.attributeMap[attr];
array.forEach(lang.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(lang.isFunction(value)){ // functions execute in the context of the widget
value = lang.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);
if(mapNode.tagName){
// Normal case, mapping to a DOMNode. Note that modern browsers will have a mapNode.set()
// method, but for consistency we still call domAttr
domAttr.set(mapNode, attrName, value);
}else{
// mapping to a sub-widget
mapNode.set(attrName, value);
}
break;
case "innerText":
mapNode.innerHTML = "";
mapNode.appendChild(this.ownerDocument.createTextNode(value));
break;
case "innerHTML":
mapNode.innerHTML = value;
break;
case "class":
domClass.replace(mapNode, value, this[attr]);
break;
}
}, this);
},
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 properties `foo` and `bar`
// and a method named `_getFooAttr()`, calling:
// `myWidget.get("foo")` would be equivalent to calling
// `widget._getFooAttr()` and `myWidget.get("bar")`
// would be equivalent to the expression
// `widget.bar2`
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 properties `foo` and `bar`
// and a method named `_setFooAttr()`, calling
// `myWidget.set("foo", "Howdy!")` would be equivalent to calling
// `widget._setFooAttr("Howdy!")` and `myWidget.set("bar", 3)`
// would be equivalent to the statement `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),
setter = this[names.s];
if(lang.isFunction(setter)){
// use the explicit setter
var result = setter.apply(this, Array.prototype.slice.call(arguments, 1));
}else{
// Mapping from widget attribute to DOMNode/subwidget attribute/value/etc.
// Map according to:
// 1. attributeMap setting, if one exists (TODO: attributeMap deprecated, remove in 2.0)
// 2. _setFooAttr: {...} type attribute in the widget (if one exists)
// 3. apply to focusNode or domNode if standard attribute name, excluding funcs like onClick.
// Checks if an attribute is a "standard attribute" by whether the DOMNode JS object has a similar
// attribute name (ex: accept-charset attribute matches jsObject.acceptCharset).
// Note also that Tree.focusNode() is a function not a DOMNode, so test for that.
var defaultNode = this.focusNode && !lang.isFunction(this.focusNode) ? "focusNode" : "domNode",
tag = this[defaultNode].tagName,
attrsForTag = tagAttrs[tag] || (tagAttrs[tag] = getAttrs(this[defaultNode])),
map = name in this.attributeMap ? this.attributeMap[name] :
names.s in this ? this[names.s] :
((names.l in attrsForTag && typeof value != "function") ||
/^aria-|^data-|^role$/.test(name)) ? defaultNode : null;
if(map != null){
this._attrToDom(name, value, map);
}
this._set(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.replace(/^[a-z]|-[a-zA-Z]/g, function(c){ return c.charAt(c.length-1).toUpperCase(); });
return (apn[name] = {
n: name+"Node",
s: "_set"+uc+"Attr", // converts dashes to camel case, ex: accept-charset --> _setAcceptCharsetAttr
g: "_get"+uc+"Attr",
l: uc.toLowerCase() // lowercase name w/out dashes, ex: acceptcharset
});
},
_set: function(/*String*/ name, /*anything*/ value){
// summary:
// Helper function to set new value for specified attribute, and call handlers
// registered with watch() if the value has changed.
var oldValue = this[name];
this[name] = value;
if(this._created && value !== oldValue){
if(this._watchCallbacks){
this._watchCallbacks(name, oldValue, value);
}
this.emit("attrmodified-" + name, {
detail: {
prevValue: oldValue,
newValue: value
}
});
}
},
emit: function(/*String*/ type, /*Object?*/ eventObj, /*Array?*/ callbackArgs){
// summary:
// Used by widgets to signal that a synthetic event occurred, ex:
// | myWidget.emit("attrmodified-selectedChildWidget", {}).
//
// Emits an event on this.domNode named type.toLowerCase(), based on eventObj.
// Also calls onType() method, if present, and returns value from that method.
// By default passes eventObj to callback, but will pass callbackArgs instead, if specified.
// Modifies eventObj by adding missing parameters (bubbles, cancelable, widget).
// tags:
// protected
// Specify fallback values for bubbles, cancelable in case they are not set in eventObj.
// Also set pointer to widget, although since we can't add a pointer to the widget for native events
// (see #14729), maybe we shouldn't do it here?
eventObj = eventObj || {};
if(eventObj.bubbles === undefined){ eventObj.bubbles = true; }
if(eventObj.cancelable === undefined){ eventObj.cancelable = true; }
if(!eventObj.detail){ eventObj.detail = {}; }
eventObj.detail.widget = this;
var ret, callback = this["on"+type];
if(callback){
ret = callback.apply(this, callbackArgs ? callbackArgs : [eventObj]);
}
// Emit event, but avoid spurious emit()'s as parent sets properties on child during startup/destroy
if(this._started && !this._beingDestroyed){
on.emit(this.domNode, type.toLowerCase(), eventObj);
}
return ret;
},
on: function(/*String|Function*/ type, /*Function*/ func){
// summary:
// Call specified function when event occurs, ex: myWidget.on("click", function(){ ... }).
// type:
// Name of event (ex: "click") or extension event like touch.press.
// description:
// Call specified function when event `type` occurs, ex: `myWidget.on("click", function(){ ... })`.
// Note that the function is not run in any particular scope, so if (for example) you want it to run in the
// widget's scope you must do `myWidget.on("click", lang.hitch(myWidget, func))`.
// For backwards compatibility, if there's an onType() method in the widget then connect to that.
// Remove in 2.0.
var widgetMethod = this._onMap(type);
if(widgetMethod){
return aspect.after(this, widgetMethod, func, true);
}
// Otherwise, just listen for the event on this.domNode.
return this.own(on(this.domNode, type, func))[0];
},
_onMap: function(/*String|Function*/ type){
// summary:
// Maps on() type parameter (ex: "mousemove") to method name (ex: "onMouseMove").
// If type is a synthetic event like touch.press then returns undefined.
var ctor = this.constructor, map = ctor._onMap;
if(!map){
map = (ctor._onMap = {});
for(var attr in ctor.prototype){
if(/^on/.test(attr)){
map[attr.replace(/^on/, "").toLowerCase()] = attr;
}
}
}
return map[typeof type == "string" && type.toLowerCase()]; // String
},
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
},
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 ? registry.findWidgets(this.containerNode) : []; // dijit/_WidgetBase[]
},
getParent: function(){
// summary:
// Returns the parent widget of this widget
return registry.getEnclosingWidget(this.domNode.parentNode);
},
connect: function(
/*Object|null*/ obj,
/*String|Function*/ event,
/*String|Function*/ method){
// summary:
// Deprecated, will be removed in 2.0, use this.own(on(...)) or this.own(aspect.after(...)) instead.
//
// Connects specified obj/event to specified method of this object
// and registers for disconnect() on widget destroy.
//
// Provide widget-specific analog to dojo.connect, except with the
// implicit use of this widget as the target object.
// Events connected with `this.connect` are disconnected upon
// destruction.
// returns:
// A handle that can be passed to `disconnect` in order to disconnect before
// the widget is destroyed.
// example:
// | var btn = new 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
return this.own(connect.connect(obj, event, this, method))[0]; // handle
},
disconnect: function(handle){
// summary:
// Deprecated, will be removed in 2.0, use handle.remove() instead.
//
// Disconnects handle created by `connect`.
// tags:
// protected
handle.remove();
},
subscribe: function(t, method){
// summary:
// Deprecated, will be removed in 2.0, use this.own(topic.subscribe()) instead.
//
// Subscribes to the specified topic and calls the specified method
// of this object and registers for unsubscribe() on widget destroy.
//
// Provide widget-specific analog to dojo.subscribe, except with the
// implicit use of this widget as the target object.
// t: String
// The topic
// method: Function
// The callback
// example:
// | var btn = new 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);
// | });
// tags:
// protected
return this.own(topic.subscribe(t, lang.hitch(this, method)))[0]; // handle
},
unsubscribe: function(/*Object*/ handle){
// summary:
// Deprecated, will be removed in 2.0, use handle.remove() instead.
//
// Unsubscribes handle created by this.subscribe.
// Also removes handle from this widget's list of subscriptions
// tags:
// protected
handle.remove();
},
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") : domGeometry.isBodyLtr(this.ownerDocument); //Boolean
},
isFocusable: function(){
// summary:
// Return true if this widget can currently be focused
// and false if not
return this.focus && (domStyle.get(this.domNode, "display") != "none");
},
placeAt: function(/* String|DomNode|_Widget */ reference, /* String|Int? */ position){
// summary:
// Place this widget somewhere in the DOM based
// on standard domConstruct.place() conventions.
// 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:
// Widget, DOMNode, or id of widget or DOMNode
// position:
// If reference is a widget (or id of widget), and that widget has an ".addChild" method,
// it will be called passing this widget instance into that method, supplying the optional
// position index passed. In this case position (if specified) should be an integer.
//
// If reference is a DOMNode (or id matching a DOMNode but not a widget),
// the position argument can be a numeric index or a string
// "first", "last", "before", or "after", same as dojo/dom-construct::place().
// returns: dijit/_WidgetBase
// 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 Button({ label:"click" }).placeAt(win.body());
// | // now, 'button' is still the widget reference to the newly created button
// | button.on("click", 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 Button({},"src").placeAt("wrapper");
// example:
// | // place a new button as the first element of some div
// | var button = new Button({ label:"click" }).placeAt("wrapper","first");
// example:
// | // create a contentpane and add it to a TabContainer
// | var tc = dijit.byId("myTabs");
// | new ContentPane({ href:"foo.html", title:"Wow!" }).placeAt(tc)
var refWidget = !reference.tagName && registry.byId(reference);
if(refWidget && refWidget.addChild && (!position || typeof position === "number")){
// Adding this to refWidget and can use refWidget.addChild() to handle everything.
refWidget.addChild(this, position);
}else{
// "reference" is a plain DOMNode, or we can't use refWidget.addChild(). Use domConstruct.place() and
// target refWidget.containerNode for nested placement (position==number, "first", "last", "only"), and
// refWidget.domNode otherwise ("after"/"before"/"replace"). (But not supported officially, see #14946.)
var ref = refWidget ?
(refWidget.containerNode && !/after|before|replace/.test(position||"") ?
refWidget.containerNode : refWidget.domNode) : dom.byId(reference, this.ownerDocument);
domConstruct.place(this.domNode, ref, position);
// Start this iff it has a parent widget that's already started.
if(!this._started && (this.getParent() || {})._started){
this.startup();
}
}
return this;
},
getTextDir: function(/*String*/ text,/*String*/ originalDir){
// summary:
// Return direction of the text.
// The function overridden in the _BidiSupport module,
// its main purpose is to calculate the direction of the
// text, if was defined by the programmer through textDir.
// tags:
// protected.
return originalDir;
},
applyTextDir: function(/*===== element, text =====*/){
// summary:
// The function overridden in the _BidiSupport module,
// originally used for setting element.dir according to this.textDir.
// In this case does nothing.
// element: DOMNode
// text: String
// tags:
// protected.
},
defer: function(fcn, delay){
// summary:
// Wrapper to setTimeout to avoid deferred functions executing
// after the originating widget has been destroyed.
// Returns an object handle with a remove method (that returns null) (replaces clearTimeout).
// fcn: function reference
// delay: Optional number (defaults to 0)
// tags:
// protected.
var timer = setTimeout(lang.hitch(this,
function(){
timer = null;
if(!this._destroyed){
lang.hitch(this, fcn)();
}
}),
delay || 0
);
return {
remove: function(){
if(timer){
clearTimeout(timer);
timer = null;
}
return null; // so this works well: handle = handle.remove();
}
};
}
});
});
},
'dijit/layout/_TabContainerBase':function(){
require({cache:{
'url:dijit/layout/templates/TabContainer.html':"<div class=\"dijitTabContainer\">\n\t<div class=\"dijitTabListWrapper\" data-dojo-attach-point=\"tablistNode\"></div>\n\t<div data-dojo-attach-point=\"tablistSpacer\" class=\"dijitTabSpacer ${baseClass}-spacer\"></div>\n\t<div class=\"dijitTabPaneWrapper ${baseClass}-container\" data-dojo-attach-point=\"containerNode\"></div>\n</div>\n"}});
define("dijit/layout/_TabContainerBase", [
"dojo/text!./templates/TabContainer.html",
"./StackContainer",
"./utils", // marginBox2contextBox, layoutChildren
"../_TemplatedMixin",
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.contentBox
"dojo/dom-style" // domStyle.style
], function(template, StackContainer, layoutUtils, _TemplatedMixin, declare, domClass, domGeometry, domStyle){
// module:
// dijit/layout/_TabContainerBase
return declare("dijit.layout._TabContainerBase", [StackContainer, _TemplatedMixin], {
// summary:
// Abstract base class for TabContainer. Must define _makeController() to instantiate
// and return the widget that displays the tab labels
// description:
// A TabContainer is a container that has multiple panes, but shows only
// one pane at a time. There are a set of tabs corresponding to each pane,
// where each tab has the name (aka title) of the pane, and optionally a close button.
// tabPosition: String
// Defines where tabs go relative to tab content.
// "top", "bottom", "left-h", "right-h"
tabPosition: "top",
baseClass: "dijitTabContainer",
// tabStrip: [const] Boolean
// Defines whether the tablist gets an extra class for layouting, putting a border/shading
// around the set of tabs. Not supported by claro theme.
tabStrip: false,
// nested: [const] Boolean
// If true, use styling for a TabContainer nested inside another TabContainer.
// For tundra etc., makes tabs look like links, and hides the outer
// border since the outer TabContainer already has a border.
nested: false,
templateString: template,
postMixInProperties: function(){
// set class name according to tab position, ex: dijitTabContainerTop
this.baseClass += this.tabPosition.charAt(0).toUpperCase() + this.tabPosition.substr(1).replace(/-.*/, "");
this.srcNodeRef && domStyle.set(this.srcNodeRef, "visibility", "hidden");
this.inherited(arguments);
},
buildRendering: function(){
this.inherited(arguments);
// Create the tab list that will have a tab (a.k.a. tab button) for each tab panel
this.tablist = this._makeController(this.tablistNode);
if(!this.doLayout){ domClass.add(this.domNode, "dijitTabContainerNoLayout"); }
if(this.nested){
/* workaround IE's lack of support for "a > b" selectors by
* tagging each node in the template.
*/
domClass.add(this.domNode, "dijitTabContainerNested");
domClass.add(this.tablist.containerNode, "dijitTabContainerTabListNested");
domClass.add(this.tablistSpacer, "dijitTabContainerSpacerNested");
domClass.add(this.containerNode, "dijitTabPaneWrapperNested");
}else{
domClass.add(this.domNode, "tabStrip-" + (this.tabStrip ? "enabled" : "disabled"));
}
},
_setupChild: function(/*dijit/_WidgetBase*/ tab){
// Overrides StackContainer._setupChild().
domClass.add(tab.domNode, "dijitTabPane");
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
// wire up the tablist and its tabs
this.tablist.startup();
this.inherited(arguments);
},
layout: function(){
// Overrides StackContainer.layout().
// Configure the content pane to take up all the space except for where the tabs are
if(!this._contentBox || typeof(this._contentBox.l) == "undefined"){return;}
var sc = this.selectedChildWidget;
if(this.doLayout){
// position and size the titles and the container node
var titleAlign = this.tabPosition.replace(/-h/, "");
this.tablist.layoutAlign = titleAlign;
var children = [this.tablist, {
domNode: this.tablistSpacer,
layoutAlign: titleAlign
}, {
domNode: this.containerNode,
layoutAlign: "client"
}];
layoutUtils.layoutChildren(this.domNode, this._contentBox, children);
// Compute size to make each of my children.
// children[2] is the margin-box size of this.containerNode, set by layoutChildren() call above
this._containerContentBox = layoutUtils.marginBox2contentBox(this.containerNode, children[2]);
if(sc && sc.resize){
sc.resize(this._containerContentBox);
}
}else{
// just layout the tab controller, so it can position left/right buttons etc.
if(this.tablist.resize){
//make the tabs zero width so that they don't interfere with width calc, then reset
var s = this.tablist.domNode.style;
s.width="0";
var width = domGeometry.getContentBox(this.domNode).w;
s.width="";
this.tablist.resize({w: width});
}
// and call resize() on the selected pane just to tell it that it's been made visible
if(sc && sc.resize){
sc.resize();
}
}
},
destroy: function(){
if(this.tablist){
this.tablist.destroy();
}
this.inherited(arguments);
}
});
});
},
'dijit/form/Form':function(){
define("dijit/form/Form", [
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event", // event.stop
"dojo/_base/kernel", // kernel.deprecated
"dojo/sniff", // has("ie")
"../_Widget",
"../_TemplatedMixin",
"./_FormMixin",
"../layout/_ContentPaneResizeMixin"
], function(declare, domAttr, event, kernel, has, _Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin){
// module:
// dijit/form/Form
return declare("dijit.form.Form", [_Widget, _TemplatedMixin, _FormMixin, _ContentPaneResizeMixin], {
// summary:
// Widget corresponding to HTML form tag, for validation and serialization
//
// example:
// | <form data-dojo-type="dijit/form/Form" id="myForm">
// | Name: <input type="text" name="name" />
// | </form>
// | myObj = {name: "John Doe"};
// | dijit.byId('myForm').set('value', myObj);
// |
// | myObj=dijit.byId('myForm').get('value');
// HTML <FORM> attributes
// name: String?
// Name of form for scripting.
name: "",
// action: String?
// Server-side form handler.
action: "",
// method: String?
// HTTP method used to submit the form, either "GET" or "POST".
method: "",
// encType: String?
// Encoding type for the form, ex: application/x-www-form-urlencoded.
encType: "",
// accept-charset: String?
// List of supported charsets.
"accept-charset": "",
// accept: String?
// List of MIME types for file upload.
accept: "",
// target: String?
// Target frame for the document to be opened in.
target: "",
templateString: "<form data-dojo-attach-point='containerNode' data-dojo-attach-event='onreset:_onReset,onsubmit:_onSubmit' ${!nameAttrSetting}></form>",
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 _setNameAttr to set the name due to IE limitations, see #8660
this.nameAttrSetting = this.name ? ("name='" + this.name + "'") : "";
this.inherited(arguments);
},
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Deprecated: use submit()
// tags:
// deprecated
},
onExecute: function(){
// summary:
// Deprecated: use onSubmit()
// tags:
// deprecated
},
_setEncTypeAttr: function(/*String*/ value){
this.encType = value;
domAttr.set(this.domNode, "encType", value);
if(has("ie")){ this.domNode.encoding = value; }
},
reset: function(/*Event?*/ e){
// summary:
// restores all widget values back to their init values,
// calls onReset() which can cancel the reset by returning false
// create fake event so we can know if preventDefault() is called
var faux = {
returnValue: true, // the IE way
preventDefault: function(){ // not IE
this.returnValue = false;
},
stopPropagation: function(){},
currentTarget: e ? e.target : this.domNode,
target: e ? e.target : this.domNode
};
// if return value is not exactly false, and haven't called preventDefault(), then reset
if(!(this.onReset(faux) === false) && faux.returnValue){
this.inherited(arguments, []);
}
},
onReset: function(/*Event?*/ /*===== e =====*/){
// summary:
// Callback when user resets the form. This method is intended
// to be over-ridden. When the `reset` method is called
// programmatically, the return value from `onReset` is used
// to compute whether or not resetting should proceed
// tags:
// callback
return true; // Boolean
},
_onReset: function(e){
this.reset(e);
event.stop(e);
return false;
},
_onSubmit: function(e){
var fp = this.constructor.prototype;
// TODO: remove this if statement beginning with 2.0
if(this.execute != fp.execute || this.onExecute != fp.onExecute){
kernel.deprecated("dijit.form.Form:execute()/onExecute() are deprecated. Use onSubmit() instead.", "", "2.0");
this.onExecute();
this.execute(this.getValues());
}
if(this.onSubmit(e) === false){ // only exactly false stops submit
event.stop(e);
}
},
onSubmit: function(/*Event?*/ /*===== e =====*/){
// summary:
// Callback when user submits the form.
// description:
// This method is intended to be over-ridden, but by default it checks and
// returns the validity of form elements. When the `submit`
// method is called programmatically, the return value from
// `onSubmit` is used to compute whether or not submission
// should proceed
// tags:
// extension
return this.isValid(); // Boolean
},
submit: function(){
// summary:
// programmatically submit form if and only if the `onSubmit` returns true
if(!(this.onSubmit() === false)){
this.containerNode.submit();
}
}
});
});
},
'dojo/store/Memory':function(){
define("dojo/store/Memory", ["../_base/declare", "./util/QueryResults", "./util/SimpleQueryEngine" /*=====, "./api/Store" =====*/],
function(declare, QueryResults, SimpleQueryEngine /*=====, Store =====*/){
// module:
// dojo/store/Memory
// No base class, but for purposes of documentation, the base class is dojo/store/api/Store
var base = null;
/*===== base = Store; =====*/
return declare("dojo.store.Memory", base, {
// summary:
// This is a basic in-memory object store. It implements dojo/store/api/Store.
constructor: function(options){
// summary:
// Creates a memory object store.
// options: dojo/store/Memory
// This provides any configuration information that will be mixed into the store.
// This should generally include the data property to provide the starting set of data.
for(var i in options){
this[i] = options[i];
}
this.setData(this.data || []);
},
// data: Array
// The array of all the objects in the memory store
data:null,
// idProperty: String
// Indicates the property to use as the identity property. The values of this
// property should be unique.
idProperty: "id",
// index: Object
// An index of data indices into the data array by id
index:null,
// queryEngine: Function
// Defines the query engine to use for querying the data store
queryEngine: SimpleQueryEngine,
get: function(id){
// summary:
// Retrieves an object by its identity
// id: Number
// The identity to use to lookup the object
// returns: Object
// The object in the store that matches the given id.
return this.data[this.index[id]];
},
getIdentity: function(object){
// summary:
// Returns an object's identity
// object: Object
// The object to get the identity from
// returns: Number
return object[this.idProperty];
},
put: function(object, options){
// summary:
// Stores an object
// object: Object
// The object to store.
// options: dojo/store/api/Store.PutDirectives?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
var data = this.data,
index = this.index,
idProperty = this.idProperty;
var id = object[idProperty] = (options && "id" in options) ? options.id : idProperty in object ? object[idProperty] : Math.random();
if(id in index){
// object exists
if(options && options.overwrite === false){
throw new Error("Object already exists");
}
// replace the entry in data
data[index[id]] = object;
}else{
// add the new object
index[id] = data.push(object) - 1;
}
return id;
},
add: function(object, options){
// summary:
// Creates an object, throws an error if the object already exists
// object: Object
// The object to store.
// options: dojo/store/api/Store.PutDirectives?
// Additional metadata for storing the data. Includes an "id"
// property if a specific id is to be used.
// returns: Number
(options = options || {}).overwrite = false;
// call put with overwrite being false
return this.put(object, options);
},
remove: function(id){
// summary:
// Deletes an object by its identity
// id: Number
// The identity to use to delete the object
// returns: Boolean
// Returns true if an object was removed, falsy (undefined) if no object matched the id
var index = this.index;
var data = this.data;
if(id in index){
data.splice(index[id], 1);
// now we have to reindex
this.setData(data);
return true;
}
},
query: function(query, options){
// summary:
// Queries the store for objects.
// query: Object
// The query to use for retrieving objects from the store.
// options: dojo/store/api/Store.QueryOptions?
// The optional arguments to apply to the resultset.
// returns: dojo/store/api/Store.QueryResults
// The results of the query, extended with iterative methods.
//
// example:
// Given the following store:
//
// | var store = new Memory({
// | data: [
// | {id: 1, name: "one", prime: false },
// | {id: 2, name: "two", even: true, prime: true},
// | {id: 3, name: "three", prime: true},
// | {id: 4, name: "four", even: true, prime: false},
// | {id: 5, name: "five", prime: true}
// | ]
// | });
//
// ...find all items where "prime" is true:
//
// | var results = store.query({ prime: true });
//
// ...or find all items where "even" is true:
//
// | var results = store.query({ even: true });
return QueryResults(this.queryEngine(query, options)(this.data));
},
setData: function(data){
// summary:
// Sets the given data as the source for this store, and indexes it
// data: Object[]
// An array of objects to use as the source of data.
if(data.items){
// just for convenience with the data format IFRS expects
this.idProperty = data.identifier;
data = this.data = data.items;
}else{
this.data = data;
}
this.index = {};
for(var i = 0, l = data.length; i < l; i++){
this.index[data[i][this.idProperty]] = i;
}
}
});
});
},
'url:dijit/templates/Tooltip.html':"<div class=\"dijitTooltip dijitTooltipLeft\" id=\"dojoTooltip\"\n\t><div class=\"dijitTooltipContainer dijitTooltipContents\" data-dojo-attach-point=\"containerNode\" role='alert'></div\n\t><div class=\"dijitTooltipConnector\" data-dojo-attach-point=\"connectorNode\"></div\n></div>\n",
'dijit/_base/sniff':function(){
define("dijit/_base/sniff", [ "dojo/uacss" ], function(){
// module:
// dijit/_base/sniff
/*=====
return {
// summary:
// Deprecated, back compatibility module, new code should require dojo/uacss directly instead of this module.
};
=====*/
});
},
'dijit/Toolbar':function(){
define("dijit/Toolbar", [
"require",
"dojo/_base/declare", // declare
"dojo/has",
"dojo/keys", // keys.LEFT_ARROW keys.RIGHT_ARROW
"dojo/ready",
"./_Widget",
"./_KeyNavContainer",
"./_TemplatedMixin"
], function(require, declare, has, keys, ready, _Widget, _KeyNavContainer, _TemplatedMixin){
// module:
// dijit/Toolbar
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/ToolbarSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Toolbar", [_Widget, _TemplatedMixin, _KeyNavContainer], {
// summary:
// A Toolbar widget, used to hold things like `dijit.Editor` buttons
templateString:
'<div class="dijit" role="toolbar" tabIndex="${tabIndex}" data-dojo-attach-point="containerNode">' +
'</div>',
baseClass: "dijitToolbar",
postCreate: function(){
this.inherited(arguments);
this.connectKeyNavHandlers(
this.isLeftToRight() ? [keys.LEFT_ARROW] : [keys.RIGHT_ARROW],
this.isLeftToRight() ? [keys.RIGHT_ARROW] : [keys.LEFT_ARROW]
);
}
});
});
},
'dijit/layout/StackContainer':function(){
define("dijit/layout/StackContainer", [
"dojo/_base/array", // array.forEach array.indexOf array.some
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.replace
"dojo/has", // has("dijit-legacy-requires")
"dojo/_base/lang", // lang.extend
"dojo/ready",
"dojo/topic", // publish
"../registry", // registry.byId
"../_WidgetBase",
"./_LayoutWidget",
"dojo/i18n!../nls/common"
], function(array, cookie, declare, domClass, has, lang, ready, topic,
registry, _WidgetBase, _LayoutWidget){
// module:
// dijit/layout/StackContainer
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/layout/StackController"];
require(requires); // use indirection so modules not rolled into a build
});
}
var StackContainer = declare("dijit.layout.StackContainer", _LayoutWidget, {
// summary:
// A container that has multiple children, but shows only
// one child at a time
//
// description:
// A container for widgets (ContentPanes, for example) That displays
// only one Widget at a time.
//
// Publishes topics [widgetId]-addChild, [widgetId]-removeChild, and [widgetId]-selectChild
//
// Can be base class for container, Wizard, Show, etc.
//
// See `StackContainer.ChildWidgetProperties` for details on the properties that can be set on
// children of a `StackContainer`.
// doLayout: Boolean
// If true, change the size of my currently displayed child to match my size
doLayout: true,
// persist: Boolean
// Remembers the selected child across sessions
persist: false,
baseClass: "dijitStackContainer",
/*=====
// selectedChildWidget: [readonly] dijit._Widget
// References the currently selected child widget, if any.
// Adjust selected child with selectChild() method.
selectedChildWidget: null,
=====*/
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitLayoutContainer");
this.containerNode.setAttribute("role", "tabpanel");
},
postCreate: function(){
this.inherited(arguments);
this.connect(this.domNode, "onkeypress", this._onKeyPress);
},
startup: function(){
if(this._started){ return; }
var children = this.getChildren();
// Setup each page panel to be initially hidden
array.forEach(children, this._setupChild, this);
// Figure out which child to initially display, defaulting to first one
if(this.persist){
this.selectedChildWidget = registry.byId(cookie(this.id + "_selectedChild"));
}else{
array.some(children, function(child){
if(child.selected){
this.selectedChildWidget = child;
}
return child.selected;
}, this);
}
var selected = this.selectedChildWidget;
if(!selected && children[0]){
selected = this.selectedChildWidget = children[0];
selected.selected = true;
}
// Publish information about myself so any StackControllers can initialize.
// This needs to happen before this.inherited(arguments) so that for
// TabContainer, this._contentBox doesn't include the space for the tab labels.
topic.publish(this.id+"-startup", {children: children, selected: selected});
// Startup each child widget, and do initial layout like setting this._contentBox,
// then calls this.resize() which does the initial sizing on the selected child.
this.inherited(arguments);
},
resize: function(){
// Overrides _LayoutWidget.resize()
// Resize is called when we are first made visible (it's called from startup()
// if we are initially visible). If this is the first time we've been made
// visible then show our first child.
if(!this._hasBeenShown){
this._hasBeenShown = true;
var selected = this.selectedChildWidget;
if(selected){
this._showChild(selected);
}
}
this.inherited(arguments);
},
_setupChild: function(/*dijit/_WidgetBase*/ child){
// Overrides _LayoutWidget._setupChild()
this.inherited(arguments);
domClass.replace(child.domNode, "dijitHidden", "dijitVisible");
// remove the title attribute so it doesn't show up when i hover
// over a node
child.domNode.title = "";
},
addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
// Overrides _Container.addChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
topic.publish(this.id+"-addChild", child, insertIndex); // publish
// in case the tab titles have overflowed from one line to two lines
// (or, if this if first child, from zero lines to one line)
// TODO: w/ScrollingTabController this is no longer necessary, although
// ScrollTabController.resize() does need to get called to show/hide
// the navigation buttons as appropriate, but that's handled in ScrollingTabController.onAddChild().
// If this is updated to not layout [except for initial child added / last child removed], update
// "childless startup" test in StackContainer.html to check for no resize event after second addChild()
this.layout();
// if this is the first child, then select it
if(!this.selectedChildWidget){
this.selectChild(child);
}
}
},
removeChild: function(/*dijit/_WidgetBase*/ page){
// Overrides _Container.removeChild() to do layout and publish events
this.inherited(arguments);
if(this._started){
// this will notify any tablists to remove a button; do this first because it may affect sizing
topic.publish(this.id + "-removeChild", page); // publish
}
// If all our children are being destroyed than don't run the code below (to select another page),
// because we are deleting every page one by one
if(this._descendantsBeingDestroyed){ return; }
// Select new page to display, also updating TabController to show the respective tab.
// Do this before layout call because it can affect the height of the TabController.
if(this.selectedChildWidget === page){
this.selectedChildWidget = undefined;
if(this._started){
var children = this.getChildren();
if(children.length){
this.selectChild(children[0]);
}
}
}
if(this._started){
// In case the tab titles now take up one line instead of two lines
// (note though that ScrollingTabController never overflows to multiple lines),
// or the height has changed slightly because of addition/removal of tab which close icon
this.layout();
}
},
selectChild: function(/*dijit/_WidgetBase|String*/ page, /*Boolean*/ animate){
// summary:
// Show the given widget (which must be one of my children)
// page:
// Reference to child widget or id of child widget
page = registry.byId(page);
if(this.selectedChildWidget != page){
// Deselect old page and select new one
var d = this._transition(page, this.selectedChildWidget, animate);
this._set("selectedChildWidget", page);
topic.publish(this.id+"-selectChild", page); // publish
if(this.persist){
cookie(this.id + "_selectedChild", this.selectedChildWidget.id);
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_transition: function(newWidget, oldWidget /*===== , animate =====*/){
// summary:
// Hide the old widget and display the new widget.
// Subclasses should override this.
// newWidget: dijit/_WidgetBase
// The newly selected widget.
// oldWidget: dijit/_WidgetBase
// The previously selected widget.
// animate: Boolean
// Used by AccordionContainer to turn on/off slide effect.
// tags:
// protected extension
if(oldWidget){
this._hideChild(oldWidget);
}
var d = this._showChild(newWidget);
// Size the new widget, in case this is the first time it's being shown,
// or I have been resized since the last time it was shown.
// Note that page must be visible for resizing to work.
if(newWidget.resize){
if(this.doLayout){
newWidget.resize(this._containerContentBox || this._contentBox);
}else{
// the child should pick it's own size but we still need to call resize()
// (with no arguments) to let the widget lay itself out
newWidget.resize();
}
}
return d; // If child has an href, promise that fires when the child's href finishes loading
},
_adjacent: function(/*Boolean*/ forward){
// summary:
// Gets the next/previous child widget in this container from the current selection.
// TODO: remove for 2.0 if this isn't being used. Otherwise, fix to skip disabled tabs.
var children = this.getChildren();
var index = array.indexOf(children, this.selectedChildWidget);
index += forward ? 1 : children.length - 1;
return children[ index % children.length ]; // dijit/_WidgetBase
},
forward: function(){
// summary:
// Advance to next page.
return this.selectChild(this._adjacent(true), true);
},
back: function(){
// summary:
// Go back to previous page.
return this.selectChild(this._adjacent(false), true);
},
_onKeyPress: function(e){
topic.publish(this.id+"-containerKeyPress", { e: e, page: this}); // publish
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
var child = this.selectedChildWidget;
if(child && child.resize){
if(this.doLayout){
child.resize(this._containerContentBox || this._contentBox);
}else{
child.resize();
}
}
},
_showChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Show the specified child by changing it's CSS, and call _onShow()/onShow() so
// it can do any updates it needs regarding loading href's etc.
// returns:
// Promise that fires when page has finished showing, or true if there's no href
var children = this.getChildren();
page.isFirstChild = (page == children[0]);
page.isLastChild = (page == children[children.length-1]);
page._set("selected", true);
domClass.replace(page.domNode, "dijitVisible", "dijitHidden");
return (page._onShow && page._onShow()) || true;
},
_hideChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Hide the specified child by changing it's CSS, and call _onHide() so
// it's notified.
page._set("selected", false);
domClass.replace(page.domNode, "dijitHidden", "dijitVisible");
page.onHide && page.onHide();
},
closeChild: function(/*dijit/_WidgetBase*/ page){
// summary:
// Callback when user clicks the [X] to remove a page.
// If onClose() returns true then remove and destroy the child.
// tags:
// private
var remove = page.onClose(this, page);
if(remove){
this.removeChild(page);
// makes sure we can clean up executeScripts in ContentPane onUnLoad
page.destroyRecursive();
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
this._descendantsBeingDestroyed = true;
this.selectedChildWidget = undefined;
array.forEach(this.getChildren(), function(child){
if(!preserveDom){
this.removeChild(child);
}
child.destroyRecursive(preserveDom);
}, this);
this._descendantsBeingDestroyed = false;
}
});
StackContainer.ChildWidgetProperties = {
// summary:
// These properties can be specified for the children of a StackContainer.
// selected: Boolean
// Specifies that this widget should be the initially displayed pane.
// Note: to change the selected child use `dijit/layout/StackContainer.selectChild`
selected: false,
// disabled: Boolean
// Specifies that the button to select this pane should be disabled.
// Doesn't affect programmatic selection of the pane, nor does it deselect the pane if it is currently selected.
disabled: false,
// closable: Boolean
// True if user can close (destroy) this child, such as (for example) clicking the X on the tab.
closable: false,
// iconClass: String
// CSS Class specifying icon to use in label associated with this pane.
iconClass: "dijitNoIcon",
// showTitle: Boolean
// When true, display title of this widget as tab label etc., rather than just using
// icon specified in iconClass
showTitle: true
};
// Since any widget can be specified as a StackContainer child, mix them
// into the base widget class. (This is a hack, but it's effective.)
// This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
lang.extend(_WidgetBase, /*===== {} || =====*/ StackContainer.ChildWidgetProperties);
return StackContainer;
});
},
'dojo/regexp':function(){
define("dojo/regexp", ["./_base/kernel", "./_base/lang"], function(dojo, lang){
// module:
// dojo/regexp
var regexp = {
// summary:
// Regular expressions and Builder resources
};
lang.setObject("dojo.regexp", regexp);
regexp.escapeString = function(/*String*/str, /*String?*/except){
// summary:
// Adds escape sequences for special characters in regular expressions
// except:
// a String with special characters to be left unescaped
return str.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, function(ch){
if(except && except.indexOf(ch) != -1){
return ch;
}
return "\\" + ch;
}); // String
};
regexp.buildGroupRE = function(/*Object|Array*/arr, /*Function*/re, /*Boolean?*/nonCapture){
// summary:
// Builds a regular expression that groups subexpressions
// description:
// A utility function used by some of the RE generators. The
// subexpressions are constructed by the function, re, in the second
// parameter. re builds one subexpression for each elem in the array
// a, in the first parameter. Returns a string for a regular
// expression that groups all the subexpressions.
// arr:
// A single value or an array of values.
// re:
// A function. Takes one parameter and converts it to a regular
// expression.
// nonCapture:
// If true, uses non-capturing match, otherwise matches are retained
// by regular expression. Defaults to false
// case 1: a is a single value.
if(!(arr instanceof Array)){
return re(arr); // String
}
// case 2: a is an array
var b = [];
for(var i = 0; i < arr.length; i++){
// convert each elem to a RE
b.push(re(arr[i]));
}
// join the REs as alternatives in a RE group.
return regexp.group(b.join("|"), nonCapture); // String
};
regexp.group = function(/*String*/expression, /*Boolean?*/nonCapture){
// summary:
// adds group match to expression
// nonCapture:
// If true, uses non-capturing match, otherwise matches are retained
// by regular expression.
return "(" + (nonCapture ? "?:":"") + expression + ")"; // String
};
return regexp;
});
},
'dijit/DropDownMenu':function(){
require({cache:{
'url:dijit/templates/Menu.html':"<table class=\"dijit dijitMenu dijitMenuPassive dijitReset dijitMenuTable\" role=\"menu\" tabIndex=\"${tabIndex}\"\n\t data-dojo-attach-event=\"onkeypress:_onKeyPress\" cellspacing=\"0\">\n\t<tbody class=\"dijitReset\" data-dojo-attach-point=\"containerNode\"></tbody>\n</table>\n"}});
define("dijit/DropDownMenu", [
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/text!./templates/Menu.html",
"./_OnDijitClickMixin",
"./_MenuBase"
], function(declare, event, keys, template, _OnDijitClickMixin, _MenuBase){
// module:
// dijit/DropDownMenu
return declare("dijit.DropDownMenu", [_MenuBase, _OnDijitClickMixin], {
// summary:
// A menu, without features for context menu (Meaning, drop down menu)
templateString: template,
baseClass: "dijitMenu",
postCreate: function(){
this.inherited(arguments);
var l = this.isLeftToRight();
this._openSubMenuKey = l ? keys.RIGHT_ARROW : keys.LEFT_ARROW;
this._closeSubMenuKey = l ? keys.LEFT_ARROW : keys.RIGHT_ARROW;
this.connectKeyNavHandlers([keys.UP_ARROW], [keys.DOWN_ARROW]);
},
_onKeyPress: function(/*Event*/ evt){
// summary:
// Handle keyboard based menu navigation.
// tags:
// protected
if(evt.ctrlKey || evt.altKey){ return; }
switch(evt.charOrCode){
case this._openSubMenuKey:
this._moveToPopup(evt);
event.stop(evt);
break;
case this._closeSubMenuKey:
if(this.parentMenu){
if(this.parentMenu._isMenuBar){
this.parentMenu.focusPrev();
}else{
this.onCancel(false);
}
}else{
event.stop(evt);
}
break;
}
}
});
});
},
'dijit/form/_FormMixin':function(){
define("dijit/form/_FormMixin", [
"dojo/_base/array", // array.every array.filter array.forEach array.indexOf array.map
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch lang.isArray
"dojo/on",
"dojo/window" // winUtils.scrollIntoView
], function(array, declare, kernel, lang, on, winUtils){
// module:
// dijit/form/_FormMixin
return declare("dijit.form._FormMixin", null, {
// summary:
// Mixin for containers of form widgets (i.e. widgets that represent a single value
// and can be children of a `<form>` node or `dijit/form/Form` widget)
// description:
// Can extract all the form widgets
// values and combine them into a single javascript object, or alternately
// take such an object and set the values for all the contained
// form widgets
/*=====
// value: Object
// Name/value hash for each child widget with a name and value.
// Child widgets without names are not part of the hash.
//
// If there are multiple child widgets w/the same name, value is an array,
// unless they are radio buttons in which case value is a scalar (since only
// one radio button can be checked at a time).
//
// If a child widget's name is a dot separated list (like a.b.c.d), it's a nested structure.
//
// Example:
// | { name: "John Smith", interests: ["sports", "movies"] }
=====*/
// state: [readonly] String
// Will be "Error" if one or more of the child widgets has an invalid value,
// "Incomplete" if not all of the required child widgets are filled in. Otherwise, "",
// which indicates that the form is ready to be submitted.
state: "",
// TODO:
// * Repeater
// * better handling for arrays. Often form elements have names with [] like
// * people[3].sex (for a list of people [{name: Bill, sex: M}, ...])
_getDescendantFormWidgets: function(/*dijit/_WidgetBase[]?*/ children){
// summary:
// Returns all form widget descendants, searching through non-form child widgets like BorderContainer
var res = [];
array.forEach(children || this.getChildren(), function(child){
if("value" in child){
res.push(child);
}else{
res = res.concat(this._getDescendantFormWidgets(child.getChildren()));
}
}, this);
return res;
},
reset: function(){
array.forEach(this._getDescendantFormWidgets(), function(widget){
if(widget.reset){
widget.reset();
}
});
},
validate: function(){
// summary:
// returns if the form is valid - same as isValid - but
// provides a few additional (ui-specific) features:
//
// 1. it will highlight any sub-widgets that are not valid
// 2. it will call focus() on the first invalid sub-widget
var didFocus = false;
return array.every(array.map(this._getDescendantFormWidgets(), function(widget){
// Need to set this so that "required" widgets get their
// state set.
widget._hasBeenBlurred = true;
var valid = widget.disabled || !widget.validate || widget.validate();
if(!valid && !didFocus){
// Set focus of the first non-valid widget
winUtils.scrollIntoView(widget.containerNode || widget.domNode);
widget.focus();
didFocus = true;
}
return valid;
}), function(item){ return item; });
},
setValues: function(val){
kernel.deprecated(this.declaredClass+"::setValues() is deprecated. Use set('value', val) instead.", "", "2.0");
return this.set('value', val);
},
_setValueAttr: function(/*Object*/ obj){
// summary:
// Fill in form values from according to an Object (in the format returned by get('value'))
// generate map from name --> [list of widgets with that name]
var map = { };
array.forEach(this._getDescendantFormWidgets(), function(widget){
if(!widget.name){ return; }
var entry = map[widget.name] || (map[widget.name] = [] );
entry.push(widget);
});
for(var name in map){
if(!map.hasOwnProperty(name)){
continue;
}
var widgets = map[name], // array of widgets w/this name
values = lang.getObject(name, false, obj); // list of values for those widgets
if(values === undefined){
continue;
}
if(!lang.isArray(values)){
values = [ values ];
}
if(typeof widgets[0].checked == 'boolean'){
// for checkbox/radio, values is a list of which widgets should be checked
array.forEach(widgets, function(w){
w.set('value', array.indexOf(values, w.value) != -1);
});
}else if(widgets[0].multiple){
// it takes an array (e.g. multi-select)
widgets[0].set('value', values);
}else{
// otherwise, values is a list of values to be assigned sequentially to each widget
array.forEach(widgets, function(w, i){
w.set('value', values[i]);
});
}
}
/***
* TODO: code for plain input boxes (this shouldn't run for inputs that are part of widgets)
array.forEach(this.containerNode.elements, function(element){
if(element.name == ''){return}; // like "continue"
var namePath = element.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var p=namePath[j - 1];
// repeater support block
var nameA=p.split("[");
if(nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
myObj=myObj[nameA[0]][nameIndex];
continue;
} // repeater support ends
if(typeof(myObj[p]) == "undefined"){
myObj=undefined;
break;
};
myObj=myObj[p];
}
if(typeof(myObj) == "undefined"){
return; // like "continue"
}
if(typeof(myObj[name]) == "undefined" && this.ignoreNullValues){
return; // like "continue"
}
// TODO: widget values (just call set('value', ...) on the widget)
// TODO: maybe should call dojo.getNodeProp() instead
switch(element.type){
case "checkbox":
element.checked = (name in myObj) &&
array.some(myObj[name], function(val){ return val == element.value; });
break;
case "radio":
element.checked = (name in myObj) && myObj[name] == element.value;
break;
case "select-multiple":
element.selectedIndex=-1;
array.forEach(element.options, function(option){
option.selected = array.some(myObj[name], function(val){ return option.value == val; });
});
break;
case "select-one":
element.selectedIndex="0";
array.forEach(element.options, function(option){
option.selected = option.value == myObj[name];
});
break;
case "hidden":
case "text":
case "textarea":
case "password":
element.value = myObj[name] || "";
break;
}
});
*/
// Note: no need to call this._set("value", ...) as the child updates will trigger onChange events
// which I am monitoring.
},
getValues: function(){
kernel.deprecated(this.declaredClass+"::getValues() is deprecated. Use get('value') instead.", "", "2.0");
return this.get('value');
},
_getValueAttr: function(){
// summary:
// Returns Object representing form values. See description of `value` for details.
// description:
// The value is updated into this.value every time a child has an onChange event,
// so in the common case this function could just return this.value. However,
// that wouldn't work when:
//
// 1. User presses return key to submit a form. That doesn't fire an onchange event,
// and even if it did it would come too late due to the defer(...) in _handleOnChange()
//
// 2. app for some reason calls this.get("value") while the user is typing into a
// form field. Not sure if that case needs to be supported or not.
// get widget values
var obj = { };
array.forEach(this._getDescendantFormWidgets(), function(widget){
var name = widget.name;
if(!name || widget.disabled){ return; }
// Single value widget (checkbox, radio, or plain <input> type widget)
var value = widget.get('value');
// Store widget's value(s) as a scalar, except for checkboxes which are automatically arrays
if(typeof widget.checked == 'boolean'){
if(/Radio/.test(widget.declaredClass)){
// radio button
if(value !== false){
lang.setObject(name, value, obj);
}else{
// give radio widgets a default of null
value = lang.getObject(name, false, obj);
if(value === undefined){
lang.setObject(name, null, obj);
}
}
}else{
// checkbox/toggle button
var ary=lang.getObject(name, false, obj);
if(!ary){
ary=[];
lang.setObject(name, ary, obj);
}
if(value !== false){
ary.push(value);
}
}
}else{
var prev=lang.getObject(name, false, obj);
if(typeof prev != "undefined"){
if(lang.isArray(prev)){
prev.push(value);
}else{
lang.setObject(name, [prev, value], obj);
}
}else{
// unique name
lang.setObject(name, value, obj);
}
}
});
/***
* code for plain input boxes (see also domForm.formToObject, can we use that instead of this code?
* but it doesn't understand [] notation, presumably)
var obj = { };
array.forEach(this.containerNode.elements, function(elm){
if(!elm.name) {
return; // like "continue"
}
var namePath = elm.name.split(".");
var myObj=obj;
var name=namePath[namePath.length-1];
for(var j=1,len2=namePath.length;j<len2;++j){
var nameIndex = null;
var p=namePath[j - 1];
var nameA=p.split("[");
if(nameA.length > 1){
if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]]=[ ];
} // if
nameIndex=parseInt(nameA[1]);
if(typeof(myObj[nameA[0]][nameIndex]) == "undefined"){
myObj[nameA[0]][nameIndex] = { };
}
}else if(typeof(myObj[nameA[0]]) == "undefined"){
myObj[nameA[0]] = { }
} // if
if(nameA.length == 1){
myObj=myObj[nameA[0]];
}else{
myObj=myObj[nameA[0]][nameIndex];
} // if
} // for
if((elm.type != "select-multiple" && elm.type != "checkbox" && elm.type != "radio") || (elm.type == "radio" && elm.checked)){
if(name == name.split("[")[0]){
myObj[name]=elm.value;
}else{
// can not set value when there is no name
}
}else if(elm.type == "checkbox" && elm.checked){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
myObj[name].push(elm.value);
}else if(elm.type == "select-multiple"){
if(typeof(myObj[name]) == 'undefined'){
myObj[name]=[ ];
}
for(var jdx=0,len3=elm.options.length; jdx<len3; ++jdx){
if(elm.options[jdx].selected){
myObj[name].push(elm.options[jdx].value);
}
}
} // if
name=undefined;
}); // forEach
***/
return obj;
},
isValid: function(){
// summary:
// Returns true if all of the widgets are valid.
// Deprecated, will be removed in 2.0. Use get("state") instead.
return this.state == "";
},
onValidStateChange: function(/*Boolean*/ /*===== isValid =====*/){
// summary:
// Stub function to connect to if you want to do something
// (like disable/enable a submit button) when the valid
// state changes on the form as a whole.
//
// Deprecated. Will be removed in 2.0. Use watch("state", ...) instead.
},
_getState: function(){
// summary:
// Compute what this.state should be based on state of children
var states = array.map(this._descendants, function(w){
return w.get("state") || "";
});
return array.indexOf(states, "Error") >= 0 ? "Error" :
array.indexOf(states, "Incomplete") >= 0 ? "Incomplete" : "";
},
disconnectChildren: function(){
// summary:
// Deprecated method. Applications no longer need to call this. Remove for 2.0.
},
connectChildren: function(/*Boolean*/ inStartup){
// summary:
// You can call this function directly, ex. in the event that you
// programmatically add a widget to the form *after* the form has been
// initialized.
// TODO: rename for 2.0
this._descendants = this._getDescendantFormWidgets();
// To get notifications from children they need to be started. Children didn't used to need to be started,
// so for back-compat, start them here
array.forEach(this._descendants, function(child){
if(!child._started){ child.startup(); }
});
if(!inStartup){
this._onChildChange();
}
},
_onChildChange: function(/*String*/ attr){
// summary:
// Called when child's value or disabled state changes
// The unit tests expect state update to be synchronous, so update it immediately.
if(!attr || attr == "state" || attr == "disabled"){
this._set("state", this._getState());
}
// Use defer() to collapse value changes in multiple children into a single
// update to my value. Multiple updates will occur on:
// 1. Form.set()
// 2. Form.reset()
// 3. user selecting a radio button (which will de-select another radio button,
// causing two onChange events)
if(!attr || attr == "value" || attr == "disabled" || attr == "checked"){
if(this._onChangeDelayTimer){
this._onChangeDelayTimer.remove();
}
this._onChangeDelayTimer = this.defer(function(){
delete this._onChangeDelayTimer;
this._set("value", this.get("value"));
}, 10);
}
},
startup: function(){
this.inherited(arguments);
// Set initial this.value and this.state. Don't emit watch() notifications.
this._descendants = this._getDescendantFormWidgets();
this.value = this.get("value");
this.state = this._getState();
// Initialize value and valid/invalid state tracking.
var self = this;
this.own(
on(
this.containerNode,
"attrmodified-state, attrmodified-disabled, attrmodified-value, attrmodified-checked",
function(evt){
if(evt.target == self.domNode){
return; // ignore events that I fire on myself because my children changed
}
self._onChildChange(evt.type.replace("attrmodified-", ""));
}
)
);
// Make state change call onValidStateChange(), will be removed in 2.0
this.watch("state", function(attr, oldVal, newVal){ this.onValidStateChange(newVal == ""); });
},
destroy: function(){
this.inherited(arguments);
}
});
});
},
'dojo/data/util/simpleFetch':function(){
define("dojo/data/util/simpleFetch", ["../../_base/lang", "../../_base/kernel", "./sorter"],
function(lang, kernel, sorter){
// module:
// dojo/data/util/simpleFetch
// summary:
// The simpleFetch mixin is designed to serve as a set of function(s) that can
// be mixed into other datastore implementations to accelerate their development.
var simpleFetch = {};
lang.setObject("dojo.data.util.simpleFetch", simpleFetch);
simpleFetch.errorHandler = function(/*Object*/ errorData, /*Object*/ requestObject){
// summary:
// The error handler when there is an error fetching items. This function should not be called
// directly and is used by simpleFetch.fetch().
if(requestObject.onError){
var scope = requestObject.scope || kernel.global;
requestObject.onError.call(scope, errorData, requestObject);
}
};
simpleFetch.fetchHandler = function(/*Array*/ items, /*Object*/ requestObject){
// summary:
// The handler when items are sucessfully fetched. This function should not be called directly
// and is used by simpleFetch.fetch().
var oldAbortFunction = requestObject.abort || null,
aborted = false,
startIndex = requestObject.start?requestObject.start: 0,
endIndex = (requestObject.count && (requestObject.count !== Infinity))?(startIndex + requestObject.count):items.length;
requestObject.abort = function(){
aborted = true;
if(oldAbortFunction){
oldAbortFunction.call(requestObject);
}
};
var scope = requestObject.scope || kernel.global;
if(!requestObject.store){
requestObject.store = this;
}
if(requestObject.onBegin){
requestObject.onBegin.call(scope, items.length, requestObject);
}
if(requestObject.sort){
items.sort(sorter.createSortFunction(requestObject.sort, this));
}
if(requestObject.onItem){
for(var i = startIndex; (i < items.length) && (i < endIndex); ++i){
var item = items[i];
if(!aborted){
requestObject.onItem.call(scope, item, requestObject);
}
}
}
if(requestObject.onComplete && !aborted){
var subset = null;
if(!requestObject.onItem){
subset = items.slice(startIndex, endIndex);
}
requestObject.onComplete.call(scope, subset, requestObject);
}
};
simpleFetch.fetch = function(/* Object? */ request){
// summary:
// The simpleFetch mixin is designed to serve as a set of function(s) that can
// be mixed into other datastore implementations to accelerate their development.
// description:
// The simpleFetch mixin should work well for any datastore that can respond to a _fetchItems()
// call by returning an array of all the found items that matched the query. The simpleFetch mixin
// is not designed to work for datastores that respond to a fetch() call by incrementally
// loading items, or sequentially loading partial batches of the result
// set. For datastores that mixin simpleFetch, simpleFetch
// implements a fetch method that automatically handles eight of the fetch()
// arguments -- onBegin, onItem, onComplete, onError, start, count, sort and scope
// The class mixing in simpleFetch should not implement fetch(),
// but should instead implement a _fetchItems() method. The _fetchItems()
// method takes three arguments, the keywordArgs object that was passed
// to fetch(), a callback function to be called when the result array is
// available, and an error callback to be called if something goes wrong.
// The _fetchItems() method should ignore any keywordArgs parameters for
// start, count, onBegin, onItem, onComplete, onError, sort, and scope.
// The _fetchItems() method needs to correctly handle any other keywordArgs
// parameters, including the query parameter and any optional parameters
// (such as includeChildren). The _fetchItems() method should create an array of
// result items and pass it to the fetchHandler along with the original request object --
// or, the _fetchItems() method may, if it wants to, create an new request object
// with other specifics about the request that are specific to the datastore and pass
// that as the request object to the handler.
//
// For more information on this specific function, see dojo/data/api/Read.fetch()
//
// request:
// The keywordArgs parameter may either be an instance of
// conforming to dojo/data/api/Request or may be a simple anonymous object
// that may contain any of the following:
// | {
// | query: query-object or query-string,
// | queryOptions: object,
// | onBegin: Function,
// | onItem: Function,
// | onComplete: Function,
// | onError: Function,
// | scope: object,
// | start: int
// | count: int
// | sort: array
// | }
// All implementations should accept keywordArgs objects with any of
// the 9 standard properties: query, onBegin, onItem, onComplete, onError
// scope, sort, start, and count. Some implementations may accept additional
// properties in the keywordArgs object as valid parameters, such as
// {includeOutliers:true}.
//
// ####The *query* parameter
//
// The query may be optional in some data store implementations.
// The dojo/data/api/Read API does not specify the syntax or semantics
// of the query itself -- each different data store implementation
// may have its own notion of what a query should look like.
// However, as of dojo 0.9, 1.0, and 1.1, all the provided datastores in dojo.data
// and dojox.data support an object structure query, where the object is a set of
// name/value parameters such as { attrFoo: valueBar, attrFoo1: valueBar1}. Most of the
// dijit widgets, such as ComboBox assume this to be the case when working with a datastore
// when they dynamically update the query. Therefore, for maximum compatibility with dijit
// widgets the recommended query parameter is a key/value object. That does not mean that the
// the datastore may not take alternative query forms, such as a simple string, a Date, a number,
// or a mix of such. Ultimately, The dojo/data/api/Read API is agnostic about what the query
// format.
//
// Further note: In general for query objects that accept strings as attribute
// value matches, the store should also support basic filtering capability, such as *
// (match any character) and ? (match single character). An example query that is a query object
// would be like: { attrFoo: "value*"}. Which generally means match all items where they have
// an attribute named attrFoo, with a value that starts with 'value'.
//
// ####The *queryOptions* parameter
//
// The queryOptions parameter is an optional parameter used to specify options that may modify
// the query in some fashion, such as doing a case insensitive search, or doing a deep search
// where all items in a hierarchical representation of data are scanned instead of just the root
// items. It currently defines two options that all datastores should attempt to honor if possible:
// | {
// | ignoreCase: boolean, // Whether or not the query should match case sensitively or not. Default behaviour is false.
// | deep: boolean // Whether or not a fetch should do a deep search of items and all child
// | // items instead of just root-level items in a datastore. Default is false.
// | }
//
// ####The *onBegin* parameter.
//
// function(size, request);
// If an onBegin callback function is provided, the callback function
// will be called just once, before the first onItem callback is called.
// The onBegin callback function will be passed two arguments, the
// the total number of items identified and the Request object. If the total number is
// unknown, then size will be -1. Note that size is not necessarily the size of the
// collection of items returned from the query, as the request may have specified to return only a
// subset of the total set of items through the use of the start and count parameters.
//
// ####The *onItem* parameter.
//
// function(item, request);
//
// If an onItem callback function is provided, the callback function
// will be called as each item in the result is received. The callback
// function will be passed two arguments: the item itself, and the
// Request object.
//
// ####The *onComplete* parameter.
//
// function(items, request);
//
// If an onComplete callback function is provided, the callback function
// will be called just once, after the last onItem callback is called.
// Note that if the onItem callback is not present, then onComplete will be passed
// an array containing all items which matched the query and the request object.
// If the onItem callback is present, then onComplete is called as:
// onComplete(null, request).
//
// ####The *onError* parameter.
//
// function(errorData, request);
//
// If an onError callback function is provided, the callback function
// will be called if there is any sort of error while attempting to
// execute the query.
// The onError callback function will be passed two arguments:
// an Error object and the Request object.
//
// ####The *scope* parameter.
//
// If a scope object is provided, all of the callback functions (onItem,
// onComplete, onError, etc) will be invoked in the context of the scope
// object. In the body of the callback function, the value of the "this"
// keyword will be the scope object. If no scope object is provided,
// the callback functions will be called in the context of dojo.global().
// For example, onItem.call(scope, item, request) vs.
// onItem.call(dojo.global(), item, request)
//
// ####The *start* parameter.
//
// If a start parameter is specified, this is a indication to the datastore to
// only start returning items once the start number of items have been located and
// skipped. When this parameter is paired with 'count', the store should be able
// to page across queries with millions of hits by only returning subsets of the
// hits for each query
//
// ####The *count* parameter.
//
// If a count parameter is specified, this is a indication to the datastore to
// only return up to that many items. This allows a fetch call that may have
// millions of item matches to be paired down to something reasonable.
//
// ####The *sort* parameter.
//
// If a sort parameter is specified, this is a indication to the datastore to
// sort the items in some manner before returning the items. The array is an array of
// javascript objects that must conform to the following format to be applied to the
// fetching of items:
// | {
// | attribute: attribute || attribute-name-string,
// | descending: true|false; // Optional. Default is false.
// | }
// Note that when comparing attributes, if an item contains no value for the attribute
// (undefined), then it the default ascending sort logic should push it to the bottom
// of the list. In the descending order case, it such items should appear at the top of the list.
request = request || {};
if(!request.store){
request.store = this;
}
this._fetchItems(request, lang.hitch(this, "fetchHandler"), lang.hitch(this, "errorHandler"));
return request; // Object
};
return simpleFetch;
});
},
'dijit/Menu':function(){
define("dijit/Menu", [
"require",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/dom", // dom.byId dom.isDescendant
"dojo/dom-attr", // domAttr.get domAttr.set domAttr.has domAttr.remove
"dojo/dom-geometry", // domStyle.getComputedStyle domGeometry.position
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/keys", // keys.F10
"dojo/_base/lang", // lang.hitch
"dojo/on",
"dojo/sniff", // has("ie"), has("quirks")
"dojo/_base/window", // win.body win.doc.documentElement win.doc.frames
"dojo/window", // winUtils.get
"./popup",
"./DropDownMenu",
"dojo/ready"
], function(require, array, declare, event, dom, domAttr, domGeometry, domStyle, keys, lang, on,
has, win, winUtils, pm, DropDownMenu, ready){
// module:
// dijit/Menu
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/MenuItem", "dijit/PopupMenuItem", "dijit/CheckedMenuItem", "dijit/MenuSeparator"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.Menu", DropDownMenu, {
// summary:
// A context menu you can assign to multiple elements
constructor: function(/*===== params, srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified:
//
// - use srcNodeRef.innerHTML as my contents
// - replace srcNodeRef with my generated DOM tree
this._bindings = [];
},
// targetNodeIds: [const] String[]
// Array of dom node ids of nodes to attach to.
// Fill this with nodeIds upon widget creation and it becomes context menu for those nodes.
targetNodeIds: [],
// selector: String?
// CSS expression to apply this Menu to descendants of targetNodeIds, rather than to
// the nodes specified by targetNodeIds themselves. Useful for applying a Menu to
// a range of rows in a table, tree, etc.
//
// The application must require() an appropriate level of dojo/query to handle the selector.
selector: "",
// TODO: in 2.0 remove support for multiple targetNodeIds. selector gives the same effect.
// So, change targetNodeIds to a targetNodeId: "", remove bindDomNode()/unBindDomNode(), etc.
/*=====
// currentTarget: [readonly] DOMNode
// For context menus, set to the current node that the Menu is being displayed for.
// Useful so that the menu actions can be tailored according to the node
currentTarget: null,
=====*/
// contextMenuForWindow: [const] Boolean
// If true, right clicking anywhere on the window will cause this context menu to open.
// If false, must specify targetNodeIds.
contextMenuForWindow: false,
// leftClickToOpen: [const] Boolean
// If true, menu will open on left click instead of right click, similar to a file menu.
leftClickToOpen: false,
// refocus: Boolean
// When this menu closes, re-focus the element which had focus before it was opened.
refocus: true,
postCreate: function(){
if(this.contextMenuForWindow){
this.bindDomNode(this.ownerDocumentBody);
}else{
// TODO: should have _setTargetNodeIds() method to handle initialization and a possible
// later set('targetNodeIds', ...) call. There's also a problem that targetNodeIds[]
// gets stale after calls to bindDomNode()/unBindDomNode() as it still is just the original list (see #9610)
array.forEach(this.targetNodeIds, this.bindDomNode, this);
}
this.inherited(arguments);
},
// thanks burstlib!
_iframeContentWindow: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns the window reference of the passed iframe
// tags:
// private
return winUtils.get(this._iframeContentDocument(iframe_el)) ||
// Moz. TODO: is this available when defaultView isn't?
this._iframeContentDocument(iframe_el)['__parent__'] ||
(iframe_el.name && win.doc.frames[iframe_el.name]) || null; // Window
},
_iframeContentDocument: function(/* HTMLIFrameElement */iframe_el){
// summary:
// Returns a reference to the document object inside iframe_el
// tags:
// protected
return iframe_el.contentDocument // W3
|| (iframe_el.contentWindow && iframe_el.contentWindow.document) // IE
|| (iframe_el.name && win.doc.frames[iframe_el.name] && win.doc.frames[iframe_el.name].document)
|| null; // HTMLDocument
},
bindDomNode: function(/*String|DomNode*/ node){
// summary:
// Attach menu to given node
node = dom.byId(node, this.ownerDocument);
var cn; // Connect node
// Support context menus on iframes. Rather than binding to the iframe itself we need
// to bind to the <body> node inside the iframe.
if(node.tagName.toLowerCase() == "iframe"){
var iframe = node,
window = this._iframeContentWindow(iframe);
cn = win.body(window.document);
}else{
// To capture these events at the top level, attach to <html>, not <body>.
// Otherwise right-click context menu just doesn't work.
cn = (node == win.body(this.ownerDocument) ? this.ownerDocument.documentElement : node);
}
// "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
var binding = {
node: node,
iframe: iframe
};
// Save info about binding in _bindings[], and make node itself record index(+1) into
// _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
// start with a number, which fails on FF/safari.
domAttr.set(node, "_dijitMenu" + this.id, this._bindings.push(binding));
// Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
// loading yet, in which case we need to wait for the onload event first, and then connect
// On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
// we need to monitor keyboard events in addition to the oncontextmenu event.
var doConnects = lang.hitch(this, function(cn){
var selector = this.selector,
delegatedEvent = selector ?
function(eventType){ return on.selector(selector, eventType); } :
function(eventType){ return eventType; },
self = this;
return [
// TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
// rather than shift-F10?
on(cn, delegatedEvent(this.leftClickToOpen ? "click" : "contextmenu"), function(evt){
// Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
event.stop(evt);
self._scheduleOpen(this, iframe, {x: evt.pageX, y: evt.pageY});
}),
on(cn, delegatedEvent("keydown"), function(evt){
if(evt.shiftKey && evt.keyCode == keys.F10){
event.stop(evt);
self._scheduleOpen(this, iframe); // no coords - open near target node
}
})
];
});
binding.connects = cn ? doConnects(cn) : [];
if(iframe){
// Setup handler to [re]bind to the iframe when the contents are initially loaded,
// and every time the contents change.
// Need to do this b/c we are actually binding to the iframe's <body> node.
// Note: can't use connect.connect(), see #9609.
binding.onloadHandler = lang.hitch(this, function(){
// want to remove old connections, but IE throws exceptions when trying to
// access the <body> node because it's already gone, or at least in a state of limbo
var window = this._iframeContentWindow(iframe);
cn = win.body(window.document)
binding.connects = doConnects(cn);
});
if(iframe.addEventListener){
iframe.addEventListener("load", binding.onloadHandler, false);
}else{
iframe.attachEvent("onload", binding.onloadHandler);
}
}
},
unBindDomNode: function(/*String|DomNode*/ nodeName){
// summary:
// Detach menu from given node
var node;
try{
node = dom.byId(nodeName, this.ownerDocument);
}catch(e){
// On IE the dom.byId() call will get an exception if the attach point was
// the <body> node of an <iframe> that has since been reloaded (and thus the
// <body> node is in a limbo state of destruction.
return;
}
// node["_dijitMenu" + this.id] contains index(+1) into my _bindings[] array
var attrName = "_dijitMenu" + this.id;
if(node && domAttr.has(node, attrName)){
var bid = domAttr.get(node, attrName)-1, b = this._bindings[bid], h;
while((h = b.connects.pop())){
h.remove();
}
// Remove listener for iframe onload events
var iframe = b.iframe;
if(iframe){
if(iframe.removeEventListener){
iframe.removeEventListener("load", b.onloadHandler, false);
}else{
iframe.detachEvent("onload", b.onloadHandler);
}
}
domAttr.remove(node, attrName);
delete this._bindings[bid];
}
},
_scheduleOpen: function(/*DomNode?*/ target, /*DomNode?*/ iframe, /*Object?*/ coords){
// summary:
// Set timer to display myself. Using a timer rather than displaying immediately solves
// two problems:
//
// 1. IE: without the delay, focus work in "open" causes the system
// context menu to appear in spite of stopEvent.
//
// 2. Avoid double-shows on linux, where shift-F10 generates an oncontextmenu event
// even after a event.stop(e). (Shift-F10 on windows doesn't generate the
// oncontextmenu event.)
if(!this._openTimer){
this._openTimer = this.defer(function(){
delete this._openTimer;
this._openMyself({
target: target,
iframe: iframe,
coords: coords
});
}, 1);
}
},
_openMyself: function(args){
// summary:
// Internal function for opening myself when the user does a right-click or something similar.
// args:
// This is an Object containing:
//
// - target: The node that is being clicked
// - iframe: If an `<iframe>` is being clicked, iframe points to that iframe
// - coords: Put menu at specified x/y position in viewport, or if iframe is
// specified, then relative to iframe.
//
// _openMyself() formerly took the event object, and since various code references
// evt.target (after connecting to _openMyself()), using an Object for parameters
// (so that old code still works).
var target = args.target,
iframe = args.iframe,
coords = args.coords;
// To be used by MenuItem event handlers to tell which node the menu was opened on
this.currentTarget = target;
// Get coordinates to open menu, either at specified (mouse) position or (if triggered via keyboard)
// then near the node the menu is assigned to.
if(coords){
if(iframe){
// Specified coordinates are on <body> node of an <iframe>, convert to match main document
var ifc = domGeometry.position(iframe, true),
window = this._iframeContentWindow(iframe),
scroll = domGeometry.docScroll(window.document);
var cs = domStyle.getComputedStyle(iframe),
tp = domStyle.toPixelValue,
left = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingLeft)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderLeftWidth) : 0),
top = (has("ie") && has("quirks") ? 0 : tp(iframe, cs.paddingTop)) + (has("ie") && has("quirks") ? tp(iframe, cs.borderTopWidth) : 0);
coords.x += ifc.x + left - scroll.x;
coords.y += ifc.y + top - scroll.y;
}
}else{
coords = domGeometry.position(target, true);
coords.x += 10;
coords.y += 10;
}
var self=this;
var prevFocusNode = this._focusManager.get("prevNode");
var curFocusNode = this._focusManager.get("curNode");
var savedFocusNode = !curFocusNode || (dom.isDescendant(curFocusNode, this.domNode)) ? prevFocusNode : curFocusNode;
function closeAndRestoreFocus(){
// user has clicked on a menu or popup
if(self.refocus && savedFocusNode){
savedFocusNode.focus();
}
pm.close(self);
}
pm.open({
popup: this,
x: coords.x,
y: coords.y,
onExecute: closeAndRestoreFocus,
onCancel: closeAndRestoreFocus,
orient: this.isLeftToRight() ? 'L' : 'R'
});
this.focus();
this._onBlur = function(){
this.inherited('_onBlur', arguments);
// Usually the parent closes the child widget but if this is a context
// menu then there is no parent
pm.close(this);
// don't try to restore focus; user has clicked another part of the screen
// and set focus there
};
},
destroy: function(){
array.forEach(this._bindings, function(b){ if(b){ this.unBindDomNode(b.node); } }, this);
this.inherited(arguments);
}
});
});
},
'dijit/form/_CheckBoxMixin':function(){
define("dijit/form/_CheckBoxMixin", [
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/_base/event" // event.stop
], function(declare, domAttr, event){
// module:
// dijit/form/_CheckBoxMixin
return declare("dijit.form._CheckBoxMixin", null, {
// summary:
// Mixin to provide widget functionality corresponding to an HTML checkbox
//
// description:
// User interacts with real html inputs.
// On onclick (which occurs by mouse click, space-bar, or
// using the arrow keys to switch the selected radio button),
// we update the state of the checkbox/radio.
//
// type: [private] String
// type attribute on `<input>` node.
// Overrides `dijit/form/Button.type`. Users should not change this value.
type: "checkbox",
// value: String
// As an initialization parameter, equivalent to value field on normal checkbox
// (if checked, the value is passed as the value when form is submitted).
value: "on",
// 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,
// aria-pressed for toggle buttons, and aria-checked for checkboxes
_aria_attr: "aria-checked",
_setReadOnlyAttr: function(/*Boolean*/ value){
this._set("readOnly", value);
domAttr.set(this.focusNode, 'readOnly', value);
},
// Override dijit/form/Button._setLabelAttr() since we don't even have a containerNode.
// Normally users won't try to set label, except when CheckBox or RadioButton is the child of a dojox/layout/TabContainer
_setLabelAttr: undefined,
_getSubmitValue: function(/*String*/ value){
return !value && value !== 0 ? "on" : value;
},
_setValueAttr: function(newValue){
newValue = this._getSubmitValue(newValue); // "on" to match browser native behavior when value unspecified
this._set("value", newValue);
domAttr.set(this.focusNode, "value", newValue);
},
reset: function(){
this.inherited(arguments);
// Handle unlikely event that the <input type=checkbox> value attribute has changed
this._set("value", this.params.value || "on");
domAttr.set(this.focusNode, 'value', this.value);
},
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions - need to check
// readOnly, since button no longer does that check.
if(this.readOnly){
event.stop(e);
return false;
}
return this.inherited(arguments);
}
});
});
},
'dijit/layout/ContentPane':function(){
define("dijit/layout/ContentPane", [
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.mixin lang.delegate lang.hitch lang.isFunction lang.isObject
"../_Widget",
"../_Container",
"./_ContentPaneResizeMixin",
"dojo/string", // string.substitute
"dojo/html", // html._ContentSetter
"dojo/i18n!../nls/loading",
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.attr
"dojo/dom-construct", // empty()
"dojo/_base/xhr", // xhr.get
"dojo/i18n", // i18n.getLocalization
"dojo/when"
], function(kernel, lang, _Widget, _Container, _ContentPaneResizeMixin, string, html, nlsLoading,
array, declare, Deferred, dom, domAttr, domConstruct, xhr, i18n, when){
// module:
// dijit/layout/ContentPane
return declare("dijit.layout.ContentPane", [_Widget, _Container, _ContentPaneResizeMixin], {
// summary:
// A widget containing an HTML fragment, specified inline
// or by uri. Fragment may include widgets.
//
// description:
// This widget embeds a document fragment in the page, specified
// either by uri, javascript generated markup or DOM reference.
// Any widgets within this content are instantiated and managed,
// but laid out according to the HTML structure. Unlike IFRAME,
// ContentPane embeds a document fragment as would be found
// inside the BODY tag of a full HTML document. It should not
// contain the HTML, HEAD, or BODY tags.
// For more advanced functionality with scripts and
// stylesheets, see dojox/layout/ContentPane. This widget may be
// used stand alone or as a base class for other widgets.
// ContentPane is useful as a child of other layout containers
// such as BorderContainer or TabContainer, but note that those
// widgets can contain any widget as a child.
//
// example:
// Some quick samples:
// To change the innerHTML:
// | cp.set('content', '<b>new content</b>')`
// Or you can send it a NodeList:
// | cp.set('content', dojo.query('div [class=selected]', userSelection))
// To do an ajax update:
// | cp.set('href', url)
// href: String
// The href of the content that displays now.
// Set this at construction if you want to load data externally when the
// pane is shown. (Set preload=true to load it immediately.)
// Changing href after creation doesn't have any effect; Use set('href', ...);
href: "",
// content: String|DomNode|NodeList|dijit/_Widget
// The innerHTML of the ContentPane.
// Note that the initialization parameter / argument to set("content", ...)
// can be a String, DomNode, Nodelist, or _Widget.
content: "",
// extractContent: Boolean
// Extract visible content from inside of `<body> .... </body>`.
// I.e., strip `<html>` and `<head>` (and it's contents) from the href
extractContent: false,
// parseOnLoad: Boolean
// Parse content and create the widgets, if any.
parseOnLoad: true,
// parserScope: String
// Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
// will search for data-dojo-type (or dojoType). For backwards compatibility
// reasons defaults to dojo._scopeName (which is "dojo" except when
// multi-version support is used, when it will be something like dojo16, dojo20, etc.)
parserScope: kernel._scopeName,
// preventCache: Boolean
// Prevent caching of data from href's by appending a timestamp to the href.
preventCache: false,
// preload: Boolean
// Force load of data on initialization even if pane is hidden.
preload: false,
// refreshOnShow: Boolean
// Refresh (re-download) content when pane goes from hidden to shown
refreshOnShow: false,
// loadingMessage: String
// Message that shows while downloading
loadingMessage: "<span class='dijitContentPaneLoading'><span class='dijitInline dijitIconLoading'></span>${loadingState}</span>",
// errorMessage: String
// Message that shows if an error occurs
errorMessage: "<span class='dijitContentPaneError'><span class='dijitInline dijitIconError'></span>${errorState}</span>",
// isLoaded: [readonly] Boolean
// True if the ContentPane has data in it, either specified
// during initialization (via href or inline content), or set
// via set('content', ...) / set('href', ...)
//
// False if it doesn't have any content, or if ContentPane is
// still in the process of downloading href.
isLoaded: false,
baseClass: "dijitContentPane",
/*======
// ioMethod: dojo/_base/xhr.get|dojo._base/xhr.post
// Function that should grab the content specified via href.
ioMethod: dojo.xhrGet,
======*/
// ioArgs: Object
// Parameters to pass to xhrGet() request, for example:
// | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="href: './bar', ioArgs: {timeout: 500}">
ioArgs: {},
// onLoadDeferred: [readonly] dojo.Deferred
// This is the `dojo.Deferred` returned by set('href', ...) and refresh().
// Calling onLoadDeferred.then() registers your
// callback to be called only once, when the prior set('href', ...) call or
// the initial href parameter to the constructor finishes loading.
//
// This is different than an onLoad() handler which gets called any time any href
// or content is loaded.
onLoadDeferred: null,
// Cancel _WidgetBase's _setTitleAttr because we don't want the title attribute (used to specify
// tab labels) to be copied to ContentPane.domNode... otherwise a tooltip shows up over the
// entire pane.
_setTitleAttr: null,
// Flag to parser that I'll parse my contents, so it shouldn't.
stopParser: true,
// template: [private] Boolean
// Flag from the parser that this ContentPane is inside a template
// so the contents are pre-parsed.
// TODO: this declaration can be commented out in 2.0
template: false,
create: function(params, srcNodeRef){
// Convert a srcNodeRef argument into a content parameter, so that the original contents are
// processed in the same way as contents set via set("content", ...), calling the parser etc.
// Avoid modifying original params object since that breaks NodeList instantiation, see #11906.
if((!params || !params.template) && srcNodeRef && !("href" in params) && !("content" in params)){
srcNodeRef = dom.byId(srcNodeRef);
var df = srcNodeRef.ownerDocument.createDocumentFragment();
while(srcNodeRef.firstChild){
df.appendChild(srcNodeRef.firstChild);
}
params = lang.delegate(params, {content: df});
}
this.inherited(arguments, [params, srcNodeRef]);
},
postMixInProperties: function(){
this.inherited(arguments);
var messages = i18n.getLocalization("dijit", "loading", this.lang);
this.loadingMessage = string.substitute(this.loadingMessage, messages);
this.errorMessage = string.substitute(this.errorMessage, messages);
},
buildRendering: function(){
this.inherited(arguments);
// Since we have no template we need to set this.containerNode ourselves, to make getChildren() work.
// For subclasses of ContentPane that do have a template, does nothing.
if(!this.containerNode){
this.containerNode = this.domNode;
}
// remove the title attribute so it doesn't show up when hovering
// over a node (TODO: remove in 2.0, no longer needed after #11490)
this.domNode.title = "";
if(!domAttr.get(this.domNode,"role")){
this.domNode.setAttribute("role", "group");
}
},
startup: function(){
// summary:
// Call startup() on all children including non _Widget ones like dojo/dnd/Source objects
// This starts all the widgets
this.inherited(arguments);
// And this catches stuff like dojo/dnd/Source
if(this._contentSetter){
array.forEach(this._contentSetter.parseResults, function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
}, this);
}
},
_startChildren: function(){
// summary:
// Called when content is loaded. Calls startup on each child widget. Similar to ContentPane.startup()
// itself, but avoids marking the ContentPane itself as "restarted" (see #15581).
// This starts all the widgets
array.forEach(this.getChildren(), function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
});
// And this catches stuff like dojo/dnd/Source
if(this._contentSetter){
array.forEach(this._contentSetter.parseResults, function(obj){
if(!obj._started && !obj._destroyed && lang.isFunction(obj.startup)){
obj.startup();
obj._started = true;
}
}, this);
}
},
setHref: function(/*String|Uri*/ href){
// summary:
// Deprecated. Use set('href', ...) instead.
kernel.deprecated("dijit.layout.ContentPane.setHref() is deprecated. Use set('href', ...) instead.", "", "2.0");
return this.set("href", href);
},
_setHrefAttr: function(/*String|Uri*/ href){
// summary:
// Hook so set("href", ...) works.
// description:
// Reset the (external defined) content of this pane and replace with new url
// Note: It delays the download until widget is shown if preload is false.
// href:
// url to the page you want to get, must be within the same domain as your mainpage
// Cancel any in-flight requests (a set('href', ...) will cancel any in-flight set('href', ...))
this.cancel();
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
this._set("href", href);
// _setHrefAttr() is called during creation and by the user, after creation.
// Assuming preload == false, only in the second case do we actually load the URL;
// otherwise it's done in startup(), and only if this widget is shown.
if(this.preload || (this._created && this._isShown())){
this._load();
}else{
// Set flag to indicate that href needs to be loaded the next time the
// ContentPane is made visible
this._hrefChanged = true;
}
return this.onLoadDeferred; // Deferred
},
setContent: function(/*String|DomNode|Nodelist*/data){
// summary:
// Deprecated. Use set('content', ...) instead.
kernel.deprecated("dijit.layout.ContentPane.setContent() is deprecated. Use set('content', ...) instead.", "", "2.0");
this.set("content", data);
},
_setContentAttr: function(/*String|DomNode|Nodelist*/data){
// summary:
// Hook to make set("content", ...) work.
// Replaces old content with data content, include style classes from old content
// data:
// the new Content may be String, DomNode or NodeList
//
// if data is a NodeList (or an array of nodes) nodes are copied
// so you can import nodes from another document implicitly
// clear href so we can't run refresh and clear content
// refresh should only work if we downloaded the content
this._set("href", "");
// Cancel any in-flight requests (a set('content', ...) will cancel any in-flight set('href', ...))
this.cancel();
// Even though user is just setting content directly, still need to define an onLoadDeferred
// because the _onLoadHandler() handler is still getting called from setContent()
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
if(this._created){
// For back-compat reasons, call onLoad() for set('content', ...)
// calls but not for content specified in srcNodeRef (ie: <div data-dojo-type=ContentPane>...</div>)
// or as initialization parameter (ie: new ContentPane({content: ...})
this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
}
this._setContent(data || "");
this._isDownloaded = false; // mark that content is from a set('content') not a set('href')
return this.onLoadDeferred; // Deferred
},
_getContentAttr: function(){
// summary:
// Hook to make get("content") work
return this.containerNode.innerHTML;
},
cancel: function(){
// summary:
// Cancels an in-flight download of content
if(this._xhrDfd && (this._xhrDfd.fired == -1)){
this._xhrDfd.cancel();
}
delete this._xhrDfd; // garbage collect
this.onLoadDeferred = null;
},
destroy: function(){
this.cancel();
this.inherited(arguments);
},
destroyRecursive: function(/*Boolean*/ preserveDom){
// summary:
// Destroy the ContentPane and its contents
// if we have multiple controllers destroying us, bail after the first
if(this._beingDestroyed){
return;
}
this.inherited(arguments);
},
_onShow: function(){
// summary:
// Called when the ContentPane is made visible
// description:
// For a plain ContentPane, this is called on initialization, from startup().
// If the ContentPane is a hidden pane of a TabContainer etc., then it's
// called whenever the pane is made visible.
//
// Does necessary processing, including href download and layout/resize of
// child widget(s)
this.inherited(arguments);
if(this.href){
if(!this._xhrDfd && // if there's an href that isn't already being loaded
(!this.isLoaded || this._hrefChanged || this.refreshOnShow)
){
return this.refresh(); // If child has an href, promise that fires when the load is complete
}
}
},
refresh: function(){
// summary:
// [Re]download contents of href and display
// description:
// 1. cancels any currently in-flight requests
// 2. posts "loading..." message
// 3. sends XHR to download new data
// Cancel possible prior in-flight request
this.cancel();
this.onLoadDeferred = new Deferred(lang.hitch(this, "cancel"));
this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
this._load();
return this.onLoadDeferred; // If child has an href, promise that fires when refresh is complete
},
_load: function(){
// summary:
// Load/reload the href specified in this.href
// display loading message
this._setContent(this.onDownloadStart(), true);
var self = this;
var getArgs = {
preventCache: (this.preventCache || this.refreshOnShow),
url: this.href,
handleAs: "text"
};
if(lang.isObject(this.ioArgs)){
lang.mixin(getArgs, this.ioArgs);
}
var hand = (this._xhrDfd = (this.ioMethod || xhr.get)(getArgs)),
returnedHtml;
hand.then(
function(html){
returnedHtml = html;
try{
self._isDownloaded = true;
return self._setContent(html, false);
}catch(err){
self._onError('Content', err); // onContentError
}
},
function(err){
if(!hand.canceled){
// show error message in the pane
self._onError('Download', err); // onDownloadError
}
delete self._xhrDfd;
return err;
}
).then(function(){
self.onDownloadEnd();
delete self._xhrDfd;
return returnedHtml;
});
// Remove flag saying that a load is needed
delete this._hrefChanged;
},
_onLoadHandler: function(data){
// summary:
// This is called whenever new content is being loaded
this._set("isLoaded", true);
try{
this.onLoadDeferred.resolve(data);
}catch(e){
console.error('Error '+this.widgetId+' running custom onLoad code: ' + e.message);
}
},
_onUnloadHandler: function(){
// summary:
// This is called whenever the content is being unloaded
this._set("isLoaded", false);
try{
this.onUnload();
}catch(e){
console.error('Error '+this.widgetId+' running custom onUnload code: ' + e.message);
}
},
destroyDescendants: function(/*Boolean*/ preserveDom){
// summary:
// Destroy all the widgets inside the ContentPane and empty containerNode
// Make sure we call onUnload (but only when the ContentPane has real content)
if(this.isLoaded){
this._onUnloadHandler();
}
// Even if this.isLoaded == false there might still be a "Loading..." message
// to erase, so continue...
// For historical reasons we need to delete all widgets under this.containerNode,
// even ones that the user has created manually.
var setter = this._contentSetter;
array.forEach(this.getChildren(), function(widget){
if(widget.destroyRecursive){
// All widgets will hit this branch
widget.destroyRecursive(preserveDom);
}else if(widget.destroy){
// Things like dojo/dnd/Source have destroy(), not destroyRecursive()
widget.destroy(preserveDom);
}
widget._destroyed = true;
});
if(setter){
// Most of the widgets in setter.parseResults have already been destroyed, but
// things like Menu that have been moved to <body> haven't yet
array.forEach(setter.parseResults, function(widget){
if(!widget._destroyed){
if(widget.destroyRecursive){
// All widgets will hit this branch
widget.destroyRecursive(preserveDom);
}else if(widget.destroy){
// Things like dojo/dnd/Source have destroy(), not destroyRecursive()
widget.destroy(preserveDom);
}
widget._destroyed = true;
}
});
delete setter.parseResults;
}
// And then clear away all the DOM nodes
if(!preserveDom){
domConstruct.empty(this.containerNode);
}
// Delete any state information we have about current contents
delete this._singleChild;
},
_setContent: function(/*String|DocumentFragment*/ cont, /*Boolean*/ isFakeContent){
// summary:
// Insert the content into the container node
// returns:
// Returns a Deferred promise that is resolved when the content is parsed.
// first get rid of child widgets
this.destroyDescendants();
// html.set will take care of the rest of the details
// we provide an override for the error handling to ensure the widget gets the errors
// configure the setter instance with only the relevant widget instance properties
// NOTE: unless we hook into attr, or provide property setters for each property,
// we need to re-configure the ContentSetter with each use
var setter = this._contentSetter;
if(! (setter && setter instanceof html._ContentSetter)){
setter = this._contentSetter = new html._ContentSetter({
node: this.containerNode,
_onError: lang.hitch(this, this._onError),
onContentError: lang.hitch(this, function(e){
// fires if a domfault occurs when we are appending this.errorMessage
// like for instance if domNode is a UL and we try append a DIV
var errMess = this.onContentError(e);
try{
this.containerNode.innerHTML = errMess;
}catch(e){
console.error('Fatal '+this.id+' could not change content due to '+e.message, e);
}
})/*,
_onError */
});
}
var setterParams = lang.mixin({
cleanContent: this.cleanContent,
extractContent: this.extractContent,
parseContent: !cont.domNode && this.parseOnLoad,
parserScope: this.parserScope,
startup: false,
dir: this.dir,
lang: this.lang,
textDir: this.textDir
}, this._contentSetterParams || {});
var p = setter.set( (lang.isObject(cont) && cont.domNode) ? cont.domNode : cont, setterParams );
// dojox/layout/html/_base::_ContentSetter.set() returns a Promise that indicates when everything is completed.
// dojo/html::_ContentSetter.set() currently returns the DOMNode, but that will be changed for 2.0.
// So, if set() returns a promise then use it, otherwise fallback to waiting on setter.parseDeferred
var self = this;
return when(p && p.then ? p : setter.parseDeferred, function(){
// setter params must be pulled afresh from the ContentPane each time
delete self._contentSetterParams;
if(!isFakeContent){
if(self._started){
// Startup each top level child widget (and they will start their children, recursively)
self._startChildren();
// Call resize() on each of my child layout widgets,
// or resize() on my single child layout widget...
// either now (if I'm currently visible) or when I become visible
self._scheduleLayout();
}
self._onLoadHandler(cont);
}
});
},
_onError: function(type, err, consoleText){
this.onLoadDeferred.reject(err);
// shows user the string that is returned by on[type]Error
// override on[type]Error and return your own string to customize
var errText = this['on' + type + 'Error'].call(this, err);
if(consoleText){
console.error(consoleText, err);
}else if(errText){// a empty string won't change current content
this._setContent(errText, true);
}
},
// EVENT's, should be overide-able
onLoad: function(/*===== data =====*/){
// summary:
// Event hook, is called after everything is loaded and widgetified
// tags:
// callback
},
onUnload: function(){
// summary:
// Event hook, is called before old content is cleared
// tags:
// callback
},
onDownloadStart: function(){
// summary:
// Called before download starts.
// description:
// The string returned by this function will be the html
// that tells the user we are loading something.
// Override with your own function if you want to change text.
// tags:
// extension
return this.loadingMessage;
},
onContentError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called on DOM faults, require faults etc. in content.
//
// In order to display an error message in the pane, return
// the error message from this method, as an HTML string.
//
// By default (if this method is not overriden), it returns
// nothing, so the error message is just printed to the console.
// tags:
// extension
},
onDownloadError: function(/*Error*/ /*===== error =====*/){
// summary:
// Called when download error occurs.
//
// In order to display an error message in the pane, return
// the error message from this method, as an HTML string.
//
// Default behavior (if this method is not overriden) is to display
// the error message inside the pane.
// tags:
// extension
return this.errorMessage;
},
onDownloadEnd: function(){
// summary:
// Called when download is finished.
// tags:
// callback
}
});
});
},
'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
'url:dijit/form/templates/TextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\" id=\"widget_${id}\" role=\"presentation\"\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n",
'dijit/_KeyNavContainer':function(){
define("dijit/_KeyNavContainer", [
"dojo/_base/kernel", // kernel.deprecated
"./_Container",
"./_FocusMixin",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.END keys.HOME
"dojo/_base/declare", // declare
"dojo/_base/event", // event.stop
"dojo/dom-attr", // domAttr.set
"dojo/_base/lang" // lang.hitch
], function(kernel, _Container, _FocusMixin, array, keys, declare, event, domAttr, lang){
// module:
// dijit/_KeyNavContainer
return declare("dijit._KeyNavContainer", [_FocusMixin, _Container], {
// summary:
// A _Container with keyboard navigation of its children.
// description:
// To use this mixin, call connectKeyNavHandlers() in
// postCreate().
// It provides normalized keyboard and focusing code for Container
// widgets.
/*=====
// focusedChild: [protected] Widget
// The currently focused child widget, or null if there isn't one
focusedChild: null,
=====*/
// tabIndex: String
// Tab index of the container; same as HTML tabIndex attribute.
// Note then when user tabs into the container, focus is immediately
// moved to the first item in the container.
tabIndex: "0",
connectKeyNavHandlers: function(/*keys[]*/ prevKeyCodes, /*keys[]*/ nextKeyCodes){
// summary:
// Call in postCreate() to attach the keyboard handlers
// to the container.
// preKeyCodes: keys[]
// Key codes for navigating to the previous child.
// nextKeyCodes: keys[]
// Key codes for navigating to the next child.
// tags:
// protected
// TODO: call this automatically from my own postCreate()
var keyCodes = (this._keyNavCodes = {});
var prev = lang.hitch(this, "focusPrev");
var next = lang.hitch(this, "focusNext");
array.forEach(prevKeyCodes, function(code){ keyCodes[code] = prev; });
array.forEach(nextKeyCodes, function(code){ keyCodes[code] = next; });
keyCodes[keys.HOME] = lang.hitch(this, "focusFirstChild");
keyCodes[keys.END] = lang.hitch(this, "focusLastChild");
this.connect(this.domNode, "onkeypress", "_onContainerKeypress");
this.connect(this.domNode, "onfocus", "_onContainerFocus");
},
startupKeyNavChildren: function(){
kernel.deprecated("startupKeyNavChildren() call no longer needed", "", "2.0");
},
startup: function(){
this.inherited(arguments);
array.forEach(this.getChildren(), lang.hitch(this, "_startupChild"));
},
addChild: function(/*dijit/_WidgetBase*/ widget, /*int?*/ insertIndex){
this.inherited(arguments);
this._startupChild(widget);
},
focus: function(){
// summary:
// Default focus() implementation: focus the first child.
this.focusFirstChild();
},
focusFirstChild: function(){
// summary:
// Focus the first focusable child in the container.
// tags:
// protected
this.focusChild(this._getFirstFocusableChild());
},
focusLastChild: function(){
// summary:
// Focus the last focusable child in the container.
// tags:
// protected
this.focusChild(this._getLastFocusableChild());
},
focusNext: function(){
// summary:
// Focus the next widget
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, 1));
},
focusPrev: function(){
// summary:
// Focus the last focusable node in the previous widget
// (ex: go to the ComboButton icon section rather than button section)
// tags:
// protected
this.focusChild(this._getNextFocusableChild(this.focusedChild, -1), true);
},
focusChild: function(/*dijit/_WidgetBase*/ widget, /*Boolean*/ last){
// summary:
// Focus specified child widget.
// widget:
// Reference to container's child widget
// last:
// If true and if widget has multiple focusable nodes, focus the
// last one instead of the first one
// tags:
// protected
if(!widget){ return; }
if(this.focusedChild && widget !== this.focusedChild){
this._onChildBlur(this.focusedChild); // used by _MenuBase
}
widget.set("tabIndex", this.tabIndex); // for IE focus outline to appear, must set tabIndex before focs
widget.focus(last ? "end" : "start");
this._set("focusedChild", widget);
},
_startupChild: function(/*dijit/_WidgetBase*/ widget){
// summary:
// Setup for each child widget
// description:
// Sets tabIndex=-1 on each child, so that the tab key will
// leave the container rather than visiting each child.
// tags:
// private
widget.set("tabIndex", "-1");
this.connect(widget, "_onFocus", function(){
// Set valid tabIndex so tabbing away from widget goes to right place, see #10272
widget.set("tabIndex", this.tabIndex);
});
this.connect(widget, "_onBlur", function(){
widget.set("tabIndex", "-1");
});
},
_onContainerFocus: function(evt){
// summary:
// Handler for when the container gets focus
// description:
// Initially the container itself has a tabIndex, but when it gets
// focus, switch focus to first child...
// tags:
// private
// Note that we can't use _onFocus() because switching focus from the
// _onFocus() handler confuses the focus.js code
// (because it causes _onFocusNode() to be called recursively)
// Also, _onFocus() would fire when focus went directly to a child widget due to mouse click.
// Ignore spurious focus events:
// 1. focus on a child widget bubbles on FF
// 2. on IE, clicking the scrollbar of a select dropdown moves focus from the focused child item to me
if(evt.target !== this.domNode || this.focusedChild){ return; }
this.focusFirstChild();
// and then set the container's tabIndex to -1,
// (don't remove as that breaks Safari 4)
// so that tab or shift-tab will go to the fields after/before
// the container, rather than the container itself
domAttr.set(this.domNode, "tabIndex", "-1");
},
_onBlur: function(evt){
// When focus is moved away the container, and its descendant (popup) widgets,
// then restore the container's tabIndex so that user can tab to it again.
// Note that using _onBlur() so that this doesn't happen when focus is shifted
// to one of my child widgets (typically a popup)
if(this.tabIndex){
domAttr.set(this.domNode, "tabIndex", this.tabIndex);
}
this.focusedChild = null;
this.inherited(arguments);
},
_onContainerKeypress: function(evt){
// summary:
// When a key is pressed, if it's an arrow key etc. then
// it's handled here.
// tags:
// private
if(evt.ctrlKey || evt.altKey){ return; }
var func = this._keyNavCodes[evt.charOrCode];
if(func){
func();
event.stop(evt);
}
},
_onChildBlur: function(/*dijit/_WidgetBase*/ /*===== widget =====*/){
// summary:
// Called when focus leaves a child widget to go
// to a sibling widget.
// Used by MenuBase.js (TODO: move code there)
// tags:
// protected
},
_getFirstFocusableChild: function(){
// summary:
// Returns first child that can be focused
return this._getNextFocusableChild(null, 1); // dijit/_WidgetBase
},
_getLastFocusableChild: function(){
// summary:
// Returns last child that can be focused
return this._getNextFocusableChild(null, -1); // dijit/_WidgetBase
},
_getNextFocusableChild: function(child, dir){
// summary:
// Returns the next or previous focusable child, compared
// to "child"
// child: Widget
// The current widget
// dir: Integer
// - 1 = after
// - -1 = before
if(child){
child = this._getSiblingOfChild(child, dir);
}
var children = this.getChildren();
for(var i=0; i < children.length; i++){
if(!child){
child = children[(dir>0) ? 0 : (children.length-1)];
}
if(child.isFocusable()){
return child; // dijit/_WidgetBase
}
child = this._getSiblingOfChild(child, dir);
}
// no focusable child found
return null; // dijit/_WidgetBase
}
});
});
},
'dijit/layout/utils':function(){
define("dijit/layout/utils", [
"dojo/_base/array", // array.filter array.forEach
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-geometry", // domGeometry.marginBox
"dojo/dom-style", // domStyle.getComputedStyle
"dojo/_base/lang", // lang.mixin
"../main" // for exporting symbols to dijit, remove in 2.0
], function(array, domClass, domGeometry, domStyle, lang, dijit){
// module:
// dijit/layout/utils
var layout = lang.getObject("layout", true, dijit);
/*=====
layout = {
// summary:
// marginBox2contentBox() and layoutChildren()
};
=====*/
layout.marginBox2contentBox = function(/*DomNode*/ node, /*Object*/ mb){
// summary:
// Given the margin-box size of a node, return its content box size.
// Functions like domGeometry.contentBox() but is more reliable since it doesn't have
// to wait for the browser to compute sizes.
var cs = domStyle.getComputedStyle(node);
var me = domGeometry.getMarginExtents(node, cs);
var pb = domGeometry.getPadBorderExtents(node, cs);
return {
l: domStyle.toPixelValue(node, cs.paddingLeft),
t: domStyle.toPixelValue(node, cs.paddingTop),
w: mb.w - (me.w + pb.w),
h: mb.h - (me.h + pb.h)
};
};
function capitalize(word){
return word.substring(0,1).toUpperCase() + word.substring(1);
}
function size(widget, dim){
// size the child
var newSize = widget.resize ? widget.resize(dim) : domGeometry.setMarginBox(widget.domNode, dim);
// record child's size
if(newSize){
// if the child returned it's new size then use that
lang.mixin(widget, newSize);
}else{
// otherwise, call getMarginBox(), but favor our own numbers when we have them.
// the browser lies sometimes
lang.mixin(widget, domGeometry.getMarginBox(widget.domNode));
lang.mixin(widget, dim);
}
}
layout.layoutChildren = function(/*DomNode*/ container, /*Object*/ dim, /*Widget[]*/ children,
/*String?*/ changedRegionId, /*Number?*/ changedRegionSize){
// 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 of Widgets or at least objects containing:
//
// - domNode: pointer to DOM node to position
// - region or layoutAlign: position to place DOM node
// - resize(): (optional) method to set size of node
// - id: (optional) Id of widgets, referenced from resize object, below.
// changedRegionId:
// If specified, the slider for the region with the specified id has been dragged, and thus
// the region's height or width should be adjusted according to changedRegionSize
// changedRegionSize:
// See changedRegionId.
// copy dim because we are going to modify it
dim = lang.mixin({}, dim);
domClass.add(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. TODO: move these lines to LayoutContainer? Unneeded other places I think.
children = array.filter(children, function(item){ return item.region != "center" && item.layoutAlign != "client"; })
.concat(array.filter(children, function(item){ return item.region == "center" || item.layoutAlign == "client"; }));
// set positions/sizes
array.forEach(children, function(child){
var elm = child.domNode,
pos = (child.region || child.layoutAlign);
if(!pos){
throw new Error("No region setting for " + child.id)
}
// 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.position = "absolute";
domClass.add(elm, "dijitAlign" + capitalize(pos));
// Size adjustments to make to this child widget
var sizeSetting = {};
// Check for optional size adjustment due to splitter drag (height adjustment for top/bottom align
// panes and width adjustment for left/right align panes.
if(changedRegionId && changedRegionId == child.id){
sizeSetting[child.region == "top" || child.region == "bottom" ? "h" : "w"] = changedRegionSize;
}
// set size && adjust record of remaining space.
// note that setting the width of a <div> may affect its height.
if(pos == "top" || pos == "bottom"){
sizeSetting.w = dim.w;
size(child, sizeSetting);
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"){
sizeSetting.h = dim.h;
size(child, sizeSetting);
dim.w -= child.w;
if(pos == "left"){
dim.l += child.w;
}else{
elmStyle.left = dim.l + dim.w + "px";
}
}else if(pos == "client" || pos == "center"){
size(child, dim);
}
});
};
return {
marginBox2contentBox: layout.marginBox2contentBox,
layoutChildren: layout.layoutChildren
};
});
},
'dijit/_Contained':function(){
define("dijit/_Contained", [
"dojo/_base/declare", // declare
"./registry" // registry.getEnclosingWidget(), registry.byNode()
], function(declare, registry){
// module:
// dijit/_Contained
return 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
// | declare("my.customClass",[dijit._Widget,dijit._Contained],{});
_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 && registry.byNode(node); // dijit/_WidgetBase
},
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/_WidgetBase
},
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/_WidgetBase
},
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
}
});
});
},
'dijit/form/DataList':function(){
define("dijit/form/DataList", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/_base/lang", // lang.trim
"dojo/query", // query
"dojo/store/Memory",
"../registry" // registry.add registry.remove
], function(declare, dom, lang, query, MemoryStore, registry){
// module:
// dijit/form/DataList
function toItem(/*DOMNode*/ option){
// summary:
// Convert `<option>` node to hash
return {
id: option.value,
value: option.value,
name: lang.trim(option.innerText || option.textContent || '')
};
}
return declare("dijit.form.DataList", MemoryStore, {
// summary:
// Inefficient but small data store specialized for inlined data via OPTION tags
//
// description:
// Provides a store for inlined data like:
//
// | <datalist>
// | <option value="AL">Alabama</option>
// | ...
constructor: function(params, srcNodeRef){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String
// Attach widget to this DOM node.
// store pointer to original DOM tree
this.domNode = dom.byId(srcNodeRef);
lang.mixin(this, params);
if(this.id){
registry.add(this); // add to registry so it can be easily found by id
}
this.domNode.style.display = "none";
this.inherited(arguments, [{
data: query("option", this.domNode).map(toItem)
}]);
},
destroy: function(){
registry.remove(this.id);
},
fetchSelectedItem: function(){
// summary:
// Get the option marked as selected, like `<option selected>`.
// Not part of dojo.data API.
var option = query("> option[selected]", this.domNode)[0] || query("> option", this.domNode)[0];
return option && toItem(option);
}
});
});
},
'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n",
'dijit/form/CheckBox':function(){
require({cache:{
'url:dijit/form/templates/CheckBox.html':"<div class=\"dijit dijitReset dijitInline\" role=\"presentation\"\n\t><input\n\t \t${!nameAttrSetting} type=\"${type}\" role=\"${type}\" aria-checked=\"false\" ${checkedAttrSetting}\n\t\tclass=\"dijitReset dijitCheckBoxInput\"\n\t\tdata-dojo-attach-point=\"focusNode\"\n\t \tdata-dojo-attach-event=\"onclick:_onClick\"\n/></div>\n"}});
define("dijit/form/CheckBox", [
"require",
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/has", // has("dijit-legacy-requires")
"dojo/query", // query
"dojo/ready",
"./ToggleButton",
"./_CheckBoxMixin",
"dojo/text!./templates/CheckBox.html",
"dojo/NodeList-dom" // NodeList.addClass/removeClass
], function(require, declare, domAttr, has, query, ready, ToggleButton, _CheckBoxMixin, template){
// module:
// dijit/form/CheckBox
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/form/RadioButton"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.form.CheckBox", [ToggleButton, _CheckBoxMixin], {
// summary:
// Same as an HTML checkbox, but with fancy styling.
//
// description:
// User interacts with real html inputs.
// On onclick (which occurs by mouse click, space-bar, or
// using the arrow keys to switch the selected radio button),
// we update the state of the checkbox/radio.
//
// There are two modes:
//
// 1. High contrast mode
// 2. Normal mode
//
// In case 1, the regular html inputs are shown and used by the user.
// In case 2, the regular html inputs are invisible but still used by
// the user. They are turned quasi-invisible and overlay the background-image.
templateString: template,
baseClass: "dijitCheckBox",
_setValueAttr: function(/*String|Boolean*/ newValue, /*Boolean*/ priorityChange){
// summary:
// Handler for value= attribute to constructor, and also calls to
// set('value', val).
// description:
// During initialization, just saves as attribute to the `<input type=checkbox>`.
//
// After initialization,
// when passed a boolean, controls whether or not the CheckBox is checked.
// If passed a string, changes the value attribute of the CheckBox (the one
// specified as "value" when the CheckBox was constructed
// (ex: `<input data-dojo-type="dijit/CheckBox" value="chicken">`).
//
// `widget.set('value', string)` will check the checkbox and change the value to the
// specified string.
//
// `widget.set('value', boolean)` will change the checked state.
if(typeof newValue == "string"){
this.inherited(arguments);
newValue = true;
}
if(this._created){
this.set('checked', newValue, priorityChange);
}
},
_getValueAttr: function(){
// summary:
// Hook so get('value') works.
// description:
// If the CheckBox is checked, returns the value attribute.
// Otherwise returns false.
return (this.checked ? this.value : false);
},
// Override behavior from Button, since we don't have an iconNode
_setIconClassAttr: null,
postMixInProperties: function(){
this.inherited(arguments);
// Need to set initial checked state as part of template, so that form submit works.
// domAttr.set(node, "checked", bool) doesn't work on IE until node has been attached
// to <body>, see #8666
this.checkedAttrSetting = this.checked ? "checked" : "";
},
_fillContent: function(){
// Override Button::_fillContent() since it doesn't make sense for CheckBox,
// since CheckBox doesn't even have a container
},
_onFocus: function(){
if(this.id){
query("label[for='"+this.id+"']").addClass("dijitFocusedLabel");
}
this.inherited(arguments);
},
_onBlur: function(){
if(this.id){
query("label[for='"+this.id+"']").removeClass("dijitFocusedLabel");
}
this.inherited(arguments);
}
});
});
},
'dijit/tree/_dndSelector':function(){
define("dijit/tree/_dndSelector", [
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/_base/connect", // connect.isCopyKey
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/_base/kernel", // global
"dojo/_base/lang", // lang.hitch
"dojo/cookie", // cookie
"dojo/mouse", // mouse.isLeft
"dojo/on",
"dojo/touch",
"./_dndContainer"
], function(array, connect, declare, Deferred, kernel, lang, cookie, mouse, on, touch, _dndContainer){
// module:
// dijit/tree/_dndSelector
return declare("dijit.tree._dndSelector", _dndContainer, {
// summary:
// This is a base class for `dijit/tree/dndSource` , and isn't meant to be used directly.
// It's based on `dojo/dnd/Selector`.
// tags:
// protected
/*=====
// selection: Object
// (id to DomNode) map for every TreeNode that's currently selected.
// The DOMNode is the TreeNode.rowNode.
selection: {},
=====*/
constructor: function(){
// summary:
// Initialization
// tags:
// private
this.selection={};
this.anchor = null;
if(!this.cookieName && this.tree.id){
this.cookieName = this.tree.id + "SaveSelectedCookie";
}
this.events.push(
on(this.tree.domNode, touch.press, lang.hitch(this,"onMouseDown")),
on(this.tree.domNode, touch.release, lang.hitch(this,"onMouseUp")),
on(this.tree.domNode, touch.move, lang.hitch(this,"onMouseMove"))
);
},
// singular: Boolean
// Allows selection of only one element, if true.
// Tree hasn't been tested in singular=true mode, unclear if it works.
singular: false,
// methods
getSelectedTreeNodes: function(){
// summary:
// Returns a list of selected node(s).
// Used by dndSource on the start of a drag.
// tags:
// protected
var nodes=[], sel = this.selection;
for(var i in sel){
nodes.push(sel[i]);
}
return nodes;
},
selectNone: function(){
// summary:
// Unselects all items
// tags:
// private
this.setSelection([]);
return this; // self
},
destroy: function(){
// summary:
// Prepares the object to be garbage-collected
this.inherited(arguments);
this.selection = this.anchor = null;
},
addTreeNode: function(/*dijit/Tree._TreeNode*/ node, /*Boolean?*/isAnchor){
// summary:
// add node to current selection
// node: Node
// node to add
// isAnchor: Boolean
// Whether the node should become anchor.
this.setSelection(this.getSelectedTreeNodes().concat( [node] ));
if(isAnchor){ this.anchor = node; }
return node;
},
removeTreeNode: function(/*dijit/Tree._TreeNode*/ node){
// summary:
// remove node from current selection
// node: Node
// node to remove
this.setSelection(this._setDifference(this.getSelectedTreeNodes(), [node]));
return node;
},
isTreeNodeSelected: function(/*dijit/Tree._TreeNode*/ node){
// summary:
// return true if node is currently selected
// node: Node
// the node to check whether it's in the current selection
return node.id && !!this.selection[node.id];
},
setSelection: function(/*dijit/Tree._TreeNode[]*/ newSelection){
// summary:
// set the list of selected nodes to be exactly newSelection. All changes to the
// selection should be passed through this function, which ensures that derived
// attributes are kept up to date. Anchor will be deleted if it has been removed
// from the selection, but no new anchor will be added by this function.
// newSelection: Node[]
// list of tree nodes to make selected
var oldSelection = this.getSelectedTreeNodes();
array.forEach(this._setDifference(oldSelection, newSelection), lang.hitch(this, function(node){
node.setSelected(false);
if(this.anchor == node){
delete this.anchor;
}
delete this.selection[node.id];
}));
array.forEach(this._setDifference(newSelection, oldSelection), lang.hitch(this, function(node){
node.setSelected(true);
this.selection[node.id] = node;
}));
this._updateSelectionProperties();
},
_setDifference: function(xs,ys){
// summary:
// Returns a copy of xs which lacks any objects
// occurring in ys. Checks for membership by
// modifying and then reading the object, so it will
// not properly handle sets of numbers or strings.
array.forEach(ys, function(y){ y.__exclude__ = true; });
var ret = array.filter(xs, function(x){ return !x.__exclude__; });
// clean up after ourselves.
array.forEach(ys, function(y){ delete y['__exclude__'] });
return ret;
},
_updateSelectionProperties: function(){
// summary:
// Update the following tree properties from the current selection:
// path[s], selectedItem[s], selectedNode[s]
var selected = this.getSelectedTreeNodes();
var paths = [], nodes = [], selects = [];
array.forEach(selected, function(node){
var ary = node.getTreePath(), model = this.tree.model;
nodes.push(node);
paths.push(ary);
ary = array.map(ary, function(item){
return model.getIdentity(item);
}, this);
selects.push(ary.join("/"))
}, this);
var items = array.map(nodes,function(node){ return node.item; });
this.tree._set("paths", paths);
this.tree._set("path", paths[0] || []);
this.tree._set("selectedNodes", nodes);
this.tree._set("selectedNode", nodes[0] || null);
this.tree._set("selectedItems", items);
this.tree._set("selectedItem", items[0] || null);
if (this.tree.persist && selects.length > 0) {
cookie(this.cookieName, selects.join(","), {expires:365});
}
},
_getSavedPaths: function(){
// summary:
// Returns paths of nodes that were selected previously and saved in the cookie.
var tree = this.tree;
if(tree.persist && tree.dndController.cookieName){
var oreo, paths = [];
oreo = cookie(tree.dndController.cookieName);
if(oreo){
paths = array.map(oreo.split(","), function(path){
return path.split("/");
})
}
return paths;
}
},
// mouse events
onMouseDown: function(e){
// summary:
// Event processor for onmousedown/ontouchstart
// e: Event
// onmousedown/ontouchstart event
// tags:
// protected
// ignore click on expando node
if(!this.current || this.tree.isExpandoNode(e.target, this.current)){ return; }
if(mouse.isLeft(e)){
// Prevent text selection while dragging on desktop, see #16328. But don't call preventDefault()
// for mobile because it will break things completely, see #15838.
e.preventDefault();
}else if(e.type != "touchstart"){
// Ignore right click
return;
}
var treeNode = this.current,
copy = connect.isCopyKey(e), id = treeNode.id;
// if shift key is not pressed, and the node is already in the selection,
// delay deselection until onmouseup so in the case of DND, deselection
// will be canceled by onmousemove.
if(!this.singular && !e.shiftKey && this.selection[id]){
this._doDeselect = true;
return;
}else{
this._doDeselect = false;
}
this.userSelect(treeNode, copy, e.shiftKey);
},
onMouseUp: function(e){
// summary:
// Event processor for onmouseup/ontouchend
// e: Event
// onmouseup/ontouchend event
// tags:
// protected
// _doDeselect is the flag to indicate that the user wants to either ctrl+click on
// a already selected item (to deselect the item), or click on a not-yet selected item
// (which should remove all current selection, and add the clicked item). This can not
// be done in onMouseDown, because the user may start a drag after mousedown. By moving
// the deselection logic here, the user can drags an already selected item.
if(!this._doDeselect){ return; }
this._doDeselect = false;
this.userSelect(this.current, connect.isCopyKey(e), e.shiftKey);
},
onMouseMove: function(/*===== e =====*/){
// summary:
// event processor for onmousemove/ontouchmove
// e: Event
// onmousemove/ontouchmove event
this._doDeselect = false;
},
_compareNodes: function(n1, n2){
if(n1 === n2){
return 0;
}
if('sourceIndex' in document.documentElement){ //IE
//TODO: does not yet work if n1 and/or n2 is a text node
return n1.sourceIndex - n2.sourceIndex;
}else if('compareDocumentPosition' in document.documentElement){ //FF, Opera
return n1.compareDocumentPosition(n2) & 2 ? 1: -1;
}else if(document.createRange){ //Webkit
var r1 = doc.createRange();
r1.setStartBefore(n1);
var r2 = doc.createRange();
r2.setStartBefore(n2);
return r1.compareBoundaryPoints(r1.END_TO_END, r2);
}else{
throw Error("dijit.tree._compareNodes don't know how to compare two different nodes in this browser");
}
},
userSelect: function(node, multi, range){
// summary:
// Add or remove the given node from selection, responding
// to a user action such as a click or keypress.
// multi: Boolean
// Indicates whether this is meant to be a multi-select action (e.g. ctrl-click)
// range: Boolean
// Indicates whether this is meant to be a ranged action (e.g. shift-click)
// tags:
// protected
if(this.singular){
if(this.anchor == node && multi){
this.selectNone();
}else{
this.setSelection([node]);
this.anchor = node;
}
}else{
if(range && this.anchor){
var cr = this._compareNodes(this.anchor.rowNode, node.rowNode),
begin, end, anchor = this.anchor;
if(cr < 0){ //current is after anchor
begin = anchor;
end = node;
}else{ //current is before anchor
begin = node;
end = anchor;
}
var nodes = [];
//add everything betweeen begin and end inclusively
while(begin != end){
nodes.push(begin);
begin = this.tree._getNextNode(begin);
}
nodes.push(end);
this.setSelection(nodes);
}else{
if( this.selection[ node.id ] && multi ){
this.removeTreeNode( node );
}else if(multi){
this.addTreeNode(node, true);
}else{
this.setSelection([node]);
this.anchor = node;
}
}
}
},
getItem: function(/*String*/ key){
// summary:
// Returns the dojo/dnd/Container._Item (representing a dragged node) by it's key (id).
// Called by dojo/dnd/Source.checkAcceptance().
// tags:
// protected
var widget = this.selection[key];
return {
data: widget,
type: ["treeNode"]
}; // dojo/dnd/Container._Item
},
forInSelectedItems: function(/*Function*/ f, /*Object?*/ o){
// summary:
// Iterates over selected items;
// see `dojo/dnd/Container.forInItems()` for details
o = o || kernel.global;
for(var id in this.selection){
// console.log("selected item id: " + id);
f.call(o, this.getItem(id), id, this);
}
}
});
});
},
'dijit/_Container':function(){
define("dijit/_Container", [
"dojo/_base/array", // array.forEach array.indexOf
"dojo/_base/declare", // declare
"dojo/dom-construct" // domConstruct.place
], function(array, declare, domConstruct){
// module:
// dijit/_Container
return declare("dijit._Container", null, {
// summary:
// Mixin for widgets that contain HTML and/or a set of widget children.
buildRendering: function(){
this.inherited(arguments);
if(!this.containerNode){
// all widgets with descendants must set containerNode
this.containerNode = this.domNode;
}
},
addChild: function(/*dijit/_WidgetBase*/ 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).
//
// Functionality is undefined if this widget contains anything besides
// a list of child widgets (ie, if it contains arbitrary non-widget HTML).
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";
}
}
domConstruct.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|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 (ie, removeChild(5) removes the sixth widget).
if(typeof widget == "number"){
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 child widgets, i.e. if this.containerNode contains widgets.
return this.getChildren().length > 0; // Boolean
},
_getSiblingOfChild: function(/*dijit/_WidgetBase*/ 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 children = this.getChildren(),
idx = array.indexOf(this.getChildren(), child); // int
return children[idx + dir];
},
getIndexOfChild: function(/*dijit/_WidgetBase*/ child){
// summary:
// Gets the index of the child in this container or -1 if not found
return array.indexOf(this.getChildren(), child); // int
}
});
});
},
'dojo/data/ItemFileReadStore':function(){
define("dojo/data/ItemFileReadStore", ["../_base/kernel", "../_base/lang", "../_base/declare", "../_base/array", "../_base/xhr",
"../Evented", "./util/filter", "./util/simpleFetch", "../date/stamp"
], function(kernel, lang, declare, array, xhr, Evented, filterUtil, simpleFetch, dateStamp){
// module:
// dojo/data/ItemFileReadStore
var ItemFileReadStore = declare("dojo.data.ItemFileReadStore", [Evented],{
// summary:
// The ItemFileReadStore implements the dojo/data/api/Read API and reads
// data from JSON files that have contents in this format --
// | { items: [
// | { name:'Kermit', color:'green', age:12, friends:['Gonzo', {_reference:{name:'Fozzie Bear'}}]},
// | { name:'Fozzie Bear', wears:['hat', 'tie']},
// | { name:'Miss Piggy', pets:'Foo-Foo'}
// | ]}
// Note that it can also contain an 'identifier' property that specified which attribute on the items
// in the array of items that acts as the unique identifier for that item.
constructor: function(/* Object */ keywordParameters){
// summary:
// constructor
// keywordParameters:
// {url: String} {data: jsonObject} {typeMap: object}
// The structure of the typeMap object is as follows:
// | {
// | type0: function || object,
// | type1: function || object,
// | ...
// | typeN: function || object
// | }
// Where if it is a function, it is assumed to be an object constructor that takes the
// value of _value as the initialization parameters. If it is an object, then it is assumed
// to be an object of general form:
// | {
// | type: function, //constructor.
// | deserialize: function(value) //The function that parses the value and constructs the object defined by type appropriately.
// | }
this._arrayOfAllItems = [];
this._arrayOfTopLevelItems = [];
this._loadFinished = false;
this._jsonFileUrl = keywordParameters.url;
this._ccUrl = keywordParameters.url;
this.url = keywordParameters.url;
this._jsonData = keywordParameters.data;
this.data = null;
this._datatypeMap = keywordParameters.typeMap || {};
if(!this._datatypeMap['Date']){
//If no default mapping for dates, then set this as default.
//We use the dojo/date/stamp here because the ISO format is the 'dojo way'
//of generically representing dates.
this._datatypeMap['Date'] = {
type: Date,
deserialize: function(value){
return dateStamp.fromISOString(value);
}
};
}
this._features = {'dojo.data.api.Read':true, 'dojo.data.api.Identity':true};
this._itemsByIdentity = null;
this._storeRefPropName = "_S"; // Default name for the store reference to attach to every item.
this._itemNumPropName = "_0"; // Default Item Id for isItem to attach to every item.
this._rootItemPropName = "_RI"; // Default Item Id for isItem to attach to every item.
this._reverseRefMap = "_RRM"; // Default attribute for constructing a reverse reference map for use with reference integrity
this._loadInProgress = false; //Got to track the initial load to prevent duelling loads of the dataset.
this._queuedFetches = [];
if(keywordParameters.urlPreventCache !== undefined){
this.urlPreventCache = keywordParameters.urlPreventCache?true:false;
}
if(keywordParameters.hierarchical !== undefined){
this.hierarchical = keywordParameters.hierarchical?true:false;
}
if(keywordParameters.clearOnClose){
this.clearOnClose = true;
}
if("failOk" in keywordParameters){
this.failOk = keywordParameters.failOk?true:false;
}
},
url: "", // use "" rather than undefined for the benefit of the parser (#3539)
//Internal var, crossCheckUrl. Used so that setting either url or _jsonFileUrl, can still trigger a reload
//when clearOnClose and close is used.
_ccUrl: "",
data: null, // define this so that the parser can populate it
typeMap: null, //Define so parser can populate.
// clearOnClose: Boolean
// Parameter to allow users to specify if a close call should force a reload or not.
// By default, it retains the old behavior of not clearing if close is called. But
// if set true, the store will be reset to default state. Note that by doing this,
// all item handles will become invalid and a new fetch must be issued.
clearOnClose: false,
// urlPreventCache: Boolean
// Parameter to allow specifying if preventCache should be passed to the xhrGet call or not when loading data from a url.
// Note this does not mean the store calls the server on each fetch, only that the data load has preventCache set as an option.
// Added for tracker: #6072
urlPreventCache: false,
// failOk: Boolean
// Parameter for specifying that it is OK for the xhrGet call to fail silently.
failOk: false,
// hierarchical: Boolean
// Parameter to indicate to process data from the url as hierarchical
// (data items can contain other data items in js form). Default is true
// for backwards compatibility. False means only root items are processed
// as items, all child objects outside of type-mapped objects and those in
// specific reference format, are left straight JS data objects.
hierarchical: true,
_assertIsItem: function(/* dojo/data/api/Item */ item){
// summary:
// This function tests whether the item passed in is indeed an item in the store.
// item:
// The item to test for being contained by the store.
if(!this.isItem(item)){
throw new Error(this.declaredClass + ": Invalid item argument.");
}
},
_assertIsAttribute: function(/* attribute-name-string */ attribute){
// summary:
// This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
// attribute:
// The attribute to test for being contained by the store.
if(typeof attribute !== "string"){
throw new Error(this.declaredClass + ": Invalid attribute argument.");
}
},
getValue: function( /* dojo/data/api/Item */ item,
/* attribute-name-string */ attribute,
/* value? */ defaultValue){
// summary:
// See dojo/data/api/Read.getValue()
var values = this.getValues(item, attribute);
return (values.length > 0)?values[0]:defaultValue; // mixed
},
getValues: function(/* dojo/data/api/Item */ item,
/* attribute-name-string */ attribute){
// summary:
// See dojo/data/api/Read.getValues()
this._assertIsItem(item);
this._assertIsAttribute(attribute);
// Clone it before returning. refs: #10474
return (item[attribute] || []).slice(0); // Array
},
getAttributes: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Read.getAttributes()
this._assertIsItem(item);
var attributes = [];
for(var key in item){
// Save off only the real item attributes, not the special id marks for O(1) isItem.
if((key !== this._storeRefPropName) && (key !== this._itemNumPropName) && (key !== this._rootItemPropName) && (key !== this._reverseRefMap)){
attributes.push(key);
}
}
return attributes; // Array
},
hasAttribute: function( /* dojo/data/api/Item */ item,
/* attribute-name-string */ attribute){
// summary:
// See dojo/data/api/Read.hasAttribute()
this._assertIsItem(item);
this._assertIsAttribute(attribute);
return (attribute in item);
},
containsValue: function(/* dojo/data/api/Item */ item,
/* attribute-name-string */ attribute,
/* anything */ value){
// summary:
// See dojo/data/api/Read.containsValue()
var regexp = undefined;
if(typeof value === "string"){
regexp = filterUtil.patternToRegExp(value, false);
}
return this._containsValue(item, attribute, value, regexp); //boolean.
},
_containsValue: function( /* dojo/data/api/Item */ item,
/* attribute-name-string */ attribute,
/* anything */ value,
/* RegExp?*/ regexp){
// summary:
// Internal function for looking at the values contained by the item.
// description:
// Internal function for looking at the values contained by the item. This
// function allows for denoting if the comparison should be case sensitive for
// strings or not (for handling filtering cases where string case should not matter)
// item:
// The data item to examine for attribute values.
// attribute:
// The attribute to inspect.
// value:
// The value to match.
// regexp:
// Optional regular expression generated off value if value was of string type to handle wildcarding.
// If present and attribute values are string, then it can be used for comparison instead of 'value'
return array.some(this.getValues(item, attribute), function(possibleValue){
if(possibleValue !== null && !lang.isObject(possibleValue) && regexp){
if(possibleValue.toString().match(regexp)){
return true; // Boolean
}
}else if(value === possibleValue){
return true; // Boolean
}
});
},
isItem: function(/* anything */ something){
// summary:
// See dojo/data/api/Read.isItem()
if(something && something[this._storeRefPropName] === this){
if(this._arrayOfAllItems[something[this._itemNumPropName]] === something){
return true;
}
}
return false; // Boolean
},
isItemLoaded: function(/* anything */ something){
// summary:
// See dojo/data/api/Read.isItemLoaded()
return this.isItem(something); //boolean
},
loadItem: function(/* object */ keywordArgs){
// summary:
// See dojo/data/api/Read.loadItem()
this._assertIsItem(keywordArgs.item);
},
getFeatures: function(){
// summary:
// See dojo/data/api/Read.getFeatures()
return this._features; //Object
},
getLabel: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Read.getLabel()
if(this._labelAttr && this.isItem(item)){
return this.getValue(item,this._labelAttr); //String
}
return undefined; //undefined
},
getLabelAttributes: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Read.getLabelAttributes()
if(this._labelAttr){
return [this._labelAttr]; //array
}
return null; //null
},
filter: function(/* Object */ requestArgs, /* item[] */ arrayOfItems, /* Function */ findCallback){
// summary:
// This method handles the basic filtering needs for ItemFile* based stores.
var items = [],
i, key;
if(requestArgs.query){
var value,
ignoreCase = requestArgs.queryOptions ? requestArgs.queryOptions.ignoreCase : false;
//See if there are any string values that can be regexp parsed first to avoid multiple regexp gens on the
//same value for each item examined. Much more efficient.
var regexpList = {};
for(key in requestArgs.query){
value = requestArgs.query[key];
if(typeof value === "string"){
regexpList[key] = filterUtil.patternToRegExp(value, ignoreCase);
}else if(value instanceof RegExp){
regexpList[key] = value;
}
}
for(i = 0; i < arrayOfItems.length; ++i){
var match = true;
var candidateItem = arrayOfItems[i];
if(candidateItem === null){
match = false;
}else{
for(key in requestArgs.query){
value = requestArgs.query[key];
if(!this._containsValue(candidateItem, key, value, regexpList[key])){
match = false;
}
}
}
if(match){
items.push(candidateItem);
}
}
findCallback(items, requestArgs);
}else{
// We want a copy to pass back in case the parent wishes to sort the array.
// We shouldn't allow resort of the internal list, so that multiple callers
// can get lists and sort without affecting each other. We also need to
// filter out any null values that have been left as a result of deleteItem()
// calls in ItemFileWriteStore.
for(i = 0; i < arrayOfItems.length; ++i){
var item = arrayOfItems[i];
if(item !== null){
items.push(item);
}
}
findCallback(items, requestArgs);
}
},
_fetchItems: function( /* Object */ keywordArgs,
/* Function */ findCallback,
/* Function */ errorCallback){
// summary:
// See dojo/data/util.simpleFetch.fetch()
var self = this;
if(this._loadFinished){
this.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
}else{
//Do a check on the JsonFileUrl and crosscheck it.
//If it doesn't match the cross-check, it needs to be updated
//This allows for either url or _jsonFileUrl to he changed to
//reset the store load location. Done this way for backwards
//compatibility. People use _jsonFileUrl (even though officially
//private.
if(this._jsonFileUrl !== this._ccUrl){
kernel.deprecated(this.declaredClass + ": ",
"To change the url, set the url property of the store," +
" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
this._ccUrl = this._jsonFileUrl;
this.url = this._jsonFileUrl;
}else if(this.url !== this._ccUrl){
this._jsonFileUrl = this.url;
this._ccUrl = this.url;
}
//See if there was any forced reset of data.
if(this.data != null){
this._jsonData = this.data;
this.data = null;
}
if(this._jsonFileUrl){
//If fetches come in before the loading has finished, but while
//a load is in progress, we have to defer the fetching to be
//invoked in the callback.
if(this._loadInProgress){
this._queuedFetches.push({args: keywordArgs, filter: lang.hitch(self, "filter"), findCallback: lang.hitch(self, findCallback)});
}else{
this._loadInProgress = true;
var getArgs = {
url: self._jsonFileUrl,
handleAs: "json-comment-optional",
preventCache: this.urlPreventCache,
failOk: this.failOk
};
var getHandler = xhr.get(getArgs);
getHandler.addCallback(function(data){
try{
self._getItemsFromLoadedData(data);
self._loadFinished = true;
self._loadInProgress = false;
self.filter(keywordArgs, self._getItemsArray(keywordArgs.queryOptions), findCallback);
self._handleQueuedFetches();
}catch(e){
self._loadFinished = true;
self._loadInProgress = false;
errorCallback(e, keywordArgs);
}
});
getHandler.addErrback(function(error){
self._loadInProgress = false;
errorCallback(error, keywordArgs);
});
//Wire up the cancel to abort of the request
//This call cancel on the deferred if it hasn't been called
//yet and then will chain to the simple abort of the
//simpleFetch keywordArgs
var oldAbort = null;
if(keywordArgs.abort){
oldAbort = keywordArgs.abort;
}
keywordArgs.abort = function(){
var df = getHandler;
if(df && df.fired === -1){
df.cancel();
df = null;
}
if(oldAbort){
oldAbort.call(keywordArgs);
}
};
}
}else if(this._jsonData){
try{
this._loadFinished = true;
this._getItemsFromLoadedData(this._jsonData);
this._jsonData = null;
self.filter(keywordArgs, this._getItemsArray(keywordArgs.queryOptions), findCallback);
}catch(e){
errorCallback(e, keywordArgs);
}
}else{
errorCallback(new Error(this.declaredClass + ": No JSON source data was provided as either URL or a nested Javascript object."), keywordArgs);
}
}
},
_handleQueuedFetches: function(){
// summary:
// Internal function to execute delayed request in the store.
//Execute any deferred fetches now.
if(this._queuedFetches.length > 0){
for(var i = 0; i < this._queuedFetches.length; i++){
var fData = this._queuedFetches[i],
delayedQuery = fData.args,
delayedFilter = fData.filter,
delayedFindCallback = fData.findCallback;
if(delayedFilter){
delayedFilter(delayedQuery, this._getItemsArray(delayedQuery.queryOptions), delayedFindCallback);
}else{
this.fetchItemByIdentity(delayedQuery);
}
}
this._queuedFetches = [];
}
},
_getItemsArray: function(/*object?*/queryOptions){
// summary:
// Internal function to determine which list of items to search over.
// queryOptions: The query options parameter, if any.
if(queryOptions && queryOptions.deep){
return this._arrayOfAllItems;
}
return this._arrayOfTopLevelItems;
},
close: function(/*dojo/data/api/Request|Object?*/ request){
// summary:
// See dojo/data/api/Read.close()
if(this.clearOnClose &&
this._loadFinished &&
!this._loadInProgress){
//Reset all internalsback to default state. This will force a reload
//on next fetch. This also checks that the data or url param was set
//so that the store knows it can get data. Without one of those being set,
//the next fetch will trigger an error.
if(((this._jsonFileUrl == "" || this._jsonFileUrl == null) &&
(this.url == "" || this.url == null)
) && this.data == null){
console.debug(this.declaredClass + ": WARNING! Data reload " +
" information has not been provided." +
" Please set 'url' or 'data' to the appropriate value before" +
" the next fetch");
}
this._arrayOfAllItems = [];
this._arrayOfTopLevelItems = [];
this._loadFinished = false;
this._itemsByIdentity = null;
this._loadInProgress = false;
this._queuedFetches = [];
}
},
_getItemsFromLoadedData: function(/* Object */ dataObject){
// summary:
// Function to parse the loaded data into item format and build the internal items array.
// description:
// Function to parse the loaded data into item format and build the internal items array.
// dataObject:
// The JS data object containing the raw data to convery into item format.
// returns: Array
// Array of items in store item format.
// First, we define a couple little utility functions...
var addingArrays = false,
self = this;
function valueIsAnItem(/* anything */ aValue){
// summary:
// Given any sort of value that could be in the raw json data,
// return true if we should interpret the value as being an
// item itself, rather than a literal value or a reference.
// example:
// | false == valueIsAnItem("Kermit");
// | false == valueIsAnItem(42);
// | false == valueIsAnItem(new Date());
// | false == valueIsAnItem({_type:'Date', _value:'1802-05-14'});
// | false == valueIsAnItem({_reference:'Kermit'});
// | true == valueIsAnItem({name:'Kermit', color:'green'});
// | true == valueIsAnItem({iggy:'pop'});
// | true == valueIsAnItem({foo:42});
return (aValue !== null) &&
(typeof aValue === "object") &&
(!lang.isArray(aValue) || addingArrays) &&
(!lang.isFunction(aValue)) &&
(aValue.constructor == Object || lang.isArray(aValue)) &&
(typeof aValue._reference === "undefined") &&
(typeof aValue._type === "undefined") &&
(typeof aValue._value === "undefined") &&
self.hierarchical;
}
function addItemAndSubItemsToArrayOfAllItems(/* dojo/data/api/Item */ anItem){
self._arrayOfAllItems.push(anItem);
for(var attribute in anItem){
var valueForAttribute = anItem[attribute];
if(valueForAttribute){
if(lang.isArray(valueForAttribute)){
var valueArray = valueForAttribute;
for(var k = 0; k < valueArray.length; ++k){
var singleValue = valueArray[k];
if(valueIsAnItem(singleValue)){
addItemAndSubItemsToArrayOfAllItems(singleValue);
}
}
}else{
if(valueIsAnItem(valueForAttribute)){
addItemAndSubItemsToArrayOfAllItems(valueForAttribute);
}
}
}
}
}
this._labelAttr = dataObject.label;
// We need to do some transformations to convert the data structure
// that we read from the file into a format that will be convenient
// to work with in memory.
// Step 1: Walk through the object hierarchy and build a list of all items
var i,
item;
this._arrayOfAllItems = [];
this._arrayOfTopLevelItems = dataObject.items;
for(i = 0; i < this._arrayOfTopLevelItems.length; ++i){
item = this._arrayOfTopLevelItems[i];
if(lang.isArray(item)){
addingArrays = true;
}
addItemAndSubItemsToArrayOfAllItems(item);
item[this._rootItemPropName]=true;
}
// Step 2: Walk through all the attribute values of all the items,
// and replace single values with arrays. For example, we change this:
// { name:'Miss Piggy', pets:'Foo-Foo'}
// into this:
// { name:['Miss Piggy'], pets:['Foo-Foo']}
//
// We also store the attribute names so we can validate our store
// reference and item id special properties for the O(1) isItem
var allAttributeNames = {},
key;
for(i = 0; i < this._arrayOfAllItems.length; ++i){
item = this._arrayOfAllItems[i];
for(key in item){
if(key !== this._rootItemPropName){
var value = item[key];
if(value !== null){
if(!lang.isArray(value)){
item[key] = [value];
}
}else{
item[key] = [null];
}
}
allAttributeNames[key]=key;
}
}
// Step 3: Build unique property names to use for the _storeRefPropName and _itemNumPropName
// This should go really fast, it will generally never even run the loop.
while(allAttributeNames[this._storeRefPropName]){
this._storeRefPropName += "_";
}
while(allAttributeNames[this._itemNumPropName]){
this._itemNumPropName += "_";
}
while(allAttributeNames[this._reverseRefMap]){
this._reverseRefMap += "_";
}
// Step 4: Some data files specify an optional 'identifier', which is
// the name of an attribute that holds the identity of each item.
// If this data file specified an identifier attribute, then build a
// hash table of items keyed by the identity of the items.
var arrayOfValues;
var identifier = dataObject.identifier;
if(identifier){
this._itemsByIdentity = {};
this._features['dojo.data.api.Identity'] = identifier;
for(i = 0; i < this._arrayOfAllItems.length; ++i){
item = this._arrayOfAllItems[i];
arrayOfValues = item[identifier];
var identity = arrayOfValues[0];
if(!Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
this._itemsByIdentity[identity] = item;
}else{
if(this._jsonFileUrl){
throw new Error(this.declaredClass + ": The json data as specified by: [" + this._jsonFileUrl + "] is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
}else if(this._jsonData){
throw new Error(this.declaredClass + ": The json data provided by the creation arguments is malformed. Items within the list have identifier: [" + identifier + "]. Value collided: [" + identity + "]");
}
}
}
}else{
this._features['dojo.data.api.Identity'] = Number;
}
// Step 5: Walk through all the items, and set each item's properties
// for _storeRefPropName and _itemNumPropName, so that store.isItem() will return true.
for(i = 0; i < this._arrayOfAllItems.length; ++i){
item = this._arrayOfAllItems[i];
item[this._storeRefPropName] = this;
item[this._itemNumPropName] = i;
}
// Step 6: We walk through all the attribute values of all the items,
// looking for type/value literals and item-references.
//
// We replace item-references with pointers to items. For example, we change:
// { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
// into this:
// { name:['Kermit'], friends:[miss_piggy] }
// (where miss_piggy is the object representing the 'Miss Piggy' item).
//
// We replace type/value pairs with typed-literals. For example, we change:
// { name:['Nelson Mandela'], born:[{_type:'Date', _value:'1918-07-18'}] }
// into this:
// { name:['Kermit'], born:(new Date(1918, 6, 18)) }
//
// We also generate the associate map for all items for the O(1) isItem function.
for(i = 0; i < this._arrayOfAllItems.length; ++i){
item = this._arrayOfAllItems[i]; // example: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
for(key in item){
arrayOfValues = item[key]; // example: [{_reference:{name:'Miss Piggy'}}]
for(var j = 0; j < arrayOfValues.length; ++j){
value = arrayOfValues[j]; // example: {_reference:{name:'Miss Piggy'}}
if(value !== null && typeof value == "object"){
if(("_type" in value) && ("_value" in value)){
var type = value._type; // examples: 'Date', 'Color', or 'ComplexNumber'
var mappingObj = this._datatypeMap[type]; // examples: Date, dojo.Color, foo.math.ComplexNumber, {type: dojo.Color, deserialize(value){ return new dojo.Color(value)}}
if(!mappingObj){
throw new Error("dojo.data.ItemFileReadStore: in the typeMap constructor arg, no object class was specified for the datatype '" + type + "'");
}else if(lang.isFunction(mappingObj)){
arrayOfValues[j] = new mappingObj(value._value);
}else if(lang.isFunction(mappingObj.deserialize)){
arrayOfValues[j] = mappingObj.deserialize(value._value);
}else{
throw new Error("dojo.data.ItemFileReadStore: Value provided in typeMap was neither a constructor, nor a an object with a deserialize function");
}
}
if(value._reference){
var referenceDescription = value._reference; // example: {name:'Miss Piggy'}
if(!lang.isObject(referenceDescription)){
// example: 'Miss Piggy'
// from an item like: { name:['Kermit'], friends:[{_reference:'Miss Piggy'}]}
arrayOfValues[j] = this._getItemByIdentity(referenceDescription);
}else{
// example: {name:'Miss Piggy'}
// from an item like: { name:['Kermit'], friends:[{_reference:{name:'Miss Piggy'}}] }
for(var k = 0; k < this._arrayOfAllItems.length; ++k){
var candidateItem = this._arrayOfAllItems[k],
found = true;
for(var refKey in referenceDescription){
if(candidateItem[refKey] != referenceDescription[refKey]){
found = false;
}
}
if(found){
arrayOfValues[j] = candidateItem;
}
}
}
if(this.referenceIntegrity){
var refItem = arrayOfValues[j];
if(this.isItem(refItem)){
this._addReferenceToMap(refItem, item, key);
}
}
}else if(this.isItem(value)){
//It's a child item (not one referenced through _reference).
//We need to treat this as a referenced item, so it can be cleaned up
//in a write store easily.
if(this.referenceIntegrity){
this._addReferenceToMap(value, item, key);
}
}
}
}
}
}
},
_addReferenceToMap: function(/*item*/ refItem, /*item*/ parentItem, /*string*/ attribute){
// summary:
// Method to add an reference map entry for an item and attribute.
// description:
// Method to add an reference map entry for an item and attribute.
// refItem:
// The item that is referenced.
// parentItem:
// The item that holds the new reference to refItem.
// attribute:
// The attribute on parentItem that contains the new reference.
//Stub function, does nothing. Real processing is in ItemFileWriteStore.
},
getIdentity: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Identity.getIdentity()
var identifier = this._features['dojo.data.api.Identity'];
if(identifier === Number){
return item[this._itemNumPropName]; // Number
}else{
var arrayOfValues = item[identifier];
if(arrayOfValues){
return arrayOfValues[0]; // Object|String
}
}
return null; // null
},
fetchItemByIdentity: function(/* Object */ keywordArgs){
// summary:
// See dojo/data/api/Identity.fetchItemByIdentity()
// Hasn't loaded yet, we have to trigger the load.
var item,
scope;
if(!this._loadFinished){
var self = this;
//Do a check on the JsonFileUrl and crosscheck it.
//If it doesn't match the cross-check, it needs to be updated
//This allows for either url or _jsonFileUrl to he changed to
//reset the store load location. Done this way for backwards
//compatibility. People use _jsonFileUrl (even though officially
//private.
if(this._jsonFileUrl !== this._ccUrl){
kernel.deprecated(this.declaredClass + ": ",
"To change the url, set the url property of the store," +
" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
this._ccUrl = this._jsonFileUrl;
this.url = this._jsonFileUrl;
}else if(this.url !== this._ccUrl){
this._jsonFileUrl = this.url;
this._ccUrl = this.url;
}
//See if there was any forced reset of data.
if(this.data != null && this._jsonData == null){
this._jsonData = this.data;
this.data = null;
}
if(this._jsonFileUrl){
if(this._loadInProgress){
this._queuedFetches.push({args: keywordArgs});
}else{
this._loadInProgress = true;
var getArgs = {
url: self._jsonFileUrl,
handleAs: "json-comment-optional",
preventCache: this.urlPreventCache,
failOk: this.failOk
};
var getHandler = xhr.get(getArgs);
getHandler.addCallback(function(data){
var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
try{
self._getItemsFromLoadedData(data);
self._loadFinished = true;
self._loadInProgress = false;
item = self._getItemByIdentity(keywordArgs.identity);
if(keywordArgs.onItem){
keywordArgs.onItem.call(scope, item);
}
self._handleQueuedFetches();
}catch(error){
self._loadInProgress = false;
if(keywordArgs.onError){
keywordArgs.onError.call(scope, error);
}
}
});
getHandler.addErrback(function(error){
self._loadInProgress = false;
if(keywordArgs.onError){
var scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
keywordArgs.onError.call(scope, error);
}
});
}
}else if(this._jsonData){
// Passed in data, no need to xhr.
self._getItemsFromLoadedData(self._jsonData);
self._jsonData = null;
self._loadFinished = true;
item = self._getItemByIdentity(keywordArgs.identity);
if(keywordArgs.onItem){
scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
keywordArgs.onItem.call(scope, item);
}
}
}else{
// Already loaded. We can just look it up and call back.
item = this._getItemByIdentity(keywordArgs.identity);
if(keywordArgs.onItem){
scope = keywordArgs.scope?keywordArgs.scope:kernel.global;
keywordArgs.onItem.call(scope, item);
}
}
},
_getItemByIdentity: function(/* Object */ identity){
// summary:
// Internal function to look an item up by its identity map.
var item = null;
if(this._itemsByIdentity){
// If this map is defined, we need to just try to get it. If it fails
// the item does not exist.
if(Object.hasOwnProperty.call(this._itemsByIdentity, identity)){
item = this._itemsByIdentity[identity];
}
}else if (Object.hasOwnProperty.call(this._arrayOfAllItems, identity)){
item = this._arrayOfAllItems[identity];
}
if(item === undefined){
item = null;
}
return item; // Object
},
getIdentityAttributes: function(/* dojo/data/api/Item */ item){
// summary:
// See dojo/data/api/Identity.getIdentityAttributes()
var identifier = this._features['dojo.data.api.Identity'];
if(identifier === Number){
// If (identifier === Number) it means getIdentity() just returns
// an integer item-number for each item. The dojo/data/api/Identity
// spec says we need to return null if the identity is not composed
// of attributes
return null; // null
}else{
return [identifier]; // Array
}
},
_forceLoad: function(){
// summary:
// Internal function to force a load of the store if it hasn't occurred yet. This is required
// for specific functions to work properly.
var self = this;
//Do a check on the JsonFileUrl and crosscheck it.
//If it doesn't match the cross-check, it needs to be updated
//This allows for either url or _jsonFileUrl to he changed to
//reset the store load location. Done this way for backwards
//compatibility. People use _jsonFileUrl (even though officially
//private.
if(this._jsonFileUrl !== this._ccUrl){
kernel.deprecated(this.declaredClass + ": ",
"To change the url, set the url property of the store," +
" not _jsonFileUrl. _jsonFileUrl support will be removed in 2.0");
this._ccUrl = this._jsonFileUrl;
this.url = this._jsonFileUrl;
}else if(this.url !== this._ccUrl){
this._jsonFileUrl = this.url;
this._ccUrl = this.url;
}
//See if there was any forced reset of data.
if(this.data != null){
this._jsonData = this.data;
this.data = null;
}
if(this._jsonFileUrl){
var getArgs = {
url: this._jsonFileUrl,
handleAs: "json-comment-optional",
preventCache: this.urlPreventCache,
failOk: this.failOk,
sync: true
};
var getHandler = xhr.get(getArgs);
getHandler.addCallback(function(data){
try{
//Check to be sure there wasn't another load going on concurrently
//So we don't clobber data that comes in on it. If there is a load going on
//then do not save this data. It will potentially clobber current data.
//We mainly wanted to sync/wait here.
//TODO: Revisit the loading scheme of this store to improve multi-initial
//request handling.
if(self._loadInProgress !== true && !self._loadFinished){
self._getItemsFromLoadedData(data);
self._loadFinished = true;
}else if(self._loadInProgress){
//Okay, we hit an error state we can't recover from. A forced load occurred
//while an async load was occurring. Since we cannot block at this point, the best
//that can be managed is to throw an error.
throw new Error(this.declaredClass + ": Unable to perform a synchronous load, an async load is in progress.");
}
}catch(e){
console.log(e);
throw e;
}
});
getHandler.addErrback(function(error){
throw error;
});
}else if(this._jsonData){
self._getItemsFromLoadedData(self._jsonData);
self._jsonData = null;
self._loadFinished = true;
}
}
});
//Mix in the simple fetch implementation to this class.
lang.extend(ItemFileReadStore,simpleFetch);
return ItemFileReadStore;
});
},
'dojo/html':function(){
define("dojo/html", ["./_base/kernel", "./_base/lang", "./_base/array", "./_base/declare", "./dom", "./dom-construct", "./parser"],
function(kernel, lang, darray, declare, dom, domConstruct, parser){
// module:
// dojo/html
var html = {
// summary:
// TODOC
};
lang.setObject("dojo.html", html);
// the parser might be needed..
// idCounter is incremented with each instantiation to allow assignment of a unique id for tracking, logging purposes
var idCounter = 0;
html._secureForInnerHtml = function(/*String*/ cont){
// summary:
// removes !DOCTYPE and title elements from the html string.
//
// khtml is picky about dom faults, you can't attach a style or `<title>` node as child of body
// must go into head, so we need to cut out those tags
// cont:
// An html string for insertion into the dom
//
return cont.replace(/(?:\s*<!DOCTYPE\s[^>]+>|<title[^>]*>[\s\S]*?<\/title>)/ig, ""); // String
};
html._emptyNode = domConstruct.empty;
/*=====
dojo.html._emptyNode = function(node){
// summary:
// Removes all child nodes from the given node. Deprecated, should use dojo/dom-constuct.empty() directly
// instead.
// node: DOMNode
// the parent element
};
=====*/
html._setNodeContent = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont){
// summary:
// inserts the given content into the given node
// node:
// the parent element
// content:
// the content to be set on the parent element.
// This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
// always empty
domConstruct.empty(node);
if(cont){
if(typeof cont == "string"){
cont = domConstruct.toDom(cont, node.ownerDocument);
}
if(!cont.nodeType && lang.isArrayLike(cont)){
// handle as enumerable, but it may shrink as we enumerate it
for(var startlen=cont.length, i=0; i<cont.length; i=startlen==cont.length ? i+1 : 0){
domConstruct.place( cont[i], node, "last");
}
}else{
// pass nodes, documentFragments and unknowns through to dojo.place
domConstruct.place(cont, node, "last");
}
}
// return DomNode
return node;
};
// we wrap up the content-setting operation in a object
html._ContentSetter = declare("dojo.html._ContentSetter", null,
{
// node: DomNode|String
// An node which will be the parent element that we set content into
node: "",
// content: String|DomNode|DomNode[]
// The content to be placed in the node. Can be an HTML string, a node reference, or a enumerable list of nodes
content: "",
// id: String?
// Usually only used internally, and auto-generated with each instance
id: "",
// cleanContent: Boolean
// Should the content be treated as a full html document,
// and the real content stripped of <html>, <body> wrapper before injection
cleanContent: false,
// extractContent: Boolean
// Should the content be treated as a full html document,
// and the real content stripped of `<html> <body>` wrapper before injection
extractContent: false,
// parseContent: Boolean
// Should the node by passed to the parser after the new content is set
parseContent: false,
// parserScope: String
// Flag passed to parser. Root for attribute names to search for. If scopeName is dojo,
// will search for data-dojo-type (or dojoType). For backwards compatibility
// reasons defaults to dojo._scopeName (which is "dojo" except when
// multi-version support is used, when it will be something like dojo16, dojo20, etc.)
parserScope: kernel._scopeName,
// startup: Boolean
// Start the child widgets after parsing them. Only obeyed if parseContent is true.
startup: true,
// lifecycle methods
constructor: function(/*Object*/ params, /*String|DomNode*/ node){
// summary:
// Provides a configurable, extensible object to wrap the setting on content on a node
// call the set() method to actually set the content..
// the original params are mixed directly into the instance "this"
lang.mixin(this, params || {});
// give precedence to params.node vs. the node argument
// and ensure its a node, not an id string
node = this.node = dom.byId( this.node || node );
if(!this.id){
this.id = [
"Setter",
(node) ? node.id || node.tagName : "",
idCounter++
].join("_");
}
},
set: function(/* String|DomNode|NodeList? */ cont, /*Object?*/ params){
// summary:
// front-end to the set-content sequence
// cont:
// An html string, node or enumerable list of nodes for insertion into the dom
// If not provided, the object's content property will be used
if(undefined !== cont){
this.content = cont;
}
// in the re-use scenario, set needs to be able to mixin new configuration
if(params){
this._mixin(params);
}
this.onBegin();
this.setContent();
var ret = this.onEnd();
if(ret && ret.then){
// Make dojox/html/_ContentSetter.set() return a Promise that resolves when load and parse complete.
return ret;
}else{
// Vanilla dojo/html._ContentSetter.set() returns a DOMNode for back compat. For 2.0, switch it to
// return a Deferred like above.
return this.node;
}
},
setContent: function(){
// summary:
// sets the content on the node
var node = this.node;
if(!node){
// can't proceed
throw new Error(this.declaredClass + ": setContent given no node");
}
try{
node = html._setNodeContent(node, this.content);
}catch(e){
// check if a domfault occurs when we are appending this.errorMessage
// like for instance if domNode is a UL and we try append a DIV
// FIXME: need to allow the user to provide a content error message string
var errMess = this.onContentError(e);
try{
node.innerHTML = errMess;
}catch(e){
console.error('Fatal ' + this.declaredClass + '.setContent could not change content due to '+e.message, e);
}
}
// always put back the node for the next method
this.node = node; // DomNode
},
empty: function(){
// summary:
// cleanly empty out existing content
// If there is a parse in progress, cancel it.
if(this.parseDeferred){
if(!this.parseDeferred.isResolved()){
this.parseDeferred.cancel();
}
delete this.parseDeferred;
}
// destroy any widgets from a previous run
// NOTE: if you don't want this you'll need to empty
// the parseResults array property yourself to avoid bad things happening
if(this.parseResults && this.parseResults.length){
darray.forEach(this.parseResults, function(w){
if(w.destroy){
w.destroy();
}
});
delete this.parseResults;
}
// this is fast, but if you know its already empty or safe, you could
// override empty to skip this step
domConstruct.empty(this.node);
},
onBegin: function(){
// summary:
// Called after instantiation, but before set();
// It allows modification of any of the object properties -
// including the node and content provided - before the set operation actually takes place
// This default implementation checks for cleanContent and extractContent flags to
// optionally pre-process html string content
var cont = this.content;
if(lang.isString(cont)){
if(this.cleanContent){
cont = html._secureForInnerHtml(cont);
}
if(this.extractContent){
var match = cont.match(/<body[^>]*>\s*([\s\S]+)\s*<\/body>/im);
if(match){ cont = match[1]; }
}
}
// clean out the node and any cruft associated with it - like widgets
this.empty();
this.content = cont;
return this.node; // DomNode
},
onEnd: function(){
// summary:
// Called after set(), when the new content has been pushed into the node
// It provides an opportunity for post-processing before handing back the node to the caller
// This default implementation checks a parseContent flag to optionally run the dojo parser over the new content
if(this.parseContent){
// populates this.parseResults and this.parseDeferred if you need those..
this._parse();
}
return this.node; // DomNode
// TODO: for 2.0 return a Promise indicating that the parse completed.
},
tearDown: function(){
// summary:
// manually reset the Setter instance if its being re-used for example for another set()
// description:
// tearDown() is not called automatically.
// In normal use, the Setter instance properties are simply allowed to fall out of scope
// but the tearDown method can be called to explicitly reset this instance.
delete this.parseResults;
delete this.parseDeferred;
delete this.node;
delete this.content;
},
onContentError: function(err){
return "Error occurred setting content: " + err;
},
onExecError: function(err){
return "Error occurred executing scripts: " + err;
},
_mixin: function(params){
// mix properties/methods into the instance
// TODO: the intention with tearDown is to put the Setter's state
// back to that of the original constructor (vs. deleting/resetting everything regardless of ctor params)
// so we could do something here to move the original properties aside for later restoration
var empty = {}, key;
for(key in params){
if(key in empty){ continue; }
// TODO: here's our opportunity to mask the properties we don't consider configurable/overridable
// .. but history shows we'll almost always guess wrong
this[key] = params[key];
}
},
_parse: function(){
// summary:
// runs the dojo parser over the node contents, storing any results in this.parseResults
// and the parse promise in this.parseDeferred
// Any errors resulting from parsing are passed to _onError for handling
var rootNode = this.node;
try{
// store the results (widgets, whatever) for potential retrieval
var inherited = {};
darray.forEach(["dir", "lang", "textDir"], function(name){
if(this[name]){
inherited[name] = this[name];
}
}, this);
var self = this;
this.parseDeferred = parser.parse({
rootNode: rootNode,
noStart: !this.startup,
inherited: inherited,
scope: this.parserScope
}).then(function(results){
return self.parseResults = results;
});
}catch(e){
this._onError('Content', e, "Error parsing in _ContentSetter#"+this.id);
}
},
_onError: function(type, err, consoleText){
// summary:
// shows user the string that is returned by on[type]Error
// override/implement on[type]Error and return your own string to customize
var errText = this['on' + type + 'Error'].call(this, err);
if(consoleText){
console.error(consoleText, err);
}else if(errText){ // a empty string won't change current content
html._setNodeContent(this.node, errText, true);
}
}
}); // end declare()
html.set = function(/*DomNode*/ node, /*String|DomNode|NodeList*/ cont, /*Object?*/ params){
// summary:
// inserts (replaces) the given content into the given node. dojo.place(cont, node, "only")
// may be a better choice for simple HTML insertion.
// description:
// Unless you need to use the params capabilities of this method, you should use
// dojo.place(cont, node, "only"). dojo.place() has more robust support for injecting
// an HTML string into the DOM, but it only handles inserting an HTML string as DOM
// elements, or inserting a DOM node. dojo.place does not handle NodeList insertions
// or the other capabilities as defined by the params object for this method.
// node:
// the parent element that will receive the content
// cont:
// the content to be set on the parent element.
// This can be an html string, a node reference or a NodeList, dojo/NodeList, Array or other enumerable list of nodes
// params:
// Optional flags/properties to configure the content-setting. See dojo/html/_ContentSetter
// example:
// A safe string/node/nodelist content replacement/injection with hooks for extension
// Example Usage:
// | html.set(node, "some string");
// | html.set(node, contentNode, {options});
// | html.set(node, myNode.childNodes, {options});
if(undefined == cont){
console.warn("dojo.html.set: no cont argument provided, using empty string");
cont = "";
}
if(!params){
// simple and fast
return html._setNodeContent(node, cont, true);
}else{
// more options but slower
// note the arguments are reversed in order, to match the convention for instantiation via the parser
var op = new html._ContentSetter(lang.mixin(
params,
{ content: cont, node: node }
));
return op.set();
}
};
return html;
});
},
'dijit/_PaletteMixin':function(){
define("dijit/_PaletteMixin", [
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-construct", // domConstruct.create domConstruct.place
"dojo/_base/event", // event.stop
"dojo/keys", // keys
"dojo/_base/lang", // lang.getObject
"./_CssStateMixin",
"./focus",
"./typematic"
], function(declare, domAttr, domClass, domConstruct, event, keys, lang, _CssStateMixin, focus, typematic){
// module:
// dijit/_PaletteMixin
return declare("dijit._PaletteMixin", [_CssStateMixin], {
// summary:
// A keyboard accessible palette, for picking a color/emoticon/etc.
// description:
// A mixin for a grid showing various entities, so the user can pick a certain entity.
// defaultTimeout: Number
// Number of milliseconds before a held key or button becomes typematic
defaultTimeout: 500,
// timeoutChangeRate: Number
// Fraction of time used to change the typematic timer between events
// 1.0 means that each typematic event fires at defaultTimeout intervals
// Less than 1.0 means that each typematic event fires at an increasing faster rate
timeoutChangeRate: 0.90,
// value: String
// Currently selected color/emoticon/etc.
value: "",
// _selectedCell: [private] Integer
// Index of the currently selected cell. Initially, none selected
_selectedCell: -1,
/*=====
// _currentFocus: [private] DomNode
// The currently focused cell (if the palette itself has focus), or otherwise
// the cell to be focused when the palette itself gets focus.
// Different from value, which represents the selected (i.e. clicked) cell.
_currentFocus: null,
=====*/
/*=====
// _xDim: [protected] Integer
// This is the number of cells horizontally across.
_xDim: null,
=====*/
/*=====
// _yDim: [protected] Integer
// This is the number of cells vertically down.
_yDim: null,
=====*/
// tabIndex: String
// Widget tab index.
tabIndex: "0",
// cellClass: [protected] String
// CSS class applied to each cell in the palette
cellClass: "dijitPaletteCell",
// dyeClass: [protected] Constructor
// Constructor for Object created for each cell of the palette.
// dyeClass should implements dijit.Dye interface
dyeClass: null,
// summary: String
// Localized summary for the palette table
summary: '',
_setSummaryAttr: "paletteTableNode",
_dyeFactory: function(value /*===== , row, col, title =====*/){
// summary:
// Return instance of dijit.Dye for specified cell of palette
// tags:
// extension
// Remove string support for 2.0
var dyeClassObj = typeof this.dyeClass == "string" ? lang.getObject(this.dyeClass) : this.dyeClass;
return new dyeClassObj(value);
},
_preparePalette: function(choices, titles) {
// summary:
// Subclass must call _preparePalette() from postCreate(), passing in the tooltip
// for each cell
// choices: String[][]
// id's for each cell of the palette, used to create Dye JS object for each cell
// titles: String[]
// Localized tooltip for each cell
this._cells = [];
var url = this._blankGif;
this.connect(this.gridNode, "ondijitclick", "_onCellClick");
for(var row=0; row < choices.length; row++){
var rowNode = domConstruct.create("tr", {tabIndex: "-1"}, this.gridNode);
for(var col=0; col < choices[row].length; col++){
var value = choices[row][col];
if(value){
var cellObject = this._dyeFactory(value, row, col, titles[value]);
var cellNode = domConstruct.create("td", {
"class": this.cellClass,
tabIndex: "-1",
title: titles[value],
role: "gridcell"
}, rowNode);
// prepare cell inner structure
cellObject.fillCell(cellNode, url);
cellNode.idx = this._cells.length;
// save cell info into _cells
this._cells.push({node:cellNode, dye:cellObject});
}
}
}
this._xDim = choices[0].length;
this._yDim = choices.length;
// Now set all events
// The palette itself is navigated to with the tab key on the keyboard
// Keyboard navigation within the Palette is with the arrow keys
// Spacebar selects the cell.
// For the up key the index is changed by negative the x dimension.
var keyIncrementMap = {
UP_ARROW: -this._xDim,
// The down key the index is increase by the x dimension.
DOWN_ARROW: this._xDim,
// Right and left move the index by 1.
RIGHT_ARROW: this.isLeftToRight() ? 1 : -1,
LEFT_ARROW: this.isLeftToRight() ? -1 : 1
};
for(var key in keyIncrementMap){
this.own(
typematic.addKeyListener(
this.domNode,
{charOrCode:keys[key], ctrlKey:false, altKey:false, shiftKey:false},
this,
function(){
var increment = keyIncrementMap[key];
return function(count){ this._navigateByKey(increment, count); };
}(),
this.timeoutChangeRate,
this.defaultTimeout
)
);
}
},
postCreate: function(){
this.inherited(arguments);
// Set initial navigable node.
this._setCurrent(this._cells[0].node);
},
focus: function(){
// summary:
// Focus this widget. Puts focus on the most recently focused cell.
// The cell already has tabIndex set, just need to set CSS and focus it
focus.focus(this._currentFocus);
},
_onCellClick: function(/*Event*/ evt){
// summary:
// Handler for click, enter key & space key. Selects the cell.
// evt:
// The event.
// tags:
// private
var target = evt.target;
// Find TD associated with click event. For ColorPalette user likely clicked IMG inside of TD
while(target.tagName != "TD"){
if(!target.parentNode || target == this.gridNode){ // probably can never happen, but just in case
return;
}
target = target.parentNode;
}
var value = this._getDye(target).getValue();
// First focus the clicked cell, and then send onChange() notification.
// onChange() (via _setValueAttr) must be after the focus call, because
// it may trigger a refocus to somewhere else (like the Editor content area), and that
// second focus should win.
this._setCurrent(target);
focus.focus(target);
this._setValueAttr(value, true);
event.stop(evt);
},
_setCurrent: function(/*DomNode*/ node){
// summary:
// Sets which node is the focused cell.
// description:
// At any point in time there's exactly one
// cell with tabIndex != -1. If focus is inside the palette then
// focus is on that cell.
//
// After calling this method, arrow key handlers and mouse click handlers
// should focus the cell in a setTimeout().
// tags:
// protected
if("_currentFocus" in this){
// Remove tabIndex on old cell
domAttr.set(this._currentFocus, "tabIndex", "-1");
}
// Set tabIndex of new cell
this._currentFocus = node;
if(node){
domAttr.set(node, "tabIndex", this.tabIndex);
}
},
_setValueAttr: function(value, priorityChange){
// summary:
// This selects a cell. It triggers the onChange event.
// value: String
// Value of the cell to select
// tags:
// protected
// priorityChange: Boolean?
// Optional parameter used to tell the select whether or not to fire
// onChange event.
// clear old selected cell
if(this._selectedCell >= 0){
domClass.remove(this._cells[this._selectedCell].node, this.cellClass + "Selected");
}
this._selectedCell = -1;
// search for cell matching specified value
if(value){
for(var i = 0; i < this._cells.length; i++){
if(value == this._cells[i].dye.getValue()){
this._selectedCell = i;
domClass.add(this._cells[i].node, this.cellClass + "Selected");
break;
}
}
}
// record new value, or null if no matching cell
this._set("value", this._selectedCell >= 0 ? value : null);
if(priorityChange || priorityChange === undefined){
this.onChange(value);
}
},
onChange: function(/*===== value =====*/){
// summary:
// Callback when a cell is selected.
// value: String
// Value corresponding to cell.
},
_navigateByKey: function(increment, typeCount){
// summary:
// This is the callback for typematic.
// It changes the focus and the highlighed cell.
// increment:
// How much the key is navigated.
// typeCount:
// How many times typematic has fired.
// tags:
// private
// typecount == -1 means the key is released.
if(typeCount == -1){ return; }
var newFocusIndex = this._currentFocus.idx + increment;
if(newFocusIndex < this._cells.length && newFocusIndex > -1){
var focusNode = this._cells[newFocusIndex].node;
this._setCurrent(focusNode);
// Actually focus the node, for the benefit of screen readers.
// Use defer because IE doesn't like changing focus inside of an event handler
this.defer(lang.hitch(focus, "focus", focusNode));
}
},
_getDye: function(/*DomNode*/ cell){
// summary:
// Get JS object for given cell DOMNode
return this._cells[cell.idx].dye;
}
});
/*=====
declare("dijit.Dye",
null,
{
// summary:
// Interface for the JS Object associated with a palette cell (i.e. DOMNode)
constructor: function(alias, row, col){
// summary:
// Initialize according to value or alias like "white"
// alias: String
},
getValue: function(){
// summary:
// Return "value" of cell; meaning of "value" varies by subclass.
// description:
// For example color hex value, emoticon ascii value etc, entity hex value.
},
fillCell: function(cell, blankGif){
// summary:
// Add cell DOMNode inner structure
// cell: DomNode
// The surrounding cell
// blankGif: String
// URL for blank cell image
}
}
);
=====*/
});
},
'dijit/form/ValidationTextBox':function(){
require({cache:{
'url:dijit/form/templates/ValidationTextBox.html':"<div class=\"dijit dijitReset dijitInline dijitLeft\"\n\tid=\"widget_${id}\" role=\"presentation\"\n\t><div class='dijitReset dijitValidationContainer'\n\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t/></div\n\t><div class=\"dijitReset dijitInputField dijitInputContainer\"\n\t\t><input class=\"dijitReset dijitInputInner\" data-dojo-attach-point='textbox,focusNode' autocomplete=\"off\"\n\t\t\t${!nameAttrSetting} type='${type}'\n\t/></div\n></div>\n"}});
define("dijit/form/ValidationTextBox", [
"dojo/_base/declare", // declare
"dojo/_base/kernel", // kernel.deprecated
"dojo/i18n", // i18n.getLocalization
"./TextBox",
"../Tooltip",
"dojo/text!./templates/ValidationTextBox.html",
"dojo/i18n!./nls/validate"
], function(declare, kernel, i18n, TextBox, Tooltip, template){
// module:
// dijit/form/ValidationTextBox
/*=====
var __Constraints = {
// locale: String
// locale used for validation, picks up value from this widget's lang attribute
// _flags_: anything
// various flags passed to pattern function
};
=====*/
var ValidationTextBox;
return ValidationTextBox = declare("dijit.form.ValidationTextBox", TextBox, {
// summary:
// Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
templateString: template,
// required: Boolean
// User is required to enter data into this field.
required: false,
// promptMessage: String
// If defined, display this hint string immediately on focus to the textbox, if empty.
// Also displays if the textbox value is Incomplete (not yet valid but will be with additional input).
// Think of this like a tooltip that tells the user what to do, not an error message
// that tells the user what they've done wrong.
//
// Message disappears when user starts typing.
promptMessage: "",
// invalidMessage: String
// The message to display if value is invalid.
// The translated string value is read from the message file by default.
// Set to "" to use the promptMessage instead.
invalidMessage: "$_unset_$",
// missingMessage: String
// The message to display if value is empty and the field is required.
// The translated string value is read from the message file by default.
// Set to "" to use the invalidMessage instead.
missingMessage: "$_unset_$",
// message: String
// Currently error/prompt message.
// When using the default tooltip implementation, this will only be
// displayed when the field is focused.
message: "",
// constraints: __Constraints
// user-defined object needed to pass parameters to the validator functions
constraints: {},
// pattern: [extension protected] String|Function(constraints) returning a string.
// This defines the regular expression used to validate the input.
// Do not add leading ^ or $ characters since the widget adds these.
// A function may be used to generate a valid pattern when dependent on constraints or other runtime factors.
// set('pattern', String|Function).
pattern: ".*",
// regExp: Deprecated [extension protected] String. Use "pattern" instead.
regExp: "",
regExpGen: function(/*__Constraints*/ /*===== constraints =====*/){
// summary:
// Deprecated. Use set('pattern', Function) instead.
},
// state: [readonly] String
// Shows current state (ie, validation result) of input (""=Normal, Incomplete, or Error)
state: "",
// tooltipPosition: String[]
// See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
tooltipPosition: [],
_deprecateRegExp: function(attr, value){
if(value != ValidationTextBox.prototype[attr]){
kernel.deprecated("ValidationTextBox id="+this.id+", set('" + attr + "', ...) is deprecated. Use set('pattern', ...) instead.", "", "2.0");
this.set('pattern', value);
}
},
_setRegExpGenAttr: function(/*Function*/ newFcn){
this._deprecateRegExp("regExpGen", newFcn);
this.regExpGen = this._getPatternAttr; // backward compat with this.regExpGen(this.constraints)
},
_setRegExpAttr: function(/*String*/ value){
this._deprecateRegExp("regExp", value);
},
_setValueAttr: function(){
// summary:
// Hook so set('value', ...) works.
this.inherited(arguments);
this.validate(this.focused);
},
validator: function(/*anything*/ value, /*__Constraints*/ constraints){
// summary:
// Overridable function used to validate the text input against the regular expression.
// tags:
// protected
return (new RegExp("^(?:" + this._getPatternAttr(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
(!this.required || !this._isEmpty(value)) &&
(this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
},
_isValidSubset: function(){
// summary:
// Returns true if the value is either already valid or could be made valid by appending characters.
// This is used for validation while the user [may be] still typing.
return this.textbox.value.search(this._partialre) == 0;
},
isValid: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Tests if value is valid.
// Can override with your own routine in a subclass.
// tags:
// protected
return this.validator(this.textbox.value, this.constraints);
},
_isEmpty: function(value){
// summary:
// Checks for whitespace
return (this.trim ? /^\s*$/ : /^$/).test(value); // Boolean
},
getErrorMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return an error message to show if appropriate
// tags:
// protected
var invalid = this.invalidMessage == "$_unset_$" ? this.messages.invalidMessage :
!this.invalidMessage ? this.promptMessage : this.invalidMessage;
var missing = this.missingMessage == "$_unset_$" ? this.messages.missingMessage :
!this.missingMessage ? invalid : this.missingMessage;
return (this.required && this._isEmpty(this.textbox.value)) ? missing : invalid; // String
},
getPromptMessage: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Return a hint message to show when widget is first focused
// tags:
// protected
return this.promptMessage; // String
},
_maskValidSubsetError: true,
validate: function(/*Boolean*/ isFocused){
// summary:
// Called by oninit, onblur, and onkeypress.
// description:
// Show missing or invalid messages if appropriate, and highlight textbox field.
// tags:
// protected
var message = "";
var isValid = this.disabled || this.isValid(isFocused);
if(isValid){ this._maskValidSubsetError = true; }
var isEmpty = this._isEmpty(this.textbox.value);
var isValidSubset = !isValid && isFocused && this._isValidSubset();
this._set("state", isValid ? "" : (((((!this._hasBeenBlurred || isFocused) && isEmpty) || isValidSubset) && (this._maskValidSubsetError || (isValidSubset && !this._hasBeenBlurred && isFocused))) ? "Incomplete" : "Error"));
this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
if(this.state == "Error"){
this._maskValidSubsetError = isFocused && isValidSubset; // we want the error to show up after a blur and refocus
message = this.getErrorMessage(isFocused);
}else if(this.state == "Incomplete"){
message = this.getPromptMessage(isFocused); // show the prompt whenever the value is not yet complete
this._maskValidSubsetError = !this._hasBeenBlurred || isFocused; // no Incomplete warnings while focused
}else if(isEmpty){
message = this.getPromptMessage(isFocused); // show the prompt whenever there's no error and no text
}
this.set("message", message);
return isValid;
},
displayMessage: function(/*String*/ message){
// summary:
// Overridable method to display validation errors/hints.
// By default uses a tooltip.
// tags:
// extension
if(message && this.focused){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
},
_refreshState: function(){
// Overrides TextBox._refreshState()
if(this._created){
this.validate(this.focused);
}
this.inherited(arguments);
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(params /*===== , srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree.
this.constraints = {};
this.baseClass += ' dijitValidationTextBox';
},
startup: function(){
this.inherited(arguments);
this._refreshState(); // after all _set* methods have run
},
_setConstraintsAttr: function(/*__Constraints*/ constraints){
if(!constraints.locale && this.lang){
constraints.locale = this.lang;
}
this._set("constraints", constraints);
this._refreshState();
},
_setPatternAttr: function(/*String|Function*/ pattern){
this._set("pattern", pattern); // don't set on INPUT to avoid native HTML5 validation
},
_getPatternAttr: function(/*__Constraints*/ constraints){
// summary:
// Hook to get the current regExp and to compute the partial validation RE.
var p = this.pattern;
var type = (typeof p).toLowerCase();
if(type == "function"){
p = this.pattern(constraints || this.constraints);
}
if(p != this._lastRegExp){
var partialre = "";
this._lastRegExp = p;
// parse the regexp and produce a new regexp that matches valid subsets
// if the regexp is .* then there's no use in matching subsets since everything is valid
if(p != ".*"){
p.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
function(re){
switch(re.charAt(0)){
case '{':
case '+':
case '?':
case '*':
case '^':
case '$':
case '|':
case '(':
partialre += re;
break;
case ")":
partialre += "|$)";
break;
default:
partialre += "(?:"+re+"|$)";
break;
}
});
}
try{ // this is needed for now since the above regexp parsing needs more test verification
"".search(partialre);
}catch(e){ // should never be here unless the original RE is bad or the parsing is bad
partialre = this.pattern;
console.warn('RegExp error in ' + this.declaredClass + ': ' + this.pattern);
} // should never be here unless the original RE is bad or the parsing is bad
this._partialre = "^(?:" + partialre + ")$";
}
return p;
},
postMixInProperties: function(){
this.inherited(arguments);
this.messages = i18n.getLocalization("dijit.form", "validate", this.lang);
this._setConstraintsAttr(this.constraints); // this needs to happen now (and later) due to codependency on _set*Attr calls attachPoints
},
_setDisabledAttr: function(/*Boolean*/ value){
this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
this._refreshState();
},
_setRequiredAttr: function(/*Boolean*/ value){
this._set("required", value);
this.focusNode.setAttribute("aria-required", value);
this._refreshState();
},
_setMessageAttr: function(/*String*/ message){
this._set("message", message);
this.displayMessage(message);
},
reset:function(){
// Overrides dijit/form/TextBox.reset() by also
// hiding errors about partial matches
this._maskValidSubsetError = true;
this.inherited(arguments);
},
_onBlur: function(){
// the message still exists but for back-compat, and to erase the tooltip
// (if the message is being displayed as a tooltip), call displayMessage('')
this.displayMessage('');
this.inherited(arguments);
}
});
});
},
'dijit/_base/typematic':function(){
define("dijit/_base/typematic", ["../typematic"], function(){
/*=====
return {
// summary:
// Deprecated, for back-compat, just loads top level module
};
=====*/
});
},
'dijit/layout/BorderContainer':function(){
define("dijit/layout/BorderContainer", [
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-construct", // domConstruct.destroy domConstruct.place
"dojo/dom-geometry", // domGeometry.marginBox
"dojo/dom-style", // domStyle.style
"dojo/_base/event", // event.stop
"dojo/keys",
"dojo/_base/lang", // lang.getObject lang.hitch
"dojo/on",
"dojo/touch",
"../_WidgetBase",
"../_Widget",
"../_TemplatedMixin",
"./_LayoutWidget",
"./utils" // layoutUtils.layoutChildren
], function(array, cookie, declare, domClass, domConstruct, domGeometry, domStyle, event, keys, lang, on, touch,
_WidgetBase, _Widget, _TemplatedMixin, _LayoutWidget, layoutUtils){
// module:
// dijit/layout/BorderContainer
var _Splitter = declare("dijit.layout._Splitter", [_Widget, _TemplatedMixin ],
{
// summary:
// A draggable spacer between two items in a `dijit/layout/BorderContainer`.
// description:
// This is instantiated by `dijit/layout/BorderContainer`. Users should not
// create it directly.
// tags:
// private
/*=====
// container: [const] dijit/layout/BorderContainer
// Pointer to the parent BorderContainer
container: null,
// child: [const] dijit/layout/_LayoutWidget
// Pointer to the pane associated with this splitter
child: null,
// region: [const] String
// Region of pane associated with this splitter.
// "top", "bottom", "left", "right".
region: null,
=====*/
// live: [const] Boolean
// If true, the child's size changes and the child widget is redrawn as you drag the splitter;
// otherwise, the size doesn't change until you drop the splitter (by mouse-up)
live: true,
templateString: '<div class="dijitSplitter" data-dojo-attach-event="onkeypress:_onKeyPress,press:_startDrag,onmouseenter:_onMouse,onmouseleave:_onMouse" tabIndex="0" role="separator"><div class="dijitSplitterThumb"></div></div>',
constructor: function(){
this._handlers = [];
},
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
this._factor = /top|left/.test(this.region) ? 1 : -1;
this._cookieName = this.container.id + "_" + this.region;
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V"));
if(this.container.persist){
// restore old size
var persistSize = cookie(this._cookieName);
if(persistSize){
this.child.domNode.style[this.horizontal ? "height" : "width"] = persistSize;
}
}
},
_computeMaxSize: function(){
// summary:
// Return the maximum size that my corresponding pane can be set to
var dim = this.horizontal ? 'h' : 'w',
childSize = domGeometry.getMarginBox(this.child.domNode)[dim],
center = array.filter(this.container.getChildren(), function(child){ return child.region == "center";})[0],
spaceAvailable = domGeometry.getMarginBox(center.domNode)[dim]; // can expand until center is crushed to 0
return Math.min(this.child.maxSize, childSize + spaceAvailable);
},
_startDrag: function(e){
if(!this.cover){
this.cover = domConstruct.place("<div class=dijitSplitterCover></div>", this.child.domNode, "after");
}
domClass.add(this.cover, "dijitSplitterCoverActive");
// Safeguard in case the stop event was missed. Shouldn't be necessary if we always get the mouse up.
if(this.fake){ domConstruct.destroy(this.fake); }
if(!(this._resize = this.live)){ //TODO: disable live for IE6?
// create fake splitter to display at old position while we drag
(this.fake = this.domNode.cloneNode(true)).removeAttribute("id");
domClass.add(this.domNode, "dijitSplitterShadow");
domConstruct.place(this.fake, this.domNode, "after");
}
domClass.add(this.domNode, "dijitSplitterActive dijitSplitter" + (this.horizontal ? "H" : "V") + "Active");
if(this.fake){
domClass.remove(this.fake, "dijitSplitterHover dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover");
}
//Performance: load data info local vars for onmousevent function closure
var factor = this._factor,
isHorizontal = this.horizontal,
axis = isHorizontal ? "pageY" : "pageX",
pageStart = e[axis],
splitterStyle = this.domNode.style,
dim = isHorizontal ? 'h' : 'w',
childStart = domGeometry.getMarginBox(this.child.domNode)[dim],
max = this._computeMaxSize(),
min = this.child.minSize || 20,
region = this.region,
splitterAttr = region == "top" || region == "bottom" ? "top" : "left", // style attribute of splitter to adjust
splitterStart = parseInt(splitterStyle[splitterAttr], 10),
resize = this._resize,
layoutFunc = lang.hitch(this.container, "_layoutChildren", this.child.id),
de = this.ownerDocument;
this._handlers = this._handlers.concat([
on(de, touch.move, this._drag = function(e, forceResize){
var delta = e[axis] - pageStart,
childSize = factor * delta + childStart,
boundChildSize = Math.max(Math.min(childSize, max), min);
if(resize || forceResize){
layoutFunc(boundChildSize);
}
// TODO: setting style directly (usually) sets content box size, need to set margin box size
splitterStyle[splitterAttr] = delta + splitterStart + factor*(boundChildSize - childSize) + "px";
}),
on(de, "dragstart", event.stop),
on(this.ownerDocumentBody, "selectstart", event.stop),
on(de, touch.release, lang.hitch(this, "_stopDrag"))
]);
event.stop(e);
},
_onMouse: function(e){
// summary:
// Handler for onmouseenter / onmouseleave events
var o = (e.type == "mouseover" || e.type == "mouseenter");
domClass.toggle(this.domNode, "dijitSplitterHover", o);
domClass.toggle(this.domNode, "dijitSplitter" + (this.horizontal ? "H" : "V") + "Hover", o);
},
_stopDrag: function(e){
try{
if(this.cover){
domClass.remove(this.cover, "dijitSplitterCoverActive");
}
if(this.fake){ domConstruct.destroy(this.fake); }
domClass.remove(this.domNode, "dijitSplitterActive dijitSplitter"
+ (this.horizontal ? "H" : "V") + "Active dijitSplitterShadow");
this._drag(e); //TODO: redundant with onmousemove?
this._drag(e, true);
}finally{
this._cleanupHandlers();
delete this._drag;
}
if(this.container.persist){
cookie(this._cookieName, this.child.domNode.style[this.horizontal ? "height" : "width"], {expires:365});
}
},
_cleanupHandlers: function(){
var h;
while(h = this._handlers.pop()){ h.remove(); }
},
_onKeyPress: function(/*Event*/ e){
// should we apply typematic to this?
this._resize = true;
var horizontal = this.horizontal;
var tick = 1;
switch(e.charOrCode){
case horizontal ? keys.UP_ARROW : keys.LEFT_ARROW:
tick *= -1;
// break;
case horizontal ? keys.DOWN_ARROW : keys.RIGHT_ARROW:
break;
default:
// this.inherited(arguments);
return;
}
var childSize = domGeometry.getMarginSize(this.child.domNode)[ horizontal ? 'h' : 'w' ] + this._factor * tick;
this.container._layoutChildren(this.child.id, Math.max(Math.min(childSize, this._computeMaxSize()), this.child.minSize));
event.stop(e);
},
destroy: function(){
this._cleanupHandlers();
delete this.child;
delete this.container;
delete this.cover;
delete this.fake;
this.inherited(arguments);
}
});
var _Gutter = declare("dijit.layout._Gutter", [_Widget, _TemplatedMixin],
{
// summary:
// Just a spacer div to separate side pane from center pane.
// Basically a trick to lookup the gutter/splitter width from the theme.
// description:
// Instantiated by `dijit/layout/BorderContainer`. Users should not
// create directly.
// tags:
// private
templateString: '<div class="dijitGutter" role="presentation"></div>',
postMixInProperties: function(){
this.inherited(arguments);
this.horizontal = /top|bottom/.test(this.region);
},
buildRendering: function(){
this.inherited(arguments);
domClass.add(this.domNode, "dijitGutter" + (this.horizontal ? "H" : "V"));
}
});
var BorderContainer = declare("dijit.layout.BorderContainer", _LayoutWidget, {
// summary:
// Provides layout in up to 5 regions, a mandatory center with optional borders along its 4 sides.
// description:
// A BorderContainer is a box with a specified size, such as style="width: 500px; height: 500px;",
// that contains a child widget marked region="center" and optionally children widgets marked
// region equal to "top", "bottom", "leading", "trailing", "left" or "right".
// Children along the edges will be laid out according to width or height dimensions and may
// include optional splitters (splitter="true") to make them resizable by the user. The remaining
// space is designated for the center region.
//
// The outer size must be specified on the BorderContainer node. Width must be specified for the sides
// and height for the top and bottom, respectively. No dimensions should be specified on the center;
// it will fill the remaining space. Regions named "leading" and "trailing" may be used just like
// "left" and "right" except that they will be reversed in right-to-left environments.
//
// For complex layouts, multiple children can be specified for a single region. In this case, the
// layoutPriority flag on the children determines which child is closer to the edge (low layoutPriority)
// and which child is closer to the center (high layoutPriority). layoutPriority can also be used
// instead of the design attribute to control layout precedence of horizontal vs. vertical panes.
//
// See `BorderContainer.ChildWidgetProperties` for details on the properties that can be set on
// children of a `BorderContainer`.
// example:
// | <div data-dojo-type="dijit/layout/BorderContainer" data-dojo-props="design: 'sidebar', gutters: false"
// | style="width: 400px; height: 300px;">
// | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'top'">header text</div>
// | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'right', splitter: true" style="width: 200px;">table of contents</div>
// | <div data-dojo-type="dijit/layout/ContentPane" data-dojo-props="region: 'center'">client area</div>
// | </div>
// design: String
// Which design is used for the layout:
//
// - "headline" (default) where the top and bottom extend the full width of the container
// - "sidebar" where the left and right sides extend from top to bottom.
design: "headline",
// gutters: [const] Boolean
// Give each pane a border and margin.
// Margin determined by domNode.paddingLeft.
// When false, only resizable panes have a gutter (i.e. draggable splitter) for resizing.
gutters: true,
// liveSplitters: [const] Boolean
// Specifies whether splitters resize as you drag (true) or only upon mouseup (false)
liveSplitters: true,
// persist: Boolean
// Save splitter positions in a cookie.
persist: false,
baseClass: "dijitBorderContainer",
// _splitterClass: Function||String
// Optional hook to override the default Splitter widget used by BorderContainer
_splitterClass: _Splitter,
postMixInProperties: function(){
// change class name to indicate that BorderContainer is being used purely for
// layout (like LayoutContainer) rather than for pretty formatting.
if(!this.gutters){
this.baseClass += "NoGutter";
}
this.inherited(arguments);
},
startup: function(){
if(this._started){ return; }
array.forEach(this.getChildren(), this._setupChild, this);
this.inherited(arguments);
},
_setupChild: function(/*dijit/_WidgetBase*/ child){
// Override _LayoutWidget._setupChild().
var region = child.region;
if(region){
this.inherited(arguments);
domClass.add(child.domNode, this.baseClass+"Pane");
var ltr = this.isLeftToRight();
if(region == "leading"){ region = ltr ? "left" : "right"; }
if(region == "trailing"){ region = ltr ? "right" : "left"; }
// Create draggable splitter for resizing pane,
// or alternately if splitter=false but BorderContainer.gutters=true then
// insert dummy div just for spacing
if(region != "center" && (child.splitter || this.gutters) && !child._splitterWidget){
var _Splitter = child.splitter ? this._splitterClass : _Gutter;
if(lang.isString(_Splitter)){
_Splitter = lang.getObject(_Splitter); // for back-compat, remove in 2.0
}
var splitter = new _Splitter({
id: child.id + "_splitter",
container: this,
child: child,
region: region,
live: this.liveSplitters
});
splitter.isSplitter = true;
child._splitterWidget = splitter;
domConstruct.place(splitter.domNode, child.domNode, "after");
// Splitters aren't added as Contained children, so we need to call startup explicitly
splitter.startup();
}
child.region = region; // TODO: technically wrong since it overwrites "trailing" with "left" etc.
}
},
layout: function(){
// Implement _LayoutWidget.layout() virtual method.
this._layoutChildren();
},
addChild: function(/*dijit/_WidgetBase*/ child, /*Integer?*/ insertIndex){
// Override _LayoutWidget.addChild().
this.inherited(arguments);
if(this._started){
this.layout(); //OPT
}
},
removeChild: function(/*dijit/_WidgetBase*/ child){
// Override _LayoutWidget.removeChild().
var region = child.region;
var splitter = child._splitterWidget;
if(splitter){
splitter.destroy();
delete child._splitterWidget;
}
this.inherited(arguments);
if(this._started){
this._layoutChildren();
}
// Clean up whatever style changes we made to the child pane.
// Unclear how height and width should be handled.
domClass.remove(child.domNode, this.baseClass+"Pane");
domStyle.set(child.domNode, {
top: "auto",
bottom: "auto",
left: "auto",
right: "auto",
position: "static"
});
domStyle.set(child.domNode, region == "top" || region == "bottom" ? "width" : "height", "auto");
},
getChildren: function(){
// Override _LayoutWidget.getChildren() to only return real children, not the splitters.
return array.filter(this.inherited(arguments), function(widget){
return !widget.isSplitter;
});
},
// TODO: remove in 2.0
getSplitter: function(/*String*/region){
// summary:
// Returns the widget responsible for rendering the splitter associated with region
// tags:
// deprecated
return array.filter(this.getChildren(), function(child){
return child.region == region;
})[0]._splitterWidget;
},
resize: function(newSize, currentSize){
// Overrides _LayoutWidget.resize().
// resetting potential padding to 0px to provide support for 100% width/height + padding
// TODO: this hack doesn't respect the box model and is a temporary fix
if(!this.cs || !this.pe){
var node = this.domNode;
this.cs = domStyle.getComputedStyle(node);
this.pe = domGeometry.getPadExtents(node, this.cs);
this.pe.r = domStyle.toPixelValue(node, this.cs.paddingRight);
this.pe.b = domStyle.toPixelValue(node, this.cs.paddingBottom);
domStyle.set(node, "padding", "0px");
}
this.inherited(arguments);
},
_layoutChildren: function(/*String?*/ changedChildId, /*Number?*/ changedChildSize){
// summary:
// This is the main routine for setting size/position of each child.
// description:
// With no arguments, measures the height of top/bottom panes, the width
// of left/right panes, and then sizes all panes accordingly.
//
// With changedRegion specified (as "left", "top", "bottom", or "right"),
// it changes that region's width/height to changedRegionSize and
// then resizes other regions that were affected.
// changedChildId:
// Id of the child which should be resized because splitter was dragged.
// changedChildSize:
// The new width/height (in pixels) to make specified child
if(!this._borderBox || !this._borderBox.h){
// We are currently hidden, or we haven't been sized by our parent yet.
// Abort. Someone will resize us later.
return;
}
// Generate list of wrappers of my children in the order that I want layoutChildren()
// to process them (i.e. from the outside to the inside)
var wrappers = array.map(this.getChildren(), function(child, idx){
return {
pane: child,
weight: [
child.region == "center" ? Infinity : 0,
child.layoutPriority,
(this.design == "sidebar" ? 1 : -1) * (/top|bottom/.test(child.region) ? 1 : -1),
idx
]
};
}, this);
wrappers.sort(function(a, b){
var aw = a.weight, bw = b.weight;
for(var i=0; i<aw.length; i++){
if(aw[i] != bw[i]){
return aw[i] - bw[i];
}
}
return 0;
});
// Make new list, combining the externally specified children with splitters and gutters
var childrenAndSplitters = [];
array.forEach(wrappers, function(wrapper){
var pane = wrapper.pane;
childrenAndSplitters.push(pane);
if(pane._splitterWidget){
childrenAndSplitters.push(pane._splitterWidget);
}
});
// Compute the box in which to lay out my children
var dim = {
l: this.pe.l,
t: this.pe.t,
w: this._borderBox.w - this.pe.w,
h: this._borderBox.h - this.pe.h
};
// Layout the children, possibly changing size due to a splitter drag
layoutUtils.layoutChildren(this.domNode, dim, childrenAndSplitters,
changedChildId, changedChildSize);
},
destroyRecursive: function(){
// Destroy splitters first, while getChildren() still works
array.forEach(this.getChildren(), function(child){
var splitter = child._splitterWidget;
if(splitter){
splitter.destroy();
}
delete child._splitterWidget;
});
// Then destroy the real children, and myself
this.inherited(arguments);
}
});
BorderContainer.ChildWidgetProperties = {
// summary:
// These properties can be specified for the children of a BorderContainer.
// region: [const] String
// Values: "top", "bottom", "leading", "trailing", "left", "right", "center".
// See the `dijit/layout/BorderContainer` description for details.
region: '',
// layoutPriority: [const] Number
// Children with a higher layoutPriority will be placed closer to the BorderContainer center,
// between children with a lower layoutPriority.
layoutPriority: 0,
// splitter: [const] Boolean
// Parameter for children where region != "center".
// If true, enables user to resize the widget by putting a draggable splitter between
// this widget and the region=center widget.
splitter: false,
// minSize: [const] Number
// Specifies a minimum size (in pixels) for this widget when resized by a splitter.
minSize: 0,
// maxSize: [const] Number
// Specifies a maximum size (in pixels) for this widget when resized by a splitter.
maxSize: Infinity
};
// Since any widget can be specified as a LayoutContainer child, mix it
// into the base widget class. (This is a hack, but it's effective.)
// This is for the benefit of the parser. Remove for 2.0. Also, hide from doc viewer.
lang.extend(_WidgetBase, /*===== {} || =====*/ BorderContainer.ChildWidgetProperties);
// For monkey patching
BorderContainer._Splitter = _Splitter;
BorderContainer._Gutter = _Gutter;
return BorderContainer;
});
},
'dijit/_base':function(){
define("dijit/_base", [
"./main",
"./a11y", // used to be in dijit/_base/manager
"./WidgetSet", // used to be in dijit/_base/manager
"./_base/focus",
"./_base/manager",
"./_base/place",
"./_base/popup",
"./_base/scroll",
"./_base/sniff",
"./_base/typematic",
"./_base/wai",
"./_base/window"
], function(dijit){
// module:
// dijit/_base
/*=====
return {
// summary:
// Includes all the modules in dijit/_base
};
=====*/
return dijit._base;
});
},
'dojo/window':function(){
define("dojo/window", ["./_base/lang", "./sniff", "./_base/window", "./dom", "./dom-geometry", "./dom-style"],
function(lang, has, baseWindow, dom, geom, style){
// module:
// dojo/window
var window = {
// summary:
// TODOC
getBox: function(/*Document?*/ doc){
// summary:
// Returns the dimensions and scroll position of the viewable area of a browser window
doc = doc || baseWindow.doc;
var
scrollRoot = (doc.compatMode == 'BackCompat') ? baseWindow.body(doc) : doc.documentElement,
// get scroll position
scroll = geom.docScroll(doc), // scrollRoot.scrollTop/Left should work
w, h;
if(has("touch")){ // if(scrollbars not supported)
var uiWindow = window.get(doc); // use UI window, not dojo.global window
// on mobile, scrollRoot.clientHeight <= uiWindow.innerHeight <= scrollRoot.offsetHeight, return uiWindow.innerHeight
w = uiWindow.innerWidth || scrollRoot.clientWidth; // || scrollRoot.clientXXX probably never evaluated
h = uiWindow.innerHeight || scrollRoot.clientHeight;
}else{
// on desktops, scrollRoot.clientHeight <= scrollRoot.offsetHeight <= uiWindow.innerHeight, return scrollRoot.clientHeight
// uiWindow.innerWidth/Height includes the scrollbar and cannot be used
w = scrollRoot.clientWidth;
h = scrollRoot.clientHeight;
}
return {
l: scroll.x,
t: scroll.y,
w: w,
h: h
};
},
get: function(/*Document*/ doc){
// summary:
// Get window object associated with document doc.
// doc:
// The document to get the associated window for.
// 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(has("ie") && 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
},
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 = dom.byId(node);
var doc = node.ownerDocument || baseWindow.doc, // TODO: why baseWindow.doc? Isn't node.ownerDocument always defined?
body = baseWindow.body(doc),
html = doc.documentElement || body.parentNode,
isIE = has("ie"), isWK = has("webkit");
// if an untested browser, then use the native method
if((!(has("mozilla") || isIE || isWK || has("opera")) || node == body || node == html) && (typeof node.scrollIntoView != "undefined")){
node.scrollIntoView(false); // short-circuit to native if possible
return;
}
var backCompat = doc.compatMode == 'BackCompat',
clientAreaRoot = (isIE >= 9 && "frameElement" in node.ownerDocument.parentWindow)
? ((html.clientHeight > 0 && html.clientWidth > 0 && (body.clientHeight == 0 || body.clientWidth == 0 || body.clientHeight > html.clientHeight || body.clientWidth > html.clientWidth)) ? html : body)
: (backCompat ? body : html),
scrollRoot = isWK ? body : clientAreaRoot,
rootWidth = clientAreaRoot.clientWidth,
rootHeight = clientAreaRoot.clientHeight,
rtl = !geom.isBodyLtr(doc),
nodePos = pos || geom.position(node),
el = node.parentNode,
isFixed = function(el){
return ((isIE <= 6 || (isIE && backCompat))? false : (style.get(el, 'position').toLowerCase() == "fixed"));
};
if(isFixed(node)){ return; } // nothing to do
while(el){
if(el == body){ el = scrollRoot; }
var elPos = geom.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 = geom.getPadBorderExtents(el);
elPos.w -= pb.w; elPos.h -= pb.h; elPos.x += pb.l; elPos.y += pb.t;
var clientSize = el.clientWidth,
scrollBarSize = elPos.w - clientSize;
if(clientSize > 0 && scrollBarSize > 0){
elPos.w = clientSize;
elPos.x += (rtl && (isIE || el.clientLeft > pb.l/*Chrome*/)) ? scrollBarSize : 0;
}
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);
if(rtl && ((isIE == 8 && !backCompat) || isIE >= 9)){ s = -s; }
nodePos.x += el.scrollLeft;
el.scrollLeft += 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);
}
}
};
1 && lang.setObject("dojo.window", window);
return window;
});
},
'dojo/number':function(){
define("dojo/number", [/*===== "./_base/declare", =====*/ "./_base/lang", "./i18n", "./i18n!./cldr/nls/number", "./string", "./regexp"],
function(/*===== declare, =====*/ lang, i18n, nlsNumber, dstring, dregexp){
// module:
// dojo/number
var number = {
// summary:
// localized formatting and parsing routines for Number
};
lang.setObject("dojo.number", number);
/*=====
number.__FormatOptions = declare(null, {
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// places: Number?
// fixed number of decimal places to show. This overrides any
// information in the provided pattern.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means do not round.
// locale: String?
// override the locale used to determine formatting rules
// fractional: Boolean?
// If false, show no decimal places, overriding places and pattern settings.
});
=====*/
number.format = function(/*Number*/ value, /*number.__FormatOptions?*/ options){
// summary:
// Format a Number as a String, using locale-specific settings
// description:
// Create a string from a Number using a known localized pattern.
// Formatting patterns appropriate to the locale are chosen from the
// [Common Locale Data Repository](http://unicode.org/cldr) as well as the appropriate symbols and
// delimiters.
// If value is Infinity, -Infinity, or is not a valid JavaScript number, return null.
// value:
// the number to be formatted
options = lang.mixin({}, options || {});
var locale = i18n.normalizeLocale(options.locale),
bundle = i18n.getLocalization("dojo.cldr", "number", locale);
options.customs = bundle;
var pattern = options.pattern || bundle[(options.type || "decimal") + "Format"];
if(isNaN(value) || Math.abs(value) == Infinity){ return null; } // null
return number._applyPattern(value, pattern, options); // String
};
//number._numberPatternRE = /(?:[#0]*,?)*[#0](?:\.0*#*)?/; // not precise, but good enough
number._numberPatternRE = /[#0,]*[#0](?:\.0*#*)?/; // not precise, but good enough
number._applyPattern = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatOptions?*/ options){
// summary:
// Apply pattern to format value as a string using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted.
// pattern:
// a pattern string as described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// options: number.__FormatOptions?
// _applyPattern is usually called via `dojo/number.format()` which
// populates an extra property in the options parameter, "customs".
// The customs object specifies group and decimal parameters if set.
//TODO: support escapes
options = options || {};
var group = options.customs.group,
decimal = options.customs.decimal,
patternList = pattern.split(';'),
positivePattern = patternList[0];
pattern = patternList[(value < 0) ? 1 : 0] || ("-" + positivePattern);
//TODO: only test against unescaped
if(pattern.indexOf('%') != -1){
value *= 100;
}else if(pattern.indexOf('\u2030') != -1){
value *= 1000; // per mille
}else if(pattern.indexOf('\u00a4') != -1){
group = options.customs.currencyGroup || group;//mixins instead?
decimal = options.customs.currencyDecimal || decimal;// Should these be mixins instead?
pattern = pattern.replace(/\u00a4{1,3}/, function(match){
var prop = ["symbol", "currency", "displayName"][match.length-1];
return options[prop] || options.currency || "";
});
}else if(pattern.indexOf('E') != -1){
throw new Error("exponential notation not supported");
}
//TODO: support @ sig figs?
var numberPatternRE = number._numberPatternRE;
var numberPattern = positivePattern.match(numberPatternRE);
if(!numberPattern){
throw new Error("unable to find a number expression in pattern: "+pattern);
}
if(options.fractional === false){ options.places = 0; }
return pattern.replace(numberPatternRE,
number._formatAbsolute(value, numberPattern[0], {decimal: decimal, group: group, places: options.places, round: options.round}));
};
number.round = function(/*Number*/ value, /*Number?*/ places, /*Number?*/ increment){
// summary:
// Rounds to the nearest value with the given number of decimal places, away from zero
// description:
// Rounds to the nearest value with the given number of decimal places, away from zero if equal.
// Similar to Number.toFixed(), but compensates for browser quirks. Rounding can be done by
// fractional increments also, such as the nearest quarter.
// NOTE: Subject to floating point errors. See dojox/math/round for experimental workaround.
// value:
// The number to round
// places:
// The number of decimal places where rounding takes place. Defaults to 0 for whole rounding.
// Must be non-negative.
// increment:
// Rounds next place to nearest value of increment/10. 10 by default.
// example:
// | >>> number.round(-0.5)
// | -1
// | >>> number.round(162.295, 2)
// | 162.29 // note floating point error. Should be 162.3
// | >>> number.round(10.71, 0, 2.5)
// | 10.75
var factor = 10 / (increment || 10);
return (factor * +value).toFixed(places) / factor; // Number
};
if((0.9).toFixed() == 0){
// (isIE) toFixed() bug workaround: Rounding fails on IE when most significant digit
// is just after the rounding place and is >=5
var round = number.round;
number.round = function(v, p, m){
var d = Math.pow(10, -p || 0), a = Math.abs(v);
if(!v || a >= d){
d = 0;
}else{
a /= d;
if(a < 0.5 || a >= 0.95){
d = 0;
}
}
return round(v, p, m) + (v > 0 ? d : -d);
};
// Use "doc hint" so the doc parser ignores this new definition of round(), and uses the one above.
/*===== number.round = round; =====*/
}
/*=====
number.__FormatAbsoluteOptions = declare(null, {
// decimal: String?
// the decimal separator
// group: String?
// the group separator
// places: Number|String?
// number of decimal places. the range "n,m" will format to m places.
// round: Number?
// 5 rounds to nearest .5; 0 rounds to nearest whole (default). -1
// means don't round.
});
=====*/
number._formatAbsolute = function(/*Number*/ value, /*String*/ pattern, /*number.__FormatAbsoluteOptions?*/ options){
// summary:
// Apply numeric pattern to absolute value using options. Gives no
// consideration to local customs.
// value:
// the number to be formatted, ignores sign
// pattern:
// the number portion of a pattern (e.g. `#,##0.00`)
options = options || {};
if(options.places === true){options.places=0;}
if(options.places === Infinity){options.places=6;} // avoid a loop; pick a limit
var patternParts = pattern.split("."),
comma = typeof options.places == "string" && options.places.indexOf(","),
maxPlaces = options.places;
if(comma){
maxPlaces = options.places.substring(comma + 1);
}else if(!(maxPlaces >= 0)){
maxPlaces = (patternParts[1] || []).length;
}
if(!(options.round < 0)){
value = number.round(value, maxPlaces, options.round);
}
var valueParts = String(Math.abs(value)).split("."),
fractional = valueParts[1] || "";
if(patternParts[1] || options.places){
if(comma){
options.places = options.places.substring(0, comma);
}
// Pad fractional with trailing zeros
var pad = options.places !== undefined ? options.places : (patternParts[1] && patternParts[1].lastIndexOf("0") + 1);
if(pad > fractional.length){
valueParts[1] = dstring.pad(fractional, pad, '0', true);
}
// Truncate fractional
if(maxPlaces < fractional.length){
valueParts[1] = fractional.substr(0, maxPlaces);
}
}else{
if(valueParts[1]){ valueParts.pop(); }
}
// Pad whole with leading zeros
var patternDigits = patternParts[0].replace(',', '');
pad = patternDigits.indexOf("0");
if(pad != -1){
pad = patternDigits.length - pad;
if(pad > valueParts[0].length){
valueParts[0] = dstring.pad(valueParts[0], pad);
}
// Truncate whole
if(patternDigits.indexOf("#") == -1){
valueParts[0] = valueParts[0].substr(valueParts[0].length - pad);
}
}
// Add group separators
var index = patternParts[0].lastIndexOf(','),
groupSize, groupSize2;
if(index != -1){
groupSize = patternParts[0].length - index - 1;
var remainder = patternParts[0].substr(0, index);
index = remainder.lastIndexOf(',');
if(index != -1){
groupSize2 = remainder.length - index - 1;
}
}
var pieces = [];
for(var whole = valueParts[0]; whole;){
var off = whole.length - groupSize;
pieces.push((off > 0) ? whole.substr(off) : whole);
whole = (off > 0) ? whole.slice(0, off) : "";
if(groupSize2){
groupSize = groupSize2;
delete groupSize2;
}
}
valueParts[0] = pieces.reverse().join(options.group || ",");
return valueParts.join(options.decimal || ".");
};
/*=====
number.__RegexpOptions = declare(null, {
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// places: Number|String?
// number of decimal places to accept: Infinity, a positive number, or
// a range "n,m". Defined by pattern or Infinity if pattern not provided.
});
=====*/
number.regexp = function(/*number.__RegexpOptions?*/ options){
// summary:
// Builds the regular needed to parse a number
// description:
// Returns regular expression with positive and negative match, group
// and decimal separators
return number._parseInfo(options).regexp; // String
};
number._parseInfo = function(/*Object?*/ options){
options = options || {};
var locale = i18n.normalizeLocale(options.locale),
bundle = i18n.getLocalization("dojo.cldr", "number", locale),
pattern = options.pattern || bundle[(options.type || "decimal") + "Format"],
//TODO: memoize?
group = bundle.group,
decimal = bundle.decimal,
factor = 1;
if(pattern.indexOf('%') != -1){
factor /= 100;
}else if(pattern.indexOf('\u2030') != -1){
factor /= 1000; // per mille
}else{
var isCurrency = pattern.indexOf('\u00a4') != -1;
if(isCurrency){
group = bundle.currencyGroup || group;
decimal = bundle.currencyDecimal || decimal;
}
}
//TODO: handle quoted escapes
var patternList = pattern.split(';');
if(patternList.length == 1){
patternList.push("-" + patternList[0]);
}
var re = dregexp.buildGroupRE(patternList, function(pattern){
pattern = "(?:"+dregexp.escapeString(pattern, '.')+")";
return pattern.replace(number._numberPatternRE, function(format){
var flags = {
signed: false,
separator: options.strict ? group : [group,""],
fractional: options.fractional,
decimal: decimal,
exponent: false
},
parts = format.split('.'),
places = options.places;
// special condition for percent (factor != 1)
// allow decimal places even if not specified in pattern
if(parts.length == 1 && factor != 1){
parts[1] = "###";
}
if(parts.length == 1 || places === 0){
flags.fractional = false;
}else{
if(places === undefined){ places = options.pattern ? parts[1].lastIndexOf('0') + 1 : Infinity; }
if(places && options.fractional == undefined){flags.fractional = true;} // required fractional, unless otherwise specified
if(!options.places && (places < parts[1].length)){ places += "," + parts[1].length; }
flags.places = places;
}
var groups = parts[0].split(',');
if(groups.length > 1){
flags.groupSize = groups.pop().length;
if(groups.length > 1){
flags.groupSize2 = groups.pop().length;
}
}
return "("+number._realNumberRegexp(flags)+")";
});
}, true);
if(isCurrency){
// substitute the currency symbol for the placeholder in the pattern
re = re.replace(/([\s\xa0]*)(\u00a4{1,3})([\s\xa0]*)/g, function(match, before, target, after){
var prop = ["symbol", "currency", "displayName"][target.length-1],
symbol = dregexp.escapeString(options[prop] || options.currency || "");
before = before ? "[\\s\\xa0]" : "";
after = after ? "[\\s\\xa0]" : "";
if(!options.strict){
if(before){before += "*";}
if(after){after += "*";}
return "(?:"+before+symbol+after+")?";
}
return before+symbol+after;
});
}
//TODO: substitute localized sign/percent/permille/etc.?
// normalize whitespace and return
return {regexp: re.replace(/[\xa0 ]/g, "[\\s\\xa0]"), group: group, decimal: decimal, factor: factor}; // Object
};
/*=====
number.__ParseOptions = declare(null, {
// pattern: String?
// override [formatting pattern](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// with this string. Default value is based on locale. Overriding this property will defeat
// localization. Literal characters in patterns are not supported.
// type: String?
// choose a format type based on the locale from the following:
// decimal, scientific (not yet supported), percent, currency. decimal by default.
// locale: String?
// override the locale used to determine formatting rules
// strict: Boolean?
// strict parsing, false by default. Strict parsing requires input as produced by the format() method.
// Non-strict is more permissive, e.g. flexible on white space, omitting thousands separators
// fractional: Boolean|Array?
// Whether to include the fractional portion, where the number of decimal places are implied by pattern
// or explicit 'places' parameter. The value [true,false] makes the fractional portion optional.
});
=====*/
number.parse = function(/*String*/ expression, /*number.__ParseOptions?*/ options){
// summary:
// Convert a properly formatted string to a primitive Number, using
// locale-specific settings.
// description:
// Create a Number from a string using a known localized pattern.
// Formatting patterns are chosen appropriate to the locale
// and follow the syntax described by
// [unicode.org TR35](http://www.unicode.org/reports/tr35/#Number_Format_Patterns)
// Note that literal characters in patterns are not supported.
// expression:
// A string representation of a Number
var info = number._parseInfo(options),
results = (new RegExp("^"+info.regexp+"$")).exec(expression);
if(!results){
return NaN; //NaN
}
var absoluteMatch = results[1]; // match for the positive expression
if(!results[1]){
if(!results[2]){
return NaN; //NaN
}
// matched the negative pattern
absoluteMatch =results[2];
info.factor *= -1;
}
// Transform it to something Javascript can parse as a number. Normalize
// decimal point and strip out group separators or alternate forms of whitespace
absoluteMatch = absoluteMatch.
replace(new RegExp("["+info.group + "\\s\\xa0"+"]", "g"), "").
replace(info.decimal, ".");
// Adjust for negative sign, percent, etc. as necessary
return absoluteMatch * info.factor; //Number
};
/*=====
number.__RealNumberRegexpFlags = declare(null, {
// places: Number?
// The integer number of decimal places or a range given as "n,m". If
// not given, the decimal part is optional and the number of places is
// unlimited.
// decimal: String?
// A string for the character used as the decimal point. Default
// is ".".
// fractional: Boolean|Array?
// Whether decimal places are used. Can be true, false, or [true,
// false]. Default is [true, false] which means optional.
// exponent: Boolean|Array?
// Express in exponential notation. Can be true, false, or [true,
// false]. Default is [true, false], (i.e. will match if the
// exponential part is present are not).
// eSigned: Boolean|Array?
// The leading plus-or-minus sign on the exponent. Can be true,
// false, or [true, false]. Default is [true, false], (i.e. will
// match if it is signed or unsigned). flags in regexp.integer can be
// applied.
});
=====*/
number._realNumberRegexp = function(/*__RealNumberRegexpFlags?*/ flags){
// summary:
// Builds a regular expression to match a real number in exponential
// notation
// assign default values to missing parameters
flags = flags || {};
//TODO: use mixin instead?
if(!("places" in flags)){ flags.places = Infinity; }
if(typeof flags.decimal != "string"){ flags.decimal = "."; }
if(!("fractional" in flags) || /^0/.test(flags.places)){ flags.fractional = [true, false]; }
if(!("exponent" in flags)){ flags.exponent = [true, false]; }
if(!("eSigned" in flags)){ flags.eSigned = [true, false]; }
var integerRE = number._integerRegexp(flags),
decimalRE = dregexp.buildGroupRE(flags.fractional,
function(q){
var re = "";
if(q && (flags.places!==0)){
re = "\\" + flags.decimal;
if(flags.places == Infinity){
re = "(?:" + re + "\\d+)?";
}else{
re += "\\d{" + flags.places + "}";
}
}
return re;
},
true
);
var exponentRE = dregexp.buildGroupRE(flags.exponent,
function(q){
if(q){ return "([eE]" + number._integerRegexp({ signed: flags.eSigned}) + ")"; }
return "";
}
);
var realRE = integerRE + decimalRE;
// allow for decimals without integers, e.g. .25
if(decimalRE){realRE = "(?:(?:"+ realRE + ")|(?:" + decimalRE + "))";}
return realRE + exponentRE; // String
};
/*=====
number.__IntegerRegexpFlags = declare(null, {
// signed: Boolean?
// The leading plus-or-minus sign. Can be true, false, or `[true,false]`.
// Default is `[true, false]`, (i.e. will match if it is signed
// or unsigned).
// separator: String?
// The character used as the thousands separator. Default is no
// separator. For more than one symbol use an array, e.g. `[",", ""]`,
// makes ',' optional.
// groupSize: Number?
// group size between separators
// groupSize2: Number?
// second grouping, where separators 2..n have a different interval than the first separator (for India)
});
=====*/
number._integerRegexp = function(/*number.__IntegerRegexpFlags?*/ flags){
// summary:
// Builds a regular expression that matches an integer
// assign default values to missing parameters
flags = flags || {};
if(!("signed" in flags)){ flags.signed = [true, false]; }
if(!("separator" in flags)){
flags.separator = "";
}else if(!("groupSize" in flags)){
flags.groupSize = 3;
}
var signRE = dregexp.buildGroupRE(flags.signed,
function(q){ return q ? "[-+]" : ""; },
true
);
var numberRE = dregexp.buildGroupRE(flags.separator,
function(sep){
if(!sep){
return "(?:\\d+)";
}
sep = dregexp.escapeString(sep);
if(sep == " "){ sep = "\\s"; }
else if(sep == "\xa0"){ sep = "\\s\\xa0"; }
var grp = flags.groupSize, grp2 = flags.groupSize2;
//TODO: should we continue to enforce that numbers with separators begin with 1-9? See #6933
if(grp2){
var grp2RE = "(?:0|[1-9]\\d{0," + (grp2-1) + "}(?:[" + sep + "]\\d{" + grp2 + "})*[" + sep + "]\\d{" + grp + "})";
return ((grp-grp2) > 0) ? "(?:" + grp2RE + "|(?:0|[1-9]\\d{0," + (grp-1) + "}))" : grp2RE;
}
return "(?:0|[1-9]\\d{0," + (grp-1) + "}(?:[" + sep + "]\\d{" + grp + "})*)";
},
true
);
return signRE + numberRE; // String
};
return number;
});
},
'dijit/_FocusMixin':function(){
define("dijit/_FocusMixin", [
"./focus",
"./_WidgetBase",
"dojo/_base/declare", // declare
"dojo/_base/lang" // lang.extend
], function(focus, _WidgetBase, declare, lang){
// module:
// dijit/_FocusMixin
// We don't know where _FocusMixin will occur in the inheritance chain, but we need the _onFocus()/_onBlur() below
// to be last in the inheritance chain, so mixin to _WidgetBase.
lang.extend(_WidgetBase, {
// focused: [readonly] Boolean
// This widget or a widget it contains has focus, or is "active" because
// it was recently clicked.
focused: false,
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(){
// 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();
}
});
return declare("dijit._FocusMixin", null, {
// summary:
// Mixin to widget to provide _onFocus() and _onBlur() methods that
// fire when a widget or its descendants get/lose focus
// flag that I want _onFocus()/_onBlur() notifications from focus manager
_focusManager: focus
});
});
},
'dojo/data/util/filter':function(){
define("dojo/data/util/filter", ["../../_base/lang"], function(lang){
// module:
// dojo/data/util/filter
// summary:
// TODOC
var filter = {};
lang.setObject("dojo.data.util.filter", filter);
filter.patternToRegExp = function(/*String*/pattern, /*boolean?*/ ignoreCase){
// summary:
// Helper function to convert a simple pattern to a regular expression for matching.
// description:
// Returns a regular expression object that conforms to the defined conversion rules.
// For example:
//
// - ca* -> /^ca.*$/
// - *ca* -> /^.*ca.*$/
// - *c\*a* -> /^.*c\*a.*$/
// - *c\*a?* -> /^.*c\*a..*$/
//
// and so on.
// pattern: string
// A simple matching pattern to convert that follows basic rules:
//
// - * Means match anything, so ca* means match anything starting with ca
// - ? Means match single character. So, b?b will match to bob and bab, and so on.
// - \ is an escape character. So for example, \* means do not treat * as a match, but literal character *.
//
// To use a \ as a character in the string, it must be escaped. So in the pattern it should be
// represented by \\ to be treated as an ordinary \ character instead of an escape.
// ignoreCase:
// An optional flag to indicate if the pattern matching should be treated as case-sensitive or not when comparing
// By default, it is assumed case sensitive.
var rxp = "^";
var c = null;
for(var i = 0; i < pattern.length; i++){
c = pattern.charAt(i);
switch(c){
case '\\':
rxp += c;
i++;
rxp += pattern.charAt(i);
break;
case '*':
rxp += ".*"; break;
case '?':
rxp += "."; break;
case '$':
case '^':
case '/':
case '+':
case '.':
case '|':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
rxp += "\\"; //fallthrough
default:
rxp += c;
}
}
rxp += "$";
if(ignoreCase){
return new RegExp(rxp,"mi"); //RegExp
}else{
return new RegExp(rxp,"m"); //RegExp
}
};
return filter;
});
},
'dijit/_WidgetsInTemplateMixin':function(){
define("dijit/_WidgetsInTemplateMixin", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/parser" // parser.parse
], function(array, declare, parser){
// module:
// dijit/_WidgetsInTemplateMixin
return declare("dijit._WidgetsInTemplateMixin", null, {
// summary:
// Mixin to supplement _TemplatedMixin when template contains widgets
// _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,
// widgetsInTemplate: [protected] Boolean
// Should we parse the template to find widgets that might be
// declared in markup inside it? (Remove for 2.0 and assume true)
widgetsInTemplate: true,
_beforeFillContent: function(){
if(this.widgetsInTemplate){
// Before copying over content, instantiate widgets in template
var node = this.domNode;
var cw = (this._startupWidgets = parser.parse(node, {
noStart: !this._earlyTemplatedStartup,
template: true,
inherited: {dir: this.dir, lang: this.lang, textDir: this.textDir},
propsThis: this, // so data-dojo-props of widgets in the template can reference "this" to refer to me
scope: "dojo" // even in multi-version mode templates use dojoType/data-dojo-type
}));
if(!cw.isFulfilled()){
throw new Error(this.declaredClass + ": parser returned unfilled promise (probably waiting for module auto-load), " +
"unsupported by _WidgetsInTemplateMixin. Must pre-load all supporting widgets before instantiation.");
}
// _WidgetBase::destroy() will destroy any supporting widgets under this.domNode.
// If we wanted to, we could call this.own() on anything in this._startupWidgets that was moved outside
// of this.domNode (like Dialog, which is moved to <body>).
this._attachTemplateNodes(cw, function(n,p){
return n[p];
});
}
},
startup: function(){
array.forEach(this._startupWidgets, function(w){
if(w && !w._started && w.startup){
w.startup();
}
});
this.inherited(arguments);
}
});
});
},
'dojo/fx/Toggler':function(){
define("dojo/fx/Toggler", ["../_base/lang","../_base/declare","../_base/fx", "../_base/connect"],
function(lang, declare, baseFx, connectUtil){
// module:
// dojo/fx/Toggler
return declare("dojo.fx.Toggler", null, {
// summary:
// A simple `dojo.Animation` toggler API.
// description:
// class constructor for an animation toggler. It accepts a packed
// set of arguments about what type of animation to use in each
// direction, duration, etc. All available members are mixed into
// these animations from the constructor (for example, `node`,
// `showDuration`, `hideDuration`).
// example:
// | var t = new dojo/fx/Toggler({
// | node: "nodeId",
// | showDuration: 500,
// | // hideDuration will default to "200"
// | showFunc: dojo/fx/wipeIn,
// | // hideFunc will default to "fadeOut"
// | });
// | t.show(100); // delay showing for 100ms
// | // ...time passes...
// | t.hide();
// node: DomNode
// the node to target for the showing and hiding animations
node: null,
// showFunc: Function
// The function that returns the `dojo.Animation` to show the node
showFunc: baseFx.fadeIn,
// hideFunc: Function
// The function that returns the `dojo.Animation` to hide the node
hideFunc: baseFx.fadeOut,
// showDuration:
// Time in milliseconds to run the show Animation
showDuration: 200,
// hideDuration:
// Time in milliseconds to run the hide Animation
hideDuration: 200,
// FIXME: need a policy for where the toggler should "be" the next
// time show/hide are called if we're stopped somewhere in the
// middle.
// FIXME: also would be nice to specify individual showArgs/hideArgs mixed into
// each animation individually.
// FIXME: also would be nice to have events from the animations exposed/bridged
/*=====
_showArgs: null,
_showAnim: null,
_hideArgs: null,
_hideAnim: null,
_isShowing: false,
_isHiding: false,
=====*/
constructor: function(args){
var _t = this;
lang.mixin(_t, args);
_t.node = args.node;
_t._showArgs = lang.mixin({}, args);
_t._showArgs.node = _t.node;
_t._showArgs.duration = _t.showDuration;
_t.showAnim = _t.showFunc(_t._showArgs);
_t._hideArgs = lang.mixin({}, args);
_t._hideArgs.node = _t.node;
_t._hideArgs.duration = _t.hideDuration;
_t.hideAnim = _t.hideFunc(_t._hideArgs);
connectUtil.connect(_t.showAnim, "beforeBegin", lang.hitch(_t.hideAnim, "stop", true));
connectUtil.connect(_t.hideAnim, "beforeBegin", lang.hitch(_t.showAnim, "stop", true));
},
show: function(delay){
// summary:
// Toggle the node to showing
// delay: Integer?
// Amount of time to stall playing the show animation
return this.showAnim.play(delay || 0);
},
hide: function(delay){
// summary:
// Toggle the node to hidden
// delay: Integer?
// Amount of time to stall playing the hide animation
return this.hideAnim.play(delay || 0);
}
});
});
},
'dijit/form/FilteringSelect':function(){
define("dijit/form/FilteringSelect", [
"dojo/data/util/filter", // filter.patternToRegExp
"dojo/_base/declare", // declare
"dojo/_base/lang", // lang.mixin
"dojo/when",
"./MappedTextBox",
"./ComboBoxMixin"
], function(filter, declare, lang, when, MappedTextBox, ComboBoxMixin){
// module:
// dijit/form/FilteringSelect
return declare("dijit.form.FilteringSelect", [MappedTextBox, ComboBoxMixin], {
// summary:
// An enhanced version of the HTML SELECT tag, populated dynamically
//
// description:
// An enhanced version of the HTML SELECT tag, populated dynamically. It works
// very nicely with very large data sets because it can load and page data as needed.
// It also resembles ComboBox, but does not allow values outside of the provided ones.
// If OPTION tags are used as the data provider via markup, then the
// OPTION tag's child text node is used as the displayed value when selected
// while the OPTION tag's value attribute is used as the widget value on form submit.
// To set the default value when using OPTION tags, specify the selected
// attribute on 1 of the child OPTION tags.
//
// Similar features:
//
// - There is a drop down list of possible values.
// - You can only enter a value from the drop down list. (You can't
// enter an arbitrary value.)
// - The value submitted with the form is the hidden value (ex: CA),
// not the displayed value a.k.a. label (ex: California)
//
// Enhancements over plain HTML version:
//
// - If you type in some text then it will filter down the list of
// possible values in the drop down list.
// - List can be specified either as a static list or via a javascript
// function (that can get the list from a server)
// required: Boolean
// True (default) if user is required to enter a value into this field.
required: true,
_lastDisplayedValue: "",
_isValidSubset: function(){
return this._opened;
},
isValid: function(){
// Overrides ValidationTextBox.isValid()
return !!this.item || (!this.required && this.get('displayedValue') == ""); // #5974
},
_refreshState: function(){
if(!this.searchTimer){ // state will be refreshed after results are returned
this.inherited(arguments);
}
},
_callbackSetLabel: function(
/*Array*/ result,
/*Object*/ query,
/*Object*/ options,
/*Boolean?*/ priorityChange){
// summary:
// Callback from dojo.store after lookup of user entered value finishes
// setValue does a synchronous lookup,
// so it calls _callbackSetLabel directly,
// and so does not pass dataObject
// still need to test against _lastQuery in case it came too late
if((query && query[this.searchAttr] !== this._lastQuery) || (!query && result.length && this.store.getIdentity(result[0]) != this._lastQuery)){
return;
}
if(!result.length){
//#3268: don't modify display value on bad input
//#3285: change CSS to indicate error
this.set("value", '', priorityChange || (priorityChange === undefined && !this.focused), this.textbox.value, null);
}else{
this.set('item', result[0], priorityChange);
}
},
_openResultList: function(/*Object*/ results, /*Object*/ query, /*Object*/ options){
// Callback when a data store query completes.
// Overrides ComboBox._openResultList()
// #3285: tap into search callback to see if user's query resembles a match
if(query[this.searchAttr] !== this._lastQuery){
return;
}
this.inherited(arguments);
if(this.item === undefined){ // item == undefined for keyboard search
// If the search returned no items that means that the user typed
// in something invalid (and they can't make it valid by typing more characters),
// so flag the FilteringSelect as being in an invalid state
this.validate(true);
}
},
_getValueAttr: function(){
// summary:
// Hook for get('value') to work.
// don't get the textbox value but rather the previously set hidden value.
// Use this.valueNode.value which isn't always set for other MappedTextBox widgets until blur
return this.valueNode.value;
},
_getValueField: function(){
// Overrides ComboBox._getValueField()
return "value";
},
_setValueAttr: function(/*String*/ value, /*Boolean?*/ priorityChange, /*String?*/ displayedValue, /*item?*/ item){
// summary:
// Hook so set('value', value) works.
// description:
// Sets the value of the select.
// Also sets the label to the corresponding value by reverse lookup.
if(!this._onChangeActive){ priorityChange = null; }
if(item === undefined){
if(value === null || value === ''){
value = '';
if(!lang.isString(displayedValue)){
this._setDisplayedValueAttr(displayedValue||'', priorityChange);
return;
}
}
var self = this;
this._lastQuery = value;
when(this.store.get(value), function(item){
self._callbackSetLabel(item? [item] : [], undefined, undefined, priorityChange);
});
}else{
this.valueNode.value = value;
this.inherited(arguments);
}
},
_setItemAttr: function(/*item*/ item, /*Boolean?*/ priorityChange, /*String?*/ displayedValue){
// summary:
// Set the displayed valued in the input box, and the hidden value
// that gets submitted, based on a dojo.data store item.
// description:
// Users shouldn't call this function; they should be calling
// set('item', value)
// tags:
// private
this.inherited(arguments);
this._lastDisplayedValue = this.textbox.value;
},
_getDisplayQueryString: function(/*String*/ text){
return text.replace(/([\\\*\?])/g, "\\$1");
},
_setDisplayedValueAttr: function(/*String*/ label, /*Boolean?*/ priorityChange){
// summary:
// Hook so set('displayedValue', label) works.
// description:
// Sets textbox to display label. Also performs reverse lookup
// to set the hidden value. label should corresponding to item.searchAttr.
if(label == null){ label = ''; }
// This is called at initialization along with every custom setter.
// Usually (or always?) the call can be ignored. If it needs to be
// processed then at least make sure that the XHR request doesn't trigger an onChange()
// event, even if it returns after creation has finished
if(!this._created){
if(!("displayedValue" in this.params)){
return;
}
priorityChange = false;
}
// Do a reverse lookup to map the specified displayedValue to the hidden value.
// Note that if there's a custom labelFunc() this code
if(this.store){
this.closeDropDown();
var query = lang.clone(this.query); // #6196: populate query with user-specifics
// Generate query
var qs = this._getDisplayQueryString(label), q;
if(this.store._oldAPI){
// remove this branch for 2.0
q = qs;
}else{
// Query on searchAttr is a regex for benefit of dojo/store/Memory,
// but with a toString() method to help dojo/store/JsonRest.
// Search string like "Co*" converted to regex like /^Co.*$/i.
q = filter.patternToRegExp(qs, this.ignoreCase);
q.toString = function(){ return qs; };
}
this._lastQuery = query[this.searchAttr] = q;
// If the label is not valid, the callback will never set it,
// so the last valid value will get the warning textbox. Set the
// textbox value now so that the impending warning will make
// sense to the user
this.textbox.value = label;
this._lastDisplayedValue = label;
this._set("displayedValue", label); // for watch("displayedValue") notification
var _this = this;
var options = {
ignoreCase: this.ignoreCase,
deep: true
};
lang.mixin(options, this.fetchProperties);
this._fetchHandle = this.store.query(query, options);
when(this._fetchHandle, function(result){
_this._fetchHandle = null;
_this._callbackSetLabel(result || [], query, options, priorityChange);
}, function(err){
_this._fetchHandle = null;
if(!_this._cancelingQuery){ // don't treat canceled query as an error
console.error('dijit.form.FilteringSelect: ' + err.toString());
}
});
}
},
undo: function(){
this.set('displayedValue', this._lastDisplayedValue);
}
});
});
},
'dojo/data/util/sorter':function(){
define("dojo/data/util/sorter", ["../../_base/lang"], function(lang){
// module:
// dojo/data/util/sorter
// summary:
// TODOC
var sorter = {};
lang.setObject("dojo.data.util.sorter", sorter);
sorter.basicComparator = function( /*anything*/ a,
/*anything*/ b){
// summary:
// Basic comparison function that compares if an item is greater or less than another item
// description:
// returns 1 if a > b, -1 if a < b, 0 if equal.
// 'null' values (null, undefined) are treated as larger values so that they're pushed to the end of the list.
// And compared to each other, null is equivalent to undefined.
//null is a problematic compare, so if null, we set to undefined.
//Makes the check logic simple, compact, and consistent
//And (null == undefined) === true, so the check later against null
//works for undefined and is less bytes.
var r = -1;
if(a === null){
a = undefined;
}
if(b === null){
b = undefined;
}
if(a == b){
r = 0;
}else if(a > b || a == null){
r = 1;
}
return r; //int {-1,0,1}
};
sorter.createSortFunction = function( /* attributes[] */sortSpec, /*dojo/data/api/Read*/ store){
// summary:
// Helper function to generate the sorting function based off the list of sort attributes.
// description:
// The sort function creation will look for a property on the store called 'comparatorMap'. If it exists
// it will look in the mapping for comparisons function for the attributes. If one is found, it will
// use it instead of the basic comparator, which is typically used for strings, ints, booleans, and dates.
// Returns the sorting function for this particular list of attributes and sorting directions.
// sortSpec:
// A JS object that array that defines out what attribute names to sort on and whether it should be descenting or asending.
// The objects should be formatted as follows:
// | {
// | attribute: "attributeName-string" || attribute,
// | descending: true|false; // Default is false.
// | }
// store:
// The datastore object to look up item values from.
var sortFunctions=[];
function createSortFunction(attr, dir, comp, s){
//Passing in comp and s (comparator and store), makes this
//function much faster.
return function(itemA, itemB){
var a = s.getValue(itemA, attr);
var b = s.getValue(itemB, attr);
return dir * comp(a,b); //int
};
}
var sortAttribute;
var map = store.comparatorMap;
var bc = sorter.basicComparator;
for(var i = 0; i < sortSpec.length; i++){
sortAttribute = sortSpec[i];
var attr = sortAttribute.attribute;
if(attr){
var dir = (sortAttribute.descending) ? -1 : 1;
var comp = bc;
if(map){
if(typeof attr !== "string" && ("toString" in attr)){
attr = attr.toString();
}
comp = map[attr] || bc;
}
sortFunctions.push(createSortFunction(attr,
dir, comp, store));
}
}
return function(rowA, rowB){
var i=0;
while(i < sortFunctions.length){
var ret = sortFunctions[i++](rowA, rowB);
if(ret !== 0){
return ret;//int
}
}
return 0; //int
}; // Function
};
return sorter;
});
},
'dijit/form/_ButtonMixin':function(){
define("dijit/form/_ButtonMixin", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/_base/event", // event.stop
"../registry" // registry.byNode
], function(declare, dom, event, registry){
// module:
// dijit/form/_ButtonMixin
return declare("dijit.form._ButtonMixin", null, {
// summary:
// A mixin to add a thin standard API wrapper to a normal HTML button
// description:
// A label should always be specified (through innerHTML) or the label attribute.
//
// Attach points:
//
// - focusNode (required): this node receives focus
// - valueNode (optional): this node's value gets submitted with FORM elements
// - containerNode (optional): this node gets the innerHTML assignment for label
// example:
// | <button data-dojo-type="dijit/form/Button" onClick="...">Hello world</button>
// example:
// | var button1 = new Button({label: "hello world", onClick: foo});
// | dojo.body().appendChild(button1.domNode);
// label: HTML String
// Content to display in button.
label: "",
// type: [const] String
// Type of button (submit, reset, button, checkbox, radio)
type: "button",
_onClick: function(/*Event*/ e){
// summary:
// Internal function to handle click actions
if(this.disabled){
event.stop(e);
return false;
}
var preventDefault = this.onClick(e) === false; // user click actions
if(!preventDefault && this.type == "submit" && !(this.valueNode||this.focusNode).form){ // see if a non-form widget needs to be signalled
for(var node=this.domNode; node.parentNode; node=node.parentNode){
var widget=registry.byNode(node);
if(widget && typeof widget._onSubmit == "function"){
widget._onSubmit(e);
preventDefault = true;
break;
}
}
}
if(preventDefault){
e.preventDefault();
}
return !preventDefault;
},
postCreate: function(){
this.inherited(arguments);
dom.setSelectable(this.focusNode, false);
},
onClick: function(/*Event*/ /*===== e =====*/){
// summary:
// Callback for when button is clicked.
// If type="submit", return true to perform submit, or false to cancel it.
// type:
// callback
return true; // Boolean
},
_setLabelAttr: function(/*String*/ content){
// summary:
// Hook for set('label', ...) to work.
// description:
// Set the label (text) of the button; takes an HTML string.
this._set("label", content);
(this.containerNode||this.focusNode).innerHTML = content;
}
});
});
},
'dojo/colors':function(){
define("dojo/colors", ["./_base/kernel", "./_base/lang", "./_base/Color", "./_base/array"], function(dojo, lang, Color, ArrayUtil){
// module:
// dojo/colors
/*=====
return {
// summary:
// Color utilities, extending Base dojo.Color
};
=====*/
var ColorExt = {};
lang.setObject("dojo.colors", ColorExt);
//TODO: this module appears to break naming conventions
// this is a standard conversion prescribed by the CSS3 Color Module
var hue2rgb = function(m1, m2, h){
if(h < 0){ ++h; }
if(h > 1){ --h; }
var h6 = 6 * h;
if(h6 < 1){ return m1 + (m2 - m1) * h6; }
if(2 * h < 1){ return m2; }
if(3 * h < 2){ return m1 + (m2 - m1) * (2 / 3 - h) * 6; }
return m1;
};
// Override base Color.fromRgb with the impl in this module
dojo.colorFromRgb = Color.fromRgb = function(/*String*/ color, /*dojo/_base/Color?*/ obj){
// summary:
// get rgb(a) array from css-style color declarations
// description:
// this function can handle all 4 CSS3 Color Module formats: rgb,
// rgba, hsl, hsla, including rgb(a) with percentage values.
var m = color.toLowerCase().match(/^(rgba?|hsla?)\(([\s\.\-,%0-9]+)\)/);
if(m){
var c = m[2].split(/\s*,\s*/), l = c.length, t = m[1], a;
if((t == "rgb" && l == 3) || (t == "rgba" && l == 4)){
var r = c[0];
if(r.charAt(r.length - 1) == "%"){
// 3 rgb percentage values
a = ArrayUtil.map(c, function(x){
return parseFloat(x) * 2.56;
});
if(l == 4){ a[3] = c[3]; }
return Color.fromArray(a, obj); // dojo/_base/Color
}
return Color.fromArray(c, obj); // dojo/_base/Color
}
if((t == "hsl" && l == 3) || (t == "hsla" && l == 4)){
// normalize hsl values
var H = ((parseFloat(c[0]) % 360) + 360) % 360 / 360,
S = parseFloat(c[1]) / 100,
L = parseFloat(c[2]) / 100,
// calculate rgb according to the algorithm
// recommended by the CSS3 Color Module
m2 = L <= 0.5 ? L * (S + 1) : L + S - L * S,
m1 = 2 * L - m2;
a = [
hue2rgb(m1, m2, H + 1 / 3) * 256,
hue2rgb(m1, m2, H) * 256,
hue2rgb(m1, m2, H - 1 / 3) * 256,
1
];
if(l == 4){ a[3] = c[3]; }
return Color.fromArray(a, obj); // dojo/_base/Color
}
}
return null; // dojo/_base/Color
};
var confine = function(c, low, high){
// summary:
// sanitize a color component by making sure it is a number,
// and clamping it to valid values
c = Number(c);
return isNaN(c) ? high : c < low ? low : c > high ? high : c; // Number
};
Color.prototype.sanitize = function(){
// summary:
// makes sure that the object has correct attributes
var t = this;
t.r = Math.round(confine(t.r, 0, 255));
t.g = Math.round(confine(t.g, 0, 255));
t.b = Math.round(confine(t.b, 0, 255));
t.a = confine(t.a, 0, 1);
return this; // dojo/_base/Color
};
ColorExt.makeGrey = Color.makeGrey = function(/*Number*/ g, /*Number?*/ a){
// summary:
// creates a greyscale color with an optional alpha
return Color.fromArray([g, g, g, a]); // dojo/_base/Color
};
// mixin all CSS3 named colors not already in _base, along with SVG 1.0 variant spellings
lang.mixin(Color.named, {
"aliceblue": [240,248,255],
"antiquewhite": [250,235,215],
"aquamarine": [127,255,212],
"azure": [240,255,255],
"beige": [245,245,220],
"bisque": [255,228,196],
"blanchedalmond": [255,235,205],
"blueviolet": [138,43,226],
"brown": [165,42,42],
"burlywood": [222,184,135],
"cadetblue": [95,158,160],
"chartreuse": [127,255,0],
"chocolate": [210,105,30],
"coral": [255,127,80],
"cornflowerblue": [100,149,237],
"cornsilk": [255,248,220],
"crimson": [220,20,60],
"cyan": [0,255,255],
"darkblue": [0,0,139],
"darkcyan": [0,139,139],
"darkgoldenrod": [184,134,11],
"darkgray": [169,169,169],
"darkgreen": [0,100,0],
"darkgrey": [169,169,169],
"darkkhaki": [189,183,107],
"darkmagenta": [139,0,139],
"darkolivegreen": [85,107,47],
"darkorange": [255,140,0],
"darkorchid": [153,50,204],
"darkred": [139,0,0],
"darksalmon": [233,150,122],
"darkseagreen": [143,188,143],
"darkslateblue": [72,61,139],
"darkslategray": [47,79,79],
"darkslategrey": [47,79,79],
"darkturquoise": [0,206,209],
"darkviolet": [148,0,211],
"deeppink": [255,20,147],
"deepskyblue": [0,191,255],
"dimgray": [105,105,105],
"dimgrey": [105,105,105],
"dodgerblue": [30,144,255],
"firebrick": [178,34,34],
"floralwhite": [255,250,240],
"forestgreen": [34,139,34],
"gainsboro": [220,220,220],
"ghostwhite": [248,248,255],
"gold": [255,215,0],
"goldenrod": [218,165,32],
"greenyellow": [173,255,47],
"grey": [128,128,128],
"honeydew": [240,255,240],
"hotpink": [255,105,180],
"indianred": [205,92,92],
"indigo": [75,0,130],
"ivory": [255,255,240],
"khaki": [240,230,140],
"lavender": [230,230,250],
"lavenderblush": [255,240,245],
"lawngreen": [124,252,0],
"lemonchiffon": [255,250,205],
"lightblue": [173,216,230],
"lightcoral": [240,128,128],
"lightcyan": [224,255,255],
"lightgoldenrodyellow": [250,250,210],
"lightgray": [211,211,211],
"lightgreen": [144,238,144],
"lightgrey": [211,211,211],
"lightpink": [255,182,193],
"lightsalmon": [255,160,122],
"lightseagreen": [32,178,170],
"lightskyblue": [135,206,250],
"lightslategray": [119,136,153],
"lightslategrey": [119,136,153],
"lightsteelblue": [176,196,222],
"lightyellow": [255,255,224],
"limegreen": [50,205,50],
"linen": [250,240,230],
"magenta": [255,0,255],
"mediumaquamarine": [102,205,170],
"mediumblue": [0,0,205],
"mediumorchid": [186,85,211],
"mediumpurple": [147,112,219],
"mediumseagreen": [60,179,113],
"mediumslateblue": [123,104,238],
"mediumspringgreen": [0,250,154],
"mediumturquoise": [72,209,204],
"mediumvioletred": [199,21,133],
"midnightblue": [25,25,112],
"mintcream": [245,255,250],
"mistyrose": [255,228,225],
"moccasin": [255,228,181],
"navajowhite": [255,222,173],
"oldlace": [253,245,230],
"olivedrab": [107,142,35],
"orange": [255,165,0],
"orangered": [255,69,0],
"orchid": [218,112,214],
"palegoldenrod": [238,232,170],
"palegreen": [152,251,152],
"paleturquoise": [175,238,238],
"palevioletred": [219,112,147],
"papayawhip": [255,239,213],
"peachpuff": [255,218,185],
"peru": [205,133,63],
"pink": [255,192,203],
"plum": [221,160,221],
"powderblue": [176,224,230],
"rosybrown": [188,143,143],
"royalblue": [65,105,225],
"saddlebrown": [139,69,19],
"salmon": [250,128,114],
"sandybrown": [244,164,96],
"seagreen": [46,139,87],
"seashell": [255,245,238],
"sienna": [160,82,45],
"skyblue": [135,206,235],
"slateblue": [106,90,205],
"slategray": [112,128,144],
"slategrey": [112,128,144],
"snow": [255,250,250],
"springgreen": [0,255,127],
"steelblue": [70,130,180],
"tan": [210,180,140],
"thistle": [216,191,216],
"tomato": [255,99,71],
"turquoise": [64,224,208],
"violet": [238,130,238],
"wheat": [245,222,179],
"whitesmoke": [245,245,245],
"yellowgreen": [154,205,50]
});
return Color; // TODO: return ColorExt, not Color
});
},
'dijit/registry':function(){
define("dijit/registry", [
"dojo/_base/array", // array.forEach array.map
"dojo/sniff", // has("ie")
"dojo/_base/unload", // unload.addOnWindowUnload
"dojo/_base/window", // win.body
"./main" // dijit._scopeName
], function(array, has, unload, win, dijit){
// module:
// dijit/registry
var _widgetTypeCtr = {}, hash = {};
var registry = {
// summary:
// Registry of existing widget on page, plus some utility methods.
// length: Number
// Number of registered widgets
length: 0,
add: function(widget){
// summary:
// Add a widget to the registry. If a duplicate ID is detected, a error is thrown.
// widget: dijit/_WidgetBase
// Any dijit/_WidgetBase subclass.
if(hash[widget.id]){
throw new Error("Tried to register widget with id==" + widget.id + " but that id is already registered");
}
hash[widget.id] = widget;
this.length++;
},
remove: function(/*String*/ id){
// summary:
// Remove a widget from the registry. Does not destroy the widget; simply
// removes the reference.
if(hash[id]){
delete hash[id];
this.length--;
}
},
byId: function(/*String|Widget*/ id){
// summary:
// Find a widget by it's id.
// If passed a widget then just returns the widget.
return typeof id == "string" ? hash[id] : id; // dijit/_WidgetBase
},
byNode: function(/*DOMNode*/ node){
// summary:
// Returns the widget corresponding to the given DOMNode
return hash[node.getAttribute("widgetId")]; // dijit/_WidgetBase
},
toArray: function(){
// summary:
// Convert registry into a true Array
//
// example:
// Work with the widget .domNodes in a real Array
// | array.map(registry.toArray(), function(w){ return w.domNode; });
var ar = [];
for(var id in hash){
ar.push(hash[id]);
}
return ar; // dijit/_WidgetBase[]
},
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
},
findWidgets: function(root, skipNode){
// summary:
// Search subtree under root returning widgets found.
// Doesn't search for nested widgets (ie, widgets inside other widgets).
// root: DOMNode
// Node to search under.
// skipNode: DOMNode
// If specified, don't search beneath this node (usually containerNode).
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){
var widget = hash[widgetId];
if(widget){ // may be null on page w/multiple dojo's loaded
outAry.push(widget);
}
}else if(node !== skipNode){
getChildrenHelper(node);
}
}
}
}
getChildrenHelper(root);
return outAry;
},
_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
array.forEach(registry.findWidgets(win.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();
}
}
});
},
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.nodeType == 1 && node.getAttribute("widgetId");
if(id){
return hash[id];
}
node = node.parentNode;
}
return null;
},
// In case someone needs to access hash.
// Actually, this is accessed from WidgetSet back-compatibility code
_hash: hash
};
dijit.registry = registry;
return registry;
});
},
'dijit/tree/_dndContainer':function(){
define("dijit/tree/_dndContainer", [
"dojo/aspect", // aspect.after
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove domClass.replace
"dojo/_base/event", // event.stop
"dojo/_base/lang", // lang.mixin lang.hitch
"dojo/on",
"dojo/touch"
], function(aspect, declare,domClass, event, lang, on, touch){
// module:
// dijit/tree/_dndContainer
/*=====
var __Args = {
// summary:
// A dict of parameters for Tree source configuration.
// isSource: Boolean?
// Can be used as a DnD source. Defaults to true.
// accept: String[]
// List of accepted types (text strings) for a target; defaults to
// ["text", "treeNode"]
// copyOnly: Boolean?
// Copy items, if true, use a state of Ctrl key otherwise,
// dragThreshold: Number
// The move delay in pixels before detecting a drag; 0 by default
// betweenThreshold: Integer
// Distance from upper/lower edge of node to allow drop to reorder nodes
};
=====*/
return declare("dijit.tree._dndContainer", null, {
// summary:
// This is a base class for `dijit/tree/_dndSelector`, and isn't meant to be used directly.
// It's modeled after `dojo/dnd/Container`.
// tags:
// protected
/*=====
// current: DomNode
// The currently hovered TreeNode.rowNode (which is the DOM node
// associated w/a given node in the tree, excluding it's descendants)
current: null,
=====*/
constructor: function(tree, params){
// summary:
// A constructor of the Container
// tree: Node
// Node or node's id to build the container on
// params: __Args
// A dict of parameters, which gets mixed into the object
// tags:
// private
this.tree = tree;
this.node = tree.domNode; // TODO: rename; it's not a TreeNode but the whole Tree
lang.mixin(this, params);
// class-specific variables
this.current = null; // current TreeNode's DOM node
// states
this.containerState = "";
domClass.add(this.node, "dojoDndContainer");
// set up events
this.events = [
// Mouse (or touch) enter/leave on Tree itself
on(this.node, touch.enter, lang.hitch(this, "onOverEvent")),
on(this.node, touch.leave, lang.hitch(this, "onOutEvent")),
// switching between TreeNodes
aspect.after(this.tree, "_onNodeMouseEnter", lang.hitch(this, "onMouseOver"), true),
aspect.after(this.tree, "_onNodeMouseLeave", lang.hitch(this, "onMouseOut"), true),
// cancel text selection and text dragging
on(this.node, "dragstart", lang.hitch(event, "stop")),
on(this.node, "selectstart", lang.hitch(event, "stop"))
];
},
destroy: function(){
// summary:
// Prepares this object to be garbage-collected
var h;
while(h = this.events.pop()){ h.remove(); }
// this.clearItems();
this.node = this.parent = null;
},
// mouse events
onMouseOver: function(widget /*===== , evt =====*/){
// summary:
// Called when mouse is moved over a TreeNode
// widget: TreeNode
// evt: Event
// tags:
// protected
this.current = widget;
},
onMouseOut: function(/*===== widget, evt =====*/){
// summary:
// Called when mouse is moved away from a TreeNode
// widget: TreeNode
// evt: Event
// tags:
// protected
this.current = null;
},
_changeState: function(type, newState){
// summary:
// Changes a named state to new state value
// type: String
// A name of the state to change
// newState: String
// new state
var prefix = "dojoDnd" + type;
var state = type.toLowerCase() + "State";
//domClass.replace(this.node, prefix + newState, prefix + this[state]);
domClass.replace(this.node, prefix + newState, prefix + this[state]);
this[state] = newState;
},
_addItemClass: function(node, type){
// summary:
// Adds a class with prefix "dojoDndItem"
// node: Node
// A node
// type: String
// A variable suffix for a class name
domClass.add(node, "dojoDndItem" + type);
},
_removeItemClass: function(node, type){
// summary:
// Removes a class with prefix "dojoDndItem"
// node: Node
// A node
// type: String
// A variable suffix for a class name
domClass.remove(node, "dojoDndItem" + type);
},
onOverEvent: function(){
// summary:
// This function is called once, when mouse is over our container
// tags:
// protected
this._changeState("Container", "Over");
},
onOutEvent: function(){
// summary:
// This function is called once, when mouse is out of our container
// tags:
// protected
this._changeState("Container", "");
}
});
});
},
'url:dijit/templates/InlineEditBox.html':"<span data-dojo-attach-point=\"editNode\" role=\"presentation\" class=\"dijitReset dijitInline dijitOffScreen\"\n\tdata-dojo-attach-event=\"onkeypress: _onKeyPress\"\n\t><span data-dojo-attach-point=\"editorPlaceholder\"></span\n\t><span data-dojo-attach-point=\"buttonContainer\"\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonSave}', 'class': 'saveButton'\"\n\t\t\tdata-dojo-attach-point=\"saveButton\" data-dojo-attach-event=\"onClick:save\"></button\n\t\t><button data-dojo-type=\"dijit/form/Button\" data-dojo-props=\"label: '${buttonCancel}', 'class': 'cancelButton'\"\n\t\t\tdata-dojo-attach-point=\"cancelButton\" data-dojo-attach-event=\"onClick:cancel\"></button\n\t></span\n></span>\n",
'dijit/_base/wai':function(){
define("dijit/_base/wai", [
"dojo/dom-attr", // domAttr.attr
"dojo/_base/lang", // lang.mixin
"../main", // export symbols to dijit
"../hccss" // not using this module directly, but loading it sets CSS flag on <html>
], function(domAttr, lang, dijit){
// module:
// dijit/_base/wai
var exports = {
// summary:
// Deprecated methods for setting/getting wai roles and states.
// New code should call setAttribute()/getAttribute() directly.
//
// Also loads hccss to apply dj_a11y class to root node if machine is in high-contrast mode.
hasWaiRole: function(/*Element*/ elem, /*String?*/ role){
// summary:
// Determines if an element has a particular role.
// returns:
// True if elem has the specific role attribute and false if not.
// For backwards compatibility if role parameter not provided,
// returns true if has a role
var waiRole = this.getWaiRole(elem);
return role ? (waiRole.indexOf(role) > -1) : (waiRole.length > 0);
},
getWaiRole: function(/*Element*/ elem){
// summary:
// Gets the role for an element (which should be a wai role).
// returns:
// The role of elem or an empty string if elem
// does not have a role.
return lang.trim((domAttr.get(elem, "role") || "").replace("wairole:",""));
},
setWaiRole: function(/*Element*/ elem, /*String*/ role){
// summary:
// Sets the role on an element.
// description:
// Replace existing role attribute with new role.
domAttr.set(elem, "role", role);
},
removeWaiRole: function(/*Element*/ elem, /*String*/ role){
// summary:
// Removes the specified role from an element.
// Removes role attribute if no specific role provided (for backwards compat.)
var roleValue = domAttr.get(elem, "role");
if(!roleValue){ return; }
if(role){
var t = lang.trim((" " + roleValue + " ").replace(" " + role + " ", " "));
domAttr.set(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);
}
};
lang.mixin(dijit, exports);
/*===== return exports; =====*/
return dijit; // for back compat :-(
});
},
'dijit/form/_FormSelectWidget':function(){
define("dijit/form/_FormSelectWidget", [
"dojo/_base/array", // array.filter array.forEach array.map array.some
"dojo/_base/Deferred",
"dojo/aspect", // aspect.after
"dojo/data/util/sorter", // util.sorter.createSortFunction
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"dojo/dom-class", // domClass.toggle
"dojo/_base/kernel", // _scopeName
"dojo/_base/lang", // lang.delegate lang.isArray lang.isObject lang.hitch
"dojo/query", // query
"dojo/when",
"dojo/store/util/QueryResults",
"./_FormValueWidget"
], function(array, Deferred, aspect, sorter, declare, dom, domClass, kernel, lang, query, when,
QueryResults, _FormValueWidget){
// module:
// dijit/form/_FormSelectWidget
/*=====
var __SelectOption = {
// value: String
// The value of the option. Setting to empty (or missing) will
// place a separator at that location
// label: String
// The label for our option. It can contain html tags.
// selected: Boolean
// Whether or not we are a selected option
// disabled: Boolean
// Whether or not this specific option is disabled
};
=====*/
var _FormSelectWidget = declare("dijit.form._FormSelectWidget", _FormValueWidget, {
// summary:
// Extends _FormValueWidget in order to provide "select-specific"
// values - i.e., those values that are unique to `<select>` elements.
// This also provides the mechanism for reading the elements from
// a store, if desired.
// multiple: [const] Boolean
// Whether or not we are multi-valued
multiple: false,
// options: __SelectOption[]
// The set of options for our select item. Roughly corresponds to
// the html `<option>` tag.
options: null,
// store: dojo/store/api/Store
// A store to use for getting our list of options - rather than reading them
// from the `<option>` html tags. Should support getIdentity().
// For back-compat store can also be a dojo/data/api/Identity.
store: null,
// query: object
// A query to use when fetching items from our store
query: null,
// queryOptions: object
// Query options to use when fetching from the store
queryOptions: null,
// labelAttr: String?
// The entries in the drop down list come from this attribute in the dojo.store items.
// If ``store`` is set, labelAttr must be set too, unless store is an old-style
// dojo.data store rather than a new dojo/store.
labelAttr: "",
// onFetch: Function
// A callback to do with an onFetch - but before any items are actually
// iterated over (i.e. to filter even further what you want to add)
onFetch: null,
// sortByLabel: Boolean
// Flag to sort the options returned from a store by the label of
// the store.
sortByLabel: true,
// loadChildrenOnOpen: Boolean
// By default loadChildren is called when the items are fetched from the
// store. This property allows delaying loadChildren (and the creation
// of the options/menuitems) until the user clicks the button to open the
// dropdown.
loadChildrenOnOpen: false,
// onLoadDeferred: [readonly] dojo.Deferred
// This is the `dojo.Deferred` returned by setStore().
// Calling onLoadDeferred.then() registers your
// callback to be called only once, when the prior setStore completes.
onLoadDeferred: null,
getOptions: function(/*anything*/ valueOrIdx){
// summary:
// Returns a given option (or options).
// valueOrIdx:
// If passed in as a string, that string is used to look up the option
// in the array of options - based on the value property.
// (See dijit/form/_FormSelectWidget.__SelectOption).
//
// If passed in a number, then the option with the given index (0-based)
// within this select will be returned.
//
// If passed in a dijit/form/_FormSelectWidget.__SelectOption, the same option will be
// returned if and only if it exists within this select.
//
// If passed an array, then an array will be returned with each element
// in the array being looked up.
//
// If not passed a value, then all options will be returned
//
// returns:
// The option corresponding with the given value or index. null
// is returned if any of the following are true:
//
// - A string value is passed in which doesn't exist
// - An index is passed in which is outside the bounds of the array of options
// - A dijit/form/_FormSelectWidget.__SelectOption is passed in which is not a part of the select
// NOTE: the compare for passing in a dijit/form/_FormSelectWidget.__SelectOption checks
// if the value property matches - NOT if the exact option exists
// NOTE: if passing in an array, null elements will be placed in the returned
// array when a value is not found.
var lookupValue = valueOrIdx, opts = this.options || [], l = opts.length;
if(lookupValue === undefined){
return opts; // __SelectOption[]
}
if(lang.isArray(lookupValue)){
return array.map(lookupValue, "return this.getOptions(item);", this); // __SelectOption[]
}
if(lang.isObject(valueOrIdx)){
// We were passed an option - so see if it's in our array (directly),
// and if it's not, try and find it by value.
if(!array.some(this.options, function(o, idx){
if(o === lookupValue ||
(o.value && o.value === lookupValue.value)){
lookupValue = idx;
return true;
}
return false;
})){
lookupValue = -1;
}
}
if(typeof lookupValue == "string"){
for(var i=0; i<l; i++){
if(opts[i].value === lookupValue){
lookupValue = i;
break;
}
}
}
if(typeof lookupValue == "number" && lookupValue >= 0 && lookupValue < l){
return this.options[lookupValue]; // __SelectOption
}
return null; // null
},
addOption: function(/*__SelectOption|__SelectOption[]*/ option){
// summary:
// Adds an option or options to the end of the select. If value
// of the option is empty or missing, a separator is created instead.
// Passing in an array of options will yield slightly better performance
// since the children are only loaded once.
if(!lang.isArray(option)){ option = [option]; }
array.forEach(option, function(i){
if(i && lang.isObject(i)){
this.options.push(i);
}
}, this);
this._loadChildren();
},
removeOption: function(/*String|__SelectOption|Number|Array*/ valueOrIdx){
// summary:
// Removes the given option or options. You can remove by string
// (in which case the value is removed), number (in which case the
// index in the options array is removed), or select option (in
// which case, the select option with a matching value is removed).
// You can also pass in an array of those values for a slightly
// better performance since the children are only loaded once.
if(!lang.isArray(valueOrIdx)){ valueOrIdx = [valueOrIdx]; }
var oldOpts = this.getOptions(valueOrIdx);
array.forEach(oldOpts, function(i){
// We can get null back in our array - if our option was not found. In
// that case, we don't want to blow up...
if(i){
this.options = array.filter(this.options, function(node){
return (node.value !== i.value || node.label !== i.label);
});
this._removeOptionItem(i);
}
}, this);
this._loadChildren();
},
updateOption: function(/*__SelectOption|__SelectOption[]*/ newOption){
// summary:
// Updates the values of the given option. The option to update
// is matched based on the value of the entered option. Passing
// in an array of new options will yield better performance since
// the children will only be loaded once.
if(!lang.isArray(newOption)){ newOption = [newOption]; }
array.forEach(newOption, function(i){
var oldOpt = this.getOptions(i), k;
if(oldOpt){
for(k in i){ oldOpt[k] = i[k]; }
}
}, this);
this._loadChildren();
},
setStore: function(store,
selectedValue,
fetchArgs){
// summary:
// Sets the store you would like to use with this select widget.
// The selected value is the value of the new store to set. This
// function returns the original store, in case you want to reuse
// it or something.
// store: dojo/store/api/Store
// The dojo.store you would like to use - it MUST implement getIdentity()
// and MAY implement observe().
// For backwards-compatibility this can also be a data.data store, in which case
// it MUST implement dojo/data/api/Identity,
// and MAY implement dojo/data/api/Notification.
// selectedValue: anything?
// The value that this widget should set itself to *after* the store
// has been loaded
// fetchArgs: Object?
// Hash of parameters to set filter on store, etc.
//
// - query: new value for Select.query,
// - queryOptions: new value for Select.queryOptions,
// - onFetch: callback function for each item in data (Deprecated)
var oStore = this.store;
fetchArgs = fetchArgs || {};
if(oStore !== store){
// Our store has changed, so cancel any listeners on old store (remove for 2.0)
var h;
while((h = this._notifyConnections.pop())){ h.remove(); }
// For backwards-compatibility, accept dojo.data store in addition to dojo.store.store. Remove in 2.0.
if(!store.get){
lang.mixin(store, {
_oldAPI: true,
get: function(id){
// summary:
// Retrieves an object by it's identity. This will trigger a fetchItemByIdentity.
// Like dojo.store.DataStore.get() except returns native item.
var deferred = new Deferred();
this.fetchItemByIdentity({
identity: id,
onItem: function(object){
deferred.resolve(object);
},
onError: function(error){
deferred.reject(error);
}
});
return deferred.promise;
},
query: function(query, options){
// summary:
// Queries the store for objects. Like dojo/store/DataStore.query()
// except returned Deferred contains array of native items.
var deferred = new Deferred(function(){ if(fetchHandle.abort){ fetchHandle.abort(); } } );
deferred.total = new Deferred();
var fetchHandle = this.fetch(lang.mixin({
query: query,
onBegin: function(count){
deferred.total.resolve(count);
},
onComplete: function(results){
deferred.resolve(results);
},
onError: function(error){
deferred.reject(error);
}
}, options));
return new QueryResults(deferred);
}
});
if(store.getFeatures()["dojo.data.api.Notification"]){
this._notifyConnections = [
aspect.after(store, "onNew", lang.hitch(this, "_onNewItem"), true),
aspect.after(store, "onDelete", lang.hitch(this, "_onDeleteItem"), true),
aspect.after(store, "onSet", lang.hitch(this, "_onSetItem"), true)
];
}
}
this._set("store", store); // Our store has changed, so update our notifications
}
// Remove existing options (if there are any)
if(this.options && this.options.length){
this.removeOption(this.options);
}
// Cancel listener for updates to old store
if(this._queryRes && this._queryRes.close){
this._queryRes.close();
}
// If user has specified new query and query options along with this new store, then use them.
if(fetchArgs.query){
this._set("query", fetchArgs.query);
this._set("queryOptions", fetchArgs.queryOptions);
}
// Add our new options
if(store){
this._loadingStore = true;
this.onLoadDeferred = new Deferred();
// Run query
// Save result in this._queryRes so we can cancel the listeners we register below
this._queryRes = store.query(this.query, this.queryOptions);
when(this._queryRes, lang.hitch(this, function(items){
if(this.sortByLabel && !fetchArgs.sort && items.length){
if(items[0].getValue){
// Old dojo.data API to access items, remove for 2.0
items.sort(sorter.createSortFunction([{
attribute: store.getLabelAttributes(items[0])[0]
}], store));
}else{
var labelAttr = this.labelAttr;
items.sort(function(a, b){
return a[labelAttr] > b[labelAttr] ? 1 : b[labelAttr] > a[labelAttr] ? -1 : 0;
});
}
}
if(fetchArgs.onFetch){
items = fetchArgs.onFetch.call(this, items, fetchArgs);
}
// TODO: Add these guys as a batch, instead of separately
array.forEach(items, function(i){
this._addOptionForItem(i);
}, this);
// Register listener for store updates
if(this._queryRes.observe){
this._queryRes.observe(lang.hitch(this, function(object, deletedFrom, insertedInto){
if(deletedFrom == insertedInto){
this._onSetItem(object);
}else{
if(deletedFrom != -1){
this._onDeleteItem(object);
}
if(insertedInto != -1){
this._onNewItem(object);
}
}
}), true);
}
// Set our value (which might be undefined), and then tweak
// it to send a change event with the real value
this._loadingStore = false;
this.set("value", "_pendingValue" in this ? this._pendingValue : selectedValue);
delete this._pendingValue;
if(!this.loadChildrenOnOpen){
this._loadChildren();
}else{
this._pseudoLoadChildren(items);
}
this.onLoadDeferred.resolve(true);
this.onSetStore();
}), function(err){
console.error('dijit.form.Select: ' + err.toString());
this.onLoadDeferred.reject(err);
});
}
return oStore; // dojo/data/api/Identity
},
// TODO: implement set() and watch() for store and query, although not sure how to handle
// setting them individually rather than together (as in setStore() above)
_setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){
// summary:
// set the value of the widget.
// If a string is passed, then we set our value from looking it up.
if(!this._onChangeActive){ priorityChange = null; }
if(this._loadingStore){
// Our store is loading - so save our value, and we'll set it when
// we're done
this._pendingValue = newValue;
return;
}
var opts = this.getOptions() || [];
if(!lang.isArray(newValue)){
newValue = [newValue];
}
array.forEach(newValue, function(i, idx){
if(!lang.isObject(i)){
i = i + "";
}
if(typeof i === "string"){
newValue[idx] = array.filter(opts, function(node){
return node.value === i;
})[0] || {value: "", label: ""};
}
}, this);
// Make sure some sane default is set
newValue = array.filter(newValue, function(i){ return i && i.value; });
if(!this.multiple && (!newValue[0] || !newValue[0].value) && opts.length){
newValue[0] = opts[0];
}
array.forEach(opts, function(i){
i.selected = array.some(newValue, function(v){ return v.value === i.value; });
});
var val = array.map(newValue, function(i){ return i.value; }),
disp = array.map(newValue, function(i){ return i.label; });
if(typeof val == "undefined" || typeof val[0] == "undefined"){ return; } // not fully initialized yet or a failed value lookup
this._setDisplay(this.multiple ? disp : disp[0]);
this.inherited(arguments, [ this.multiple ? val : val[0], priorityChange ]);
this._updateSelection();
},
_getDisplayedValueAttr: function(){
// summary:
// returns the displayed value of the widget
var val = this.get("value");
if(!lang.isArray(val)){
val = [val];
}
var ret = array.map(this.getOptions(val), function(v){
if(v && "label" in v){
return v.label;
}else if(v){
return v.value;
}
return null;
}, this);
return this.multiple ? ret : ret[0];
},
_loadChildren: function(){
// summary:
// Loads the children represented by this widget's options.
// reset the menu to make it populatable on the next click
if(this._loadingStore){ return; }
array.forEach(this._getChildren(), function(child){
child.destroyRecursive();
});
// Add each menu item
array.forEach(this.options, this._addOptionItem, this);
// Update states
this._updateSelection();
},
_updateSelection: function(){
// summary:
// Sets the "selected" class on the item for styling purposes
this._set("value", this._getValueFromOpts());
var val = this.value;
if(!lang.isArray(val)){
val = [val];
}
if(val && val[0]){
array.forEach(this._getChildren(), function(child){
var isSelected = array.some(val, function(v){
return child.option && (v === child.option.value);
});
domClass.toggle(child.domNode, this.baseClass.replace(/\s+|$/g, "SelectedOption "), isSelected);
child.domNode.setAttribute("aria-selected", isSelected ? "true" : "false");
}, this);
}
},
_getValueFromOpts: function(){
// summary:
// Returns the value of the widget by reading the options for
// the selected flag
var opts = this.getOptions() || [];
if(!this.multiple && opts.length){
// Mirror what a select does - choose the first one
var opt = array.filter(opts, function(i){
return i.selected;
})[0];
if(opt && opt.value){
return opt.value;
}else{
opts[0].selected = true;
return opts[0].value;
}
}else if(this.multiple){
// Set value to be the sum of all selected
return array.map(array.filter(opts, function(i){
return i.selected;
}), function(i){
return i.value;
}) || [];
}
return "";
},
// Internal functions to call when we have store notifications come in
_onNewItem: function(/*item*/ item, /*Object?*/ parentInfo){
if(!parentInfo || !parentInfo.parent){
// Only add it if we are top-level
this._addOptionForItem(item);
}
},
_onDeleteItem: function(/*item*/ item){
var store = this.store;
this.removeOption(store.getIdentity(item));
},
_onSetItem: function(/*item*/ item){
this.updateOption(this._getOptionObjForItem(item));
},
_getOptionObjForItem: function(item){
// summary:
// Returns an option object based off the given item. The "value"
// of the option item will be the identity of the item, the "label"
// of the option will be the label of the item.
// remove getLabel() call for 2.0 (it's to support the old dojo.data API)
var store = this.store,
label = (this.labelAttr && this.labelAttr in item) ? item[this.labelAttr] : store.getLabel(item),
value = (label ? store.getIdentity(item) : null);
return {value: value, label: label, item: item}; // __SelectOption
},
_addOptionForItem: function(/*item*/ item){
// summary:
// Creates (and adds) the option for the given item
var store = this.store;
if(store.isItemLoaded && !store.isItemLoaded(item)){
// We are not loaded - so let's load it and add later.
// Remove for 2.0 (it's the old dojo.data API)
store.loadItem({item: item, onItem: function(i){
this._addOptionForItem(i);
},
scope: this});
return;
}
var newOpt = this._getOptionObjForItem(item);
this.addOption(newOpt);
},
constructor: function(params /*===== , srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// If a srcNodeRef (DOM node) is specified, replace srcNodeRef with my generated DOM tree
// Saves off our value, if we have an initial one set so we
// can use it if we have a store as well (see startup())
this._oValue = (params || {}).value || null;
this._notifyConnections = []; // remove for 2.0
},
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.focusNode, false);
},
_fillContent: function(){
// summary:
// Loads our options and sets up our dropdown correctly. We
// don't want any content, so we don't call any inherit chain
// function.
if(!this.options){
this.options =
this.srcNodeRef
? query("> *", this.srcNodeRef).map(
function(node){
if(node.getAttribute("type") === "separator"){
return { value: "", label: "", selected: false, disabled: false };
}
return {
value: (node.getAttribute("data-" + kernel._scopeName + "-value") || node.getAttribute("value")),
label: String(node.innerHTML),
// FIXME: disabled and selected are not valid on complex markup children (which is why we're
// looking for data-dojo-value above. perhaps we should data-dojo-props="" this whole thing?)
// decide before 1.6
selected: node.getAttribute("selected") || false,
disabled: node.getAttribute("disabled") || false
};
},
this)
: [];
}
if(!this.value){
this._set("value", this._getValueFromOpts());
}else if(this.multiple && typeof this.value == "string"){
this._set("value", this.value.split(","));
}
},
postCreate: function(){
// summary:
// sets up our event handling that we need for functioning
// as a select
this.inherited(arguments);
// Make our event connections for updating state
this.connect(this, "onChange", "_updateSelection");
// moved from startup
// Connects in our store, if we have one defined
var store = this.store;
if(store && (store.getIdentity || store.getFeatures()["dojo.data.api.Identity"])){
// Temporarily set our store to null so that it will get set
// and connected appropriately
this.store = null;
this.setStore(store, this._oValue);
}
},
startup: function(){
// summary:
this._loadChildren();
this.inherited(arguments);
},
destroy: function(){
// summary:
// Clean up our connections
var h;
while((h = this._notifyConnections.pop())){ h.remove(); }
// Cancel listener for store updates
if(this._queryRes && this._queryRes.close){
this._queryRes.close();
}
this.inherited(arguments);
},
_addOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
// summary:
// User-overridable function which, for the given option, adds an
// item to the select. If the option doesn't have a value, then a
// separator is added in that place. Make sure to store the option
// in the created option widget.
},
_removeOptionItem: function(/*__SelectOption*/ /*===== option =====*/){
// summary:
// User-overridable function which, for the given option, removes
// its item from the select.
},
_setDisplay: function(/*String or String[]*/ /*===== newDisplay =====*/){
// summary:
// Overridable function which will set the display for the
// widget. newDisplay is either a string (in the case of
// single selects) or array of strings (in the case of multi-selects)
},
_getChildren: function(){
// summary:
// Overridable function to return the children that this widget contains.
return [];
},
_getSelectedOptionsAttr: function(){
// summary:
// hooks into this.attr to provide a mechanism for getting the
// option items for the current value of the widget.
return this.getOptions(this.get("value"));
},
_pseudoLoadChildren: function(/*item[]*/ /*===== items =====*/){
// summary:
// a function that will "fake" loading children, if needed, and
// if we have set to not load children until the widget opens.
// items:
// An array of items that will be loaded, when needed
},
onSetStore: function(){
// summary:
// a function that can be connected to in order to receive a
// notification that the store has finished loading and all options
// from that store are available
}
});
/*=====
_FormSelectWidget.__SelectOption = __SelectOption;
=====*/
return _FormSelectWidget;
});
},
'dijit/form/Select':function(){
require({cache:{
'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t/></div\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer\"\n\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n"}});
define("dijit/form/Select", [
"dojo/_base/array", // array.forEach
"dojo/_base/declare", // declare
"dojo/dom-attr", // domAttr.set
"dojo/dom-class", // domClass.add domClass.remove domClass.toggle
"dojo/dom-geometry", // domGeometry.setMarginBox
"dojo/_base/event", // event.stop
"dojo/i18n", // i18n.getLocalization
"dojo/_base/lang", // lang.hitch
"dojo/sniff", // has("ie")
"./_FormSelectWidget",
"../_HasDropDown",
"../Menu",
"../MenuItem",
"../MenuSeparator",
"../Tooltip",
"dojo/text!./templates/Select.html",
"dojo/i18n!./nls/validate"
], function(array, declare, domAttr, domClass, domGeometry, event, i18n, lang, has,
_FormSelectWidget, _HasDropDown, Menu, MenuItem, MenuSeparator, Tooltip, template){
// module:
// dijit/form/Select
var _SelectMenu = declare("dijit.form._SelectMenu", Menu, {
// summary:
// An internally-used menu for dropdown that allows us a vertical scrollbar
// Override Menu.autoFocus setting so that opening a Select highlights the current value.
autoFocus: true,
buildRendering: function(){
// summary:
// Stub in our own changes, so that our domNode is not a table
// otherwise, we won't respond correctly to heights/overflows
this.inherited(arguments);
var o = (this.menuTableNode = this.domNode);
var n = (this.domNode = this.ownerDocument.createElement("div"));
n.style.cssText = "overflow-x: hidden; overflow-y: scroll";
if(o.parentNode){
o.parentNode.replaceChild(n, o);
}
domClass.remove(o, "dijitMenuTable");
n.className = o.className + " dijitSelectMenu";
o.className = "dijitReset dijitMenuTable";
o.setAttribute("role", "listbox");
n.setAttribute("role", "presentation");
n.appendChild(o);
},
postCreate: function(){
// summary:
// stop mousemove from selecting text on IE to be consistent with other browsers
this.inherited(arguments);
this.connect(this.domNode, "onselectstart", event.stop);
},
focus: function(){
// summary:
// Overridden so that the previously selected value will be focused instead of only the first item
var found = false,
val = this.parentWidget.value;
if(lang.isArray(val)){
val = val[val.length-1];
}
if(val){ // if focus selected
array.forEach(this.parentWidget._getChildren(), function(child){
if(child.option && (val === child.option.value)){ // find menu item widget with this value
found = true;
this.focusChild(child, false); // focus previous selection
}
}, this);
}
if(!found){
this.inherited(arguments); // focus first item by default
}
},
resize: function(/*Object*/ mb){
// summary:
// Overridden so that we are able to handle resizing our
// internal widget. Note that this is not a "full" resize
// implementation - it only works correctly if you pass it a
// marginBox.
//
// mb: Object
// The margin box to set this dropdown to.
if(mb){
domGeometry.setMarginBox(this.domNode, mb);
if("w" in mb){
// We've explicitly set the wrapper <div>'s width, so set <table> width to match.
// 100% is safer than a pixel value because there may be a scroll bar with
// browser/OS specific width.
this.menuTableNode.style.width = "100%";
}
}
}
});
var Select = declare("dijit.form.Select", [_FormSelectWidget, _HasDropDown], {
// summary:
// This is a "styleable" select box - it is basically a DropDownButton which
// can take a `<select>` as its input.
baseClass: "dijitSelect dijitValidationTextBox",
templateString: template,
_buttonInputDisabled: has("ie") ? "disabled" : "", // allows IE to disallow focus, but Firefox cannot be disabled for mousedown events
// required: Boolean
// Can be true or false, default is false.
required: false,
// state: [readonly] String
// "Incomplete" if this select is required but unset (i.e. blank value), "" otherwise
state: "",
// message: String
// Currently displayed error/prompt message
message: "",
// tooltipPosition: String[]
// See description of `dijit/Tooltip.defaultPosition` for details on this parameter.
tooltipPosition: [],
// emptyLabel: string
// What to display in an "empty" dropdown
emptyLabel: "&#160;", // &nbsp;
// _isLoaded: Boolean
// Whether or not we have been loaded
_isLoaded: false,
// _childrenLoaded: Boolean
// Whether or not our children have been loaded
_childrenLoaded: false,
_fillContent: function(){
// summary:
// Set the value to be the first, or the selected index
this.inherited(arguments);
// set value from selected option
if(this.options.length && !this.value && this.srcNodeRef){
var si = this.srcNodeRef.selectedIndex || 0; // || 0 needed for when srcNodeRef is not a SELECT
this.value = this.options[si >= 0 ? si : 0].value;
}
// Create the dropDown widget
this.dropDown = new _SelectMenu({ id: this.id + "_menu", parentWidget: this });
domClass.add(this.dropDown.domNode, this.baseClass.replace(/\s+|$/g, "Menu "));
},
_getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){
// summary:
// For the given option, return the menu item that should be
// used to display it. This can be overridden as needed
if(!option.value && !option.label){
// We are a separator (no label set for it)
return new MenuSeparator({ownerDocument: this.ownerDocument});
}else{
// Just a regular menu option
var click = lang.hitch(this, "_setValueAttr", option);
var item = new MenuItem({
option: option,
label: option.label || this.emptyLabel,
onClick: click,
ownerDocument: this.ownerDocument,
dir: this.dir,
disabled: option.disabled || false
});
item.focusNode.setAttribute("role", "option");
return item;
}
},
_addOptionItem: function(/*_FormSelectWidget.__SelectOption*/ option){
// summary:
// For the given option, add an option to our dropdown.
// If the option doesn't have a value, then a separator is added
// in that place.
if(this.dropDown){
this.dropDown.addChild(this._getMenuItemForOption(option));
}
},
_getChildren: function(){
if(!this.dropDown){
return [];
}
return this.dropDown.getChildren();
},
_loadChildren: function(/*Boolean*/ loadMenuItems){
// summary:
// Resets the menu and the length attribute of the button - and
// ensures that the label is appropriately set.
// loadMenuItems: Boolean
// actually loads the child menu items - we only do this when we are
// populating for showing the dropdown.
if(loadMenuItems === true){
// this.inherited destroys this.dropDown's child widgets (MenuItems).
// Avoid this.dropDown (Menu widget) having a pointer to a destroyed widget (which will cause
// issues later in _setSelected). (see #10296)
if(this.dropDown){
delete this.dropDown.focusedChild;
}
if(this.options.length){
this.inherited(arguments);
}else{
// Drop down menu is blank but add one blank entry just so something appears on the screen
// to let users know that they are no choices (mimicing native select behavior)
array.forEach(this._getChildren(), function(child){ child.destroyRecursive(); });
var item = new MenuItem({
ownerDocument: this.ownerDocument,
label: this.emptyLabel
});
this.dropDown.addChild(item);
}
}else{
this._updateSelection();
}
this._isLoaded = false;
this._childrenLoaded = true;
if(!this._loadingStore){
// Don't call this if we are loading - since we will handle it later
this._setValueAttr(this.value, false);
}
},
_refreshState: function(){
if(this._started){
this.validate(this.focused);
}
},
startup: function(){
this.inherited(arguments);
this._refreshState(); // after all _set* methods have run
},
_setValueAttr: function(value){
this.inherited(arguments);
domAttr.set(this.valueNode, "value", this.get("value"));
this._refreshState(); // to update this.state
},
_setDisabledAttr: function(/*Boolean*/ value){
this.inherited(arguments);
this._refreshState(); // to update this.state
},
_setRequiredAttr: function(/*Boolean*/ value){
this._set("required", value);
this.focusNode.setAttribute("aria-required", value);
this._refreshState(); // to update this.state
},
_setOptionsAttr: function(/*Array*/ options){
this._isLoaded = false;
this._set('options', options);
},
_setDisplay: function(/*String*/ newDisplay){
// summary:
// sets the display for the given value (or values)
var lbl = newDisplay || this.emptyLabel;
this.containerNode.innerHTML = '<span role="option" class="dijitReset dijitInline ' + this.baseClass.replace(/\s+|$/g, "Label ")+'">' + lbl + '</span>';
},
validate: function(/*Boolean*/ isFocused){
// summary:
// Called by oninit, onblur, and onkeypress, and whenever required/disabled state changes
// description:
// Show missing or invalid messages if appropriate, and highlight textbox field.
// Used when a select is initially set to no value and the user is required to
// set the value.
var isValid = this.disabled || this.isValid(isFocused);
this._set("state", isValid ? "" : (this._hasBeenBlurred ? "Error" : "Incomplete"));
this.focusNode.setAttribute("aria-invalid", isValid ? "false" : "true");
var message = isValid ? "" : this._missingMsg;
if(message && this.focused && this._hasBeenBlurred){
Tooltip.show(message, this.domNode, this.tooltipPosition, !this.isLeftToRight());
}else{
Tooltip.hide(this.domNode);
}
this._set("message", message);
return isValid;
},
isValid: function(/*Boolean*/ /*===== isFocused =====*/){
// summary:
// Whether or not this is a valid value. The only way a Select
// can be invalid is when it's required but nothing is selected.
return (!this.required || this.value === 0 || !(/^\s*$/.test(this.value || ""))); // handle value is null or undefined
},
reset: function(){
// summary:
// Overridden so that the state will be cleared.
this.inherited(arguments);
Tooltip.hide(this.domNode);
this._refreshState(); // to update this.state
},
postMixInProperties: function(){
// summary:
// set the missing message
this.inherited(arguments);
this._missingMsg = i18n.getLocalization("dijit.form", "validate", this.lang).missingMessage;
},
postCreate: function(){
// summary:
// stop mousemove from selecting text on IE to be consistent with other browsers
this.inherited(arguments);
this.connect(this.domNode, "onselectstart", event.stop);
this.domNode.setAttribute("aria-expanded", "false");
if(has("ie") < 9){
// IE INPUT tag fontFamily has to be set directly using STYLE
// the defer gives IE a chance to render the TextBox and to deal with font inheritance
this.defer(function(){
try{
var s = domStyle.getComputedStyle(this.domNode); // can throw an exception if widget is immediately destroyed
if(s){
var ff = s.fontFamily;
if(ff){
var inputs = this.domNode.getElementsByTagName("INPUT");
if(inputs){
for(var i=0; i < inputs.length; i++){
inputs[i].style.fontFamily = ff;
}
}
}
}
}catch(e){/*when used in a Dialog, and this is called before the dialog is
shown, s.fontFamily would trigger "Invalid Argument" error.*/}
});
}
},
_setStyleAttr: function(/*String||Object*/ value){
this.inherited(arguments);
domClass.toggle(this.domNode, this.baseClass.replace(/\s+|$/g, "FixedWidth "), !!this.domNode.style.width);
},
isLoaded: function(){
return this._isLoaded;
},
loadDropDown: function(/*Function*/ loadCallback){
// summary:
// populates the menu
this._loadChildren(true);
this._isLoaded = true;
loadCallback();
},
closeDropDown: function(){
// overriding _HasDropDown.closeDropDown()
this.inherited(arguments);
if(this.dropDown && this.dropDown.menuTableNode){
// Erase possible width: 100% setting from _SelectMenu.resize().
// Leaving it would interfere with the next openDropDown() call, which
// queries the natural size of the drop down.
this.dropDown.menuTableNode.style.width = "";
}
},
destroy: function(preserveDom){
if(this.dropDown && !this.dropDown._destroyed){
this.dropDown.destroyRecursive(preserveDom);
delete this.dropDown;
}
this.inherited(arguments);
},
_onFocus: function(){
this.validate(true); // show tooltip if second focus of required tooltip, but no selection
this.inherited(arguments);
},
_onBlur: function(){
Tooltip.hide(this.domNode);
this.inherited(arguments);
this.validate(false);
}
});
Select._Menu = _SelectMenu; // for monkey patching
return Select;
});
},
'dojo/store/util/QueryResults':function(){
define("dojo/store/util/QueryResults", ["../../_base/array", "../../_base/lang", "../../_base/Deferred"
], function(array, lang, Deferred){
// module:
// dojo/store/util/QueryResults
var QueryResults = function(results){
// summary:
// A function that wraps the results of a store query with additional
// methods.
// description:
// QueryResults is a basic wrapper that allows for array-like iteration
// over any kind of returned data from a query. While the simplest store
// will return a plain array of data, other stores may return deferreds or
// promises; this wrapper makes sure that *all* results can be treated
// the same.
//
// Additional methods include `forEach`, `filter` and `map`.
// results: Array|dojo/promise/Promise
// The result set as an array, or a promise for an array.
// returns:
// An array-like object that can be used for iterating over.
// example:
// Query a store and iterate over the results.
//
// | store.query({ prime: true }).forEach(function(item){
// | // do something
// | });
if(!results){
return results;
}
// if it is a promise it may be frozen
if(results.then){
results = lang.delegate(results);
}
function addIterativeMethod(method){
if(!results[method]){
results[method] = function(){
var args = arguments;
return Deferred.when(results, function(results){
Array.prototype.unshift.call(args, results);
return QueryResults(array[method].apply(array, args));
});
};
}
}
addIterativeMethod("forEach");
addIterativeMethod("filter");
addIterativeMethod("map");
if(!results.total){
results.total = Deferred.when(results, function(results){
return results.length;
});
}
return results; // Object
};
lang.setObject("dojo.store.util.QueryResults", QueryResults);
return QueryResults;
});
},
'dijit/form/_ListBase':function(){
define("dijit/form/_ListBase", [
"dojo/_base/declare", // declare
"dojo/on",
"dojo/window" // winUtils.scrollIntoView
], function(declare, on, winUtils){
// module:
// dijit/form/_ListBase
return declare( "dijit.form._ListBase", null, {
// summary:
// Focus-less menu to handle UI events consistently
// Abstract methods that must be defined externally:
//
// - onSelect: item is active (mousedown but not yet mouseup, or keyboard arrow selected but no Enter)
// - onDeselect: cancels onSelect
// tags:
// private
// selected: DOMNode
// currently selected node
selected: null,
_listConnect: function(/*String|Function*/ eventType, /*String*/ callbackFuncName){
// summary:
// Connects 'containerNode' to specified method of this object
// and automatically registers for 'disconnect' on widget destroy.
// description:
// Provide widget-specific analog to 'connect'.
// The callback function is called with the normal event object,
// but also a second parameter is passed that indicates which list item
// actually received the event.
// returns:
// A handle that can be passed to `disconnect` in order to disconnect
// before the widget is destroyed.
// tags:
// private
var self = this;
return self.own(on(self.containerNode,
on.selector(
function(eventTarget, selector, target){
return eventTarget.parentNode == target;
},
eventType
),
function(evt){
evt.preventDefault();
self[callbackFuncName](evt, this);
}
));
},
selectFirstNode: function(){
// summary:
// Select the first displayed item in the list.
var first = this.containerNode.firstChild;
while(first && first.style.display == "none"){
first = first.nextSibling;
}
this._setSelectedAttr(first);
},
selectLastNode: function(){
// summary:
// Select the last displayed item in the list
var last = this.containerNode.lastChild;
while(last && last.style.display == "none"){
last = last.previousSibling;
}
this._setSelectedAttr(last);
},
selectNextNode: function(){
// summary:
// Select the item just below the current selection.
// If nothing selected, select first node.
var selectedNode = this.selected;
if(!selectedNode){
this.selectFirstNode();
}else{
var next = selectedNode.nextSibling;
while(next && next.style.display == "none"){
next = next.nextSibling;
}
if(!next){
this.selectFirstNode();
}else{
this._setSelectedAttr(next);
}
}
},
selectPreviousNode: function(){
// summary:
// Select the item just above the current selection.
// If nothing selected, select last node (if
// you select Previous and try to keep scrolling up the list).
var selectedNode = this.selected;
if(!selectedNode){
this.selectLastNode();
}else{
var prev = selectedNode.previousSibling;
while(prev && prev.style.display == "none"){
prev = prev.previousSibling;
}
if(!prev){
this.selectLastNode();
}else{
this._setSelectedAttr(prev);
}
}
},
_setSelectedAttr: function(/*DomNode*/ node){
// summary:
// Does the actual select.
if(this.selected != node){
var selectedNode = this.selected;
if(selectedNode){
this.onDeselect(selectedNode);
this.selected = null;
}
if(node){
this.selected = node;
winUtils.scrollIntoView(node);
this.onSelect(node);
}
}else if(node){
this.onSelect(node);
}
}
});
});
},
'dijit/form/_FormWidget':function(){
define("dijit/form/_FormWidget", [
"dojo/_base/declare", // declare
"dojo/has", // has("dijit-legacy-requires")
"dojo/_base/kernel", // kernel.deprecated
"dojo/ready",
"../_Widget",
"../_CssStateMixin",
"../_TemplatedMixin",
"./_FormWidgetMixin"
], function(declare, has, kernel, ready, _Widget, _CssStateMixin, _TemplatedMixin, _FormWidgetMixin){
// module:
// dijit/form/_FormWidget
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/form/_FormValueWidget"];
require(requires); // use indirection so modules not rolled into a build
});
}
return declare("dijit.form._FormWidget", [_Widget, _TemplatedMixin, _CssStateMixin, _FormWidgetMixin], {
// 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/_WidgetBase.set()`.
//
// They also share some common methods.
setDisabled: function(/*Boolean*/ disabled){
// summary:
// Deprecated. Use set('disabled', ...) instead.
kernel.deprecated("setDisabled("+disabled+") is deprecated. Use set('disabled',"+disabled+") instead.", "", "2.0");
this.set('disabled', disabled);
},
setValue: function(/*String*/ value){
// summary:
// Deprecated. Use set('value', ...) instead.
kernel.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.
kernel.deprecated(this.declaredClass+"::getValue() is deprecated. Use get('value') instead.", "", "2.0");
return this.get('value');
},
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 _setNameAttr to set the name due to IE limitations, see #8484, #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, "&quot;") + '"') : '';
this.inherited(arguments);
},
// Override automatic assigning type --> focusNode, it causes exception on IE.
// Instead, type must be specified as ${type} in the template, as part of the original DOM
_setTypeAttr: null
});
});
},
'dojo/DeferredList':function(){
define("dojo/DeferredList", ["./_base/kernel", "./_base/Deferred", "./_base/array"], function(dojo, Deferred, darray){
// module:
// dojo/DeferredList
dojo.DeferredList = function(/*Array*/ list, /*Boolean?*/ fireOnOneCallback, /*Boolean?*/ fireOnOneErrback, /*Boolean?*/ consumeErrors, /*Function?*/ canceller){
// summary:
// Deprecated, use dojo/promise/all instead.
// Provides event handling for a group of Deferred objects.
// description:
// DeferredList takes an array of existing deferreds and returns a new deferred of its own
// this new deferred will typically have its callback fired when all of the deferreds in
// the given list have fired their own deferreds. The parameters `fireOnOneCallback` and
// fireOnOneErrback, will fire before all the deferreds as appropriate
// list:
// The list of deferreds to be synchronizied with this DeferredList
// fireOnOneCallback:
// Will cause the DeferredLists callback to be fired as soon as any
// of the deferreds in its list have been fired instead of waiting until
// the entire list has finished
// fireonOneErrback:
// Will cause the errback to fire upon any of the deferreds errback
// canceller:
// A deferred canceller function, see dojo.Deferred
var resultList = [];
Deferred.call(this);
var self = this;
if(list.length === 0 && !fireOnOneCallback){
this.resolve([0, []]);
}
var finished = 0;
darray.forEach(list, function(item, i){
item.then(function(result){
if(fireOnOneCallback){
self.resolve([i, result]);
}else{
addResult(true, result);
}
},function(error){
if(fireOnOneErrback){
self.reject(error);
}else{
addResult(false, error);
}
if(consumeErrors){
return null;
}
throw error;
});
function addResult(succeeded, result){
resultList[i] = [succeeded, result];
finished++;
if(finished === list.length){
self.resolve(resultList);
}
}
});
};
dojo.DeferredList.prototype = new Deferred();
dojo.DeferredList.prototype.gatherResults = function(deferredList){
// summary:
// Gathers the results of the deferreds for packaging
// as the parameters to the Deferred Lists' callback
// deferredList: dojo/DeferredList
// The deferred list from which this function gathers results.
// returns: dojo/DeferredList
// The newly created deferred list which packs results as
// parameters to its callback.
var d = new dojo.DeferredList(deferredList, false, true, false);
d.addCallback(function(results){
var ret = [];
darray.forEach(results, function(result){
ret.push(result[1]);
});
return ret;
});
return d;
};
return dojo.DeferredList;
});
},
'dojo/dnd/common':function(){
define("dojo/dnd/common", ["../_base/connect", "../_base/kernel", "../_base/lang", "../dom"],
function(connect, kernel, lang, dom){
// module:
// dojo/dnd/common
var exports = lang.getObject("dojo.dnd", true);
/*=====
// TODO: for 2.0, replace line above with this code.
var exports = {
// summary:
// TODOC
};
=====*/
exports.getCopyKeyState = connect.isCopyKey;
exports._uniqueId = 0;
exports.getUniqueId = function(){
// summary:
// returns a unique string for use with any DOM element
var id;
do{
id = kernel._scopeName + "Unique" + (++exports._uniqueId);
}while(dom.byId(id));
return id;
};
exports._empty = {};
exports.isFormElement = function(/*Event*/ e){
// summary:
// returns true if user clicked on a form element
var t = e.target;
if(t.nodeType == 3 /*TEXT_NODE*/){
t = t.parentNode;
}
return " button textarea input select option ".indexOf(" " + t.tagName.toLowerCase() + " ") >= 0; // Boolean
};
return exports;
});
},
'dijit/Viewport':function(){
define("dijit/Viewport", [
"dojo/Evented",
"dojo/on",
"dojo/ready",
"dojo/sniff",
"dojo/_base/window", // global
"dojo/window" // getBox()
], function(Evented, on, ready, has, win, winUtils){
// module:
// dijit/Viewport
/*=====
return {
// summary:
// Utility singleton to watch for viewport resizes, avoiding duplicate notifications
// which can lead to infinite loops.
// description:
// Usage: Viewport.on("resize", myCallback).
//
// myCallback() is called without arguments in case it's _WidgetBase.resize(),
// which would interpret the argument as the size to make the widget.
};
=====*/
var Viewport = new Evented();
ready(200, function(){
var oldBox = winUtils.getBox();
Viewport._rlh = on(win.global, "resize", function(){
var newBox = winUtils.getBox();
if(oldBox.h == newBox.h && oldBox.w == newBox.w){ return; }
oldBox = newBox;
Viewport.emit("resize");
});
// Also catch zoom changes on IE8, since they don't naturally generate resize events
if(has("ie") == 8){
var deviceXDPI = screen.deviceXDPI;
setInterval(function(){
if(screen.deviceXDPI != deviceXDPI){
deviceXDPI = screen.deviceXDPI;
Viewport.emit("resize");
}
}, 500);
}
});
return Viewport;
});
},
'dijit/_base/place':function(){
define("dijit/_base/place", [
"dojo/_base/array", // array.forEach
"dojo/_base/lang", // lang.isArray, lang.mixin
"dojo/window", // windowUtils.getBox
"../place",
"../main" // export to dijit namespace
], function(array, lang, windowUtils, place, dijit){
// module:
// dijit/_base/place
var exports = {
// summary:
// Deprecated back compatibility module, new code should use dijit/place directly instead of using this module.
};
exports.getViewport = function(){
// summary:
// Deprecated method to return the dimensions and scroll position of the viewable area of a browser window.
// New code should use windowUtils.getBox()
return windowUtils.getBox();
};
exports.placeOnScreen = place.at;
exports.placeOnScreenAroundElement = function(node, aroundNode, aroundCorners, layoutNode){
// summary:
// Like dijit.placeOnScreenAroundNode(), except it accepts an arbitrary object
// for the "around" argument and finds a proper processor to place a node.
// Deprecated, new code should use dijit/place.around() instead.
// Convert old style {"BL": "TL", "BR": "TR"} type argument
// to style needed by dijit.place code:
// [
// {aroundCorner: "BL", corner: "TL" },
// {aroundCorner: "BR", corner: "TR" }
// ]
var positions;
if(lang.isArray(aroundCorners)){
positions = aroundCorners;
}else{
positions = [];
for(var key in aroundCorners){
positions.push({aroundCorner: key, corner: aroundCorners[key]});
}
}
return place.around(node, aroundNode, positions, true, layoutNode);
};
exports.placeOnScreenAroundNode = exports.placeOnScreenAroundElement;
/*=====
exports.placeOnScreenAroundNode = function(node, aroundNode, aroundCorners, layoutNode){
// summary:
// Position node adjacent or kitty-corner to aroundNode
// such that it's fully visible in viewport.
// Deprecated, new code should use dijit/place.around() instead.
};
=====*/
exports.placeOnScreenAroundRectangle = exports.placeOnScreenAroundElement;
/*=====
exports.placeOnScreenAroundRectangle = function(node, aroundRect, aroundCorners, 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.
// Deprecated, new code should use dijit/place.around() instead.
};
=====*/
exports.getPopupAroundAlignment = function(/*Array*/ position, /*Boolean*/ leftToRight){
// summary:
// Deprecated method, unneeded when using dijit/place directly.
// Transforms the passed array of preferred positions into a format suitable for
// passing as the aroundCorners argument to dijit/place.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 = {};
array.forEach(position, function(pos){
var ltr = leftToRight;
switch(pos){
case "after":
align[leftToRight ? "BR" : "BL"] = leftToRight ? "BL" : "BR";
break;
case "before":
align[leftToRight ? "BL" : "BR"] = leftToRight ? "BR" : "BL";
break;
case "below-alt":
ltr = !ltr;
// fall through
case "below":
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
align[ltr ? "BL" : "BR"] = ltr ? "TL" : "TR";
align[ltr ? "BR" : "BL"] = ltr ? "TR" : "TL";
break;
case "above-alt":
ltr = !ltr;
// fall through
case "above":
default:
// first try to align left borders, next try to align right borders (or reverse for RTL mode)
align[ltr ? "TL" : "TR"] = ltr ? "BL" : "BR";
align[ltr ? "TR" : "TL"] = ltr ? "BR" : "BL";
break;
}
});
return align;
};
lang.mixin(dijit, exports);
/*===== return exports; =====*/
return dijit; // for back compat :-(
});
},
'dijit/MenuSeparator':function(){
require({cache:{
'url:dijit/templates/MenuSeparator.html':"<tr class=\"dijitMenuSeparator\">\n\t<td class=\"dijitMenuSeparatorIconCell\">\n\t\t<div class=\"dijitMenuSeparatorTop\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n\t<td colspan=\"3\" class=\"dijitMenuSeparatorLabelCell\">\n\t\t<div class=\"dijitMenuSeparatorTop dijitMenuSeparatorLabel\"></div>\n\t\t<div class=\"dijitMenuSeparatorBottom\"></div>\n\t</td>\n</tr>"}});
define("dijit/MenuSeparator", [
"dojo/_base/declare", // declare
"dojo/dom", // dom.setSelectable
"./_WidgetBase",
"./_TemplatedMixin",
"./_Contained",
"dojo/text!./templates/MenuSeparator.html"
], function(declare, dom, _WidgetBase, _TemplatedMixin, _Contained, template){
// module:
// dijit/MenuSeparator
return declare("dijit.MenuSeparator", [_WidgetBase, _TemplatedMixin, _Contained], {
// summary:
// A line between two menu items
templateString: template,
buildRendering: function(){
this.inherited(arguments);
dom.setSelectable(this.domNode, false);
},
isFocusable: function(){
// summary:
// Override to always return false
// tags:
// protected
return false; // Boolean
}
});
});
},
'dijit/form/_ComboBoxMenu':function(){
define("dijit/form/_ComboBoxMenu", [
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add domClass.remove
"dojo/dom-style", // domStyle.get
"dojo/keys", // keys.DOWN_ARROW keys.PAGE_DOWN keys.PAGE_UP keys.UP_ARROW
"../_WidgetBase",
"../_TemplatedMixin",
"./_ComboBoxMenuMixin",
"./_ListMouseMixin"
], function(declare, domClass, domStyle, keys,
_WidgetBase, _TemplatedMixin, _ComboBoxMenuMixin, _ListMouseMixin){
// module:
// dijit/form/_ComboBoxMenu
return declare("dijit.form._ComboBoxMenu",[_WidgetBase, _TemplatedMixin, _ListMouseMixin, _ComboBoxMenuMixin], {
// summary:
// Focus-less menu for internal use in `dijit/form/ComboBox`
// Abstract methods that must be defined externally:
//
// - onChange: item was explicitly chosen (mousedown somewhere on the menu and mouseup somewhere on the menu)
// - onPage: next(1) or previous(-1) button pressed
// tags:
// private
templateString: "<div class='dijitReset dijitMenu' data-dojo-attach-point='containerNode' style='overflow: auto; overflow-x: hidden;' role='listbox'>"
+"<div class='dijitMenuItem dijitMenuPreviousButton' data-dojo-attach-point='previousButton' role='option'></div>"
+"<div class='dijitMenuItem dijitMenuNextButton' data-dojo-attach-point='nextButton' role='option'></div>"
+"</div>",
baseClass: "dijitComboBoxMenu",
postCreate: function(){
this.inherited(arguments);
if(!this.isLeftToRight()){
domClass.add(this.previousButton, "dijitMenuItemRtl");
domClass.add(this.nextButton, "dijitMenuItemRtl");
}
},
_createMenuItem: function(){
// note: not using domConstruct.create() because need to specify document
var item = this.ownerDocument.createElement("div");
item.className = "dijitReset dijitMenuItem" +(this.isLeftToRight() ? "" : " dijitMenuItemRtl");
item.setAttribute("role", "option");
return item;
},
onHover: function(/*DomNode*/ node){
// summary:
// Add hover CSS
domClass.add(node, "dijitMenuItemHover");
},
onUnhover: function(/*DomNode*/ node){
// summary:
// Remove hover CSS
domClass.remove(node, "dijitMenuItemHover");
},
onSelect: function(/*DomNode*/ node){
// summary:
// Add selected CSS
domClass.add(node, "dijitMenuItemSelected");
},
onDeselect: function(/*DomNode*/ node){
// summary:
// Remove selected CSS
domClass.remove(node, "dijitMenuItemSelected");
},
_page: function(/*Boolean*/ up){
// summary:
// Handles page-up and page-down keypresses
var scrollamount = 0;
var oldscroll = this.domNode.scrollTop;
var height = domStyle.get(this.domNode, "height");
// if no item is highlighted, highlight the first option
if(!this.getHighlightedOption()){
this.selectNextNode();
}
while(scrollamount<height){
var highlighted_option = this.getHighlightedOption();
if(up){
// stop at option 1
if(!highlighted_option.previousSibling ||
highlighted_option.previousSibling.style.display == "none"){
break;
}
this.selectPreviousNode();
}else{
// stop at last option
if(!highlighted_option.nextSibling ||
highlighted_option.nextSibling.style.display == "none"){
break;
}
this.selectNextNode();
}
// going backwards
var newscroll = this.domNode.scrollTop;
scrollamount += (newscroll-oldscroll)*(up ? -1:1);
oldscroll = newscroll;
}
},
handleKey: function(evt){
// summary:
// Handle keystroke event forwarded from ComboBox, returning false if it's
// a keystroke I recognize and process, true otherwise.
switch(evt.keyCode){
case keys.DOWN_ARROW:
this.selectNextNode();
return false;
case keys.PAGE_DOWN:
this._page(false);
return false;
case keys.UP_ARROW:
this.selectPreviousNode();
return false;
case keys.PAGE_UP:
this._page(true);
return false;
default:
return true;
}
}
});
});
},
'url:dijit/layout/templates/ScrollingTabController.html':"<div class=\"dijitTabListContainer-${tabPosition}\" style=\"visibility:hidden\">\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerMenuButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_menuBtn\"\n\t\t\tdata-dojo-props=\"containerId: '${containerId}', iconClass: 'dijitTabStripMenuIcon',\n\t\t\t\t\tdropDownPosition: ['below-alt', 'above-alt']\"\n\t\t\tdata-dojo-attach-point=\"_menuBtn\" showLabel=\"false\" title=\"\">&#9660;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_leftBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideLeftIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_leftBtn\" data-dojo-attach-event=\"onClick: doSlideLeft\">&#9664;</div>\n\t<div data-dojo-type=\"dijit.layout._ScrollingTabControllerButton\"\n\t\t\tclass=\"tabStripButton-${tabPosition}\"\n\t\t\tid=\"${id}_rightBtn\"\n\t\t\tdata-dojo-props=\"iconClass:'dijitTabStripSlideRightIcon', showLabel:false, title:''\"\n\t\t\tdata-dojo-attach-point=\"_rightBtn\" data-dojo-attach-event=\"onClick: doSlideRight\">&#9654;</div>\n\t<div class='dijitTabListWrapper' data-dojo-attach-point='tablistWrapper'>\n\t\t<div role='tablist' data-dojo-attach-event='onkeypress:onkeypress'\n\t\t\t\tdata-dojo-attach-point='containerNode' class='nowrapTabStrip'></div>\n\t</div>\n</div>",
'dijit/Dialog':function(){
require({cache:{
'url:dijit/templates/Dialog.html':"<div class=\"dijitDialog\" role=\"dialog\" aria-labelledby=\"${id}_title\">\n\t<div data-dojo-attach-point=\"titleBar\" class=\"dijitDialogTitleBar\">\n\t\t<span data-dojo-attach-point=\"titleNode\" class=\"dijitDialogTitle\" id=\"${id}_title\"\n\t\t\t\trole=\"heading\" level=\"1\"></span>\n\t\t<span data-dojo-attach-point=\"closeButtonNode\" class=\"dijitDialogCloseIcon\" data-dojo-attach-event=\"ondijitclick: onCancel\" title=\"${buttonCancel}\" role=\"button\" tabIndex=\"-1\">\n\t\t\t<span data-dojo-attach-point=\"closeText\" class=\"closeText\" title=\"${buttonCancel}\">x</span>\n\t\t</span>\n\t</div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitDialogPaneContent\"></div>\n</div>\n"}});
define("dijit/Dialog", [
"require",
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/connect", // connect._keypress
"dojo/_base/declare", // declare
"dojo/_base/Deferred", // Deferred
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.contains
"dojo/dom-geometry", // domGeometry.position
"dojo/dom-style", // domStyle.set
"dojo/_base/event", // event.stop
"dojo/_base/fx", // fx.fadeIn fx.fadeOut
"dojo/i18n", // i18n.getLocalization
"dojo/keys",
"dojo/_base/lang", // lang.mixin lang.hitch
"dojo/on",
"dojo/ready",
"dojo/sniff", // has("ie") has("opera") has("dijit-legacy-requires")
"dojo/window", // winUtils.getBox, winUtils.get
"dojo/dnd/Moveable", // Moveable
"dojo/dnd/TimedMoveable", // TimedMoveable
"./focus",
"./_base/manager", // manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_CssStateMixin",
"./form/_FormMixin",
"./_DialogMixin",
"./DialogUnderlay",
"./layout/ContentPane",
"dojo/text!./templates/Dialog.html",
"./main", // for back-compat, exporting dijit._underlay (remove in 2.0)
"dojo/i18n!./nls/common"
], function(require, array, connect, declare, Deferred,
dom, domClass, domGeometry, domStyle, event, fx, i18n, keys, lang, on, ready, has, winUtils,
Moveable, TimedMoveable, focus, manager, _Widget, _TemplatedMixin, _CssStateMixin, _FormMixin, _DialogMixin,
DialogUnderlay, ContentPane, template, dijit){
// module:
// dijit/Dialog
/*=====
dijit._underlay = function(kwArgs){
// summary:
// A shared instance of a `dijit.DialogUnderlay`
//
// description:
// A shared instance of a `dijit.DialogUnderlay` created and
// used by `dijit.Dialog`, though never created until some Dialog
// or subclass thereof is shown.
};
=====*/
var _DialogBase = declare("dijit._DialogBase", [_TemplatedMixin, _FormMixin, _DialogMixin, _CssStateMixin], {
templateString: template,
baseClass: "dijitDialog",
cssStateNodes: {
closeButtonNode: "dijitDialogCloseIcon"
},
// Map widget attributes to DOMNode attributes.
_setTitleAttr: [
{ node: "titleNode", type: "innerHTML" },
{ node: "titleBar", type: "attribute" }
],
// open: [readonly] Boolean
// True if Dialog is currently displayed on screen.
open: false,
// duration: Integer
// The time in milliseconds it takes the dialog to fade in and out
duration: manager.defaultDuration,
// refocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to re-focus the element which had focus before being opened.
// False will disable refocusing. Default: true
refocus: true,
// autofocus: Boolean
// A Toggle to modify the default focus behavior of a Dialog, which
// is to focus on the first dialog element after opening the dialog.
// False will disable autofocusing. Default: true
autofocus: true,
// _firstFocusItem: [private readonly] DomNode
// The pointer to the first focusable node in the dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_firstFocusItem: null,
// _lastFocusItem: [private readonly] DomNode
// The pointer to which node has focus prior to our dialog.
// Set by `dijit/_DialogMixin._getFocusItems()`.
_lastFocusItem: null,
// doLayout: [protected] Boolean
// Don't change this parameter from the default value.
// This ContentPane parameter doesn't make sense for Dialog, since Dialog
// is never a child of a layout container, nor can you specify the size of
// Dialog in order to control the size of an inner widget.
doLayout: false,
// draggable: Boolean
// Toggles the moveable aspect of the Dialog. If true, Dialog
// can be dragged by it's title. If false it will remain centered
// in the viewport.
draggable: true,
_setDraggableAttr: function(/*Boolean*/ val){
// Avoid _WidgetBase behavior of copying draggable attribute to this.domNode,
// as that prevents text select on modern browsers (#14452)
this._set("draggable", val);
},
// aria-describedby: String
// Allows the user to add an aria-describedby attribute onto the dialog. The value should
// be the id of the container element of text that describes the dialog purpose (usually
// the first text in the dialog).
// | <div data-dojo-type="dijit/Dialog" aria-describedby="intro" .....>
// | <div id="intro">Introductory text</div>
// | <div>rest of dialog contents</div>
// | </div>
"aria-describedby": "",
// maxRatio: Number
// Maximum size to allow the dialog to expand to, relative to viewport size
maxRatio: 0.9,
postMixInProperties: function(){
var _nlsResources = i18n.getLocalization("dijit", "common");
lang.mixin(this, _nlsResources);
this.inherited(arguments);
},
postCreate: function(){
domStyle.set(this.domNode, {
display: "none",
position:"absolute"
});
this.ownerDocumentBody.appendChild(this.domNode);
this.inherited(arguments);
this.connect(this, "onExecute", "hide");
this.connect(this, "onCancel", "hide");
this._modalconnects = [];
},
onLoad: function(){
// summary:
// Called when data has been loaded from an href.
// Unlike most other callbacks, this function can be connected to (via `dojo.connect`)
// but should *not* be overridden.
// tags:
// callback
// when href is specified we need to reposition the dialog after the data is loaded
// and find the focusable elements
this._position();
if(this.autofocus && DialogLevelManager.isTop(this)){
this._getFocusItems(this.domNode);
focus.focus(this._firstFocusItem);
}
this.inherited(arguments);
},
_onBlur: function(by){
this.inherited(arguments);
// If focus was accidentally removed from the dialog, such as if the user clicked a blank
// area of the screen, or clicked the browser's address bar and then tabbed into the page,
// then refocus. Won't do anything if focus was removed because the Dialog was closed, or
// because a new Dialog popped up on top of the old one.
var refocus = lang.hitch(this, function(){
if(this.open && !this._destroyed && DialogLevelManager.isTop(this)){
this._getFocusItems(this.domNode);
focus.focus(this._firstFocusItem);
}
});
if(by == "mouse"){
// wait for mouse up, and then refocus dialog; otherwise doesn't work
on.once(this.ownerDocument, "mouseup", refocus);
}else{
refocus();
}
},
_endDrag: function(){
// summary:
// Called after dragging the Dialog. Saves the position of the dialog in the viewport,
// and also adjust position to be fully within the viewport, so user doesn't lose access to handle
var nodePosition = domGeometry.position(this.domNode),
viewport = winUtils.getBox(this.ownerDocument);
nodePosition.y = Math.min(Math.max(nodePosition.y, 0), (viewport.h - nodePosition.h));
nodePosition.x = Math.min(Math.max(nodePosition.x, 0), (viewport.w - nodePosition.w));
this._relativePosition = nodePosition;
this._position();
},
_setup: function(){
// summary:
// Stuff we need to do before showing the Dialog for the first
// time (but we defer it until right beforehand, for
// performance reasons).
// tags:
// private
var node = this.domNode;
if(this.titleBar && this.draggable){
this._moveable = new ((has("ie") == 6) ? TimedMoveable // prevent overload, see #5285
: Moveable)(node, { handle: this.titleBar });
this.connect(this._moveable, "onMoveStop", "_endDrag");
}else{
domClass.add(node,"dijitDialogFixed");
}
this.underlayAttrs = {
dialogId: this.id,
"class": array.map(this["class"].split(/\s/), function(s){ return s+"_underlay"; }).join(" "),
ownerDocument: this.ownerDocument
};
},
_size: function(){
// summary:
// If necessary, shrink dialog contents so dialog fits in viewport
// tags:
// private
this._checkIfSingleChild();
// If we resized the dialog contents earlier, reset them back to original size, so
// that if the user later increases the viewport size, the dialog can display w/out a scrollbar.
// Need to do this before the domGeometry.position(this.domNode) call below.
if(this._singleChild){
if(typeof this._singleChildOriginalStyle != "undefined"){
this._singleChild.domNode.style.cssText = this._singleChildOriginalStyle;
delete this._singleChildOriginalStyle;
}
}else{
domStyle.set(this.containerNode, {
width:"auto",
height:"auto"
});
}
var bb = domGeometry.position(this.domNode);
// Get viewport size but then reduce it by a bit; Dialog should always have some space around it
// to indicate that it's a popup. This will also compensate for possible scrollbars on viewport.
var viewport = winUtils.getBox(this.ownerDocument);
viewport.w *= this.maxRatio;
viewport.h *= this.maxRatio;
if(bb.w >= viewport.w || bb.h >= viewport.h){
// Reduce size of dialog contents so that dialog fits in viewport
var containerSize = domGeometry.position(this.containerNode),
w = Math.min(bb.w, viewport.w) - (bb.w - containerSize.w),
h = Math.min(bb.h, viewport.h) - (bb.h - containerSize.h);
if(this._singleChild && this._singleChild.resize){
if(typeof this._singleChildOriginalStyle == "undefined"){
this._singleChildOriginalStyle = this._singleChild.domNode.style.cssText;
}
this._singleChild.resize({w: w, h: h});
}else{
domStyle.set(this.containerNode, {
width: w + "px",
height: h + "px",
overflow: "auto",
position: "relative" // workaround IE bug moving scrollbar or dragging dialog
});
}
}else{
if(this._singleChild && this._singleChild.resize){
this._singleChild.resize();
}
}
},
_position: function(){
// summary:
// Position modal dialog in the viewport. If no relative offset
// in the viewport has been determined (by dragging, for instance),
// center the node. Otherwise, use the Dialog's stored relative offset,
// and position the node to top: left: values based on the viewport.
if(!domClass.contains(this.ownerDocumentBody, "dojoMove")){ // don't do anything if called during auto-scroll
var node = this.domNode,
viewport = winUtils.getBox(this.ownerDocument),
p = this._relativePosition,
bb = p ? null : domGeometry.position(node),
l = Math.floor(viewport.l + (p ? p.x : (viewport.w - bb.w) / 2)),
t = Math.floor(viewport.t + (p ? p.y : (viewport.h - bb.h) / 2))
;
domStyle.set(node,{
left: l + "px",
top: t + "px"
});
}
},
_onKey: function(/*Event*/ evt){
// summary:
// Handles the keyboard events for accessibility reasons
// tags:
// private
if(evt.charOrCode){
var node = evt.target;
if(evt.charOrCode === keys.TAB){
this._getFocusItems(this.domNode);
}
var singleFocusItem = (this._firstFocusItem == this._lastFocusItem);
// see if we are shift-tabbing from first focusable item on dialog
if(node == this._firstFocusItem && evt.shiftKey && evt.charOrCode === keys.TAB){
if(!singleFocusItem){
focus.focus(this._lastFocusItem); // send focus to last item in dialog
}
event.stop(evt);
}else if(node == this._lastFocusItem && evt.charOrCode === keys.TAB && !evt.shiftKey){
if(!singleFocusItem){
focus.focus(this._firstFocusItem); // send focus to first item in dialog
}
event.stop(evt);
}else{
// see if the key is for the dialog
while(node){
if(node == this.domNode || domClass.contains(node, "dijitPopup")){
if(evt.charOrCode == keys.ESCAPE){
this.onCancel();
}else{
return; // just let it go
}
}
node = node.parentNode;
}
// this key is for the disabled document window
if(evt.charOrCode !== keys.TAB){ // allow tabbing into the dialog for a11y
event.stop(evt);
// opera won't tab to a div
}else if(!has("opera")){
try{
this._firstFocusItem.focus();
}catch(e){ /*squelch*/ }
}
}
}
},
show: function(){
// summary:
// Display the dialog
// returns: dojo/_base/Deferred
// Deferred object that resolves when the display animation is complete
if(this.open){ return; }
if(!this._started){
this.startup();
}
// first time we show the dialog, there's some initialization stuff to do
if(!this._alreadyInitialized){
this._setup();
this._alreadyInitialized=true;
}
if(this._fadeOutDeferred){
this._fadeOutDeferred.cancel();
}
// Recenter Dialog if user scrolls browser. Connecting to document doesn't work on IE, need to use window.
var win = winUtils.get(this.ownerDocument);
this._modalconnects.push(on(win, "scroll", lang.hitch(this, "resize")));
this._modalconnects.push(on(this.domNode, connect._keypress, lang.hitch(this, "_onKey")));
domStyle.set(this.domNode, {
opacity:0,
display:""
});
this._set("open", true);
this._onShow(); // lazy load trigger
this._size();
this._position();
// fade-in Animation object, setup below
var fadeIn;
this._fadeInDeferred = new Deferred(lang.hitch(this, function(){
fadeIn.stop();
delete this._fadeInDeferred;
}));
fadeIn = fx.fadeIn({
node: this.domNode,
duration: this.duration,
beforeBegin: lang.hitch(this, function(){
DialogLevelManager.show(this, this.underlayAttrs);
}),
onEnd: lang.hitch(this, function(){
if(this.autofocus && DialogLevelManager.isTop(this)){
// find focusable items each time dialog is shown since if dialog contains a widget the
// first focusable items can change
this._getFocusItems(this.domNode);
focus.focus(this._firstFocusItem);
}
this._fadeInDeferred.resolve(true);
delete this._fadeInDeferred;
})
}).play();
return this._fadeInDeferred;
},
hide: function(){
// summary:
// Hide the dialog
// returns: dojo/_base/Deferred
// Deferred object that resolves when the hide animation is complete
// If we haven't been initialized yet then we aren't showing and we can just return.
// Likewise if we are already hidden, or are currently fading out.
if(!this._alreadyInitialized || !this.open){
return;
}
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
// fade-in Animation object, setup below
var fadeOut;
this._fadeOutDeferred = new Deferred(lang.hitch(this, function(){
fadeOut.stop();
delete this._fadeOutDeferred;
}));
// fire onHide when the promise resolves.
this._fadeOutDeferred.then(lang.hitch(this, 'onHide'));
fadeOut = fx.fadeOut({
node: this.domNode,
duration: this.duration,
onEnd: lang.hitch(this, function(){
this.domNode.style.display = "none";
DialogLevelManager.hide(this);
this._fadeOutDeferred.resolve(true);
delete this._fadeOutDeferred;
})
}).play();
if(this._scrollConnected){
this._scrollConnected = false;
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
if(this._relativePosition){
delete this._relativePosition;
}
this._set("open", false);
return this._fadeOutDeferred;
},
resize: function(){
// summary:
// Called when viewport scrolled or size changed. Position the Dialog and the underlay.
// tags:
// private
if(this.domNode.style.display != "none"){
if(DialogUnderlay._singleton){ // avoid race condition during show()
DialogUnderlay._singleton.layout();
}
this._position();
this._size();
}
},
destroy: function(){
if(this._fadeInDeferred){
this._fadeInDeferred.cancel();
}
if(this._fadeOutDeferred){
this._fadeOutDeferred.cancel();
}
if(this._moveable){
this._moveable.destroy();
}
var h;
while(h = this._modalconnects.pop()){
h.remove();
}
DialogLevelManager.hide(this);
this.inherited(arguments);
}
});
var Dialog = declare("dijit.Dialog", [ContentPane, _DialogBase], {
// summary:
// A modal dialog Widget.
// description:
// Pops up a modal dialog window, blocking access to the screen
// and also graying out the screen Dialog is extended from
// ContentPane so it supports all the same parameters (href, etc.).
// example:
// | <div data-dojo-type="dijit/Dialog" data-dojo-props="href: 'test.html'"></div>
// example:
// | var foo = new Dialog({ title: "test dialog", content: "test content" };
// | foo.placeAt(win.body());
// | foo.startup();
});
Dialog._DialogBase = _DialogBase; // for monkey patching and dojox/widget/DialogSimple
var DialogLevelManager = Dialog._DialogLevelManager = {
// summary:
// Controls the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
_beginZIndex: 950,
show: function(/*dijit/_WidgetBase*/ dialog, /*Object*/ underlayAttrs){
// summary:
// Call right before fade-in animation for new dialog.
// Saves current focus, displays/adjusts underlay for new dialog,
// and sets the z-index of the dialog itself.
//
// New dialog will be displayed on top of all currently displayed dialogs.
//
// Caller is responsible for setting focus in new dialog after the fade-in
// animation completes.
// Save current focus
ds[ds.length-1].focus = focus.curNode;
// Display the underlay, or if already displayed then adjust for this new dialog
// TODO: one underlay per document (based on dialog.ownerDocument)
var underlay = DialogUnderlay._singleton;
if(!underlay || underlay._destroyed){
underlay = dijit._underlay = DialogUnderlay._singleton = new DialogUnderlay(underlayAttrs);
}else{
underlay.set(dialog.underlayAttrs);
}
// Set z-index a bit above previous dialog
var zIndex = ds[ds.length-1].dialog ? ds[ds.length-1].zIndex + 2 : Dialog._DialogLevelManager._beginZIndex;
if(ds.length == 1){ // first dialog
underlay.show();
}
domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', zIndex - 1);
// Dialog
domStyle.set(dialog.domNode, 'zIndex', zIndex);
ds.push({dialog: dialog, underlayAttrs: underlayAttrs, zIndex: zIndex});
},
hide: function(/*dijit/_WidgetBase*/ dialog){
// summary:
// Called when the specified dialog is hidden/destroyed, after the fade-out
// animation ends, in order to reset page focus, fix the underlay, etc.
// If the specified dialog isn't open then does nothing.
//
// Caller is responsible for either setting display:none on the dialog domNode,
// or calling dijit/popup.hide(), or removing it from the page DOM.
if(ds[ds.length-1].dialog == dialog){
// Removing the top (or only) dialog in the stack, return focus
// to previous dialog
ds.pop();
var pd = ds[ds.length-1]; // the new active dialog (or the base page itself)
// Adjust underlay, unless the underlay widget has already been destroyed
// because we are being called during page unload (when all widgets are destroyed)
if(!DialogUnderlay._singleton._destroyed){
if(ds.length == 1){
// Returning to original page. Hide the underlay.
DialogUnderlay._singleton.hide();
}else{
// Popping back to previous dialog, adjust underlay.
domStyle.set(DialogUnderlay._singleton.domNode, 'zIndex', pd.zIndex - 1);
DialogUnderlay._singleton.set(pd.underlayAttrs);
}
}
// Adjust focus
if(dialog.refocus){
// If we are returning control to a previous dialog but for some reason
// that dialog didn't have a focused field, set focus to first focusable item.
// This situation could happen if two dialogs appeared at nearly the same time,
// since a dialog doesn't set it's focus until the fade-in is finished.
var focus = pd.focus;
if(pd.dialog && (!focus || !dom.isDescendant(focus, pd.dialog.domNode))){
pd.dialog._getFocusItems(pd.dialog.domNode);
focus = pd.dialog._firstFocusItem;
}
if(focus){
// Refocus the button that spawned the Dialog. This will fail in corner cases including
// page unload on IE, because the dijit/form/Button that launched the Dialog may get destroyed
// before this code runs. (#15058)
try{
focus.focus();
}catch(e){}
}
}
}else{
// Removing a dialog out of order (#9944, #10705).
// Don't need to mess with underlay or z-index or anything.
var idx = array.indexOf(array.map(ds, function(elem){return elem.dialog}), dialog);
if(idx != -1){
ds.splice(idx, 1);
}
}
},
isTop: function(/*dijit/_WidgetBase*/ dialog){
// summary:
// Returns true if specified Dialog is the top in the task
return ds[ds.length-1].dialog == dialog;
}
};
// Stack representing the various active "levels" on the page, starting with the
// stuff initially visible on the page (at z-index 0), and then having an entry for
// each Dialog shown.
// Each element in stack has form {
// dialog: dialogWidget,
// focus: returnFromGetFocus(),
// underlayAttrs: attributes to set on underlay (when this widget is active)
// }
var ds = Dialog._dialogStack = [
{dialog: null, focus: null, underlayAttrs: null} // entry for stuff at z-index: 0
];
// Back compat w/1.6, remove for 2.0
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/TooltipDialog"];
require(requires); // use indirection so modules not rolled into a build
});
}
return Dialog;
});
},
'dijit/_base/focus':function(){
define("dijit/_base/focus", [
"dojo/_base/array", // array.forEach
"dojo/dom", // dom.isDescendant
"dojo/_base/lang", // lang.isArray
"dojo/topic", // publish
"dojo/_base/window", // win.doc win.doc.selection win.global win.global.getSelection win.withGlobal
"../focus",
"../main" // for exporting symbols to dijit
], function(array, dom, lang, topic, win, focus, dijit){
// module:
// dijit/_base/focus
var exports = {
// summary:
// Deprecated module to monitor currently focused node and stack of currently focused widgets.
// New code should access dijit/focus directly.
// _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 = win.doc.selection, cf = focus.curNode;
if(win.global.getSelection){
//W3C Range API for selections.
sel = win.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};
if(sel.rangeCount){
bm.mark = sel.getRangeAt(0).cloneRange();
}
}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 = win.doc,
mark = bookmark.mark;
if(mark){
if(win.global.getSelection){
//W3C Rangi API (FF, WebKit, Opera, etc)
var sel = win.global.getSelection();
if(sel && sel.removeAllRanges){
if(mark.pRange){
var n = mark.node;
n.selectionStart = mark.start;
n.selectionEnd = mark.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(lang.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
array.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/_WidgetBase|{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 = !focus.curNode || (menu && dom.isDescendant(focus.curNode, menu.domNode)) ? dijit._prevFocus : focus.curNode;
return {
node: node,
bookmark: node && (node == focus.curNode) && win.withGlobal(openedForWindow || win.global, dijit.getBookmark),
openedForWindow: openedForWindow
}; // Object
},
// _activeStack: dijit/_WidgetBase[]
// 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 focus.registerIframe(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()
handle && handle.remove();
},
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()
return focus.registerWin(targetWindow, effectiveNode);
},
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.
handle && handle.remove();
}
};
// Override focus singleton's focus function so that dijit.focus()
// has backwards compatible behavior of restoring selection (although
// probably no one is using that).
focus.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*/}
}
focus._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 && win.withGlobal(openedForWindow || win.global, dijit.isCollapsed) && !collapsed){
if(openedForWindow){
openedForWindow.focus();
}
try{
win.withGlobal(openedForWindow || win.global, dijit.moveToBookmark, null, [bookmark]);
}catch(e2){
/*squelch IE internal error, see http://trac.dojotoolkit.org/ticket/1984 */
}
}
};
// For back compatibility, monitor changes to focused node and active widget stack,
// publishing events and copying changes from focus manager variables into dijit (top level) variables
focus.watch("curNode", function(name, oldVal, newVal){
dijit._curFocus = newVal;
dijit._prevFocus = oldVal;
if(newVal){
topic.publish("focusNode", newVal); // publish
}
});
focus.watch("activeStack", function(name, oldVal, newVal){
dijit._activeStack = newVal;
});
focus.on("widget-blur", function(widget, by){
topic.publish("widgetBlur", widget, by); // publish
});
focus.on("widget-focus", function(widget, by){
topic.publish("widgetFocus", widget, by); // publish
});
lang.mixin(dijit, exports);
/*===== return exports; =====*/
return dijit; // for back compat :-(
});
},
'dijit/tree/dndSource':function(){
define("dijit/tree/dndSource", [
"dojo/_base/array", // array.forEach array.indexOf array.map
"dojo/_base/connect", // isCopyKey
"dojo/_base/declare", // declare
"dojo/dom-class", // domClass.add
"dojo/dom-geometry", // domGeometry.position
"dojo/_base/lang", // lang.mixin lang.hitch
"dojo/on", // subscribe
"dojo/touch",
"dojo/topic",
"dojo/dnd/Manager", // DNDManager.manager
"./_dndSelector"
], function(array, connect, declare, domClass, domGeometry, lang, on, touch, topic, DNDManager, _dndSelector){
// module:
// dijit/tree/dndSource
// summary:
// Handles drag and drop operations (as a source or a target) for `dijit.Tree`
/*=====
var __Item = {
// summary:
// New item to be added to the Tree, like:
// id: Anything
id: "",
// name: String
name: ""
};
=====*/
var dndSource = declare("dijit.tree.dndSource", _dndSelector, {
// summary:
// Handles drag and drop operations (as a source or a target) for `dijit.Tree`
// isSource: Boolean
// Can be used as a DnD source.
isSource: true,
// accept: String[]
// List of accepted types (text strings) for the Tree; defaults to
// ["text"]
accept: ["text", "treeNode"],
// copyOnly: [private] Boolean
// Copy items, if true, use a state of Ctrl key otherwise
copyOnly: false,
// dragThreshold: Number
// The move delay in pixels before detecting a drag; 5 by default
dragThreshold: 5,
// betweenThreshold: Integer
// Distance from upper/lower edge of node to allow drop to reorder nodes
betweenThreshold: 0,
// Flag used by Avatar.js to signal to generate text node when dragging
generateText: true,
constructor: function(/*dijit/Tree*/ tree, /*dijit/tree/dndSource*/ params){
// summary:
// a constructor of the Tree DnD Source
// tags:
// private
if(!params){ params = {}; }
lang.mixin(this, params);
var type = params.accept instanceof Array ? params.accept : ["text", "treeNode"];
this.accept = null;
if(type.length){
this.accept = {};
for(var i = 0; i < type.length; ++i){
this.accept[type[i]] = 1;
}
}
// class-specific variables
this.isDragging = false;
this.mouseDown = false;
this.targetAnchor = null; // DOMNode corresponding to the currently moused over TreeNode
this.targetBox = null; // coordinates of this.targetAnchor
this.dropPosition = ""; // whether mouse is over/after/before this.targetAnchor
this._lastX = 0;
this._lastY = 0;
// states
this.sourceState = "";
if(this.isSource){
domClass.add(this.node, "dojoDndSource");
}
this.targetState = "";
if(this.accept){
domClass.add(this.node, "dojoDndTarget");
}
// set up events
this.topics = [
topic.subscribe("/dnd/source/over", lang.hitch(this, "onDndSourceOver")),
topic.subscribe("/dnd/start", lang.hitch(this, "onDndStart")),
topic.subscribe("/dnd/drop", lang.hitch(this, "onDndDrop")),
topic.subscribe("/dnd/cancel", lang.hitch(this, "onDndCancel"))
];
},
// methods
checkAcceptance: function(/*===== source, nodes =====*/){
// summary:
// Checks if the target can accept nodes from this source
// source: dijit/tree/dndSource
// The source which provides items
// nodes: DOMNode[]
// Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
// source is a dijit/Tree.
// tags:
// extension
return true; // Boolean
},
copyState: function(keyPressed){
// summary:
// Returns true, if we need to copy items, false to move.
// It is separated to be overwritten dynamically, if needed.
// keyPressed: Boolean
// The "copy" control key was pressed
// tags:
// protected
return this.copyOnly || keyPressed; // Boolean
},
destroy: function(){
// summary:
// Prepares the object to be garbage-collected.
this.inherited(arguments);
var h;
while(h = this.topics.pop()){ h.remove(); }
this.targetAnchor = null;
},
_onDragMouse: function(e, firstTime){
// summary:
// Helper method for processing onmousemove/onmouseover events while drag is in progress.
// Keeps track of current drop target.
// e: Event
// The mousemove event.
// firstTime: Boolean?
// If this flag is set, this is the first mouse move event of the drag, so call m.canDrop() etc.
// even if newTarget == null because the user quickly dragged a node in the Tree to a position
// over Tree.containerNode but not over any TreeNode (#7971)
var m = DNDManager.manager(),
oldTarget = this.targetAnchor, // the TreeNode corresponding to TreeNode mouse was previously over
newTarget = this.current, // TreeNode corresponding to TreeNode mouse is currently over
oldDropPosition = this.dropPosition; // the previous drop position (over/before/after)
// calculate if user is indicating to drop the dragged node before, after, or over
// (i.e., to become a child of) the target node
var newDropPosition = "Over";
if(newTarget && this.betweenThreshold > 0){
// If mouse is over a new TreeNode, then get new TreeNode's position and size
if(!this.targetBox || oldTarget != newTarget){
this.targetBox = domGeometry.position(newTarget.rowNode, true);
}
if((e.pageY - this.targetBox.y) <= this.betweenThreshold){
newDropPosition = "Before";
}else if((e.pageY - this.targetBox.y) >= (this.targetBox.h - this.betweenThreshold)){
newDropPosition = "After";
}
}
if(firstTime || newTarget != oldTarget || newDropPosition != oldDropPosition){
if(oldTarget){
this._removeItemClass(oldTarget.rowNode, oldDropPosition);
}
if(newTarget){
this._addItemClass(newTarget.rowNode, newDropPosition);
}
// Check if it's ok to drop the dragged node on/before/after the target node.
if(!newTarget){
m.canDrop(false);
}else if(newTarget == this.tree.rootNode && newDropPosition != "Over"){
// Can't drop before or after tree's root node; the dropped node would just disappear (at least visually)
m.canDrop(false);
}else{
// Guard against dropping onto yourself (TODO: guard against dropping onto your descendant, #7140)
var sameId = false;
if(m.source == this){
for(var dragId in this.selection){
var dragNode = this.selection[dragId];
if(dragNode.item === newTarget.item){
sameId = true;
break;
}
}
}
if(sameId){
m.canDrop(false);
}else if(this.checkItemAcceptance(newTarget.rowNode, m.source, newDropPosition.toLowerCase())
&& !this._isParentChildDrop(m.source, newTarget.rowNode)){
m.canDrop(true);
}else{
m.canDrop(false);
}
}
this.targetAnchor = newTarget;
this.dropPosition = newDropPosition;
}
},
onMouseMove: function(e){
// summary:
// Called for any onmousemove/ontouchmove events over the Tree
// e: Event
// onmousemouse/ontouchmove event
// tags:
// private
if(this.isDragging && this.targetState == "Disabled"){ return; }
this.inherited(arguments);
var m = DNDManager.manager();
if(this.isDragging){
this._onDragMouse(e);
}else{
if(this.mouseDown && this.isSource &&
(Math.abs(e.pageX-this._lastX)>=this.dragThreshold || Math.abs(e.pageY-this._lastY)>=this.dragThreshold)){
var nodes = this.getSelectedTreeNodes();
if(nodes.length){
if(nodes.length > 1){
//filter out all selected items which has one of their ancestor selected as well
var seen = this.selection, i = 0, r = [], n, p;
nextitem: while((n = nodes[i++])){
for(p = n.getParent(); p && p !== this.tree; p = p.getParent()){
if(seen[p.id]){ //parent is already selected, skip this node
continue nextitem;
}
}
//this node does not have any ancestors selected, add it
r.push(n);
}
nodes = r;
}
nodes = array.map(nodes, function(n){return n.domNode});
m.startDrag(this, nodes, this.copyState(connect.isCopyKey(e)));
this._onDragMouse(e, true); // because this may be the only mousemove event we get before the drop
}
}
}
},
onMouseDown: function(e){
// summary:
// Event processor for onmousedown/ontouchstart
// e: Event
// onmousedown/ontouchend event
// tags:
// private
this.mouseDown = true;
this.mouseButton = e.button;
this._lastX = e.pageX;
this._lastY = e.pageY;
this.inherited(arguments);
},
onMouseUp: function(e){
// summary:
// Event processor for onmouseup/ontouchend
// e: Event
// onmouseup/ontouchend event
// tags:
// private
if(this.mouseDown){
this.mouseDown = false;
this.inherited(arguments);
}
},
onMouseOut: function(){
// summary:
// Event processor for when mouse is moved away from a TreeNode
// tags:
// private
this.inherited(arguments);
this._unmarkTargetAnchor();
},
checkItemAcceptance: function(/*===== target, source, position =====*/){
// summary:
// Stub function to be overridden if one wants to check for the ability to drop at the node/item level
// description:
// In the base case, this is called to check if target can become a child of source.
// When betweenThreshold is set, position="before" or "after" means that we
// are asking if the source node can be dropped before/after the target node.
// target: DOMNode
// The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
// Use dijit.getEnclosingWidget(target) to get the TreeNode.
// source: dijit/tree/dndSource
// The (set of) nodes we are dropping
// position: String
// "over", "before", or "after"
// tags:
// extension
return true;
},
// topic event processors
onDndSourceOver: function(source){
// summary:
// Topic event processor for /dnd/source/over, called when detected a current source.
// source: Object
// The dijit/tree/dndSource / dojo/dnd/Source which has the mouse over it
// tags:
// private
if(this != source){
this.mouseDown = false;
this._unmarkTargetAnchor();
}else if(this.isDragging){
var m = DNDManager.manager();
m.canDrop(false);
}
},
onDndStart: function(source, nodes, copy){
// summary:
// Topic event processor for /dnd/start, called to initiate the DnD operation
// source: Object
// The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
// nodes: DomNode[]
// The list of transferred items, dndTreeNode nodes if dragging from a Tree
// copy: Boolean
// Copy items, if true, move items otherwise
// tags:
// private
if(this.isSource){
this._changeState("Source", this == source ? (copy ? "Copied" : "Moved") : "");
}
var accepted = this.checkAcceptance(source, nodes);
this._changeState("Target", accepted ? "" : "Disabled");
if(this == source){
DNDManager.manager().overSource(this);
}
this.isDragging = true;
},
itemCreator: function(nodes /*===== , target, source =====*/){
// summary:
// Returns objects passed to `Tree.model.newItem()` based on DnD nodes
// dropped onto the tree. Developer must override this method to enable
// dropping from external sources onto this Tree, unless the Tree.model's items
// happen to look like {id: 123, name: "Apple" } with no other attributes.
// description:
// For each node in nodes[], which came from source, create a hash of name/value
// pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
// nodes: DomNode[]
// target: DomNode
// source: dojo/dnd/Source
// returns: __Item[]
// Array of name/value hashes for each new item to be added to the Tree
// tags:
// extension
// TODO: for 2.0 refactor so itemCreator() is called once per drag node, and
// make signature itemCreator(sourceItem, node, target) (or similar).
return array.map(nodes, function(node){
return {
"id": node.id,
"name": node.textContent || node.innerText || ""
};
}); // Object[]
},
onDndDrop: function(source, nodes, copy){
// summary:
// Topic event processor for /dnd/drop, called to finish the DnD operation.
// description:
// Updates data store items according to where node was dragged from and dropped
// to. The tree will then respond to those data store updates and redraw itself.
// source: Object
// The dijit/tree/dndSource / dojo/dnd/Source which is providing the items
// nodes: DomNode[]
// The list of transferred items, dndTreeNode nodes if dragging from a Tree
// copy: Boolean
// Copy items, if true, move items otherwise
// tags:
// protected
if(this.containerState == "Over"){
var tree = this.tree,
model = tree.model,
target = this.targetAnchor;
this.isDragging = false;
// Compute the new parent item
var newParentItem;
var insertIndex;
var before; // drop source before (aka previous sibling) of target
newParentItem = (target && target.item) || tree.item;
if(this.dropPosition == "Before" || this.dropPosition == "After"){
// TODO: if there is no parent item then disallow the drop.
// Actually this should be checked during onMouseMove too, to make the drag icon red.
newParentItem = (target.getParent() && target.getParent().item) || tree.item;
// Compute the insert index for reordering
insertIndex = target.getIndexInParent();
if(this.dropPosition == "After"){
insertIndex = target.getIndexInParent() + 1;
before = target.getNextSibling() && target.getNextSibling().item;
}else{
before = target.item;
}
}else{
newParentItem = (target && target.item) || tree.item;
}
// If necessary, use this variable to hold array of hashes to pass to model.newItem()
// (one entry in the array for each dragged node).
var newItemsParams;
array.forEach(nodes, function(node, idx){
// dojo/dnd/Item representing the thing being dropped.
// Don't confuse the use of item here (meaning a DnD item) with the
// uses below where item means dojo.data item.
var sourceItem = source.getItem(node.id);
// Information that's available if the source is another Tree
// (possibly but not necessarily this tree, possibly but not
// necessarily the same model as this Tree)
if(array.indexOf(sourceItem.type, "treeNode") != -1){
var childTreeNode = sourceItem.data,
childItem = childTreeNode.item,
oldParentItem = childTreeNode.getParent().item;
}
if(source == this){
// This is a node from my own tree, and we are moving it, not copying.
// Remove item from old parent's children attribute.
// TODO: dijit/tree/dndSelector should implement deleteSelectedNodes()
// and this code should go there.
if(typeof insertIndex == "number"){
if(newParentItem == oldParentItem && childTreeNode.getIndexInParent() < insertIndex){
insertIndex -= 1;
}
}
model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
}else if(model.isItem(childItem)){
// Item from same model
// (maybe we should only do this branch if the source is a tree?)
model.pasteItem(childItem, oldParentItem, newParentItem, copy, insertIndex, before);
}else{
// Get the hash to pass to model.newItem(). A single call to
// itemCreator() returns an array of hashes, one for each drag source node.
if(!newItemsParams){
newItemsParams = this.itemCreator(nodes, target.rowNode, source);
}
// Create new item in the tree, based on the drag source.
model.newItem(newItemsParams[idx], newParentItem, insertIndex, before);
}
}, this);
// Expand the target node (if it's currently collapsed) so the user can see
// where their node was dropped. In particular since that node is still selected.
this.tree._expandNode(target);
}
this.onDndCancel();
},
onDndCancel: function(){
// summary:
// Topic event processor for /dnd/cancel, called to cancel the DnD operation
// tags:
// private
this._unmarkTargetAnchor();
this.isDragging = false;
this.mouseDown = false;
delete this.mouseButton;
this._changeState("Source", "");
this._changeState("Target", "");
},
// When focus moves in/out of the entire Tree
onOverEvent: function(){
// summary:
// This method is called when mouse is moved over our container (like onmouseenter)
// tags:
// private
this.inherited(arguments);
DNDManager.manager().overSource(this);
},
onOutEvent: function(){
// summary:
// This method is called when mouse is moved out of our container (like onmouseleave)
// tags:
// private
this._unmarkTargetAnchor();
var m = DNDManager.manager();
if(this.isDragging){
m.canDrop(false);
}
m.outSource(this);
this.inherited(arguments);
},
_isParentChildDrop: function(source, targetRow){
// summary:
// Checks whether the dragged items are parent rows in the tree which are being
// dragged into their own children.
//
// source:
// The DragSource object.
//
// targetRow:
// The tree row onto which the dragged nodes are being dropped.
//
// tags:
// private
// If the dragged object is not coming from the tree this widget belongs to,
// it cannot be invalid.
if(!source.tree || source.tree != this.tree){
return false;
}
var root = source.tree.domNode;
var ids = source.selection;
var node = targetRow.parentNode;
// Iterate up the DOM hierarchy from the target drop row,
// checking of any of the dragged nodes have the same ID.
while(node != root && !ids[node.id]){
node = node.parentNode;
}
return node.id && ids[node.id];
},
_unmarkTargetAnchor: function(){
// summary:
// Removes hover class of the current target anchor
// tags:
// private
if(!this.targetAnchor){ return; }
this._removeItemClass(this.targetAnchor.rowNode, this.dropPosition);
this.targetAnchor = null;
this.targetBox = null;
this.dropPosition = null;
},
_markDndStatus: function(copy){
// summary:
// Changes source's state based on "copy" status
this._changeState("Source", copy ? "Copied" : "Moved");
}
});
/*=====
dndSource.__Item = __Item;
=====*/
return dndSource;
});
},
'dijit/a11y':function(){
define("dijit/a11y", [
"dojo/_base/array", // array.forEach array.map
"dojo/_base/config", // defaultDuration
"dojo/_base/declare", // declare
"dojo/dom", // dom.byId
"dojo/dom-attr", // domAttr.attr domAttr.has
"dojo/dom-style", // style.style
"dojo/sniff", // has("ie")
"./main" // for exporting methods to dijit namespace
], function(array, config, declare, dom, domAttr, domStyle, has, dijit){
// module:
// dijit/a11y
var shown = (dijit._isElementShown = function(/*Element*/ elem){
var s = domStyle.get(elem);
return (s.visibility != "hidden")
&& (s.visibility != "collapsed")
&& (s.display != "none")
&& (domAttr.get(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 domAttr.has(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.
var body;
try{
// non-IE
var contentDocument = elem.contentDocument;
if("designMode" in contentDocument && contentDocument.designMode == "on"){
return true;
}
body = contentDocument.body;
}catch(e1){
// 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{
body = elem.contentWindow.document.body;
}catch(e2){
return false;
}
}
return body && (body.contentEditable == 'true' ||
(body.firstChild && body.firstChild.contentEditable == 'true'));
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(domAttr.get(elem, "disabled")){
return false;
}else if(domAttr.has(elem, "tabIndex")){
// Explicit tab index setting
return domAttr.get(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, radioSelected = {};
function radioName(node){
// If this element is part of a radio button group, return the name for that group.
return node && node.tagName.toLowerCase() == "input" &&
node.type && node.type.toLowerCase() == "radio" &&
node.name && node.name.toLowerCase();
}
var walkTree = function(/*DOMNode*/ parent){
for(var child = parent.firstChild; child; child = child.nextSibling){
// Skip text elements, 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(child.nodeType != 1 || (has("ie") <= 9 && child.scopeName !== "HTML") || !shown(child)){
continue;
}
if(isTabNavigable(child)){
var tabindex = +domAttr.get(child, "tabIndex"); // + to convert string --> number
if(!domAttr.has(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;
}
}
var rn = radioName(child);
if(domAttr.get(child, "checked") && rn){
radioSelected[rn] = child;
}
}
if(child.nodeName.toUpperCase() != 'SELECT'){
walkTree(child);
}
}
};
if(shown(root)){
walkTree(root);
}
function rs(node){
// substitute checked radio button for unchecked one, if there is a checked one with the same name.
return radioSelected[radioName(node)] || node;
}
return { first: rs(first), last: rs(last), lowest: rs(lowest), highest: rs(highest) };
};
dijit.getFirstInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
// summary:
// Finds the descendant of the specified root node
// that is first in the tabbing order
var elems = dijit._getTabNavigable(dom.byId(root, doc));
return elems.lowest ? elems.lowest : elems.first; // DomNode
};
dijit.getLastInTabbingOrder = function(/*String|DOMNode*/ root, /*Document?*/ doc){
// summary:
// Finds the descendant of the specified root node
// that is last in the tabbing order
var elems = dijit._getTabNavigable(dom.byId(root, doc));
return elems.last ? elems.last : elems.highest; // DomNode
};
return {
// summary:
// Accessibility utility functions (keyboard, tab stops, etc.)
hasDefaultTabStop: dijit.hasDefaultTabStop,
isTabNavigable: dijit.isTabNavigable,
_getTabNavigable: dijit._getTabNavigable,
getFirstInTabbingOrder: dijit.getFirstInTabbingOrder,
getLastInTabbingOrder: dijit.getLastInTabbingOrder
};
});
},
'dijit/form/_ToggleButtonMixin':function(){
define("dijit/form/_ToggleButtonMixin", [
"dojo/_base/declare", // declare
"dojo/dom-attr" // domAttr.set
], function(declare, domAttr){
// module:
// dijit/form/_ToggleButtonMixin
return declare("dijit.form._ToggleButtonMixin", null, {
// summary:
// A mixin to provide functionality to allow a button that can be in two states (checked or not).
// checked: Boolean
// Corresponds to the native HTML `<input>` element's attribute.
// In markup, specified as "checked='checked'" or just "checked".
// True if the button is depressed, or the checkbox is checked,
// or the radio button is selected, etc.
checked: false,
// aria-pressed for toggle buttons, and aria-checked for checkboxes
_aria_attr: "aria-pressed",
_onClick: function(/*Event*/ evt){
var original = this.checked;
this._set('checked', !original); // partially set the toggled value, assuming the toggle will work, so it can be overridden in the onclick handler
var ret = this.inherited(arguments); // the user could reset the value here
this.set('checked', ret ? this.checked : original); // officially set the toggled or user value, or reset it back
return ret;
},
_setCheckedAttr: function(/*Boolean*/ value, /*Boolean?*/ priorityChange){
this._set("checked", value);
var node = this.focusNode || this.domNode;
domAttr.set(node, "checked", !!value); // "mixed" -> true
if(value){
node.setAttribute("checked", "");
}else{
node.removeAttribute("checked");
}
node.setAttribute(this._aria_attr, String(value)); // aria values should be strings
this._handleOnChange(value, priorityChange);
},
reset: function(){
// summary:
// Reset the widget's value to what it was at initialization time
this._hasBeenBlurred = false;
// set checked state to original setting
this.set('checked', this.params.checked || false);
}
});
});
},
'dijit/_Widget':function(){
define("dijit/_Widget", [
"dojo/aspect", // aspect.around
"dojo/_base/config", // config.isDebug
"dojo/_base/connect", // connect.connect
"dojo/_base/declare", // declare
"dojo/has",
"dojo/_base/kernel", // kernel.deprecated
"dojo/_base/lang", // lang.hitch
"dojo/query",
"dojo/ready",
"./registry", // registry.byNode
"./_WidgetBase",
"./_OnDijitClickMixin",
"./_FocusMixin",
"dojo/uacss", // browser sniffing (included for back-compat; subclasses may be using)
"./hccss" // high contrast mode sniffing (included to set CSS classes on <body>, module ret value unused)
], function(aspect, config, connect, declare, has, kernel, lang, query, ready,
registry, _WidgetBase, _OnDijitClickMixin, _FocusMixin){
// module:
// dijit/_Widget
function connectToDomNode(){
// summary:
// If user connects to a widget method === this function, then they will
// instead actually be connecting the equivalent event on this.domNode
}
// Trap dojo.connect() calls to connectToDomNode methods, and redirect to _Widget.on()
function aroundAdvice(originalConnect){
return function(obj, event, scope, method){
if(obj && typeof event == "string" && obj[event] == connectToDomNode){
return obj.on(event.substring(2).toLowerCase(), lang.hitch(scope, method));
}
return originalConnect.apply(connect, arguments);
};
}
aspect.around(connect, "connect", aroundAdvice);
if(kernel.connect){
aspect.around(kernel, "connect", aroundAdvice);
}
var _Widget = declare("dijit._Widget", [_WidgetBase, _OnDijitClickMixin, _FocusMixin], {
// summary:
// Old base class for widgets. New widgets should extend `dijit/_WidgetBase` instead
// description:
// Old Base class for Dijit widgets.
//
// Extends _WidgetBase, adding support for:
//
// - declaratively/programatically specifying widget initialization parameters like
// onMouseMove="foo" that call foo when this.domNode gets a mousemove event
// - ondijitclick:
// Support new data-dojo-attach-event="ondijitclick: ..." that is triggered by a mouse click or a SPACE/ENTER keypress
// - focus related functions:
// In particular, the onFocus()/onBlur() callbacks. Driven internally by
// dijit/_base/focus.js.
// - deprecated methods
// - onShow(), onHide(), onClose()
//
// Also, by loading code in dijit/_base, turns on:
//
// - browser sniffing (putting browser class like `dj_ie` on `<html>` node)
// - high contrast mode sniffing (add `dijit_a11y` class to `<body>` if machine is in high contrast mode)
////////////////// DEFERRED CONNECTS ///////////////////
onClick: connectToDomNode,
/*=====
onClick: function(event){
// summary:
// Connect to this function to receive notifications of mouse click events.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onDblClick: connectToDomNode,
/*=====
onDblClick: function(event){
// summary:
// Connect to this function to receive notifications of mouse double click events.
// event:
// mouse Event
// tags:
// callback
},
=====*/
onKeyDown: connectToDomNode,
/*=====
onKeyDown: function(event){
// summary:
// Connect to this function to receive notifications of keys being pressed down.
// event:
// key Event
// tags:
// callback
},
=====*/
onKeyPress: connectToDomNode,
/*=====
onKeyPress: function(event){
// summary:
// Connect to this function to receive notifications of printable keys being typed.
// event:
// key Event
// tags:
// callback
},
=====*/
onKeyUp: connectToDomNode,
/*=====
onKeyUp: function(event){
// summary:
// Connect to this function to receive notifications of keys being released.
// event:
// key Event
// tags:
// callback
},
=====*/
onMouseDown: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
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: connectToDomNode,
/*=====
onMouseUp: function(event){
// summary:
// Connect to this function to receive notifications of when the mouse button is released.
// event:
// mouse Event
// tags:
// callback
},
=====*/
constructor: function(params /*===== ,srcNodeRef =====*/){
// summary:
// Create the widget.
// params: Object|null
// Hash of initialization parameters for widget, including scalar values (like title, duration etc.)
// and functions, typically callbacks like onClick.
// The hash can contain any of the widget's properties, excluding read-only properties.
// srcNodeRef: DOMNode|String?
// 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
// extract parameters like onMouseMove that should connect directly to this.domNode
this._toConnect = {};
for(var name in params){
if(this[name] === connectToDomNode){
this._toConnect[name.replace(/^on/, "").toLowerCase()] = params[name];
delete params[name];
}
}
},
postCreate: function(){
this.inherited(arguments);
// perform connection from this.domNode to user specified handlers (ex: onMouseMove)
for(var name in this._toConnect){
this.on(name, this._toConnect[name]);
}
delete this._toConnect;
},
on: function(/*String|Function*/ type, /*Function*/ func){
if(this[this._onMap(type)] === connectToDomNode){
// Use connect.connect() rather than on() to get handling for "onmouseenter" on non-IE,
// normalization of onkeypress/onkeydown to behave like firefox, etc.
// Also, need to specify context as "this" rather than the default context of the DOMNode
// Remove in 2.0.
return connect.connect(this.domNode, type.toLowerCase(), this, func);
}
return this.inherited(arguments);
},
_setFocusedAttr: function(val){
// Remove this method in 2.0 (or sooner), just here to set _focused == focused, for back compat
// (but since it's a private variable we aren't required to keep supporting it).
this._focused = val;
this._set("focused", val);
},
////////////////// DEPRECATED METHODS ///////////////////
setAttribute: function(/*String*/ attr, /*anything*/ value){
// summary:
// Deprecated. Use set() instead.
// tags:
// deprecated
kernel.deprecated(this.declaredClass+"::setAttribute(attr, value) is deprecated. Use set() instead.", "", "2.0");
this.set(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(config.isDebug){
var alreadyCalledHash = arguments.callee._ach || (arguments.callee._ach = {}),
caller = (arguments.callee.caller || "unknown caller").toString();
if(!alreadyCalledHash[caller]){
kernel.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);
}
},
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.
kernel.deprecated(this.declaredClass+"::getDescendants() is deprecated. Use getChildren() instead.", "", "2.0");
return this.containerNode ? query('[widgetId]', this.containerNode).map(registry.byNode) : []; // dijit/_WidgetBase[]
},
////////////////// MISCELLANEOUS METHODS ///////////////////
_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
}
});
// For back-compat, remove in 2.0.
if(has("dijit-legacy-requires")){
ready(0, function(){
var requires = ["dijit/_base"];
require(requires); // use indirection so modules not rolled into a build
});
}
return _Widget;
});
},
'dojo/touch':function(){
define("dojo/touch", ["./_base/kernel", "./aspect", "./dom", "./on", "./has", "./mouse", "./ready", "./_base/window"],
function(dojo, aspect, dom, on, has, mouse, ready, win){
// module:
// dojo/touch
var hasTouch = has("touch");
// TODO: get iOS version from dojo/sniff after #15827 is fixed
var ios4 = false;
if(has("ios")){
var ua = navigator.userAgent;
var v = ua.match(/OS ([\d_]+)/) ? RegExp.$1 : "1";
var os = parseFloat(v.replace(/_/, '.').replace(/_/g, ''));
ios4 = os < 5;
}
var touchmove, hoveredNode;
if(hasTouch){
ready(function(){
// Keep track of currently hovered node
hoveredNode = win.body(); // currently hovered node
win.doc.addEventListener("touchstart", function(evt){
// Precede touchstart event with touch.over event. DnD depends on this.
// Use addEventListener(cb, true) to run cb before any touchstart handlers on node run,
// and to ensure this code runs even if the listener on the node does event.stop().
var oldNode = hoveredNode;
hoveredNode = evt.target;
on.emit(oldNode, "dojotouchout", {
target: oldNode,
relatedTarget: hoveredNode,
bubbles: true
});
on.emit(hoveredNode, "dojotouchover", {
target: hoveredNode,
relatedTarget: oldNode,
bubbles: true
});
}, true);
// Fire synthetic touchover and touchout events on nodes since the browser won't do it natively.
on(win.doc, "touchmove", function(evt){
var newNode = win.doc.elementFromPoint(
evt.pageX - (ios4 ? 0 : win.global.pageXOffset), // iOS 4 expects page coords
evt.pageY - (ios4 ? 0 : win.global.pageYOffset)
);
if(newNode && hoveredNode !== newNode){
// touch out on the old node
on.emit(hoveredNode, "dojotouchout", {
target: hoveredNode,
relatedTarget: newNode,
bubbles: true
});
// touchover on the new node
on.emit(newNode, "dojotouchover", {
target: newNode,
relatedTarget: hoveredNode,
bubbles: true
});
hoveredNode = newNode;
}
});
});
// Define synthetic touch.move event that unlike the native touchmove, fires for the node the finger is
// currently dragging over rather than the node where the touch started.
touchmove = function(node, listener){
return on(win.doc, "touchmove", function(evt){
if(node === win.doc || dom.isDescendant(hoveredNode, node)){
evt.target = hoveredNode;
listener.call(this, evt);
}
});
};
}
function _handle(type){
// type: String
// press | move | release | cancel
return function(node, listener){//called by on(), see dojo.on
return on(node, type, listener);
};
}
//device neutral events - touch.press|move|release|cancel/over/out
var touch = {
press: _handle(hasTouch ? "touchstart": "mousedown"),
move: hasTouch ? touchmove :_handle("mousemove"),
release: _handle(hasTouch ? "touchend": "mouseup"),
cancel: hasTouch ? _handle("touchcancel") : mouse.leave,
over: _handle(hasTouch ? "dojotouchover": "mouseover"),
out: _handle(hasTouch ? "dojotouchout": "mouseout"),
enter: mouse._eventHandler(hasTouch ? "dojotouchover" : "mouseover"),
leave: mouse._eventHandler(hasTouch ? "dojotouchout" : "mouseout")
};
/*=====
touch = {
// summary:
// This module provides unified touch event handlers by exporting
// press, move, release and cancel which can also run well on desktop.
// Based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
//
// example:
// Used with dojo.on
// | define(["dojo/on", "dojo/touch"], function(on, touch){
// | on(node, touch.press, function(e){});
// | on(node, touch.move, function(e){});
// | on(node, touch.release, function(e){});
// | on(node, touch.cancel, function(e){});
// example:
// Used with touch.* directly
// | touch.press(node, function(e){});
// | touch.move(node, function(e){});
// | touch.release(node, function(e){});
// | touch.cancel(node, function(e){});
press: function(node, listener){
// summary:
// Register a listener to 'touchstart'|'mousedown' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
move: function(node, listener){
// summary:
// Register a listener to 'touchmove'|'mousemove' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
release: function(node, listener){
// summary:
// Register a listener to 'touchend'|'mouseup' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
cancel: function(node, listener){
// summary:
// Register a listener to 'touchcancel'|'mouseleave' for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
over: function(node, listener){
// summary:
// Register a listener to 'mouseover' or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
out: function(node, listener){
// summary:
// Register a listener to 'mouseout' or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
enter: function(node, listener){
// summary:
// Register a listener to mouse.enter or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
},
leave: function(node, listener){
// summary:
// Register a listener to mouse.leave or touch equivalent for the given node
// node: Dom
// Target node to listen to
// listener: Function
// Callback function
// returns:
// A handle which will be used to remove the listener by handle.remove()
}
};
=====*/
1 && (dojo.touch = touch);
return touch;
});
},
'url:dijit/form/templates/Select.html':"<table class=\"dijit dijitReset dijitInline dijitLeft\"\n\tdata-dojo-attach-point=\"_buttonNode,tableNode,focusNode\" cellspacing='0' cellpadding='0'\n\trole=\"listbox\" aria-haspopup=\"true\"\n\t><tbody role=\"presentation\"><tr role=\"presentation\"\n\t\t><td class=\"dijitReset dijitStretch dijitButtonContents\" role=\"presentation\"\n\t\t\t><div class=\"dijitReset dijitInputField dijitButtonText\" data-dojo-attach-point=\"containerNode,_popupStateNode\" role=\"presentation\"></div\n\t\t\t><div class=\"dijitReset dijitValidationContainer\"\n\t\t\t\t><input class=\"dijitReset dijitInputField dijitValidationIcon dijitValidationInner\" value=\"&#935; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t/></div\n\t\t\t><input type=\"hidden\" ${!nameAttrSetting} data-dojo-attach-point=\"valueNode\" value=\"${value}\" aria-hidden=\"true\"\n\t\t/></td\n\t\t><td class=\"dijitReset dijitRight dijitButtonNode dijitArrowButton dijitDownArrowButton dijitArrowButtonContainer\"\n\t\t\tdata-dojo-attach-point=\"titleNode\" role=\"presentation\"\n\t\t\t><input class=\"dijitReset dijitInputField dijitArrowButtonInner\" value=\"&#9660; \" type=\"text\" tabIndex=\"-1\" readonly=\"readonly\" role=\"presentation\"\n\t\t\t\t${_buttonInputDisabled}\n\t\t/></td\n\t></tr></tbody\n></table>\n",
'dojo/fx':function(){
define("dojo/fx", [
"./_base/lang",
"./Evented",
"./_base/kernel",
"./_base/array",
"./_base/connect",
"./_base/fx",
"./dom",
"./dom-style",
"./dom-geometry",
"./ready",
"require" // for context sensitive loading of Toggler
], function(lang, Evented, dojo, arrayUtil, connect, baseFx, dom, domStyle, geom, ready, require){
// module:
// dojo/fx
// For back-compat, remove in 2.0.
if(!dojo.isAsync){
ready(0, function(){
var requires = ["./fx/Toggler"];
require(requires); // use indirection so modules not rolled into a build
});
}
var coreFx = dojo.fx = {
// summary:
// Effects library on top of Base animations
};
var _baseObj = {
_fire: function(evt, args){
if(this[evt]){
this[evt].apply(this, args||[]);
}
return this;
}
};
var _chain = function(animations){
this._index = -1;
this._animations = animations||[];
this._current = this._onAnimateCtx = this._onEndCtx = null;
this.duration = 0;
arrayUtil.forEach(this._animations, function(a){
this.duration += a.duration;
if(a.delay){ this.duration += a.delay; }
}, this);
};
_chain.prototype = new Evented();
lang.extend(_chain, {
_onAnimate: function(){
this._fire("onAnimate", arguments);
},
_onEnd: function(){
connect.disconnect(this._onAnimateCtx);
connect.disconnect(this._onEndCtx);
this._onAnimateCtx = this._onEndCtx = null;
if(this._index + 1 == this._animations.length){
this._fire("onEnd");
}else{
// switch animations
this._current = this._animations[++this._index];
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
this._current.play(0, true);
}
},
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
if(!this._current){ this._current = this._animations[this._index = 0]; }
if(!gotoStart && this._current.status() == "playing"){ return this; }
var beforeBegin = connect.connect(this._current, "beforeBegin", this, function(){
this._fire("beforeBegin");
}),
onBegin = connect.connect(this._current, "onBegin", this, function(arg){
this._fire("onBegin", arguments);
}),
onPlay = connect.connect(this._current, "onPlay", this, function(arg){
this._fire("onPlay", arguments);
connect.disconnect(beforeBegin);
connect.disconnect(onBegin);
connect.disconnect(onPlay);
});
if(this._onAnimateCtx){
connect.disconnect(this._onAnimateCtx);
}
this._onAnimateCtx = connect.connect(this._current, "onAnimate", this, "_onAnimate");
if(this._onEndCtx){
connect.disconnect(this._onEndCtx);
}
this._onEndCtx = connect.connect(this._current, "onEnd", this, "_onEnd");
this._current.play.apply(this._current, arguments);
return this;
},
pause: function(){
if(this._current){
var e = connect.connect(this._current, "onPause", this, function(arg){
this._fire("onPause", arguments);
connect.disconnect(e);
});
this._current.pause();
}
return this;
},
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
this.pause();
var offset = this.duration * percent;
this._current = null;
arrayUtil.some(this._animations, function(a){
if(a.duration <= offset){
this._current = a;
return true;
}
offset -= a.duration;
return false;
});
if(this._current){
this._current.gotoPercent(offset / this._current.duration, andPlay);
}
return this;
},
stop: function(/*boolean?*/ gotoEnd){
if(this._current){
if(gotoEnd){
for(; this._index + 1 < this._animations.length; ++this._index){
this._animations[this._index].stop(true);
}
this._current = this._animations[this._index];
}
var e = connect.connect(this._current, "onStop", this, function(arg){
this._fire("onStop", arguments);
connect.disconnect(e);
});
this._current.stop();
}
return this;
},
status: function(){
return this._current ? this._current.status() : "stopped";
},
destroy: function(){
if(this._onAnimateCtx){ connect.disconnect(this._onAnimateCtx); }
if(this._onEndCtx){ connect.disconnect(this._onEndCtx); }
}
});
lang.extend(_chain, _baseObj);
coreFx.chain = function(/*dojo/_base/fx.Animation[]*/ animations){
// summary:
// Chain a list of `dojo.Animation`s to run in sequence
//
// description:
// Return a `dojo.Animation` which will play all passed
// `dojo.Animation` instances in sequence, firing its own
// synthesized events simulating a single animation. (eg:
// onEnd of this animation means the end of the chain,
// not the individual animations within)
//
// example:
// Once `node` is faded out, fade in `otherNode`
// | fx.chain([
// | dojo.fadeIn({ node:node }),
// | dojo.fadeOut({ node:otherNode })
// | ]).play();
//
return new _chain(animations); // dojo/_base/fx.Animation
};
var _combine = function(animations){
this._animations = animations||[];
this._connects = [];
this._finished = 0;
this.duration = 0;
arrayUtil.forEach(animations, function(a){
var duration = a.duration;
if(a.delay){ duration += a.delay; }
if(this.duration < duration){ this.duration = duration; }
this._connects.push(connect.connect(a, "onEnd", this, "_onEnd"));
}, this);
this._pseudoAnimation = new baseFx.Animation({curve: [0, 1], duration: this.duration});
var self = this;
arrayUtil.forEach(["beforeBegin", "onBegin", "onPlay", "onAnimate", "onPause", "onStop", "onEnd"],
function(evt){
self._connects.push(connect.connect(self._pseudoAnimation, evt,
function(){ self._fire(evt, arguments); }
));
}
);
};
lang.extend(_combine, {
_doAction: function(action, args){
arrayUtil.forEach(this._animations, function(a){
a[action].apply(a, args);
});
return this;
},
_onEnd: function(){
if(++this._finished > this._animations.length){
this._fire("onEnd");
}
},
_call: function(action, args){
var t = this._pseudoAnimation;
t[action].apply(t, args);
},
play: function(/*int?*/ delay, /*Boolean?*/ gotoStart){
this._finished = 0;
this._doAction("play", arguments);
this._call("play", arguments);
return this;
},
pause: function(){
this._doAction("pause", arguments);
this._call("pause", arguments);
return this;
},
gotoPercent: function(/*Decimal*/percent, /*Boolean?*/ andPlay){
var ms = this.duration * percent;
arrayUtil.forEach(this._animations, function(a){
a.gotoPercent(a.duration < ms ? 1 : (ms / a.duration), andPlay);
});
this._call("gotoPercent", arguments);
return this;
},
stop: function(/*boolean?*/ gotoEnd){
this._doAction("stop", arguments);
this._call("stop", arguments);
return this;
},
status: function(){
return this._pseudoAnimation.status();
},
destroy: function(){
arrayUtil.forEach(this._connects, connect.disconnect);
}
});
lang.extend(_combine, _baseObj);
coreFx.combine = function(/*dojo/_base/fx.Animation[]*/ animations){
// summary:
// Combine a list of `dojo.Animation`s to run in parallel
//
// description:
// Combine an array of `dojo.Animation`s to run in parallel,
// providing a new `dojo.Animation` instance encompasing each
// animation, firing standard animation events.
//
// example:
// Fade out `node` while fading in `otherNode` simultaneously
// | fx.combine([
// | dojo.fadeIn({ node:node }),
// | dojo.fadeOut({ node:otherNode })
// | ]).play();
//
// example:
// When the longest animation ends, execute a function:
// | var anim = fx.combine([
// | dojo.fadeIn({ node: n, duration:700 }),
// | dojo.fadeOut({ node: otherNode, duration: 300 })
// | ]);
// | dojo.connect(anim, "onEnd", function(){
// | // overall animation is done.
// | });
// | anim.play(); // play the animation
//
return new _combine(animations); // dojo/_base/fx.Animation
};
coreFx.wipeIn = function(/*Object*/ args){
// summary:
// Expand a node to it's natural height.
//
// description:
// Returns an animation that will expand the
// node defined in 'args' object from it's current height to
// it's natural height (with no scrollbar).
// Node must have no margin/border/padding.
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on)
//
// example:
// | fx.wipeIn({
// | node:"someId"
// | }).play()
var node = args.node = dom.byId(args.node), s = node.style, o;
var anim = baseFx.animateProperty(lang.mixin({
properties: {
height: {
// wrapped in functions so we wait till the last second to query (in case value has changed)
start: function(){
// start at current [computed] height, but use 1px rather than 0
// because 0 causes IE to display the whole panel
o = s.overflow;
s.overflow = "hidden";
if(s.visibility == "hidden" || s.display == "none"){
s.height = "1px";
s.display = "";
s.visibility = "";
return 1;
}else{
var height = domStyle.get(node, "height");
return Math.max(height, 1);
}
},
end: function(){
return node.scrollHeight;
}
}
}
}, args));
var fini = function(){
s.height = "auto";
s.overflow = o;
};
connect.connect(anim, "onStop", fini);
connect.connect(anim, "onEnd", fini);
return anim; // dojo/_base/fx.Animation
};
coreFx.wipeOut = function(/*Object*/ args){
// summary:
// Shrink a node to nothing and hide it.
//
// description:
// Returns an animation that will shrink node defined in "args"
// from it's current height to 1px, and then hide it.
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on)
//
// example:
// | fx.wipeOut({ node:"someId" }).play()
var node = args.node = dom.byId(args.node), s = node.style, o;
var anim = baseFx.animateProperty(lang.mixin({
properties: {
height: {
end: 1 // 0 causes IE to display the whole panel
}
}
}, args));
connect.connect(anim, "beforeBegin", function(){
o = s.overflow;
s.overflow = "hidden";
s.display = "";
});
var fini = function(){
s.overflow = o;
s.height = "auto";
s.display = "none";
};
connect.connect(anim, "onStop", fini);
connect.connect(anim, "onEnd", fini);
return anim; // dojo/_base/fx.Animation
};
coreFx.slideTo = function(/*Object*/ args){
// summary:
// Slide a node to a new top/left position
//
// description:
// Returns an animation that will slide "node"
// defined in args Object from its current position to
// the position defined by (args.left, args.top).
//
// args: Object
// A hash-map of standard `dojo.Animation` constructor properties
// (such as easing: node: duration: and so on). Special args members
// are `top` and `left`, which indicate the new position to slide to.
//
// example:
// | .slideTo({ node: node, left:"40", top:"50", units:"px" }).play()
var node = args.node = dom.byId(args.node),
top = null, left = null;
var init = (function(n){
return function(){
var cs = domStyle.getComputedStyle(n);
var pos = cs.position;
top = (pos == 'absolute' ? n.offsetTop : parseInt(cs.top) || 0);
left = (pos == 'absolute' ? n.offsetLeft : parseInt(cs.left) || 0);
if(pos != 'absolute' && pos != 'relative'){
var ret = geom.position(n, true);
top = ret.y;
left = ret.x;
n.style.position="absolute";
n.style.top=top+"px";
n.style.left=left+"px";
}
};
})(node);
init();
var anim = baseFx.animateProperty(lang.mixin({
properties: {
top: args.top || 0,
left: args.left || 0
}
}, args));
connect.connect(anim, "beforeBegin", anim, init);
return anim; // dojo/_base/fx.Animation
};
return coreFx;
});
},
'dijit/_DialogMixin':function(){
define("dijit/_DialogMixin", [
"dojo/_base/declare", // declare
"./a11y" // _getTabNavigable
], function(declare, a11y){
// module:
// dijit/_DialogMixin
return declare("dijit._DialogMixin", null, {
// summary:
// This provides functions useful to Dialog and TooltipDialog
execute: function(/*Object*/ /*===== formContents =====*/){
// summary:
// Callback when the user hits the submit button.
// Override this method to handle Dialog execution.
// description:
// After the user has pressed the submit button, the Dialog
// first calls onExecute() to notify the container to hide the
// dialog and restore focus to wherever it used to be.
//
// *Then* this method is called.
// type:
// callback
},
onCancel: function(){
// summary:
// Called when user has pressed the Dialog's cancel button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
onExecute: function(){
// summary:
// Called when user has pressed the dialog's OK button, to notify container.
// description:
// Developer shouldn't override or connect to this method;
// it's a private communication device between the TooltipDialog
// and the thing that opened it (ex: `dijit/form/DropDownButton`)
// type:
// protected
},
_onSubmit: function(){
// summary:
// Callback when user hits submit button
// type:
// protected
this.onExecute(); // notify container that we are about to execute
this.execute(this.get('value'));
},
_getFocusItems: function(){
// summary:
// Finds focusable items in dialog,
// and sets this._firstFocusItem and this._lastFocusItem
// tags:
// protected
var elems = a11y._getTabNavigable(this.containerNode);
this._firstFocusItem = elems.lowest || elems.first || this.closeButtonNode || this.domNode;
this._lastFocusItem = elems.last || elems.highest || this._firstFocusItem;
}
});
});
},
'dijit/Tree':function(){
require({cache:{
'url:dijit/templates/TreeNode.html':"<div class=\"dijitTreeNode\" role=\"presentation\"\n\t><div data-dojo-attach-point=\"rowNode\" class=\"dijitTreeRow dijitInline\" role=\"presentation\"\n\t\t><div data-dojo-attach-point=\"indentNode\" class=\"dijitInline\"></div\n\t\t><img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"expandoNode\" class=\"dijitTreeExpando\" role=\"presentation\"\n\t\t/><span data-dojo-attach-point=\"expandoNodeText\" class=\"dijitExpandoText\" role=\"presentation\"\n\t\t></span\n\t\t><span data-dojo-attach-point=\"contentNode\"\n\t\t\tclass=\"dijitTreeContent\" role=\"presentation\">\n\t\t\t<img src=\"${_blankGif}\" alt=\"\" data-dojo-attach-point=\"iconNode\" class=\"dijitIcon dijitTreeIcon\" role=\"presentation\"\n\t\t\t/><span data-dojo-attach-point=\"labelNode\" class=\"dijitTreeLabel\" role=\"treeitem\" tabindex=\"-1\" aria-selected=\"false\"></span>\n\t\t</span\n\t></div>\n\t<div data-dojo-attach-point=\"containerNode\" class=\"dijitTreeContainer\" role=\"presentation\" style=\"display: none;\"></div>\n</div>\n",
'url:dijit/templates/Tree.html':"<div class=\"dijitTree dijitTreeContainer\" role=\"tree\">\n\t<div class=\"dijitInline dijitTreeIndent\" style=\"position: absolute; top: -9999px\" data-dojo-attach-point=\"indentDetector\"></div>\n</div>\n"}});
define("dijit/Tree", [
"dojo/_base/array", // array.filter array.forEach array.map
"dojo/_base/connect", // connect.isCopyKey()
"dojo/cookie", // cookie
"dojo/_base/declare", // declare
"dojo/Deferred", // Deferred
"dojo/DeferredList", // DeferredList
"dojo/dom", // dom.isDescendant
"dojo/dom-class", // domClass.add domClass.remove domClass.replace domClass.toggle
"dojo/dom-geometry", // domGeometry.setMarginBox domGeometry.position
"dojo/dom-style",// domStyle.set
"dojo/_base/event", // event.stop
"dojo/errors/create", // createError
"dojo/fx", // fxUtils.wipeIn fxUtils.wipeOut
"dojo/_base/kernel", // kernel.deprecated
"dojo/keys", // arrows etc.
"dojo/_base/lang", // lang.getObject lang.mixin lang.hitch
"dojo/on", // on(), on.selector()
"dojo/topic",
"dojo/touch",
"dojo/when",
"./focus",
"./registry", // registry.byNode(), registry.getEnclosingWidget()
"./_base/manager", // manager.defaultDuration
"./_Widget",
"./_TemplatedMixin",
"./_Container",
"./_Contained",
"./_CssStateMixin",
"dojo/text!./templates/TreeNode.html",
"dojo/text!./templates/Tree.html",
"./tree/TreeStoreModel",
"./tree/ForestStoreModel",
"./tree/_dndSelector"
], function(array, connect, cookie, declare, Deferred, DeferredList,
dom, domClass, domGeometry, domStyle, event, createError, fxUtils, kernel, keys, lang, on, topic, touch, when,
focus, registry, manager, _Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin,
treeNodeTemplate, treeTemplate, TreeStoreModel, ForestStoreModel, _dndSelector){
// module:
// dijit/Tree
// Back-compat shim
Deferred = declare(Deferred, {
addCallback: function(callback){ this.then(callback); },
addErrback: function(errback){ this.then(null, errback); }
});
var TreeNode = declare(
"dijit._TreeNode",
[_Widget, _TemplatedMixin, _Container, _Contained, _CssStateMixin],
{
// summary:
// Single node within a tree. This class is used internally
// by Tree and should not be accessed directly.
// tags:
// private
// item: [const] Item
// the dojo.data entry this tree represents
item: null,
// isTreeNode: [protected] Boolean
// Indicates that this is a TreeNode. Used by `dijit.Tree` only,
// should not be accessed directly.
isTreeNode: true,
// label: String
// Text of this tree node
label: "",
_setLabelAttr: {node: "labelNode", type: "innerText"},
// isExpandable: [private] Boolean
// This node has children, so show the expando node (+ sign)
isExpandable: null,
// isExpanded: [readonly] Boolean
// This node is currently expanded (ie, opened)
isExpanded: false,
// state: [private] String
// Dynamic loading-related stuff.
// When an empty folder node appears, it is "UNCHECKED" first,
// then after dojo.data query it becomes "LOADING" and, finally "LOADED"
state: "UNCHECKED",
templateString: treeNodeTemplate,
baseClass: "dijitTreeNode",
// For hover effect for tree node, and focus effect for label
cssStateNodes: {
rowNode: "dijitTreeRow"
},
// Tooltip is defined in _WidgetBase but we need to handle the mapping to DOM here
_setTooltipAttr: {node: "rowNode", type: "attribute", attribute: "title"},
buildRendering: function(){
this.inherited(arguments);
// set expand icon for leaf
this._setExpando();
// set icon and label class based on item
this._updateItemClasses(this.item);
if(this.isExpandable){
this.labelNode.setAttribute("aria-expanded", this.isExpanded);
}
//aria-selected should be false on all selectable elements.
this.setSelected(false);
},
_setIndentAttr: function(indent){
// summary:
// Tell this node how many levels it should be indented
// description:
// 0 for top level nodes, 1 for their children, 2 for their
// grandchildren, etc.
// Math.max() is to prevent negative padding on hidden root node (when indent == -1)
var pixels = (Math.max(indent, 0) * this.tree._nodePixelIndent) + "px";
domStyle.set(this.domNode, "backgroundPosition", pixels + " 0px"); // TODOC: what is this for???
domStyle.set(this.indentNode, this.isLeftToRight() ? "paddingLeft" : "paddingRight", pixels);
array.forEach(this.getChildren(), function(child){
child.set("indent", indent+1);
});
this._set("indent", indent);
},
markProcessing: function(){
// summary:
// Visually denote that tree is loading data, etc.
// tags:
// private
this.state = "LOADING";
this._setExpando(true);
},
unmarkProcessing: function(){
// summary:
// Clear markup from markProcessing() call
// tags:
// private
this._setExpando(false);
},
_updateItemClasses: function(item){
// summary:
// Set appropriate CSS classes for icon and label dom node
// (used to allow for item updates to change respective CSS)
// tags:
// private
var tree = this.tree, model = tree.model;
if(tree._v10Compat && item === model.root){
// For back-compat with 1.0, need to use null to specify root item (TODO: remove in 2.0)
item = null;
}
this._applyClassAndStyle(item, "icon", "Icon");
this._applyClassAndStyle(item, "label", "Label");
this._applyClassAndStyle(item, "row", "Row");
this.tree._startPaint(true); // signifies paint started and finished (synchronously)
},
_applyClassAndStyle: function(item, lower, upper){
// summary:
// Set the appropriate CSS classes and styles for labels, icons and rows.
//
// item:
// The data item.
//
// lower:
// The lower case attribute to use, e.g. 'icon', 'label' or 'row'.
//
// upper:
// The upper case attribute to use, e.g. 'Icon', 'Label' or 'Row'.
//
// tags:
// private
var clsName = "_" + lower + "Class";
var nodeName = lower + "Node";
var oldCls = this[clsName];
this[clsName] = this.tree["get" + upper + "Class"](item, this.isExpanded);
domClass.replace(this[nodeName], this[clsName] || "", oldCls || "");
domStyle.set(this[nodeName], this.tree["get" + upper + "Style"](item, this.isExpanded) || {});
},
_updateLayout: function(){
// summary:
// Set appropriate CSS classes for this.domNode
// tags:
// private
var parent = this.getParent();
if(!parent || !parent.rowNode || parent.rowNode.style.display == "none"){
/* if we are hiding the root node then make every first level child look like a root node */
domClass.add(this.domNode, "dijitTreeIsRoot");
}else{
domClass.toggle(this.domNode, "dijitTreeIsLast", !this.getNextSibling());
}
},
_setExpando: function(/*Boolean*/ processing){
// summary:
// Set the right image for the expando node
// tags:
// private
var styles = ["dijitTreeExpandoLoading", "dijitTreeExpandoOpened",
"dijitTreeExpandoClosed", "dijitTreeExpandoLeaf"],
_a11yStates = ["*","-","+","*"],
idx = processing ? 0 : (this.isExpandable ? (this.isExpanded ? 1 : 2) : 3);
// apply the appropriate class to the expando node
domClass.replace(this.expandoNode, styles[idx], styles);
// provide a non-image based indicator for images-off mode
this.expandoNodeText.innerHTML = _a11yStates[idx];
},
expand: function(){
// summary:
// Show my children
// returns:
// Deferred that fires when expansion is complete
// If there's already an expand in progress or we are already expanded, just return
if(this._expandDeferred){
return this._expandDeferred; // dojo/_base/Deferred
}
// cancel in progress collapse operation
if(this._collapseDeferred){
this._collapseDeferred.cancel();
delete this._collapseDeferred;
}
// All the state information for when a node is expanded, maybe this should be
// set when the animation completes instead
this.isExpanded = true;
this.labelNode.setAttribute("aria-expanded", "true");
if(this.tree.showRoot || this !== this.tree.rootNode){
this.containerNode.setAttribute("role", "group");
}
domClass.add(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
if(this == this.tree.rootNode && this.tree.showRoot){
this.tree.domNode.setAttribute("aria-expanded", "true");
}
var def,
wipeIn = fxUtils.wipeIn({
node: this.containerNode,
duration: manager.defaultDuration,
onEnd: function(){
def.resolve(true);
}
});
// Deferred that fires when expand is complete
def = (this._expandDeferred = new Deferred(function(){
// Canceller
wipeIn.stop();
}));
wipeIn.play();
return def; // dojo/_base/Deferred
},
collapse: function(){
// summary:
// Collapse this node (if it's expanded)
if(this._collapseDeferred){
// Node is already collapsed, or there's a collapse in progress, just return that Deferred
return this._collapseDeferred;
}
// cancel in progress expand operation
if(this._expandDeferred){
this._expandDeferred.cancel();
delete this._expandDeferred;
}
this.isExpanded = false;
this.labelNode.setAttribute("aria-expanded", "false");
if(this == this.tree.rootNode && this.tree.showRoot){
this.tree.domNode.setAttribute("aria-expanded", "false");
}
domClass.remove(this.contentNode,'dijitTreeContentExpanded');
this._setExpando();
this._updateItemClasses(this.item);
var def,
wipeOut = fxUtils.wipeOut({
node: this.containerNode,
duration: manager.defaultDuration,
onEnd: function(){
def.resolve(true);
}
});
// Deferred that fires when expand is complete
def = (this._collapseDeferred = new Deferred(function(){
// Canceller
wipeOut.stop();
}));
wipeOut.play();
return def; // dojo/_base/Deferred
},
// indent: Integer
// Levels from this node to the root node
indent: 0,
setChildItems: function(/* Object[] */ items){
// summary:
// Sets the child items of this node, removing/adding nodes
// from current children to match specified items[] array.
// Also, if this.persist == true, expands any children that were previously
// opened.
// returns:
// Deferred object that fires after all previously opened children
// have been expanded again (or fires instantly if there are no such children).
var tree = this.tree,
model = tree.model,
defs = []; // list of deferreds that need to fire before I am complete
// Orphan all my existing children.
// If items contains some of the same items as before then we will reattach them.
// Don't call this.removeChild() because that will collapse the tree etc.
var oldChildren = this.getChildren();
array.forEach(oldChildren, function(child){
_Container.prototype.removeChild.call(this, child);
}, this);
// All the old children of this TreeNode are subject for destruction if
// 1) they aren't listed in the new children array (items)
// 2) they aren't immediately adopted by another node (DnD)
this.defer(function(){
array.forEach(oldChildren, function(node){
if(!node._destroyed && !node.getParent()){
// If node is in selection then remove it.
tree.dndController.removeTreeNode(node);
// Deregister mapping from item id --> this node
var id = model.getIdentity(node.item),
ary = tree._itemNodesMap[id];
if(ary.length == 1){
delete tree._itemNodesMap[id];
}else{
var index = array.indexOf(ary, node);
if(index != -1){
ary.splice(index, 1);
}
}
// And finally we can destroy the node
node.destroyRecursive();
}
});
});
this.state = "LOADED";
if(items && items.length > 0){
this.isExpandable = true;
// Create _TreeNode widget for each specified tree node, unless one already
// exists and isn't being used (presumably it's from a DnD move and was recently
// released
array.forEach(items, function(item){ // MARKER: REUSE NODE
var id = model.getIdentity(item),
existingNodes = tree._itemNodesMap[id],
node;
if(existingNodes){
for(var i=0;i<existingNodes.length;i++){
if(existingNodes[i] && !existingNodes[i].getParent()){
node = existingNodes[i];
node.set('indent', this.indent+1);
break;
}
}
}
if(!node){
node = this.tree._createTreeNode({
item: item,
tree: tree,
isExpandable: model.mayHaveChildren(item),
label: tree.getLabel(item),
tooltip: tree.getTooltip(item),
ownerDocument: tree.ownerDocument,
dir: tree.dir,
lang: tree.lang,
textDir: tree.textDir,
indent: this.indent + 1
});
if(existingNodes){
existingNodes.push(node);
}else{
tree._itemNodesMap[id] = [node];
}
}
this.addChild(node);
// If node was previously opened then open it again now (this may trigger
// more data store accesses, recursively)
if(this.tree.autoExpand || this.tree._state(node)){
defs.push(tree._expandNode(node));
}
}, this);
// note that updateLayout() needs to be called on each child after
// _all_ the children exist
array.forEach(this.getChildren(), function(child){
child._updateLayout();
});
}else{
this.isExpandable=false;
}
if(this._setExpando){
// change expando to/from dot or + icon, as appropriate
this._setExpando(false);
}
// Set leaf icon or folder icon, as appropriate
this._updateItemClasses(this.item);
// On initial tree show, make the selected TreeNode as either the root node of the tree,
// or the first child, if the root node is hidden
if(this == tree.rootNode){
var fc = this.tree.showRoot ? this : this.getChildren()[0];
if(fc){
fc.setFocusable(true);
tree.lastFocused = fc;
}else{
// fallback: no nodes in tree so focus on Tree <div> itself
tree.domNode.setAttribute("tabIndex", "0");
}
}
var def = new DeferredList(defs);
this.tree._startPaint(def); // to reset TreeNode widths after an item is added/removed from the Tree
return def; // dojo/_base/Deferred
},
getTreePath: function(){
var node = this;
var path = [];
while(node && node !== this.tree.rootNode){
path.unshift(node.item);
node = node.getParent();
}
path.unshift(this.tree.rootNode.item);
return path;
},
getIdentity: function(){
return this.tree.model.getIdentity(this.item);
},
removeChild: function(/* treeNode */ node){
this.inherited(arguments);
var children = this.getChildren();
if(children.length == 0){
this.isExpandable = false;
this.collapse();
}
array.forEach(children, function(child){
child._updateLayout();
});
},
makeExpandable: function(){
// summary:
// if this node wasn't already showing the expando node,
// turn it into one and call _setExpando()
// TODO: hmm this isn't called from anywhere, maybe should remove it for 2.0
this.isExpandable = true;
this._setExpando(false);
},
setSelected: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) currently selected node.
// Mark that this node is/isn't that currently selected node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("aria-selected", selected ? "true" : "false");
domClass.toggle(this.rowNode, "dijitTreeRowSelected", selected);
},
setFocusable: function(/*Boolean*/ selected){
// summary:
// A Tree has a (single) node that's focusable.
// Mark that this node is/isn't that currently focsuable node.
// description:
// In particular, setting a node as selected involves setting tabIndex
// so that when user tabs to the tree, focus will go to that node (only).
this.labelNode.setAttribute("tabIndex", selected ? "0" : "-1");
},
_setTextDirAttr: function(textDir){
if(textDir &&((this.textDir != textDir) || !this._created)){
this._set("textDir", textDir);
this.applyTextDir(this.labelNode, this.labelNode.innerText || this.labelNode.textContent || "");
array.forEach(this.getChildren(), function(childNode){
childNode.set("textDir", textDir);
}, this);
}
}
});
var Tree = declare("dijit.Tree", [_Widget, _TemplatedMixin], {
// summary:
// This widget displays hierarchical data from a store.
// store: [deprecated] String|dojo/data/Store
// Deprecated. Use "model" parameter instead.
// The store to get data to display in the tree.
store: null,
// model: dijit/tree/model
// Interface to read tree data, get notifications of changes to tree data,
// and for handling drop operations (i.e drag and drop onto the tree)
model: null,
// query: [deprecated] anything
// Deprecated. User should specify query to the model directly instead.
// Specifies datastore query to return the root item or top items for the tree.
query: null,
// label: [deprecated] String
// Deprecated. Use dijit/tree/ForestStoreModel directly instead.
// Used in conjunction with query parameter.
// If a query is specified (rather than a root node id), and a label is also specified,
// then a fake root node is created and displayed, with this label.
label: "",
// showRoot: [const] Boolean
// Should the root node be displayed, or hidden?
showRoot: true,
// childrenAttr: [deprecated] String[]
// Deprecated. This information should be specified in the model.
// One ore more attributes that holds children of a tree node
childrenAttr: ["children"],
// paths: String[][] or Item[][]
// Full paths from rootNode to selected nodes expressed as array of items or array of ids.
// Since setting the paths may be asynchronous (because of waiting on dojo.data), set("paths", ...)
// returns a Deferred to indicate when the set is complete.
paths: [],
// path: String[] or Item[]
// Backward compatible singular variant of paths.
path: [],
// selectedItems: [readonly] Item[]
// The currently selected items in this tree.
// This property can only be set (via set('selectedItems', ...)) when that item is already
// visible in the tree. (I.e. the tree has already been expanded to show that node.)
// Should generally use `paths` attribute to set the selected items instead.
selectedItems: null,
// selectedItem: [readonly] Item
// Backward compatible singular variant of selectedItems.
selectedItem: null,
// openOnClick: Boolean
// If true, clicking a folder node's label will open it, rather than calling onClick()
openOnClick: false,
// openOnDblClick: Boolean
// If true, double-clicking a folder node's label will open it, rather than calling onDblClick()
openOnDblClick: false,
templateString: treeTemplate,
// persist: Boolean
// Enables/disables use of cookies for state saving.
persist: true,
// autoExpand: Boolean
// Fully expand the tree on load. Overrides `persist`.
autoExpand: false,
// dndController: [protected] Function|String
// Class to use as as the dnd controller. Specifying this class enables DnD.
// Generally you should specify this as dijit/tree/dndSource.
// Setting of dijit/tree/_dndSelector handles selection only (no actual DnD).
dndController: _dndSelector,
// parameters to pull off of the tree and pass on to the dndController as its params
dndParams: ["onDndDrop","itemCreator","onDndCancel","checkAcceptance", "checkItemAcceptance", "dragThreshold", "betweenThreshold"],
//declare the above items so they can be pulled from the tree's markup
// onDndDrop: [protected] Function
// Parameter to dndController, see `dijit/tree/dndSource.onDndDrop()`.
// Generally this doesn't need to be set.
onDndDrop: null,
itemCreator: null,
/*=====
itemCreator: function(nodes, target, source){
// summary:
// Returns objects passed to `Tree.model.newItem()` based on DnD nodes
// dropped onto the tree. Developer must override this method to enable
// dropping from external sources onto this Tree, unless the Tree.model's items
// happen to look like {id: 123, name: "Apple" } with no other attributes.
//
// For each node in nodes[], which came from source, create a hash of name/value
// pairs to be passed to Tree.model.newItem(). Returns array of those hashes.
// nodes: DomNode[]
// The DOMNodes dragged from the source container
// target: DomNode
// The target TreeNode.rowNode
// source: dojo/dnd/Source
// The source container the nodes were dragged from, perhaps another Tree or a plain dojo/dnd/Source
// returns: Object[]
// Array of name/value hashes for each new item to be added to the Tree, like:
// | [
// | { id: 123, label: "apple", foo: "bar" },
// | { id: 456, label: "pear", zaz: "bam" }
// | ]
// tags:
// extension
return [{}];
},
=====*/
// onDndCancel: [protected] Function
// Parameter to dndController, see `dijit/tree/dndSource.onDndCancel()`.
// Generally this doesn't need to be set.
onDndCancel: null,
/*=====
checkAcceptance: function(source, nodes){
// summary:
// Checks if the Tree itself can accept nodes from this source
// source: dijit/tree/dndSource
// The source which provides items
// nodes: DOMNode[]
// Array of DOM nodes corresponding to nodes being dropped, dijitTreeRow nodes if
// source is a dijit/Tree.
// tags:
// extension
return true; // Boolean
},
=====*/
checkAcceptance: null,
/*=====
checkItemAcceptance: function(target, source, position){
// summary:
// Stub function to be overridden if one wants to check for the ability to drop at the node/item level
// description:
// In the base case, this is called to check if target can become a child of source.
// When betweenThreshold is set, position="before" or "after" means that we
// are asking if the source node can be dropped before/after the target node.
// target: DOMNode
// The dijitTreeRoot DOM node inside of the TreeNode that we are dropping on to
// Use registry.getEnclosingWidget(target) to get the TreeNode.
// source: dijit/tree/dndSource
// The (set of) nodes we are dropping
// position: String
// "over", "before", or "after"
// tags:
// extension
return true; // Boolean
},
=====*/
checkItemAcceptance: null,
// dragThreshold: Integer
// Number of pixels mouse moves before it's considered the start of a drag operation
dragThreshold: 5,
// betweenThreshold: Integer
// Set to a positive value to allow drag and drop "between" nodes.
//
// If during DnD mouse is over a (target) node but less than betweenThreshold
// pixels from the bottom edge, dropping the the dragged node will make it
// the next sibling of the target node, rather than the child.
//
// Similarly, if mouse is over a target node but less that betweenThreshold
// pixels from the top edge, dropping the dragged node will make it
// the target node's previous sibling rather than the target node's child.
betweenThreshold: 0,
// _nodePixelIndent: Integer
// Number of pixels to indent tree nodes (relative to parent node).
// Default is 19 but can be overridden by setting CSS class dijitTreeIndent
// and calling resize() or startup() on tree after it's in the DOM.
_nodePixelIndent: 19,
_publish: function(/*String*/ topicName, /*Object*/ message){
// summary:
// Publish a message for this widget/topic
topic.publish(this.id, lang.mixin({tree: this, event: topicName}, message || {})); // publish
},
postMixInProperties: function(){
this.tree = this;
if(this.autoExpand){
// There's little point in saving opened/closed state of nodes for a Tree
// that initially opens all it's nodes.
this.persist = false;
}
this._itemNodesMap = {};
if(!this.cookieName && this.id){
this.cookieName = this.id + "SaveStateCookie";
}
// Deferred that fires when all the children have loaded.
this.expandChildrenDeferred = new Deferred();
// Deferred that fires when all pending operations complete.
this.pendingCommandsDeferred = this.expandChildrenDeferred;
this.inherited(arguments);
},
postCreate: function(){
this._initState();
// Catch events on TreeNodes
var self = this;
this.own(
on(this.domNode, on.selector(".dijitTreeNode", touch.enter), function(evt){
self._onNodeMouseEnter(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", touch.leave), function(evt){
self._onNodeMouseLeave(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "click"), function(evt){
self._onClick(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "dblclick"), function(evt){
self._onDblClick(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "keypress"), function(evt){
self._onKeyPress(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeNode", "keydown"), function(evt){
self._onKeyDown(registry.byNode(this), evt);
}),
on(this.domNode, on.selector(".dijitTreeRow", "focusin"), function(evt){
self._onNodeFocus(registry.getEnclosingWidget(this), evt);
})
);
// Create glue between store and Tree, if not specified directly by user
if(!this.model){
this._store2model();
}
// monitor changes to items
this.connect(this.model, "onChange", "_onItemChange");
this.connect(this.model, "onChildrenChange", "_onItemChildrenChange");
this.connect(this.model, "onDelete", "_onItemDelete");
this.inherited(arguments);
if(this.dndController){
if(lang.isString(this.dndController)){
this.dndController = lang.getObject(this.dndController);
}
var params={};
for(var i=0; i<this.dndParams.length;i++){
if(this[this.dndParams[i]]){
params[this.dndParams[i]] = this[this.dndParams[i]];
}
}
this.dndController = new this.dndController(this, params);
}
this._load();
// If no path was specified to the constructor, use path saved in cookie
if(!this.params.path && !this.params.paths && this.persist){
this.set("paths", this.dndController._getSavedPaths());
}
// onLoadDeferred should fire when all commands that are part of initialization have completed.
// It will include all the set("paths", ...) commands that happen during initialization.
this.onLoadDeferred = this.pendingCommandsDeferred;
this.onLoadDeferred.then(lang.hitch(this, "onLoad"));
},
_store2model: function(){
// summary:
// User specified a store&query rather than model, so create model from store/query
this._v10Compat = true;
kernel.deprecated("Tree: from version 2.0, should specify a model object rather than a store/query");
var modelParams = {
id: this.id + "_ForestStoreModel",
store: this.store,
query: this.query,
childrenAttrs: this.childrenAttr
};
// Only override the model's mayHaveChildren() method if the user has specified an override
if(this.params.mayHaveChildren){
modelParams.mayHaveChildren = lang.hitch(this, "mayHaveChildren");
}
if(this.params.getItemChildren){
modelParams.getChildren = lang.hitch(this, function(item, onComplete, onError){
this.getItemChildren((this._v10Compat && item === this.model.root) ? null : item, onComplete, onError);
});
}
this.model = new ForestStoreModel(modelParams);
// For backwards compatibility, the visibility of the root node is controlled by
// whether or not the user has specified a label
this.showRoot = Boolean(this.label);
},
onLoad: function(){
// summary:
// Called when tree finishes loading and expanding.
// description:
// If persist == true the loading may encompass many levels of fetches
// from the data store, each asynchronous. Waits for all to finish.
// tags:
// callback
},
_load: function(){
// summary:
// Initial load of the tree.
// Load root node (possibly hidden) and it's children.
this.model.getRoot(
lang.hitch(this, function(item){
var rn = (this.rootNode = this.tree._createTreeNode({
item: item,
tree: this,
isExpandable: true,
label: this.label || this.getLabel(item),
textDir: this.textDir,
indent: this.showRoot ? 0 : -1
}));
if(!this.showRoot){
rn.rowNode.style.display="none";
// if root is not visible, move tree role to the invisible
// root node's containerNode, see #12135
this.domNode.setAttribute("role", "presentation");
this.domNode.removeAttribute("aria-expanded");
this.domNode.removeAttribute("aria-multiselectable");
rn.labelNode.setAttribute("role", "presentation");
rn.containerNode.setAttribute("role", "tree");
rn.containerNode.setAttribute("aria-expanded","true");
rn.containerNode.setAttribute("aria-multiselectable", !this.dndController.singular);
}else{
this.domNode.setAttribute("aria-multiselectable", !this.dndController.singular);
}
this.domNode.appendChild(rn.domNode);
var identity = this.model.getIdentity(item);
if(this._itemNodesMap[identity]){
this._itemNodesMap[identity].push(rn);
}else{
this._itemNodesMap[identity] = [rn];
}
rn._updateLayout(); // sets "dijitTreeIsRoot" CSS classname
// Load top level children, and if persist==true, all nodes that were previously opened
this._expandNode(rn).then(lang.hitch(this, function(){
// Then, select the nodes that were selected last time, or
// the ones specified by params.paths[].
this.expandChildrenDeferred.resolve(true);
}));
}),
lang.hitch(this, function(err){
console.error(this, ": error loading root: ", err);
})
);
},
getNodesByItem: function(/*Item or id*/ item){
// summary:
// Returns all tree nodes that refer to an item
// returns:
// Array of tree nodes that refer to passed item
if(!item){ return []; }
var identity = lang.isString(item) ? item : this.model.getIdentity(item);
// return a copy so widget don't get messed up by changes to returned array
return [].concat(this._itemNodesMap[identity]);
},
_setSelectedItemAttr: function(/*Item or id*/ item){
this.set('selectedItems', [item]);
},
_setSelectedItemsAttr: function(/*Items or ids*/ items){
// summary:
// Select tree nodes related to passed items.
// WARNING: if model use multi-parented items or desired tree node isn't already loaded
// behavior is undefined. Use set('paths', ...) instead.
var tree = this;
return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then( lang.hitch(this, function(){
var identities = array.map(items, function(item){
return (!item || lang.isString(item)) ? item : tree.model.getIdentity(item);
});
var nodes = [];
array.forEach(identities, function(id){
nodes = nodes.concat(tree._itemNodesMap[id] || []);
});
this.set('selectedNodes', nodes);
}));
},
_setPathAttr: function(/*Item[]|String[]*/ path){
// summary:
// Singular variant of _setPathsAttr
if(path.length){
return this.set("paths", [path]);
}else{
// Empty list is interpreted as "select nothing"
return this.set("paths", []);
}
},
_setPathsAttr: function(/*Item[][]|String[][]*/ paths){
// summary:
// Select the tree nodes identified by passed paths.
// paths:
// Array of arrays of items or item id's
// returns:
// Deferred to indicate when the set is complete
var tree = this;
// Let any previous set("path", ...) commands complete before this one starts.
return this.pendingCommandsDeferred = this.pendingCommandsDeferred.then(function(){
// We may need to wait for some nodes to expand, so setting
// each path will involve a Deferred. We bring those deferreds
// together with a DeferredList.
return new DeferredList(array.map(paths, function(path){
var d = new Deferred();
// normalize path to use identity
path = array.map(path, function(item){
return lang.isString(item) ? item : tree.model.getIdentity(item);
});
if(path.length){
// Wait for the tree to load, if it hasn't already.
selectPath(path, [tree.rootNode], d);
}else{
d.reject(new Tree.PathError("Empty path"));
}
return d;
}));
}).then(setNodes);
function selectPath(path, nodes, def){
// Traverse path; the next path component should be among "nodes".
var nextPath = path.shift();
var nextNode = array.filter(nodes, function(node){
return node.getIdentity() == nextPath;
})[0];
if(!!nextNode){
if(path.length){
tree._expandNode(nextNode).then(function(){ selectPath(path, nextNode.getChildren(), def); });
}else{
// Successfully reached the end of this path
def.resolve(nextNode);
}
}else{
def.reject(new Tree.PathError("Could not expand path at " + nextPath));
}
}
function setNodes(newNodes){
// After all expansion is finished, set the selection to
// the set of nodes successfully found.
tree.set("selectedNodes", array.map(
array.filter(newNodes,function(x){return x[0];}),
function(x){return x[1];}));
}
},
_setSelectedNodeAttr: function(node){
this.set('selectedNodes', [node]);
},
_setSelectedNodesAttr: function(nodes){
// summary:
// Marks the specified TreeNodes as selected.
// nodes: TreeNode[]
// TreeNodes to mark.
this.dndController.setSelection(nodes);
},
expandAll: function(){
// summary:
// Expand all nodes in the tree
// returns:
// Deferred that fires when all nodes have expanded
var _this = this;
function expand(node){
var def = new dojo.Deferred();
// Expand the node
_this._expandNode(node).then(function(){
// When node has expanded, call expand() recursively on each non-leaf child
var childBranches = array.filter(node.getChildren() || [], function(node){
return node.isExpandable;
}),
defs = array.map(childBranches, expand);
// And when all those recursive calls finish, signal that I'm finished
new dojo.DeferredList(defs).then(function(){
def.resolve(true);
});
});
return def;
}
return expand(this.rootNode);
},
collapseAll: function(){
// summary:
// Collapse all nodes in the tree
// returns:
// Deferred that fires when all nodes have collapsed
var _this = this;
function collapse(node){
var def = new dojo.Deferred();
def.label = "collapseAllDeferred";
// Collapse children first
var childBranches = array.filter(node.getChildren() || [], function(node){
return node.isExpandable;
}),
defs = array.map(childBranches, collapse);
// And when all those recursive calls finish, collapse myself, unless I'm the invisible root node,
// in which case collapseAll() is finished
new dojo.DeferredList(defs).then(function(){
if(!node.isExpanded || (node == _this.rootNode && !_this.showRoot)){
def.resolve(true);
}else{
_this._collapseNode(node).then(function(){
// When node has collapsed, signal that call is finished
def.resolve(true);
});
}
});
return def;
}
return collapse(this.rootNode);
},
////////////// Data store related functions //////////////////////
// These just get passed to the model; they are here for back-compat
mayHaveChildren: function(/*dojo/data/Item*/ /*===== item =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
// Overridable function to tell if an item has or may have children.
// Controls whether or not +/- expando icon is shown.
// (For efficiency reasons we may not want to check if an element actually
// has children until user clicks the expando node)
// tags:
// deprecated
},
getItemChildren: function(/*===== parentItem, onComplete =====*/){
// summary:
// Deprecated. This should be specified on the model itself.
//
// Overridable function that return array of child items of given parent item,
// or if parentItem==null then return top items in tree
// tags:
// deprecated
},
///////////////////////////////////////////////////////
// Functions for converting an item to a TreeNode
getLabel: function(/*dojo/data/Item*/ item){
// summary:
// Overridable function to get the label for a tree node (given the item)
// tags:
// extension
return this.model.getLabel(item); // String
},
getIconClass: function(/*dojo/data/Item*/ item, /*Boolean*/ opened){
// summary:
// Overridable function to return CSS class name to display icon
// tags:
// extension
return (!item || this.model.mayHaveChildren(item)) ? (opened ? "dijitFolderOpened" : "dijitFolderClosed") : "dijitLeaf"
},
getLabelClass: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS class name to display label
// item: dojo/data/Item
// opened: Boolean
// returns: String
// CSS class name
// tags:
// extension
},
getRowClass: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS class name to display row
// item: dojo/data/Item
// opened: Boolean
// returns: String
// CSS class name
// tags:
// extension
},
getIconStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display icon
// item: dojo/data/Item
// opened: Boolean
// returns: Object
// Object suitable for input to dojo.style() like {backgroundImage: "url(...)"}
// tags:
// extension
},
getLabelStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display label
// item: dojo/data/Item
// opened: Boolean
// returns:
// Object suitable for input to dojo.style() like {color: "red", background: "green"}
// tags:
// extension
},
getRowStyle: function(/*===== item, opened =====*/){
// summary:
// Overridable function to return CSS styles to display row
// item: dojo/data/Item
// opened: Boolean
// returns:
// Object suitable for input to dojo.style() like {background-color: "#bbb"}
// tags:
// extension
},
getTooltip: function(/*dojo/data/Item*/ /*===== item =====*/){
// summary:
// Overridable function to get the tooltip for a tree node (given the item)
// tags:
// extension
return ""; // String
},
/////////// Keyboard and Mouse handlers ////////////////////
_onKeyPress: function(/*TreeNode*/ treeNode, /*Event*/ e){
// summary:
// Handles keystrokes for printable keys, doing search navigation
if(e.charCode <= 32){
// Avoid duplicate events on firefox (this is an arrow key that will be handled by keydown handler)
return;
}
if(!e.altKey && !e.ctrlKey && !e.shiftKey && !e.metaKey){
var c = String.fromCharCode(e.charCode);
this._onLetterKeyNav( { node: treeNode, key: c.toLowerCase() } );
event.stop(e);
}
},
_onKeyDown: function(/*TreeNode*/ treeNode, /*Event*/ e){
// summary:
// Handles arrow, space, and enter keys
var key = e.keyCode;
var map = this._keyHandlerMap;
if(!map){
// Setup table mapping keys to events.
// On WebKit based browsers, the combination ctrl-enter does not get passed through. To allow accessible
// multi-select on those browsers, the space key is also used for selection.
// Therefore, also allow space key for keyboard "click" operation.
map = {};
map[keys.ENTER] = map[keys.SPACE] = map[" "] = "_onEnterKey";
map[this.isLeftToRight() ? keys.LEFT_ARROW : keys.RIGHT_ARROW] = "_onLeftArrow";
map[this.isLeftToRight() ? keys.RIGHT_ARROW : keys.LEFT_ARROW] = "_onRightArrow";
map[keys.UP_ARROW] = "_onUpArrow";
map[keys.DOWN_ARROW] = "_onDownArrow";
map[keys.HOME] = "_onHomeKey";
map[keys.END] = "_onEndKey";
this._keyHandlerMap = map;
}
if(this._keyHandlerMap[key]){
// clear record of recent printables (being saved for multi-char letter navigation),
// because "a", down-arrow, "b" shouldn't search for "ab"
if(this._curSearch){
this._curSearch.timer.remove();
delete this._curSearch;
}
this[this._keyHandlerMap[key]]( { node: treeNode, item: treeNode.item, evt: e } );
event.stop(e);
}
},
_onEnterKey: function(/*Object*/ message){
this._publish("execute", { item: message.item, node: message.node } );
this.dndController.userSelect(message.node, connect.isCopyKey( message.evt ), message.evt.shiftKey);
this.onClick(message.item, message.node, message.evt);
},
_onDownArrow: function(/*Object*/ message){
// summary:
// down arrow pressed; get next visible node, set focus there
var node = this._getNextNode(message.node);
if(node && node.isTreeNode){
this.focusNode(node);
}
},
_onUpArrow: function(/*Object*/ message){
// summary:
// Up arrow pressed; move to previous visible node
var node = message.node;
// if younger siblings
var previousSibling = node.getPreviousSibling();
if(previousSibling){
node = previousSibling;
// if the previous node is expanded, dive in deep
while(node.isExpandable && node.isExpanded && node.hasChildren()){
// move to the last child
var children = node.getChildren();
node = children[children.length-1];
}
}else{
// if this is the first child, return the parent
// unless the parent is the root of a tree with a hidden root
var parent = node.getParent();
if(!(!this.showRoot && parent === this.rootNode)){
node = parent;
}
}
if(node && node.isTreeNode){
this.focusNode(node);
}
},
_onRightArrow: function(/*Object*/ message){
// summary:
// Right arrow pressed; go to child node
var node = message.node;
// if not expanded, expand, else move to 1st child
if(node.isExpandable && !node.isExpanded){
this._expandNode(node);
}else if(node.hasChildren()){
node = node.getChildren()[0];
if(node && node.isTreeNode){
this.focusNode(node);
}
}
},
_onLeftArrow: function(/*Object*/ message){
// summary:
// Left arrow pressed.
// If not collapsed, collapse, else move to parent.
var node = message.node;
if(node.isExpandable && node.isExpanded){
this._collapseNode(node);
}else{
var parent = node.getParent();
if(parent && parent.isTreeNode && !(!this.showRoot && parent === this.rootNode)){
this.focusNode(parent);
}
}
},
_onHomeKey: function(){
// summary:
// Home key pressed; get first visible node, and set focus there
var node = this._getRootOrFirstNode();
if(node){
this.focusNode(node);
}
},
_onEndKey: function(){
// summary:
// End key pressed; go to last visible node.
var node = this.rootNode;
while(node.isExpanded){
var c = node.getChildren();
node = c[c.length - 1];
}
if(node && node.isTreeNode){
this.focusNode(node);
}
},
// multiCharSearchDuration: Number
// If multiple characters are typed where each keystroke happens within
// multiCharSearchDuration of the previous keystroke,
// search for nodes matching all the keystrokes.
//
// For example, typing "ab" will search for entries starting with
// "ab" unless the delay between "a" and "b" is greater than multiCharSearchDuration.
multiCharSearchDuration: 250,
_onLetterKeyNav: function(message){
// summary:
// Called when user presses a prinatable key; search for node starting with recently typed letters.
// message: Object
// Like { node: TreeNode, key: 'a' } where key is the key the user pressed.
// Branch depending on whether this key starts a new search, or modifies an existing search
var cs = this._curSearch;
if(cs){
// We are continuing a search. Ex: user has pressed 'a', and now has pressed
// 'b', so we want to search for nodes starting w/"ab".
cs.pattern = cs.pattern + message.key;
cs.timer.remove();
}else{
// We are starting a new search
cs = this._curSearch = {
pattern: message.key,
startNode: message.node
};
}
// set/reset timer to forget recent keystrokes
cs.timer = this.defer(function(){
delete this._curSearch;
}, this.multiCharSearchDuration);
// Navigate to TreeNode matching keystrokes [entered so far].
var node = cs.startNode;
do{
node = this._getNextNode(node);
//check for last node, jump to first node if necessary
if(!node){
node = this._getRootOrFirstNode();
}
}while(node !== cs.startNode && (node.label.toLowerCase().substr(0, cs.pattern.length) != cs.pattern));
if(node && node.isTreeNode){
// no need to set focus if back where we started
if(node !== cs.startNode){
this.focusNode(node);
}
}
},
isExpandoNode: function(node, widget){
// summary:
// check whether a dom node is the expandoNode for a particular TreeNode widget
return dom.isDescendant(node, widget.expandoNode) || dom.isDescendant(node, widget.expandoNodeText);
},
_onClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
// Translates click events into commands for the controller to process
var domElement = e.target,
isExpandoClick = this.isExpandoNode(domElement, nodeWidget);
if( (this.openOnClick && nodeWidget.isExpandable) || isExpandoClick ){
// expando node was clicked, or label of a folder node was clicked; open it
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
}else{
this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
this.onClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
event.stop(e);
},
_onDblClick: function(/*TreeNode*/ nodeWidget, /*Event*/ e){
// summary:
// Translates double-click events into commands for the controller to process
var domElement = e.target,
isExpandoClick = (domElement == nodeWidget.expandoNode || domElement == nodeWidget.expandoNodeText);
if( (this.openOnDblClick && nodeWidget.isExpandable) ||isExpandoClick ){
// expando node was clicked, or label of a folder node was clicked; open it
if(nodeWidget.isExpandable){
this._onExpandoClick({node:nodeWidget});
}
}else{
this._publish("execute", { item: nodeWidget.item, node: nodeWidget, evt: e } );
this.onDblClick(nodeWidget.item, nodeWidget, e);
this.focusNode(nodeWidget);
}
event.stop(e);
},
_onExpandoClick: function(/*Object*/ message){
// summary:
// User clicked the +/- icon; expand or collapse my children.
var node = message.node;
// If we are collapsing, we might be hiding the currently focused node.
// Also, clicking the expando node might have erased focus from the current node.
// For simplicity's sake just focus on the node with the expando.
this.focusNode(node);
if(node.isExpanded){
this._collapseNode(node);
}else{
this._expandNode(node);
}
},
onClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is clicked
// item: Object
// Object from the dojo/store corresponding to this TreeNode
// node: TreeNode
// The TreeNode itself
// evt: Event
// The event
// tags:
// callback
},
onDblClick: function(/*===== item, node, evt =====*/){
// summary:
// Callback when a tree node is double-clicked
// item: Object
// Object from the dojo/store corresponding to this TreeNode
// node: TreeNode
// The TreeNode itself
// evt: Event
// The event
// tags:
// callback
},
onOpen: function(/*===== item, node =====*/){
// summary:
// Callback when a node is opened
// item: dojo/data/Item
// node: TreeNode
// tags:
// callback
},
onClose: function(/*===== item, node =====*/){
// summary:
// Callback when a node is closed
// item: Object
// Object from the dojo/store corresponding to this TreeNode
// node: TreeNode
// The TreeNode itself
// tags:
// callback
},
_getNextNode: function(node){
// summary:
// Get next visible node
if(node.isExpandable && node.isExpanded && node.hasChildren()){
// if this is an expanded node, get the first child
return node.getChildren()[0]; // TreeNode
}else{
// find a parent node with a sibling
while(node && node.isTreeNode){
var returnNode = node.getNextSibling();
if(returnNode){
return returnNode; // TreeNode
}
node = node.getParent();
}
return null;
}
},
_getRootOrFirstNode: function(){
// summary:
// Get first visible node
return this.showRoot ? this.rootNode : this.rootNode.getChildren()[0];
},
_collapseNode: function(/*TreeNode*/ node){
// summary:
// Called when the user has requested to collapse the node
// returns:
// Deferred that fires when the node is closed
if(node._expandNodeDeferred){
delete node._expandNodeDeferred;
}
if(node.state == "LOADING"){
// ignore clicks while we are in the process of loading data
return;
}
if(node.isExpanded){
var ret = node.collapse();
this.onClose(node.item, node);
this._state(node, false);
this._startPaint(ret); // after this finishes, need to reset widths of TreeNodes
return ret;
}
},
_expandNode: function(/*TreeNode*/ node){
// summary:
// Called when the user has requested to expand the node
// returns:
// Deferred that fires when the node is loaded and opened and (if persist=true) all it's descendants
// that were previously opened too
// Signal that this call is complete
var def = new Deferred();
if(node._expandNodeDeferred){
// there's already an expand in progress, or completed, so just return
return node._expandNodeDeferred; // dojo/_base/Deferred
}
var model = this.model,
item = node.item,
_this = this;
// Load data if it's not already loaded
if(!node._loadDeferred){
// need to load all the children before expanding
node.markProcessing();
// Setup deferred to signal when the load and expand are finished.
// Save that deferred in this._expandDeferred as a flag that operation is in progress.
node._loadDeferred = new Deferred();
// Get the children
model.getChildren(
item,
function(items){
node.unmarkProcessing();
// Display the children and also start expanding any children that were previously expanded
// (if this.persist == true). The returned Deferred will fire when those expansions finish.
node.setChildItems(items).then(function(){
node._loadDeferred.resolve(items);
});
},
function(err){
console.error(_this, ": error loading " + node.label + " children: ", err);
node._loadDeferred.reject(err);
}
);
}
// Expand the node after data has loaded
node._loadDeferred.then(lang.hitch(this, function(){
node.expand().then(function(){
def.resolve(true); // signal that this _expandNode() call is complete
});
// seems like these should be inside of then(), but left here for back-compat about
// when this.isOpen flag gets set (ie, at the beginning of the animation)
this.onOpen(node.item, node);
this._state(node, true);
}));
this._startPaint(def); // after this finishes, need to reset widths of TreeNodes
return def; // dojo/_base/Deferred
},
////////////////// Miscellaneous functions ////////////////
focusNode: function(/* _tree.Node */ node){
// summary:
// Focus on the specified node (which must be visible)
// tags:
// protected
// set focus so that the label will be voiced using screen readers
focus.focus(node.labelNode);
},
_onNodeFocus: function(/*dijit/_WidgetBase*/ node){
// summary:
// Called when a TreeNode gets focus, either by user clicking
// it, or programatically by arrow key handling code.
// description:
// It marks that the current node is the selected one, and the previously
// selected node no longer is.
if(node && node != this.lastFocused){
if(this.lastFocused && !this.lastFocused._destroyed){
// mark that the previously focsable node is no longer focusable
this.lastFocused.setFocusable(false);
}
// mark that the new node is the currently selected one
node.setFocusable(true);
this.lastFocused = node;
}
},
_onNodeMouseEnter: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
// summary:
// Called when mouse is over a node (onmouseenter event),
// this is monitored by the DND code
},
_onNodeMouseLeave: function(/*dijit/_WidgetBase*/ /*===== node =====*/){
// summary:
// Called when mouse leaves a node (onmouseleave event),
// this is monitored by the DND code
},
//////////////// Events from the model //////////////////////////
_onItemChange: function(/*Item*/ item){
// summary:
// Processes notification of a change to an item's scalar values like label
var model = this.model,
identity = model.getIdentity(item),
nodes = this._itemNodesMap[identity];
if(nodes){
var label = this.getLabel(item),
tooltip = this.getTooltip(item);
array.forEach(nodes, function(node){
node.set({
item: item, // theoretically could be new JS Object representing same item
label: label,
tooltip: tooltip
});
node._updateItemClasses(item);
});
}
},
_onItemChildrenChange: function(/*dojo/data/Item*/ parent, /*dojo/data/Item[]*/ newChildrenList){
// summary:
// Processes notification of a change to an item's children
var model = this.model,
identity = model.getIdentity(parent),
parentNodes = this._itemNodesMap[identity];
if(parentNodes){
array.forEach(parentNodes,function(parentNode){
parentNode.setChildItems(newChildrenList);
});
}
},
_onItemDelete: function(/*Item*/ item){
// summary:
// Processes notification of a deletion of an item.
// Not called from new dojo.store interface but there's cleanup code in setChildItems() instead.
var model = this.model,
identity = model.getIdentity(item),
nodes = this._itemNodesMap[identity];
if(nodes){
array.forEach(nodes,function(node){
// Remove node from set of selected nodes (if it's selected)
this.dndController.removeTreeNode(node);
var parent = node.getParent();
if(parent){
// if node has not already been orphaned from a _onSetItem(parent, "children", ..) call...
parent.removeChild(node);
}
node.destroyRecursive();
}, this);
delete this._itemNodesMap[identity];
}
},
/////////////// Miscellaneous funcs
_initState: function(){
// summary:
// Load in which nodes should be opened automatically
this._openedNodes = {};
if(this.persist && this.cookieName){
var oreo = cookie(this.cookieName);
if(oreo){
array.forEach(oreo.split(','), function(item){
this._openedNodes[item] = true;
}, this);
}
}
},
_state: function(node, expanded){
// summary:
// Query or set expanded state for an node
if(!this.persist){
return false;
}
var path = array.map(node.getTreePath(), function(item){
return this.model.getIdentity(item);
}, this).join("/");
if(arguments.length === 1){
return this._openedNodes[path];
}else{
if(expanded){
this._openedNodes[path] = true;
}else{
delete this._openedNodes[path];
}
if(this.persist && this.cookieName){
var ary = [];
for(var id in this._openedNodes){
ary.push(id);
}
cookie(this.cookieName, ary.join(","), {expires:365});
}
}
},
destroy: function(){
if(this._curSearch){
this._curSearch.timer.remove();
delete this._curSearch;
}
if(this.rootNode){
this.rootNode.destroyRecursive();
}
if(this.dndController && !lang.isString(this.dndController)){
this.dndController.destroy();
}
this.rootNode = null;
this.inherited(arguments);
},
destroyRecursive: function(){
// A tree is treated as a leaf, not as a node with children (like a grid),
// but defining destroyRecursive for back-compat.
this.destroy();
},
resize: function(changeSize){
if(changeSize){
domGeometry.setMarginBox(this.domNode, changeSize);
}
// The main JS sizing involved w/tree is the indentation, which is specified
// in CSS and read in through this dummy indentDetector node (tree must be
// visible and attached to the DOM to read this).
// If the Tree is hidden domGeometry.position(this.tree.indentDetector).w will return 0, in which case just
// keep the default value.
this._nodePixelIndent = domGeometry.position(this.tree.indentDetector).w || this._nodePixelIndent;
// resize() may be called before this.rootNode is created, so wait until it's available
this.expandChildrenDeferred.then(lang.hitch(this, function(){
// If tree has already loaded, then reset indent for all the nodes
this.rootNode.set('indent', this.showRoot ? 0 : -1);
// Also, adjust widths of all rows to match width of Tree
this._adjustWidths();
}));
},
_outstandingPaintOperations: 0,
_startPaint: function(/*Promise|Boolean*/ p){
// summary:
// Called at the start of an operation that will change what's displayed.
// p:
// Promise that tells when the operation will complete. Alternately, if it's just a Boolean, it signifies
// that the operation was synchronous, and already completed.
this._outstandingPaintOperations++;
if(this._adjustWidthsTimer){
this._adjustWidthsTimer.remove();
delete this._adjustWidthsTimer;
}
var oc = lang.hitch(this, function(){
this._outstandingPaintOperations--;
if(this._outstandingPaintOperations <= 0 && !this._adjustWidthsTimer && this._started){
// Use defer() to avoid a width adjustment when another operation will immediately follow,
// such as a sequence of opening a node, then it's children, then it's grandchildren, etc.
this._adjustWidthsTimer = this.defer("_adjustWidths");
}
});
when(p, oc, oc);
},
_adjustWidths: function(){
// summary:
// Get width of widest TreeNode, or the width of the Tree itself, whichever is greater,
// and then set all TreeNodes to that width, so that selection/hover highlighting
// extends to the edge of the Tree (#13141)
if(this._adjustWidthsTimer){
this._adjustWidthsTimer.remove();
delete this._adjustWidthsTimer;
}
var maxWidth = 0,
nodes = [];
function collect(/*TreeNode*/ parent){
var node = parent.rowNode;
node.style.width = "auto"; // erase setting from previous run
maxWidth = Math.max(maxWidth, node.clientWidth);
nodes.push(node);
if(parent.isExpanded){
array.forEach(parent.getChildren(), collect);
}
}
collect(this.rootNode);
maxWidth = Math.max(maxWidth, domGeometry.getContentBox(this.domNode).w); // do after node.style.width="auto"
array.forEach(nodes, function(node){
node.style.width = maxWidth + "px"; // assumes no horizontal padding, border, or margin on rowNode
});
},
_createTreeNode: function(/*Object*/ args){
// summary:
// creates a TreeNode
// description:
// Developers can override this method to define their own TreeNode class;
// However it will probably be removed in a future release in favor of a way
// of just specifying a widget for the label, rather than one that contains
// the children too.
return new TreeNode(args);
},
_setTextDirAttr: function(textDir){
if(textDir && this.textDir!= textDir){
this._set("textDir",textDir);
this.rootNode.set("textDir", textDir);
}
}
});
Tree.PathError = createError("TreePathError");
Tree._TreeNode = TreeNode; // for monkey patching or creating subclasses of TreeNode
return Tree;
});
},
'dijit/form/_FormValueWidget':function(){
define("dijit/form/_FormValueWidget", [
"dojo/_base/declare", // declare
"dojo/sniff", // has("ie")
"./_FormWidget",
"./_FormValueMixin"
], function(declare, has, _FormWidget, _FormValueMixin){
// module:
// dijit/form/_FormValueWidget
return declare("dijit.form._FormValueWidget", [_FormWidget, _FormValueMixin],
{
// 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.
_layoutHackIE7: function(){
// summary:
// Work around table sizing bugs on IE7 by forcing redraw
if(has("ie") == 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(){
_this.disconnect(disconnectHandle); // only call once
pingNode.style.filter = (new Date()).getMilliseconds(); // set to anything that's unique
_this.defer(function(){ pingNode.style.filter = origFilter; }); // restore custom filter, if any
}
);
})();
parent = parent.parentNode;
}
}
}
});
});
},
'*now':function(r){r(['dojo/i18n!*preload*dojo/nls/tt-rss-layer*["ar","ca","cs","da","de","el","en-gb","en-us","es-es","fi-fi","fr-fr","he-il","hu","it-it","ja-jp","ko-kr","nl-nl","nb","pl","pt-br","pt-pt","ru","sk","sl","sv","th","tr","zh-tw","zh-cn","ROOT"]']);}
}});
define("dojo/tt-rss-layer", [], 1);